Skip to content

Commit 648c149

Browse files
committed
[CCR] Introduce leader index name & last fetch time stats to stats api response (#33155)
1 parent 6912e43 commit 648c149

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java

+66-11
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
6767

6868
private static final Logger LOGGER = Loggers.getLogger(ShardFollowNodeTask.class);
6969

70+
private final String leaderIndex;
7071
private final ShardFollowTask params;
7172
private final TimeValue retryTimeout;
7273
private final TimeValue idleShardChangesRequestDelay;
@@ -90,6 +91,7 @@ public abstract class ShardFollowNodeTask extends AllocatedPersistentTask {
9091
private long numberOfSuccessfulBulkOperations = 0;
9192
private long numberOfFailedBulkOperations = 0;
9293
private long numberOfOperationsIndexed = 0;
94+
private long lastFetchTime = -1;
9395
private final Queue<Translog.Operation> buffer = new PriorityQueue<>(Comparator.comparing(Translog.Operation::seqNo));
9496
private final LinkedHashMap<Long, ElasticsearchException> fetchExceptions;
9597

@@ -112,6 +114,12 @@ protected boolean removeEldestEntry(final Map.Entry<Long, ElasticsearchException
112114
return size() > params.getMaxConcurrentReadBatches();
113115
}
114116
};
117+
118+
if (params.getLeaderClusterAlias() != null) {
119+
leaderIndex = params.getLeaderClusterAlias() + ":" + params.getLeaderShardId().getIndexName();
120+
} else {
121+
leaderIndex = params.getLeaderShardId().getIndexName();
122+
}
115123
}
116124

