9
9
package org .elasticsearch .action .get ;
10
10
11
11
import org .elasticsearch .action .ActionListener ;
12
+ import org .elasticsearch .action .ActionListenerResponseHandler ;
12
13
import org .elasticsearch .action .ActionRunnable ;
14
+ import org .elasticsearch .action .NoShardAvailableActionException ;
15
+ import org .elasticsearch .action .admin .indices .refresh .TransportShardRefreshAction ;
13
16
import org .elasticsearch .action .support .ActionFilters ;
17
+ import org .elasticsearch .action .support .replication .BasicReplicationRequest ;
14
18
import org .elasticsearch .action .support .single .shard .TransportSingleShardAction ;
19
+ import org .elasticsearch .client .internal .node .NodeClient ;
15
20
import org .elasticsearch .cluster .ClusterState ;
16
21
import org .elasticsearch .cluster .metadata .IndexNameExpressionResolver ;
22
+ import org .elasticsearch .cluster .node .DiscoveryNode ;
23
+ import org .elasticsearch .cluster .routing .PlainShardIterator ;
17
24
import org .elasticsearch .cluster .routing .ShardIterator ;
25
+ import org .elasticsearch .cluster .routing .ShardRouting ;
18
26
import org .elasticsearch .cluster .service .ClusterService ;
19
27
import org .elasticsearch .common .inject .Inject ;
20
28
import org .elasticsearch .common .io .stream .Writeable ;
21
29
import org .elasticsearch .index .IndexService ;
22
30
import org .elasticsearch .index .get .GetResult ;
23
31
import org .elasticsearch .index .shard .IndexShard ;
24
32
import org .elasticsearch .index .shard .ShardId ;
33
+ import org .elasticsearch .index .shard .ShardNotFoundException ;
25
34
import org .elasticsearch .indices .ExecutorSelector ;
26
35
import org .elasticsearch .indices .IndicesService ;
36
+ import org .elasticsearch .logging .LogManager ;
37
+ import org .elasticsearch .logging .Logger ;
27
38
import org .elasticsearch .threadpool .ThreadPool ;
28
39
import org .elasticsearch .transport .TransportService ;
29
40
34
45
*/
35
46
public class TransportGetAction extends TransportSingleShardAction <GetRequest , GetResponse > {
36
47
48
+ private static final Logger logger = LogManager .getLogger (TransportGetAction .class );
49
+
37
50
private final IndicesService indicesService ;
38
51
private final ExecutorSelector executorSelector ;
52
+ private final NodeClient client ;
39
53
40
54
@ Inject
41
55
public TransportGetAction (
@@ -45,7 +59,8 @@ public TransportGetAction(
45
59
ThreadPool threadPool ,
46
60
ActionFilters actionFilters ,
47
61
IndexNameExpressionResolver indexNameExpressionResolver ,
48
- ExecutorSelector executorSelector
62
+ ExecutorSelector executorSelector ,
63
+ NodeClient client
49
64
) {
50
65
super (
51
66
GetAction .NAME ,
@@ -59,6 +74,7 @@ public TransportGetAction(
59
74
);
60
75
this .indicesService = indicesService ;
61
76
this .executorSelector = executorSelector ;
77
+ this .client = client ;
62
78
// register the internal TransportGetFromTranslogAction
63
79
new TransportGetFromTranslogAction (transportService , indicesService , actionFilters );
64
80
}
@@ -78,7 +94,10 @@ protected ShardIterator shards(ClusterState state, InternalRequest request) {
78
94
request .request ().routing (),
79
95
request .request ().preference ()
80
96
);
81
- return clusterService .operationRouting ().useOnlyPromotableShardsForStateless (iterator );
97
+ if (iterator == null ) {
98
+ return null ;
99
+ }
100
+ return new PlainShardIterator (iterator .shardId (), iterator .getShardRoutings ().stream ().filter (ShardRouting ::isSearchable ).toList ());
82
101
}
83
102
84
103
@ Override
@@ -91,6 +110,16 @@ protected void resolveRequest(ClusterState state, InternalRequest request) {
91
110
protected void asyncShardOperation (GetRequest request , ShardId shardId , ActionListener <GetResponse > listener ) throws IOException {
92
111
IndexService indexService = indicesService .indexServiceSafe (shardId .getIndex ());
93
112
IndexShard indexShard = indexService .getShard (shardId .id ());
113
+ if (indexShard .routingEntry () == null ) {
114
+ listener .onFailure (new ShardNotFoundException (shardId ));
115
+ return ;
116
+ }
117
+ if (indexShard .routingEntry ().isPromotableToPrimary () == false ) {
118
+ handleGetOnUnpromotableShard (request , indexShard , listener );
119
+ return ;
120
+ }
121
+ assert DiscoveryNode .isStateless (clusterService .getSettings ()) == false
122
+ : "A TransportGetAction should always be handled by a search shard in Stateless" ;
94
123
if (request .realtime ()) { // we are not tied to a refresh cycle here anyway
95
124
asyncGet (request , shardId , listener );
96
125
} else {
@@ -148,6 +177,67 @@ private void asyncGet(GetRequest request, ShardId shardId, ActionListener<GetRes
148
177
}
149
178
}
150
179
180
+ private void handleGetOnUnpromotableShard (GetRequest request , IndexShard indexShard , ActionListener <GetResponse > listener )
181
+ throws IOException {
182
+ ShardId shardId = indexShard .shardId ();
183
+ DiscoveryNode node = getCurrentNodeOfPrimary (shardId );
184
+ if (request .refresh ()) {
185
+ logger .trace ("send refresh action for shard {} to node {}" , shardId , node .getId ());
186
+ var refreshRequest = new BasicReplicationRequest (shardId );
187
+ refreshRequest .setParentTask (request .getParentTask ());
188
+ client .executeLocally (
189
+ TransportShardRefreshAction .TYPE ,
190
+ refreshRequest ,
191
+ ActionListener .wrap (replicationResponse -> super .asyncShardOperation (request , shardId , listener ), listener ::onFailure )
192
+ );
193
+ } else if (request .realtime ()) {
194
+ TransportGetFromTranslogAction .Request getFromTranslogRequest = new TransportGetFromTranslogAction .Request (request , shardId );
195
+ getFromTranslogRequest .setParentTask (request .getParentTask ());
196
+ transportService .sendRequest (
197
+ node ,
198
+ TransportGetFromTranslogAction .NAME ,
199
+ getFromTranslogRequest ,
200
+ new ActionListenerResponseHandler <>(listener .delegateFailure ((l , r ) -> {
201
+ if (r .getResult () != null ) {
202
+ logger .debug ("received result for real-time get for id '{}' from promotable shard" , request .id ());
203
+ l .onResponse (new GetResponse (r .getResult ()));
204
+ } else {
205
+ logger .debug (
206
+ "no result for real-time get for id '{}' from promotable shard (segment generation to wait for: {})" ,
207
+ request .id (),
208
+ r .segmentGeneration ()
209
+ );
210
+ if (r .segmentGeneration () == -1 ) {
211
+ // Nothing to wait for (no previous unsafe generation), just handle the Get locally.
212
+ ActionRunnable .supply (listener , () -> shardOperation (request , shardId )).run ();
213
+ } else {
214
+ assert r .segmentGeneration () > -1L ;
215
+ indexShard .waitForSegmentGeneration (
216
+ r .segmentGeneration (),
217
+ ActionListener .wrap (aLong -> super .asyncShardOperation (request , shardId , listener ), listener ::onFailure )
218
+ );
219
+ }
220
+ }
221
+ }), TransportGetFromTranslogAction .Response ::new , getExecutor (request , shardId ))
222
+ );
223
+ } else {
224
+ // A non-real-time get with no explicit refresh requested.
225
+ super .asyncShardOperation (request , shardId , listener );
226
+ }
227
+ }
228
+
229
+ private DiscoveryNode getCurrentNodeOfPrimary (ShardId shardId ) {
230
+ var shardRoutingTable = clusterService .state ().routingTable ().shardRoutingTable (shardId );
231
+ if (shardRoutingTable .primaryShard () == null || shardRoutingTable .primaryShard ().active () == false ) {
232
+ throw new NoShardAvailableActionException (shardId , "primary shard is not active" );
233
+ }
234
+ DiscoveryNode node = clusterService .state ().nodes ().get (shardRoutingTable .primaryShard ().currentNodeId ());
235
+ if (node == null ) {
236
+ throw new NoShardAvailableActionException (shardId );
237
+ }
238
+ return node ;
239
+ }
240
+
151
241
private IndexShard getIndexShard (ShardId shardId ) {
152
242
IndexService indexService = indicesService .indexServiceSafe (shardId .getIndex ());
153
243
return indexService .getShard (shardId .id ());
0 commit comments