From f4aac8d3f17ab92d03166b9ae4b0aeb682f06d3f Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Sun, 9 Dec 2018 19:59:08 +0100 Subject: [PATCH] [HLRC] Added support for Follow Stats API (#36253) This change also adds documentation for the Follow Stats API. Relates to #33824 --- .../org/elasticsearch/client/CcrClient.java | 48 +++- .../client/CcrRequestConverters.java | 9 + .../client/ccr/FollowStatsRequest.java | 37 +++ .../client/ccr/FollowStatsResponse.java | 39 +++ .../java/org/elasticsearch/client/CCRIT.java | 16 +- .../client/ccr/CcrStatsResponseTests.java | 2 +- .../client/ccr/FollowStatsResponseTests.java | 253 ++++++++++++++++++ .../documentation/CCRDocumentationIT.java | 47 ++++ .../high-level/ccr/get_follow_stats.asciidoc | 35 +++ .../high-level/supported-apis.asciidoc | 2 + 10 files changed, 480 insertions(+), 8 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java create mode 100644 docs/java-rest/high-level/ccr/get_follow_stats.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java index 243eee12195..9a17dabf395 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java @@ -23,6 +23,8 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowStatsRequest; +import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; import org.elasticsearch.client.ccr.PauseFollowRequest; @@ -385,7 +387,7 @@ public final class CcrClient { } /** - * Gets all CCR stats. + * Asynchronously gets all CCR stats. * * See * the docs for more. @@ -406,4 +408,48 @@ public final class CcrClient { ); } + /** + * Gets follow stats for specific indices. + * + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public FollowStatsResponse getFollowStats(FollowStatsRequest request, + RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + CcrRequestConverters::getFollowStats, + options, + FollowStatsResponse::fromXContent, + Collections.emptySet() + ); + } + + /** + * Asynchronously gets follow stats for specific indices. + * + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + */ + public void getFollowStatsAsync(FollowStatsRequest request, + RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity( + request, + CcrRequestConverters::getFollowStats, + options, + FollowStatsResponse::fromXContent, + listener, + Collections.emptySet() + ); + } + } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java index 8644651c591..df1e5dc01ae 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java @@ -25,6 +25,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowStatsRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.PauseFollowRequest; import org.elasticsearch.client.ccr.PutAutoFollowPatternRequest; @@ -108,4 +109,12 @@ final class CcrRequestConverters { return new Request(HttpGet.METHOD_NAME, endpoint); } + static Request getFollowStats(FollowStatsRequest followStatsRequest) { + String endpoint = new RequestConverters.EndpointBuilder() + .addPathPart(followStatsRequest.getFollowerIndex()) + .addPathPartAsIs("_ccr", "stats") + .build(); + return new Request(HttpGet.METHOD_NAME, endpoint); + } + } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsRequest.java new file mode 100644 index 00000000000..4eb82e43e3c --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsRequest.java @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.ccr; + +import org.elasticsearch.client.Validatable; + +import java.util.Objects; + +public final class FollowStatsRequest implements Validatable { + + private final String followerIndex; + + public FollowStatsRequest(String followerIndex) { + this.followerIndex = Objects.requireNonNull(followerIndex); + } + + public String getFollowerIndex() { + return followerIndex; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsResponse.java new file mode 100644 index 00000000000..9c12f80c68e --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowStatsResponse.java @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.ccr; + +import org.elasticsearch.common.xcontent.XContentParser; + +public final class FollowStatsResponse { + + public static FollowStatsResponse fromXContent(XContentParser parser) { + return new FollowStatsResponse(IndicesFollowStats.PARSER.apply(parser, null)); + } + + private final IndicesFollowStats indicesFollowStats; + + public FollowStatsResponse(IndicesFollowStats indicesFollowStats) { + this.indicesFollowStats = indicesFollowStats; + } + + public IndicesFollowStats getIndicesFollowStats() { + return indicesFollowStats; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java index 21ff66d2434..0c36f5d5865 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java @@ -32,6 +32,8 @@ import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowStatsRequest; +import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; import org.elasticsearch.client.ccr.IndicesFollowStats.ShardFollowStats; @@ -108,9 +110,10 @@ public class CCRIT extends ESRestHighLevelClientTestCase { assertThat(leaderSearchResponse.getHits().getTotalHits().value, equalTo(1L)); assertBusy(() -> { - CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); - CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); - List shardFollowStats = ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); + FollowStatsRequest followStatsRequest = new FollowStatsRequest("follower"); + FollowStatsResponse followStatsResponse = + execute(followStatsRequest, ccrClient::getFollowStats, ccrClient::getFollowStatsAsync); + List shardFollowStats = followStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); long followerGlobalCheckpoint = shardFollowStats.stream() .mapToLong(ShardFollowStats::getFollowerGlobalCheckpoint) .max() @@ -133,9 +136,10 @@ public class CCRIT extends ESRestHighLevelClientTestCase { assertThat(resumeFollowResponse.isAcknowledged(), is(true)); assertBusy(() -> { - CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); - CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); - List shardFollowStats = ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); + FollowStatsRequest followStatsRequest = new FollowStatsRequest("follower"); + FollowStatsResponse followStatsResponse = + execute(followStatsRequest, ccrClient::getFollowStats, ccrClient::getFollowStatsAsync); + List shardFollowStats = followStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); long followerGlobalCheckpoint = shardFollowStats.stream() .mapToLong(ShardFollowStats::getFollowerGlobalCheckpoint) .max() diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java index 66e80130baf..039e31151c4 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java @@ -323,7 +323,7 @@ public class CcrStatsResponseTests extends ESTestCase { ); } - private static IndicesFollowStats randomIndicesFollowStats() { + static IndicesFollowStats randomIndicesFollowStats() { int numIndices = randomIntBetween(0, 16); NavigableMap> shardFollowStats = new TreeMap<>(); for (int i = 0; i < numIndices; i++) { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java new file mode 100644 index 00000000000..c8d15bc5085 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java @@ -0,0 +1,253 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.ccr; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.client.ccr.IndicesFollowStats.ShardFollowStats; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.unit.ByteSizeUnit; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.elasticsearch.client.ccr.CcrStatsResponseTests.randomIndicesFollowStats; +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +public class FollowStatsResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + xContentTester(this::createParser, + FollowStatsResponseTests::createTestInstance, + FollowStatsResponseTests::toXContent, + FollowStatsResponse::fromXContent) + .supportsUnknownFields(false) + .assertEqualsConsumer(FollowStatsResponseTests::assertEqualInstances) + .assertToXContentEquivalence(false) + .test(); + } + + // Needed, because exceptions in IndicesFollowStats cannot be compared + private static void assertEqualInstances(FollowStatsResponse expectedInstance, FollowStatsResponse newInstance) { + assertNotSame(expectedInstance, newInstance); + { + IndicesFollowStats newIndicesFollowStats = newInstance.getIndicesFollowStats(); + IndicesFollowStats expectedIndicesFollowStats = expectedInstance.getIndicesFollowStats(); + assertThat(newIndicesFollowStats.getShardFollowStats().size(), + equalTo(expectedIndicesFollowStats.getShardFollowStats().size())); + assertThat(newIndicesFollowStats.getShardFollowStats().keySet(), + equalTo(expectedIndicesFollowStats.getShardFollowStats().keySet())); + for (Map.Entry> indexEntry : newIndicesFollowStats.getShardFollowStats().entrySet()) { + List newStats = indexEntry.getValue(); + List expectedStats = expectedIndicesFollowStats.getShardFollowStats(indexEntry.getKey()); + assertThat(newStats.size(), equalTo(expectedStats.size())); + for (int i = 0; i < newStats.size(); i++) { + ShardFollowStats actualShardFollowStats = newStats.get(i); + ShardFollowStats expectedShardFollowStats = expectedStats.get(i); + + assertThat(actualShardFollowStats.getRemoteCluster(), equalTo(expectedShardFollowStats.getRemoteCluster())); + assertThat(actualShardFollowStats.getLeaderIndex(), equalTo(expectedShardFollowStats.getLeaderIndex())); + assertThat(actualShardFollowStats.getFollowerIndex(), equalTo(expectedShardFollowStats.getFollowerIndex())); + assertThat(actualShardFollowStats.getShardId(), equalTo(expectedShardFollowStats.getShardId())); + assertThat(actualShardFollowStats.getLeaderGlobalCheckpoint(), + equalTo(expectedShardFollowStats.getLeaderGlobalCheckpoint())); + assertThat(actualShardFollowStats.getLeaderMaxSeqNo(), equalTo(expectedShardFollowStats.getLeaderMaxSeqNo())); + assertThat(actualShardFollowStats.getFollowerGlobalCheckpoint(), + equalTo(expectedShardFollowStats.getFollowerGlobalCheckpoint())); + assertThat(actualShardFollowStats.getLastRequestedSeqNo(), equalTo(expectedShardFollowStats.getLastRequestedSeqNo())); + assertThat(actualShardFollowStats.getOutstandingReadRequests(), + equalTo(expectedShardFollowStats.getOutstandingReadRequests())); + assertThat(actualShardFollowStats.getOutstandingWriteRequests(), + equalTo(expectedShardFollowStats.getOutstandingWriteRequests())); + assertThat(actualShardFollowStats.getWriteBufferOperationCount(), + equalTo(expectedShardFollowStats.getWriteBufferOperationCount())); + assertThat(actualShardFollowStats.getFollowerMappingVersion(), + equalTo(expectedShardFollowStats.getFollowerMappingVersion())); + assertThat(actualShardFollowStats.getFollowerSettingsVersion(), + equalTo(expectedShardFollowStats.getFollowerSettingsVersion())); + assertThat(actualShardFollowStats.getTotalReadTimeMillis(), + equalTo(expectedShardFollowStats.getTotalReadTimeMillis())); + assertThat(actualShardFollowStats.getSuccessfulReadRequests(), + equalTo(expectedShardFollowStats.getSuccessfulReadRequests())); + assertThat(actualShardFollowStats.getFailedReadRequests(), equalTo(expectedShardFollowStats.getFailedReadRequests())); + assertThat(actualShardFollowStats.getOperationsReads(), equalTo(expectedShardFollowStats.getOperationsReads())); + assertThat(actualShardFollowStats.getBytesRead(), equalTo(expectedShardFollowStats.getBytesRead())); + assertThat(actualShardFollowStats.getTotalWriteTimeMillis(), + equalTo(expectedShardFollowStats.getTotalWriteTimeMillis())); + assertThat(actualShardFollowStats.getSuccessfulWriteRequests(), + equalTo(expectedShardFollowStats.getSuccessfulWriteRequests())); + assertThat(actualShardFollowStats.getFailedWriteRequests(), + equalTo(expectedShardFollowStats.getFailedWriteRequests())); + assertThat(actualShardFollowStats.getOperationWritten(), equalTo(expectedShardFollowStats.getOperationWritten())); + assertThat(actualShardFollowStats.getReadExceptions().size(), + equalTo(expectedShardFollowStats.getReadExceptions().size())); + assertThat(actualShardFollowStats.getReadExceptions().keySet(), + equalTo(expectedShardFollowStats.getReadExceptions().keySet())); + for (final Map.Entry> entry : + actualShardFollowStats.getReadExceptions().entrySet()) { + final Tuple expectedTuple = + expectedShardFollowStats.getReadExceptions().get(entry.getKey()); + assertThat(entry.getValue().v1(), equalTo(expectedTuple.v1())); + // x-content loses the exception + final ElasticsearchException expected = expectedTuple.v2(); + assertThat(entry.getValue().v2().getMessage(), containsString(expected.getMessage())); + assertNotNull(entry.getValue().v2().getCause()); + assertThat( + entry.getValue().v2().getCause(), + anyOf(instanceOf(ElasticsearchException.class), instanceOf(IllegalStateException.class))); + assertThat(entry.getValue().v2().getCause().getMessage(), containsString(expected.getCause().getMessage())); + } + assertThat(actualShardFollowStats.getTimeSinceLastReadMillis(), + equalTo(expectedShardFollowStats.getTimeSinceLastReadMillis())); + } + } + } + } + + private static void toXContent(FollowStatsResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + { + builder.startArray(IndicesFollowStats.INDICES_FIELD.getPreferredName()); + for (Map.Entry> indexEntry : + response.getIndicesFollowStats().getShardFollowStats().entrySet()) { + builder.startObject(); + { + builder.field(IndicesFollowStats.INDEX_FIELD.getPreferredName(), indexEntry.getKey()); + builder.startArray(IndicesFollowStats.SHARDS_FIELD.getPreferredName()); + { + for (ShardFollowStats stats : indexEntry.getValue()) { + builder.startObject(); + { + builder.field(ShardFollowStats.LEADER_CLUSTER.getPreferredName(), stats.getRemoteCluster()); + builder.field(ShardFollowStats.LEADER_INDEX.getPreferredName(), stats.getLeaderIndex()); + builder.field(ShardFollowStats.FOLLOWER_INDEX.getPreferredName(), stats.getFollowerIndex()); + builder.field(ShardFollowStats.SHARD_ID.getPreferredName(), stats.getShardId()); + builder.field(ShardFollowStats.LEADER_GLOBAL_CHECKPOINT_FIELD.getPreferredName(), + stats.getLeaderGlobalCheckpoint()); + builder.field(ShardFollowStats.LEADER_MAX_SEQ_NO_FIELD.getPreferredName(), stats.getLeaderMaxSeqNo()); + builder.field(ShardFollowStats.FOLLOWER_GLOBAL_CHECKPOINT_FIELD.getPreferredName(), + stats.getFollowerGlobalCheckpoint()); + builder.field(ShardFollowStats.FOLLOWER_MAX_SEQ_NO_FIELD.getPreferredName(), + stats.getFollowerMaxSeqNo()); + builder.field(ShardFollowStats.LAST_REQUESTED_SEQ_NO_FIELD.getPreferredName(), + stats.getLastRequestedSeqNo()); + builder.field(ShardFollowStats.OUTSTANDING_READ_REQUESTS.getPreferredName(), + stats.getOutstandingReadRequests()); + builder.field(ShardFollowStats.OUTSTANDING_WRITE_REQUESTS.getPreferredName(), + stats.getOutstandingWriteRequests()); + builder.field(ShardFollowStats.WRITE_BUFFER_OPERATION_COUNT_FIELD.getPreferredName(), + stats.getWriteBufferOperationCount()); + builder.humanReadableField( + ShardFollowStats.WRITE_BUFFER_SIZE_IN_BYTES_FIELD.getPreferredName(), + "write_buffer_size", + new ByteSizeValue(stats.getWriteBufferSizeInBytes())); + builder.field(ShardFollowStats.FOLLOWER_MAPPING_VERSION_FIELD.getPreferredName(), + stats.getFollowerMappingVersion()); + builder.field(ShardFollowStats.FOLLOWER_SETTINGS_VERSION_FIELD.getPreferredName(), + stats.getFollowerSettingsVersion()); + builder.humanReadableField( + ShardFollowStats.TOTAL_READ_TIME_MILLIS_FIELD.getPreferredName(), + "total_read_time", + new TimeValue(stats.getTotalReadTimeMillis(), TimeUnit.MILLISECONDS)); + builder.humanReadableField( + ShardFollowStats.TOTAL_READ_REMOTE_EXEC_TIME_MILLIS_FIELD.getPreferredName(), + "total_read_remote_exec_time", + new TimeValue(stats.getTotalReadRemoteExecTimeMillis(), TimeUnit.MILLISECONDS)); + builder.field(ShardFollowStats.SUCCESSFUL_READ_REQUESTS_FIELD.getPreferredName(), + stats.getSuccessfulReadRequests()); + builder.field(ShardFollowStats.FAILED_READ_REQUESTS_FIELD.getPreferredName(), + stats.getFailedReadRequests()); + builder.field(ShardFollowStats.OPERATIONS_READ_FIELD.getPreferredName(), stats.getOperationsReads()); + builder.humanReadableField( + ShardFollowStats.BYTES_READ.getPreferredName(), + "total_read", + new ByteSizeValue(stats.getBytesRead(), ByteSizeUnit.BYTES)); + builder.humanReadableField( + ShardFollowStats.TOTAL_WRITE_TIME_MILLIS_FIELD.getPreferredName(), + "total_write_time", + new TimeValue(stats.getTotalWriteTimeMillis(), TimeUnit.MILLISECONDS)); + builder.field(ShardFollowStats.SUCCESSFUL_WRITE_REQUESTS_FIELD.getPreferredName(), + stats.getSuccessfulWriteRequests()); + builder.field(ShardFollowStats.FAILED_WRITE_REQUEST_FIELD.getPreferredName(), + stats.getFailedWriteRequests()); + builder.field(ShardFollowStats.OPERATIONS_WRITTEN.getPreferredName(), stats.getOperationWritten()); + builder.startArray(ShardFollowStats.READ_EXCEPTIONS.getPreferredName()); + { + for (final Map.Entry> entry : + stats.getReadExceptions().entrySet()) { + builder.startObject(); + { + builder.field(ShardFollowStats.READ_EXCEPTIONS_ENTRY_FROM_SEQ_NO.getPreferredName(), + entry.getKey()); + builder.field(ShardFollowStats.READ_EXCEPTIONS_RETRIES.getPreferredName(), + entry.getValue().v1()); + builder.field(ShardFollowStats.READ_EXCEPTIONS_ENTRY_EXCEPTION.getPreferredName()); + builder.startObject(); + { + ElasticsearchException.generateThrowableXContent(builder, ToXContent.EMPTY_PARAMS, + entry.getValue().v2()); + } + builder.endObject(); + } + builder.endObject(); + } + } + builder.endArray(); + builder.humanReadableField( + ShardFollowStats.TIME_SINCE_LAST_READ_MILLIS_FIELD.getPreferredName(), + "time_since_last_read", + new TimeValue(stats.getTimeSinceLastReadMillis(), TimeUnit.MILLISECONDS)); + if (stats.getFatalException() != null) { + builder.field(ShardFollowStats.FATAL_EXCEPTION.getPreferredName()); + builder.startObject(); + { + ElasticsearchException.generateThrowableXContent(builder, ToXContent.EMPTY_PARAMS, + stats.getFatalException()); + } + builder.endObject(); + } + } + builder.endObject(); + } + } + builder.endArray(); + } + builder.endObject(); + } + builder.endArray(); + } + builder.endObject(); + } + + private static FollowStatsResponse createTestInstance() { + return new FollowStatsResponse(randomIndicesFollowStats()); + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java index 42bd600a224..3ee2083f475 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java @@ -37,6 +37,8 @@ import org.elasticsearch.client.ccr.AutoFollowStats; import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowStatsRequest; +import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse.Pattern; @@ -622,6 +624,51 @@ public class CCRDocumentationIT extends ESRestHighLevelClientTestCase { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } + public void testGetFollowStats() throws Exception { + RestHighLevelClient client = highLevelClient(); + + // tag::ccr-get-follow-stats-request + FollowStatsRequest request = + new FollowStatsRequest("follower"); // <1> + // end::ccr-get-follow-stats-request + + // tag::ccr-get-follow-stats-execute + FollowStatsResponse response = client.ccr() + .getFollowStats(request, RequestOptions.DEFAULT); + // end::ccr-get-follow-stats-execute + + // tag::ccr-get-follow-stats-response + IndicesFollowStats indicesFollowStats = + response.getIndicesFollowStats(); // <1> + // end::ccr-get-follow-stats-response + + // tag::ccr-get-follow-stats-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(FollowStatsResponse response) { // <1> + IndicesFollowStats indicesFollowStats = + response.getIndicesFollowStats(); + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::ccr-get-follow-stats-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::ccr-get-follow-stats-execute-async + client.ccr().getFollowStatsAsync(request, + RequestOptions.DEFAULT, listener); // <1> + // end::ccr-get-follow-stats-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } static Map toMap(Response response) throws IOException { return XContentHelper.convertToMap(JsonXContent.jsonXContent, EntityUtils.toString(response.getEntity()), false); diff --git a/docs/java-rest/high-level/ccr/get_follow_stats.asciidoc b/docs/java-rest/high-level/ccr/get_follow_stats.asciidoc new file mode 100644 index 00000000000..15b98abc686 --- /dev/null +++ b/docs/java-rest/high-level/ccr/get_follow_stats.asciidoc @@ -0,0 +1,35 @@ +-- +:api: ccr-get-follow-stats +:request: FollowStatsRequest +:response: FollowStatsResponse +-- + +[id="{upid}-{api}"] +=== Get Follow Stats API + + +[id="{upid}-{api}-request"] +==== Request + +The Get Follow Stats API allows you to get follow statistics for specific follower indices. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> The follower index to get follow statistics for. + +[id="{upid}-{api}-response"] +==== Response + +The returned +{response}+ includes follow statistics for the specified follower indices + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> The follow stats for specified follower indices. + +include::../execution.asciidoc[] + + diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 680ffdea825..a25205d9533 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -486,6 +486,7 @@ The Java High Level REST Client supports the following CCR APIs: * <<{upid}-ccr-delete-auto-follow-pattern>> * <<{upid}-ccr-get-auto-follow-pattern>> * <<{upid}-ccr-get-stats>> +* <<{upid}-ccr-get-follow-stats>> include::ccr/put_follow.asciidoc[] include::ccr/pause_follow.asciidoc[] @@ -495,6 +496,7 @@ include::ccr/put_auto_follow_pattern.asciidoc[] include::ccr/delete_auto_follow_pattern.asciidoc[] include::ccr/get_auto_follow_pattern.asciidoc[] include::ccr/get_stats.asciidoc[] +include::ccr/get_follow_stats.asciidoc[] == Index Lifecycle Management APIs