117125
void start(
@@ -235,6 +243,9 @@ private void sendShardChangesRequest(long from, int maxOperationCount, long maxR
235243

236244
private void sendShardChangesRequest(long from, int maxOperationCount, long maxRequiredSeqNo, AtomicInteger retryCounter) {
237245
final long startTime = relativeTimeProvider.getAsLong();
246+
synchronized (this) {
247+
lastFetchTime = startTime;
248+
}
238249
innerSendShardChangesRequest(from, maxOperationCount,
239250
response -> {
240251
synchronized (ShardFollowNodeTask.this) {
@@ -411,7 +422,15 @@ public ShardId getFollowShardId() {
411422

412423
@Override
413424
public synchronized Status getStatus() {
425+
final long timeSinceLastFetchMillis;
426+
if (lastFetchTime != -1) {
427+
timeSinceLastFetchMillis = TimeUnit.NANOSECONDS.toMillis(relativeTimeProvider.getAsLong() - lastFetchTime);
428+
} else {
429+
// To avoid confusion when ccr didn't yet execute a fetch:
430+
timeSinceLastFetchMillis = -1;
431+
}
414432
return new Status(
433+
leaderIndex,
415434
getFollowShardId().getId(),
416435
leaderGlobalCheckpoint,
417436
leaderMaxSeqNo,
@@ -431,13 +450,15 @@ public synchronized Status getStatus() {
431450
numberOfSuccessfulBulkOperations,
432451
numberOfFailedBulkOperations,
433452
numberOfOperationsIndexed,
434-
new TreeMap<>(fetchExceptions));
453+
new TreeMap<>(fetchExceptions),
454+
timeSinceLastFetchMillis);
435455
}
436456

437457
public static class Status implements Task.Status {
438458

439459
public static final String STATUS_PARSER_NAME = "shard-follow-node-task-status";
440460

461+
static final ParseField LEADER_INDEX = new ParseField("leader_index");
441462
static final ParseField SHARD_ID = new ParseField("shard_id");
442463
static final ParseField LEADER_GLOBAL_CHECKPOINT_FIELD = new ParseField("leader_global_checkpoint");
443464
static final ParseField LEADER_MAX_SEQ_NO_FIELD = new ParseField("leader_max_seq_no");
@@ -458,20 +479,21 @@ public static class Status implements Task.Status {
458479
static final ParseField NUMBER_OF_FAILED_BULK_OPERATIONS_FIELD = new ParseField("number_of_failed_bulk_operations");
459480
static final ParseField NUMBER_OF_OPERATIONS_INDEXED_FIELD = new ParseField("number_of_operations_indexed");
460481
static final ParseField FETCH_EXCEPTIONS = new ParseField("fetch_exceptions");
482+
static final ParseField TIME_SINCE_LAST_FETCH_MILLIS_FIELD = new ParseField("time_since_last_fetch_millis");
461483

462484
@SuppressWarnings("unchecked")
463485
static final ConstructingObjectParser<Status, Void> STATUS_PARSER = new ConstructingObjectParser<>(STATUS_PARSER_NAME,
464486
args -> new Status(
465-
(int) args[0],
466-
(long) args[1],
487+
(String) args[0],
488+
(int) args[1],
467489
(long) args[2],
468490
(long) args[3],
469491
(long) args[4],
470492
(long) args[5],
471-
(int) args[6],
493+
(long) args[6],
472494
(int) args[7],
473495
(int) args[8],
474-
(long) args[9],
496+
(int) args[9],
475497
(long) args[10],
476498
(long) args[11],
477499
(long) args[12],
@@ -481,10 +503,12 @@ public static class Status implements Task.Status {
481503
(long) args[16],
482504
(long) args[17],
483505
(long) args[18],
506+
(long) args[19],
484507
new TreeMap<>(
485-
((List<Map.Entry<Long, ElasticsearchException>>) args[19])
508+
((List<Map.Entry<Long, ElasticsearchException>>) args[20])
486509
.stream()
487-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))));
510+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))),
511+
(long) args[21]));
488512

489513
public static final String FETCH_EXCEPTIONS_ENTRY_PARSER_NAME = "shard-follow-node-task-status-fetch-exceptions-entry";
490514

@@ -494,6 +518,7 @@ public static class Status implements Task.Status {
494518
args -> new AbstractMap.SimpleEntry<>((long) args[0], (ElasticsearchException) args[1]));
495519

496520
static {
521+
STATUS_PARSER.declareString(ConstructingObjectParser.constructorArg(), LEADER_INDEX);
497522
STATUS_PARSER.declareInt(ConstructingObjectParser.constructorArg(), SHARD_ID);
498523
STATUS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), LEADER_GLOBAL_CHECKPOINT_FIELD);
499524
STATUS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), LEADER_MAX_SEQ_NO_FIELD);
@@ -514,6 +539,7 @@ public static class Status implements Task.Status {
514539
STATUS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_FAILED_BULK_OPERATIONS_FIELD);
515540
STATUS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_OPERATIONS_INDEXED_FIELD);
516541
STATUS_PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), FETCH_EXCEPTIONS_ENTRY_PARSER, FETCH_EXCEPTIONS);
542+
STATUS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), TIME_SINCE_LAST_FETCH_MILLIS_FIELD);
517543
}
518544

519545
static final ParseField FETCH_EXCEPTIONS_ENTRY_FROM_SEQ_NO = new ParseField("from_seq_no");
@@ -527,6 +553,12 @@ public static class Status implements Task.Status {
527553
FETCH_EXCEPTIONS_ENTRY_EXCEPTION);
528554
}
529555

556+
private final String leaderIndex;
557+
558+
public String leaderIndex() {
559+
return leaderIndex;
560+
}
561+
530562
private final int shardId;
531563

