5
5
*/
6
6
package org .elasticsearch .xpack .ccr .action ;
7
7
8
+ import org .apache .logging .log4j .message .ParameterizedMessage ;
8
9
import org .elasticsearch .action .Action ;
10
+ import org .elasticsearch .action .ActionListener ;
9
11
import org .elasticsearch .action .ActionRequestValidationException ;
10
12
import org .elasticsearch .action .ActionResponse ;
11
13
import org .elasticsearch .action .support .ActionFilters ;
19
21
import org .elasticsearch .common .io .stream .StreamInput ;
20
22
import org .elasticsearch .common .io .stream .StreamOutput ;
21
23
import org .elasticsearch .common .settings .Settings ;
24
+ import org .elasticsearch .common .unit .TimeValue ;
22
25
import org .elasticsearch .index .IndexService ;
23
26
import org .elasticsearch .index .seqno .SeqNoStats ;
24
27
import org .elasticsearch .index .shard .IndexShard ;
36
39
import java .util .Arrays ;
37
40
import java .util .List ;
38
41
import java .util .Objects ;
42
+ import java .util .concurrent .TimeoutException ;
39
43
40
44
import static org .elasticsearch .action .ValidateActions .addValidationError ;
45
+ import static org .elasticsearch .index .seqno .SequenceNumbers .UNASSIGNED_SEQ_NO ;
41
46
42
47
public class ShardChangesAction extends Action <ShardChangesAction .Response > {
43
48
@@ -59,6 +64,7 @@ public static class Request extends SingleShardRequest<Request> {
59
64
private int maxOperationCount ;
60
65
private ShardId shardId ;
61
66
private String expectedHistoryUUID ;
67
+ private TimeValue pollTimeout = FollowIndexAction .DEFAULT_POLL_TIMEOUT ;
62
68
private long maxOperationSizeInBytes = FollowIndexAction .DEFAULT_MAX_BATCH_SIZE_IN_BYTES ;
63
69
64
70
public Request (ShardId shardId , String expectedHistoryUUID ) {
@@ -102,6 +108,14 @@ public String getExpectedHistoryUUID() {
102
108
return expectedHistoryUUID ;
103
109
}
104
110
111
+ public TimeValue getPollTimeout () {
112
+ return pollTimeout ;
113
+ }
114
+
115
+ public void setPollTimeout (final TimeValue pollTimeout ) {
116
+ this .pollTimeout = Objects .requireNonNull (pollTimeout , "pollTimeout" );
117
+ }
118
+
105
119
@ Override
106
120
public ActionRequestValidationException validate () {
107
121
ActionRequestValidationException validationException = null ;
@@ -126,6 +140,7 @@ public void readFrom(StreamInput in) throws IOException {
126
140
maxOperationCount = in .readVInt ();
127
141
shardId = ShardId .readShardId (in );
128
142
expectedHistoryUUID = in .readString ();
143
+ pollTimeout = in .readTimeValue ();
129
144
maxOperationSizeInBytes = in .readVLong ();
130
145
}
131
146
@@ -136,6 +151,7 @@ public void writeTo(StreamOutput out) throws IOException {
136
151
out .writeVInt (maxOperationCount );
137
152
shardId .writeTo (out );
138
153
out .writeString (expectedHistoryUUID );
154
+ out .writeTimeValue (pollTimeout );
139
155
out .writeVLong (maxOperationSizeInBytes );
140
156
}
141
157
@@ -149,12 +165,13 @@ public boolean equals(final Object o) {
149
165
maxOperationCount == request .maxOperationCount &&
150
166
Objects .equals (shardId , request .shardId ) &&
151
167
Objects .equals (expectedHistoryUUID , request .expectedHistoryUUID ) &&
168
+ Objects .equals (pollTimeout , request .pollTimeout ) &&
152
169
maxOperationSizeInBytes == request .maxOperationSizeInBytes ;
153
170
}
154
171
155
172
@ Override
156
173
public int hashCode () {
157
- return Objects .hash (fromSeqNo , maxOperationCount , shardId , expectedHistoryUUID , maxOperationSizeInBytes );
174
+ return Objects .hash (fromSeqNo , maxOperationCount , shardId , expectedHistoryUUID , pollTimeout , maxOperationSizeInBytes );
158
175
}
159
176
160
177
@ Override
@@ -164,6 +181,7 @@ public String toString() {
164
181
", maxOperationCount=" + maxOperationCount +
165
182
", shardId=" + shardId +
166
183
", expectedHistoryUUID=" + expectedHistoryUUID +
184
+ ", pollTimeout=" + pollTimeout +
167
185
", maxOperationSizeInBytes=" + maxOperationSizeInBytes +
168
186
'}' ;
169
187
}
@@ -265,19 +283,90 @@ public TransportAction(Settings settings,
265
283
266
284
@ Override
267
285
protected Response shardOperation (Request request , ShardId shardId ) throws IOException {
268
- IndexService indexService = indicesService .indexServiceSafe (request .getShard ().getIndex ());
269
- IndexShard indexShard = indexService .getShard (request .getShard ().id ());
270
- final SeqNoStats seqNoStats = indexShard .seqNoStats ();
286
+ final IndexService indexService = indicesService .indexServiceSafe (request .getShard ().getIndex ());
287
+ final IndexShard indexShard = indexService .getShard (request .getShard ().id ());
288
+ final SeqNoStats seqNoStats = indexShard .seqNoStats ();
271
289
final long mappingVersion = clusterService .state ().metaData ().index (shardId .getIndex ()).getMappingVersion ();
272
290
273
291
final Translog .Operation [] operations = getOperations (
274
292
indexShard ,
275
293
seqNoStats .getGlobalCheckpoint (),
276
- request .fromSeqNo ,
277
- request .maxOperationCount ,
278
- request .expectedHistoryUUID ,
279
- request .maxOperationSizeInBytes );
280
- return new Response (mappingVersion , seqNoStats .getGlobalCheckpoint (), seqNoStats .getMaxSeqNo (), operations );
294
+ request .getFromSeqNo (),
295
+ request .getMaxOperationCount (),
296
+ request .getExpectedHistoryUUID (),
297
+ request .getMaxOperationSizeInBytes ());
298
+ return getResponse (mappingVersion , seqNoStats , operations );
299
+ }
300
+
301
+ @ Override
302
+ protected void asyncShardOperation (
303
+ final Request request ,
304
+ final ShardId shardId ,
305
+ final ActionListener <Response > listener ) throws IOException {
306
+ final IndexService indexService = indicesService .indexServiceSafe (request .getShard ().getIndex ());
307
+ final IndexShard indexShard = indexService .getShard (request .getShard ().id ());
308
+ final SeqNoStats seqNoStats = indexShard .seqNoStats ();
309
+
310
+ if (request .getFromSeqNo () > seqNoStats .getGlobalCheckpoint ()) {
311
+ logger .trace (
312
+ "{} waiting for global checkpoint advancement from [{}] to [{}]" ,
313
+ shardId ,
314
+ seqNoStats .getGlobalCheckpoint (),
315
+ request .getFromSeqNo ());
316
+ indexShard .addGlobalCheckpointListener (
317
+ request .getFromSeqNo (),
318
+ (g , e ) -> {
319
+ if (g != UNASSIGNED_SEQ_NO ) {
320
+ assert request .getFromSeqNo () <= g
321
+ : shardId + " only advanced to [" + g + "] while waiting for [" + request .getFromSeqNo () + "]" ;
322
+ globalCheckpointAdvanced (shardId , g , request , listener );
323
+ } else {
324
+ assert e != null ;
325
+ globalCheckpointAdvancementFailure (shardId , e , request , listener , indexShard );
326
+ }
327
+ },
328
+ request .getPollTimeout ());
329
+ } else {
330
+ super .asyncShardOperation (request , shardId , listener );
331
+ }
332
+ }
333
+
334
+ private void globalCheckpointAdvanced (
335
+ final ShardId shardId ,
336
+ final long globalCheckpoint ,
337
+ final Request request ,
338
+ final ActionListener <Response > listener ) {
339
+ logger .trace ("{} global checkpoint advanced to [{}] after waiting for [{}]" , shardId , globalCheckpoint , request .getFromSeqNo ());
340
+ try {
341
+ super .asyncShardOperation (request , shardId , listener );
342
+ } catch (final IOException caught ) {
343
+ listener .onFailure (caught );
344
+ }
345
+ }
346
+
347
+ private void globalCheckpointAdvancementFailure (
348
+ final ShardId shardId ,
349
+ final Exception e ,
350
+ final Request request ,
351
+ final ActionListener <Response > listener ,
352
+ final IndexShard indexShard ) {
353
+ logger .trace (
354
+ () -> new ParameterizedMessage (
355
+ "{} exception waiting for global checkpoint advancement to [{}]" , shardId , request .getFromSeqNo ()),
356
+ e );
357
+ if (e instanceof TimeoutException ) {
358
+ try {
359
+ final long mappingVersion =
360
+ clusterService .state ().metaData ().index (shardId .getIndex ()).getMappingVersion ();
361
+ final SeqNoStats latestSeqNoStats = indexShard .seqNoStats ();
362
+ listener .onResponse (getResponse (mappingVersion , latestSeqNoStats , EMPTY_OPERATIONS_ARRAY ));
363
+ } catch (final Exception caught ) {
364
+ caught .addSuppressed (e );
365
+ listener .onFailure (caught );
366
+ }
367
+ } else {
368
+ listener .onFailure (e );
369
+ }
281
370
}
282
371
283
372
@ Override
@@ -300,7 +389,7 @@ protected Response newResponse() {
300
389
301
390
}
302
391
303
- private static final Translog .Operation [] EMPTY_OPERATIONS_ARRAY = new Translog .Operation [0 ];
392
+ static final Translog .Operation [] EMPTY_OPERATIONS_ARRAY = new Translog .Operation [0 ];
304
393
305
394
/**
306
395
* Returns at most maxOperationCount operations from the specified from sequence number.
@@ -324,7 +413,8 @@ static Translog.Operation[] getOperations(IndexShard indexShard,
324
413
historyUUID + "]" );
325
414
}
326
415
if (fromSeqNo > globalCheckpoint ) {
327
- return EMPTY_OPERATIONS_ARRAY ;
416
+ throw new IllegalStateException (
417
+ "not exposing operations from [" + fromSeqNo + "] greater than the global checkpoint [" + globalCheckpoint + "]" );
328
418
}
329
419
int seenBytes = 0 ;
330
420
// - 1 is needed, because toSeqNo is inclusive
@@ -344,4 +434,8 @@ static Translog.Operation[] getOperations(IndexShard indexShard,
344
434
return operations .toArray (EMPTY_OPERATIONS_ARRAY );
345
435
}
346
436
437
+ static Response getResponse (final long mappingVersion , final SeqNoStats seqNoStats , final Translog .Operation [] operations ) {
438
+ return new Response (mappingVersion , seqNoStats .getGlobalCheckpoint (), seqNoStats .getMaxSeqNo (), operations );
439
+ }
440
+
347
441
}
0 commit comments