532564
public int getShardId() {
@@ -647,7 +679,14 @@ public NavigableMap<Long, ElasticsearchException> fetchExceptions() {
647679
return fetchExceptions;
648680
}
649681

682+
private final long timeSinceLastFetchMillis;
683+
684+
public long timeSinceLastFetchMillis() {
685+
return timeSinceLastFetchMillis;
686+
}
687+
650688
Status(
689+
final String leaderIndex,
651690
final int shardId,
652691
final long leaderGlobalCheckpoint,
653692
final long leaderMaxSeqNo,
@@ -667,7 +706,9 @@ public NavigableMap<Long, ElasticsearchException> fetchExceptions() {
667706
final long numberOfSuccessfulBulkOperations,
668707
final long numberOfFailedBulkOperations,
669708
final long numberOfOperationsIndexed,
670-
final NavigableMap<Long, ElasticsearchException> fetchExceptions) {
709+
final NavigableMap<Long, ElasticsearchException> fetchExceptions,
710+
final long timeSinceLastFetchMillis) {
711+
this.leaderIndex = leaderIndex;
671712
this.shardId = shardId;
672713
this.leaderGlobalCheckpoint = leaderGlobalCheckpoint;
673714
this.leaderMaxSeqNo = leaderMaxSeqNo;
@@ -688,9 +729,11 @@ public NavigableMap<Long, ElasticsearchException> fetchExceptions() {
688729
this.numberOfFailedBulkOperations = numberOfFailedBulkOperations;
689730
this.numberOfOperationsIndexed = numberOfOperationsIndexed;
690731
this.fetchExceptions = Objects.requireNonNull(fetchExceptions);
732+
this.timeSinceLastFetchMillis = timeSinceLastFetchMillis;
691733
}
692734

693735
public Status(final StreamInput in) throws IOException {
736+
this.leaderIndex = in.readString();
694737
this.shardId = in.readVInt();
695738
this.leaderGlobalCheckpoint = in.readZLong();
696739
this.leaderMaxSeqNo = in.readZLong();
@@ -711,6 +754,7 @@ public Status(final StreamInput in) throws IOException {
711754
this.numberOfFailedBulkOperations = in.readVLong();
712755
this.numberOfOperationsIndexed = in.readVLong();
713756
this.fetchExceptions = new TreeMap<>(in.readMap(StreamInput::readVLong, StreamInput::readException));
757+
this.timeSinceLastFetchMillis = in.readZLong();
714758
}
715759

716760
@Override
@@ -720,6 +764,7 @@ public String getWriteableName() {
720764

721765
@Override
722766
public void writeTo(final StreamOutput out) throws IOException {
767+
out.writeString(leaderIndex);
723768
out.writeVInt(shardId);
724769
out.writeZLong(leaderGlobalCheckpoint);
725770
out.writeZLong(leaderMaxSeqNo);
@@ -740,12 +785,14 @@ public void writeTo(final StreamOutput out) throws IOException {
740785
out.writeVLong(numberOfFailedBulkOperations);
741786
out.writeVLong(numberOfOperationsIndexed);
742787
out.writeMap(fetchExceptions, StreamOutput::writeVLong, StreamOutput::writeException);
788+
out.writeZLong(timeSinceLastFetchMillis);
743789
}
744790

745791
@Override
746792
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
747793
builder.startObject();
748794
{
795+
builder.field(LEADER_INDEX.getPreferredName(), leaderIndex);
749796
builder.field(SHARD_ID.getPreferredName(), shardId);
750797
builder.field(LEADER_GLOBAL_CHECKPOINT_FIELD.getPreferredName(), leaderGlobalCheckpoint);
751798
builder.field(LEADER_MAX_SEQ_NO_FIELD.getPreferredName(), leaderMaxSeqNo);
@@ -791,6 +838,10 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa
791838
}
792839
}
793840
builder.endArray();
841+
builder.humanReadableField(
842+
TIME_SINCE_LAST_FETCH_MILLIS_FIELD.getPreferredName(),
843+
"time_since_last_fetch",
844+
new TimeValue(timeSinceLastFetchMillis, TimeUnit.MILLISECONDS));
794845
}
795846
builder.endObject();
796847
return builder;
@@ -805,7 +856,8 @@ public boolean equals(final Object o) {
805856
if (this == o) return true;
806857
if (o == null || getClass() != o.getClass()) return false;
807858
final Status that = (Status) o;
808-
return shardId == that.shardId &&
859+
return leaderIndex.equals(that.leaderIndex) &&
860+
shardId == that.shardId &&
809861
leaderGlobalCheckpoint == that.leaderGlobalCheckpoint &&
810862
leaderMaxSeqNo == that.leaderMaxSeqNo &&
811863
followerGlobalCheckpoint == that.followerGlobalCheckpoint &&
@@ -829,12 +881,14 @@ public boolean equals(final Object o) {
829881
* keys.
830882
*/
831883
fetchExceptions.keySet().equals(that.fetchExceptions.keySet()) &&
832-
getFetchExceptionMessages(this).equals(getFetchExceptionMessages(that));
884+
getFetchExceptionMessages(this).equals(getFetchExceptionMessages(that)) &&
885+
timeSinceLastFetchMillis == that.timeSinceLastFetchMillis;
833886
}
834887

835888
@Override
836889
public int hashCode() {
837890
return Objects.hash(
891+
leaderIndex,
838892
shardId,
839893
leaderGlobalCheckpoint,
840894
leaderMaxSeqNo,
@@ -858,7 +912,8 @@ public int hashCode() {
858912
* messages. Note that we are relying on the fact that the fetch exceptions are ordered by keys.
859913
*/
860914
fetchExceptions.keySet(),
861-
getFetchExceptionMessages(this));
915+
getFetchExceptionMessages(this),
916+
timeSinceLastFetchMillis);
862917
}
863918

864919
private static List<String> getFetchExceptionMessages(final Status status) {

x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTaskStatusTests.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ protected ShardFollowNodeTask.Status doParseInstance(XContentParser parser) thro
3232
protected ShardFollowNodeTask.Status createTestInstance() {
3333
// if you change this constructor, reflect the changes in the hand-written assertions below
3434
return new ShardFollowNodeTask.Status(
35+
randomAlphaOfLength(4),
3536
randomInt(),
3637
randomNonNegativeLong(),
3738
randomNonNegativeLong(),
@@ -51,12 +52,14 @@ protected ShardFollowNodeTask.Status createTestInstance() {
5152
randomNonNegativeLong(),
5253
randomNonNegativeLong(),
5354
randomNonNegativeLong(),
54-
randomReadExceptions());
55+
randomReadExceptions(),
56+
randomLong());
5557
}
5658

5759
@Override
5860
protected void assertEqualInstances(final ShardFollowNodeTask.Status expectedInstance, final ShardFollowNodeTask.Status newInstance) {
5961
assertNotSame(expectedInstance, newInstance);
62+
assertThat(newInstance.leaderIndex(), equalTo(expectedInstance.leaderIndex()));
6063
assertThat(newInstance.getShardId(), equalTo(expectedInstance.getShardId()));
6164
assertThat(newInstance.leaderGlobalCheckpoint(), equalTo(expectedInstance.leaderGlobalCheckpoint()));
6265
assertThat(newInstance.leaderMaxSeqNo(), equalTo(expectedInstance.leaderMaxSeqNo()));
@@ -87,6 +90,7 @@ protected void assertEqualInstances(final ShardFollowNodeTask.Status expectedIns
8790
anyOf(instanceOf(ElasticsearchException.class), instanceOf(IllegalStateException.class)));
8891
assertThat(entry.getValue().getCause().getMessage(), containsString(expected.getCause().getMessage()));
8992
}
93+
assertThat(newInstance.timeSinceLastFetchMillis(), equalTo(expectedInstance.timeSinceLastFetchMillis()));
9094
}
9195

9296
@Override

x-pack/plugin/src/test/resources/rest-api-spec/test/ccr/stats.yml

+5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@
2020
index: bar
2121
body:
2222
leader_index: foo
23+
- is_true: follow_index_created
24+
- is_true: follow_index_shards_acked
25+
- is_true: index_following_started
2326

2427
# we can not reliably wait for replication to occur so we test the endpoint without indexing any documents
2528
- do:
2629
ccr.stats:
2730
index: bar
31+
- match: { bar.0.leader_index: "foo" }
2832
- match: { bar.0.shard_id: 0 }
2933
- gte: { bar.0.leader_global_checkpoint: -1 }
3034
- gte: { bar.0.leader_max_seq_no: -1 }
@@ -45,6 +49,7 @@
4549
- match: { bar.0.number_of_failed_bulk_operations: 0 }
4650
- match: { bar.0.number_of_operations_indexed: 0 }
4751
- length: { bar.0.fetch_exceptions: 0 }
52+
- gte: { bar.0.time_since_last_fetch_millis: -1 }
4853

4954
- do:
5055
ccr.unfollow_index:

0 commit comments

Comments
 (0)