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 25eb260eec4df..243eee12195d8 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 @@ -20,6 +20,8 @@ package org.elasticsearch.client; 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.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; @@ -360,4 +362,48 @@ public void getAutoFollowPatternAsync(GetAutoFollowPatternRequest request, ); } + /** + * Gets all CCR stats. + * + * 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 CcrStatsResponse getCcrStats(CcrStatsRequest request, + RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + CcrRequestConverters::getCcrStats, + options, + CcrStatsResponse::fromXContent, + Collections.emptySet() + ); + } + + /** + * Gets all CCR stats. + * + * 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 getCcrStatsAsync(CcrStatsRequest request, + RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity( + request, + CcrRequestConverters::getCcrStats, + options, + CcrStatsResponse::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 5bcb0c04d3b86..8644651c59110 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 @@ -23,6 +23,7 @@ import org.apache.http.client.methods.HttpGet; 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.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.PauseFollowRequest; @@ -100,4 +101,11 @@ static Request getAutoFollowPattern(GetAutoFollowPatternRequest getAutoFollowPat return new Request(HttpGet.METHOD_NAME, endpoint); } + static Request getCcrStats(CcrStatsRequest ccrStatsRequest) { + String endpoint = new RequestConverters.EndpointBuilder() + .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/AutoFollowStats.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java new file mode 100644 index 0000000000000..09b57e68ff522 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java @@ -0,0 +1,105 @@ +/* + * 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.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public final class AutoFollowStats { + + static final ParseField NUMBER_OF_SUCCESSFUL_INDICES_AUTO_FOLLOWED = new ParseField("number_of_successful_follow_indices"); + static final ParseField NUMBER_OF_FAILED_INDICES_AUTO_FOLLOWED = new ParseField("number_of_failed_follow_indices"); + static final ParseField NUMBER_OF_FAILED_REMOTE_CLUSTER_STATE_REQUESTS = + new ParseField("number_of_failed_remote_cluster_state_requests"); + static final ParseField RECENT_AUTO_FOLLOW_ERRORS = new ParseField("recent_auto_follow_errors"); + static final ParseField LEADER_INDEX = new ParseField("leader_index"); + static final ParseField AUTO_FOLLOW_EXCEPTION = new ParseField("auto_follow_exception"); + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser STATS_PARSER = new ConstructingObjectParser<>("auto_follow_stats", + args -> new AutoFollowStats( + (Long) args[0], + (Long) args[1], + (Long) args[2], + new TreeMap<>( + ((List>) args[3]) + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) + )); + + private static final ConstructingObjectParser, Void> AUTO_FOLLOW_EXCEPTIONS_PARSER = + new ConstructingObjectParser<>( + "auto_follow_stats_errors", + args -> new AbstractMap.SimpleEntry<>((String) args[0], (ElasticsearchException) args[1])); + + static { + AUTO_FOLLOW_EXCEPTIONS_PARSER.declareString(ConstructingObjectParser.constructorArg(), LEADER_INDEX); + AUTO_FOLLOW_EXCEPTIONS_PARSER.declareObject( + ConstructingObjectParser.constructorArg(), + (p, c) -> ElasticsearchException.fromXContent(p), + AUTO_FOLLOW_EXCEPTION); + + STATS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_FAILED_INDICES_AUTO_FOLLOWED); + STATS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_FAILED_REMOTE_CLUSTER_STATE_REQUESTS); + STATS_PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_SUCCESSFUL_INDICES_AUTO_FOLLOWED); + STATS_PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), AUTO_FOLLOW_EXCEPTIONS_PARSER, + RECENT_AUTO_FOLLOW_ERRORS); + } + + private final long numberOfFailedFollowIndices; + private final long numberOfFailedRemoteClusterStateRequests; + private final long numberOfSuccessfulFollowIndices; + private final NavigableMap recentAutoFollowErrors; + + AutoFollowStats(long numberOfFailedFollowIndices, + long numberOfFailedRemoteClusterStateRequests, + long numberOfSuccessfulFollowIndices, + NavigableMap recentAutoFollowErrors) { + this.numberOfFailedFollowIndices = numberOfFailedFollowIndices; + this.numberOfFailedRemoteClusterStateRequests = numberOfFailedRemoteClusterStateRequests; + this.numberOfSuccessfulFollowIndices = numberOfSuccessfulFollowIndices; + this.recentAutoFollowErrors = recentAutoFollowErrors; + } + + public long getNumberOfFailedFollowIndices() { + return numberOfFailedFollowIndices; + } + + public long getNumberOfFailedRemoteClusterStateRequests() { + return numberOfFailedRemoteClusterStateRequests; + } + + public long getNumberOfSuccessfulFollowIndices() { + return numberOfSuccessfulFollowIndices; + } + + public NavigableMap getRecentAutoFollowErrors() { + return recentAutoFollowErrors; + } + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsRequest.java new file mode 100644 index 0000000000000..97f48535fef99 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsRequest.java @@ -0,0 +1,25 @@ +/* + * 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; + +public final class CcrStatsRequest implements Validatable { +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java new file mode 100644 index 0000000000000..889a96683bfb3 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java @@ -0,0 +1,62 @@ +/* + * 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.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +public final class CcrStatsResponse { + + static final ParseField AUTO_FOLLOW_STATS_FIELD = new ParseField("auto_follow_stats"); + static final ParseField FOLLOW_STATS_FIELD = new ParseField("follow_stats"); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices", + args -> { + AutoFollowStats autoFollowStats = (AutoFollowStats) args[0]; + IndicesFollowStats indicesFollowStats = (IndicesFollowStats) args[1]; + return new CcrStatsResponse(autoFollowStats, indicesFollowStats); + }); + + static { + PARSER.declareObject(ConstructingObjectParser.constructorArg(), AutoFollowStats.STATS_PARSER, AUTO_FOLLOW_STATS_FIELD); + PARSER.declareObject(ConstructingObjectParser.constructorArg(), IndicesFollowStats.PARSER, FOLLOW_STATS_FIELD); + } + + public static CcrStatsResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final AutoFollowStats autoFollowStats; + private final IndicesFollowStats indicesFollowStats; + + public CcrStatsResponse(AutoFollowStats autoFollowStats, IndicesFollowStats indicesFollowStats) { + this.autoFollowStats = autoFollowStats; + this.indicesFollowStats = indicesFollowStats; + } + + public AutoFollowStats getAutoFollowStats() { + return autoFollowStats; + } + + public IndicesFollowStats getIndicesFollowStats() { + return indicesFollowStats; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java new file mode 100644 index 0000000000000..02e2fc4f4ed18 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java @@ -0,0 +1,403 @@ +/* + * 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.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public final class IndicesFollowStats { + + static final ParseField INDICES_FIELD = new ParseField("indices"); + static final ParseField INDEX_FIELD = new ParseField("index"); + static final ParseField SHARDS_FIELD = new ParseField("shards"); + + private static final ConstructingObjectParser>, Void> ENTRY_PARSER = + new ConstructingObjectParser<>( + "entry", + args -> { + String index = (String) args[0]; + @SuppressWarnings("unchecked") + List shardFollowStats = (List) args[1]; + return new Tuple<>(index, shardFollowStats); + } + ); + + static { + ENTRY_PARSER.declareString(ConstructingObjectParser.constructorArg(), INDEX_FIELD); + ENTRY_PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), ShardFollowStats.PARSER, SHARDS_FIELD); + } + + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices", + args -> { + @SuppressWarnings("unchecked") + List>> entries = (List>>) args[0]; + Map> shardFollowStats = entries.stream().collect(Collectors.toMap(Tuple::v1, Tuple::v2)); + return new IndicesFollowStats(new TreeMap<>(shardFollowStats)); + }); + + static { + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), ENTRY_PARSER, INDICES_FIELD); + } + + private final NavigableMap> shardFollowStats; + + IndicesFollowStats(NavigableMap> shardFollowStats) { + this.shardFollowStats = Collections.unmodifiableNavigableMap(shardFollowStats); + } + + public List getShardFollowStats(String index) { + return shardFollowStats.get(index); + } + + public Map> getShardFollowStats() { + return shardFollowStats; + } + + public static final class ShardFollowStats { + + + static final ParseField LEADER_CLUSTER = new ParseField("remote_cluster"); + static final ParseField LEADER_INDEX = new ParseField("leader_index"); + static final ParseField FOLLOWER_INDEX = new ParseField("follower_index"); + static final ParseField SHARD_ID = new ParseField("shard_id"); + static final ParseField LEADER_GLOBAL_CHECKPOINT_FIELD = new ParseField("leader_global_checkpoint"); + static final ParseField LEADER_MAX_SEQ_NO_FIELD = new ParseField("leader_max_seq_no"); + static final ParseField FOLLOWER_GLOBAL_CHECKPOINT_FIELD = new ParseField("follower_global_checkpoint"); + static final ParseField FOLLOWER_MAX_SEQ_NO_FIELD = new ParseField("follower_max_seq_no"); + static final ParseField LAST_REQUESTED_SEQ_NO_FIELD = new ParseField("last_requested_seq_no"); + static final ParseField OUTSTANDING_READ_REQUESTS = new ParseField("outstanding_read_requests"); + static final ParseField OUTSTANDING_WRITE_REQUESTS = new ParseField("outstanding_write_requests"); + static final ParseField WRITE_BUFFER_OPERATION_COUNT_FIELD = new ParseField("write_buffer_operation_count"); + static final ParseField WRITE_BUFFER_SIZE_IN_BYTES_FIELD = new ParseField("write_buffer_size_in_bytes"); + static final ParseField FOLLOWER_MAPPING_VERSION_FIELD = new ParseField("follower_mapping_version"); + static final ParseField FOLLOWER_SETTINGS_VERSION_FIELD = new ParseField("follower_settings_version"); + static final ParseField TOTAL_READ_TIME_MILLIS_FIELD = new ParseField("total_read_time_millis"); + static final ParseField TOTAL_READ_REMOTE_EXEC_TIME_MILLIS_FIELD = new ParseField("total_read_remote_exec_time_millis"); + static final ParseField SUCCESSFUL_READ_REQUESTS_FIELD = new ParseField("successful_read_requests"); + static final ParseField FAILED_READ_REQUESTS_FIELD = new ParseField("failed_read_requests"); + static final ParseField OPERATIONS_READ_FIELD = new ParseField("operations_read"); + static final ParseField BYTES_READ = new ParseField("bytes_read"); + static final ParseField TOTAL_WRITE_TIME_MILLIS_FIELD = new ParseField("total_write_time_millis"); + static final ParseField SUCCESSFUL_WRITE_REQUESTS_FIELD = new ParseField("successful_write_requests"); + static final ParseField FAILED_WRITE_REQUEST_FIELD = new ParseField("failed_write_requests"); + static final ParseField OPERATIONS_WRITTEN = new ParseField("operations_written"); + static final ParseField READ_EXCEPTIONS = new ParseField("read_exceptions"); + static final ParseField TIME_SINCE_LAST_READ_MILLIS_FIELD = new ParseField("time_since_last_read_millis"); + static final ParseField FATAL_EXCEPTION = new ParseField("fatal_exception"); + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>( + "shard-follow-stats", + args -> new ShardFollowStats( + (String) args[0], + (String) args[1], + (String) args[2], + (int) args[3], + (long) args[4], + (long) args[5], + (long) args[6], + (long) args[7], + (long) args[8], + (int) args[9], + (int) args[10], + (int) args[11], + (long) args[12], + (long) args[13], + (long) args[14], + (long) args[15], + (long) args[16], + (long) args[17], + (long) args[18], + (long) args[19], + (long) args[20], + (long) args[21], + (long) args[22], + (long) args[23], + (long) args[24], + (long) args[25], + new TreeMap<>( + ((List>>) args[26]) + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))), + (ElasticsearchException) args[27])); + + static final ConstructingObjectParser>, Void> READ_EXCEPTIONS_ENTRY_PARSER = + new ConstructingObjectParser<>( + "shard-follow-stats-read-exceptions-entry", + args -> new AbstractMap.SimpleEntry<>((long) args[0], Tuple.tuple((Integer) args[1], (ElasticsearchException)args[2]))); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), LEADER_CLUSTER); + PARSER.declareString(ConstructingObjectParser.constructorArg(), LEADER_INDEX); + PARSER.declareString(ConstructingObjectParser.constructorArg(), FOLLOWER_INDEX); + PARSER.declareInt(ConstructingObjectParser.constructorArg(), SHARD_ID); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), LEADER_GLOBAL_CHECKPOINT_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), LEADER_MAX_SEQ_NO_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FOLLOWER_GLOBAL_CHECKPOINT_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FOLLOWER_MAX_SEQ_NO_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), LAST_REQUESTED_SEQ_NO_FIELD); + PARSER.declareInt(ConstructingObjectParser.constructorArg(), OUTSTANDING_READ_REQUESTS); + PARSER.declareInt(ConstructingObjectParser.constructorArg(), OUTSTANDING_WRITE_REQUESTS); + PARSER.declareInt(ConstructingObjectParser.constructorArg(), WRITE_BUFFER_OPERATION_COUNT_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), WRITE_BUFFER_SIZE_IN_BYTES_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FOLLOWER_MAPPING_VERSION_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FOLLOWER_SETTINGS_VERSION_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), TOTAL_READ_TIME_MILLIS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), TOTAL_READ_REMOTE_EXEC_TIME_MILLIS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), SUCCESSFUL_READ_REQUESTS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FAILED_READ_REQUESTS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), OPERATIONS_READ_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), BYTES_READ); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), TOTAL_WRITE_TIME_MILLIS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), SUCCESSFUL_WRITE_REQUESTS_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), FAILED_WRITE_REQUEST_FIELD); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), OPERATIONS_WRITTEN); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), TIME_SINCE_LAST_READ_MILLIS_FIELD); + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), READ_EXCEPTIONS_ENTRY_PARSER, READ_EXCEPTIONS); + PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), + (p, c) -> ElasticsearchException.fromXContent(p), + FATAL_EXCEPTION); + } + + static final ParseField READ_EXCEPTIONS_ENTRY_FROM_SEQ_NO = new ParseField("from_seq_no"); + static final ParseField READ_EXCEPTIONS_RETRIES = new ParseField("retries"); + static final ParseField READ_EXCEPTIONS_ENTRY_EXCEPTION = new ParseField("exception"); + + static { + READ_EXCEPTIONS_ENTRY_PARSER.declareLong(ConstructingObjectParser.constructorArg(), READ_EXCEPTIONS_ENTRY_FROM_SEQ_NO); + READ_EXCEPTIONS_ENTRY_PARSER.declareInt(ConstructingObjectParser.constructorArg(), READ_EXCEPTIONS_RETRIES); + READ_EXCEPTIONS_ENTRY_PARSER.declareObject( + ConstructingObjectParser.constructorArg(), + (p, c) -> ElasticsearchException.fromXContent(p), + READ_EXCEPTIONS_ENTRY_EXCEPTION); + } + + private final String remoteCluster; + private final String leaderIndex; + private final String followerIndex; + private final int shardId; + private final long leaderGlobalCheckpoint; + private final long leaderMaxSeqNo; + private final long followerGlobalCheckpoint; + private final long followerMaxSeqNo; + private final long lastRequestedSeqNo; + private final int outstandingReadRequests; + private final int outstandingWriteRequests; + private final int writeBufferOperationCount; + private final long writeBufferSizeInBytes; + private final long followerMappingVersion; + private final long followerSettingsVersion; + private final long totalReadTimeMillis; + private final long totalReadRemoteExecTimeMillis; + private final long successfulReadRequests; + private final long failedReadRequests; + private final long operationsReads; + private final long bytesRead; + private final long totalWriteTimeMillis; + private final long successfulWriteRequests; + private final long failedWriteRequests; + private final long operationWritten; + private final long timeSinceLastReadMillis; + private final NavigableMap> readExceptions; + private final ElasticsearchException fatalException; + + ShardFollowStats(String remoteCluster, + String leaderIndex, + String followerIndex, + int shardId, + long leaderGlobalCheckpoint, + long leaderMaxSeqNo, + long followerGlobalCheckpoint, + long followerMaxSeqNo, + long lastRequestedSeqNo, + int outstandingReadRequests, + int outstandingWriteRequests, + int writeBufferOperationCount, + long writeBufferSizeInBytes, + long followerMappingVersion, + long followerSettingsVersion, + long totalReadTimeMillis, + long totalReadRemoteExecTimeMillis, + long successfulReadRequests, + long failedReadRequests, + long operationsReads, + long bytesRead, + long totalWriteTimeMillis, + long successfulWriteRequests, + long failedWriteRequests, + long operationWritten, + long timeSinceLastReadMillis, + NavigableMap> readExceptions, + ElasticsearchException fatalException) { + this.remoteCluster = remoteCluster; + this.leaderIndex = leaderIndex; + this.followerIndex = followerIndex; + this.shardId = shardId; + this.leaderGlobalCheckpoint = leaderGlobalCheckpoint; + this.leaderMaxSeqNo = leaderMaxSeqNo; + this.followerGlobalCheckpoint = followerGlobalCheckpoint; + this.followerMaxSeqNo = followerMaxSeqNo; + this.lastRequestedSeqNo = lastRequestedSeqNo; + this.outstandingReadRequests = outstandingReadRequests; + this.outstandingWriteRequests = outstandingWriteRequests; + this.writeBufferOperationCount = writeBufferOperationCount; + this.writeBufferSizeInBytes = writeBufferSizeInBytes; + this.followerMappingVersion = followerMappingVersion; + this.followerSettingsVersion = followerSettingsVersion; + this.totalReadTimeMillis = totalReadTimeMillis; + this.totalReadRemoteExecTimeMillis = totalReadRemoteExecTimeMillis; + this.successfulReadRequests = successfulReadRequests; + this.failedReadRequests = failedReadRequests; + this.operationsReads = operationsReads; + this.bytesRead = bytesRead; + this.totalWriteTimeMillis = totalWriteTimeMillis; + this.successfulWriteRequests = successfulWriteRequests; + this.failedWriteRequests = failedWriteRequests; + this.operationWritten = operationWritten; + this.timeSinceLastReadMillis = timeSinceLastReadMillis; + this.readExceptions = readExceptions; + this.fatalException = fatalException; + } + + public String getRemoteCluster() { + return remoteCluster; + } + + public String getLeaderIndex() { + return leaderIndex; + } + + public String getFollowerIndex() { + return followerIndex; + } + + public int getShardId() { + return shardId; + } + + public long getLeaderGlobalCheckpoint() { + return leaderGlobalCheckpoint; + } + + public long getLeaderMaxSeqNo() { + return leaderMaxSeqNo; + } + + public long getFollowerGlobalCheckpoint() { + return followerGlobalCheckpoint; + } + + public long getFollowerMaxSeqNo() { + return followerMaxSeqNo; + } + + public long getLastRequestedSeqNo() { + return lastRequestedSeqNo; + } + + public int getOutstandingReadRequests() { + return outstandingReadRequests; + } + + public int getOutstandingWriteRequests() { + return outstandingWriteRequests; + } + + public int getWriteBufferOperationCount() { + return writeBufferOperationCount; + } + + public long getWriteBufferSizeInBytes() { + return writeBufferSizeInBytes; + } + + public long getFollowerMappingVersion() { + return followerMappingVersion; + } + + public long getFollowerSettingsVersion() { + return followerSettingsVersion; + } + + public long getTotalReadTimeMillis() { + return totalReadTimeMillis; + } + + public long getTotalReadRemoteExecTimeMillis() { + return totalReadRemoteExecTimeMillis; + } + + public long getSuccessfulReadRequests() { + return successfulReadRequests; + } + + public long getFailedReadRequests() { + return failedReadRequests; + } + + public long getOperationsReads() { + return operationsReads; + } + + public long getBytesRead() { + return bytesRead; + } + + public long getTotalWriteTimeMillis() { + return totalWriteTimeMillis; + } + + public long getSuccessfulWriteRequests() { + return successfulWriteRequests; + } + + public long getFailedWriteRequests() { + return failedWriteRequests; + } + + public long getOperationWritten() { + return operationWritten; + } + + public long getTimeSinceLastReadMillis() { + return timeSinceLastReadMillis; + } + + public NavigableMap> getReadExceptions() { + return readExceptions; + } + + public ElasticsearchException getFatalException() { + return fatalException; + } + } + +} 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 391ee1fcd18b4..2c63835af8c52 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 @@ -29,9 +29,12 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; 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.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; +import org.elasticsearch.client.ccr.IndicesFollowStats.ShardFollowStats; import org.elasticsearch.client.ccr.PauseFollowRequest; import org.elasticsearch.client.ccr.PutAutoFollowPatternRequest; import org.elasticsearch.client.ccr.PutFollowRequest; @@ -39,7 +42,6 @@ import org.elasticsearch.client.ccr.ResumeFollowRequest; import org.elasticsearch.client.ccr.UnfollowRequest; import org.elasticsearch.client.core.AcknowledgedResponse; -import org.elasticsearch.common.xcontent.ObjectPath; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; @@ -47,6 +49,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.equalTo; @@ -104,6 +107,15 @@ public void testIndexFollowing() throws Exception { assertThat(leaderSearchResponse.getHits().getTotalHits(), equalTo(1L)); assertBusy(() -> { + CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); + CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); + List shardFollowStats = ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); + long followerGlobalCheckpoint = shardFollowStats.stream() + .mapToLong(ShardFollowStats::getFollowerGlobalCheckpoint) + .max() + .getAsLong(); + assertThat(followerGlobalCheckpoint, equalTo(0L)); + SearchRequest followerSearchRequest = new SearchRequest("follower"); SearchResponse followerSearchResponse = highLevelClient().search(followerSearchRequest, RequestOptions.DEFAULT); assertThat(followerSearchResponse.getHits().getTotalHits(), equalTo(1L)); @@ -120,6 +132,15 @@ public void testIndexFollowing() throws Exception { assertThat(resumeFollowResponse.isAcknowledged(), is(true)); assertBusy(() -> { + CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); + CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); + List shardFollowStats = ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("follower"); + long followerGlobalCheckpoint = shardFollowStats.stream() + .mapToLong(ShardFollowStats::getFollowerGlobalCheckpoint) + .max() + .getAsLong(); + assertThat(followerGlobalCheckpoint, equalTo(1L)); + SearchRequest followerSearchRequest = new SearchRequest("follower"); SearchResponse followerSearchResponse = highLevelClient().search(followerSearchRequest, RequestOptions.DEFAULT); assertThat(followerSearchResponse.getHits().getTotalHits(), equalTo(2L)); @@ -156,15 +177,12 @@ public void testAutoFollowing() throws Exception { assertThat(response.isAcknowledged(), is(true)); assertBusy(() -> { - assertThat(indexExists("copy-logs-20200101"), is(true)); - // TODO: replace with HLRC follow stats when available: - Map rsp = toMap(client().performRequest(new Request("GET", "/copy-logs-20200101/_ccr/stats"))); - String index = null; - try { - index = ObjectPath.eval("indices.0.index", rsp); - } catch (Exception e){ } - assertThat(index, equalTo("copy-logs-20200101")); + CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); + CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); + assertThat(ccrStatsResponse.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), equalTo(1L)); + assertThat(ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("copy-logs-20200101"), notNullValue()); }); + assertThat(indexExists("copy-logs-20200101"), is(true)); GetAutoFollowPatternRequest getAutoFollowPatternRequest = randomBoolean() ? new GetAutoFollowPatternRequest("pattern1") : new GetAutoFollowPatternRequest(); 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 new file mode 100644 index 0000000000000..66e80130baf4a --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java @@ -0,0 +1,376 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +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 CcrStatsResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + xContentTester(this::createParser, + CcrStatsResponseTests::createTestInstance, + CcrStatsResponseTests::toXContent, + CcrStatsResponse::fromXContent) + .supportsUnknownFields(false) + .assertEqualsConsumer(CcrStatsResponseTests::assertEqualInstances) + .assertToXContentEquivalence(false) + .test(); + } + + // Needed, because exceptions in IndicesFollowStats and AutoFollowStats cannot be compared + private static void assertEqualInstances(CcrStatsResponse expectedInstance, CcrStatsResponse newInstance) { + assertNotSame(expectedInstance, newInstance); + + { + AutoFollowStats newAutoFollowStats = newInstance.getAutoFollowStats(); + AutoFollowStats expectedAutoFollowStats = expectedInstance.getAutoFollowStats(); + assertThat(newAutoFollowStats.getNumberOfSuccessfulFollowIndices(), + equalTo(expectedAutoFollowStats.getNumberOfSuccessfulFollowIndices())); + assertThat(newAutoFollowStats.getNumberOfFailedRemoteClusterStateRequests(), + equalTo(expectedAutoFollowStats.getNumberOfFailedRemoteClusterStateRequests())); + assertThat(newAutoFollowStats.getNumberOfFailedFollowIndices(), + equalTo(expectedAutoFollowStats.getNumberOfFailedFollowIndices())); + assertThat(newAutoFollowStats.getRecentAutoFollowErrors().size(), + equalTo(expectedAutoFollowStats.getRecentAutoFollowErrors().size())); + assertThat(newAutoFollowStats.getRecentAutoFollowErrors().keySet(), + equalTo(expectedAutoFollowStats.getRecentAutoFollowErrors().keySet())); + for (final Map.Entry entry : newAutoFollowStats.getRecentAutoFollowErrors().entrySet()) { + // x-content loses the exception + final ElasticsearchException expected = expectedAutoFollowStats.getRecentAutoFollowErrors().get(entry.getKey()); + assertThat(entry.getValue().getMessage(), containsString(expected.getMessage())); + assertNotNull(entry.getValue().getCause()); + assertThat( + entry.getValue().getCause(), + anyOf(instanceOf(ElasticsearchException.class), instanceOf(IllegalStateException.class))); + assertThat(entry.getValue().getCause().getMessage(), containsString(expected.getCause().getMessage())); + } + } + { + 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(CcrStatsResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + { + AutoFollowStats autoFollowStats = response.getAutoFollowStats(); + builder.startObject(CcrStatsResponse.AUTO_FOLLOW_STATS_FIELD.getPreferredName()); + { + builder.field(AutoFollowStats.NUMBER_OF_SUCCESSFUL_INDICES_AUTO_FOLLOWED.getPreferredName(), + autoFollowStats.getNumberOfSuccessfulFollowIndices()); + builder.field(AutoFollowStats.NUMBER_OF_FAILED_REMOTE_CLUSTER_STATE_REQUESTS.getPreferredName(), + autoFollowStats.getNumberOfFailedRemoteClusterStateRequests()); + builder.field(AutoFollowStats.NUMBER_OF_FAILED_INDICES_AUTO_FOLLOWED.getPreferredName(), + autoFollowStats.getNumberOfFailedFollowIndices()); + builder.startArray(AutoFollowStats.RECENT_AUTO_FOLLOW_ERRORS.getPreferredName()); + for (Map.Entry entry : autoFollowStats.getRecentAutoFollowErrors().entrySet()) { + builder.startObject(); + { + builder.field(AutoFollowStats.LEADER_INDEX.getPreferredName(), entry.getKey()); + builder.field(AutoFollowStats.AUTO_FOLLOW_EXCEPTION.getPreferredName()); + builder.startObject(); + { + ElasticsearchException.generateThrowableXContent(builder, ToXContent.EMPTY_PARAMS, entry.getValue()); + } + builder.endObject(); + } + builder.endObject(); + } + builder.endArray(); + } + builder.endObject(); + + IndicesFollowStats indicesFollowStats = response.getIndicesFollowStats(); + builder.startObject(CcrStatsResponse.FOLLOW_STATS_FIELD.getPreferredName()); + { + builder.startArray(IndicesFollowStats.INDICES_FIELD.getPreferredName()); + for (Map.Entry> indexEntry : + indicesFollowStats.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(); + } + builder.endObject(); + } + + private static CcrStatsResponse createTestInstance() { + return new CcrStatsResponse(randomAutoFollowStats(), randomIndicesFollowStats()); + } + + private static AutoFollowStats randomAutoFollowStats() { + final int count = randomIntBetween(0, 16); + final NavigableMap readExceptions = new TreeMap<>(); + for (int i = 0; i < count; i++) { + readExceptions.put("" + i, new ElasticsearchException(new IllegalStateException("index [" + i + "]"))); + } + return new AutoFollowStats( + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + readExceptions + ); + } + + private static IndicesFollowStats randomIndicesFollowStats() { + int numIndices = randomIntBetween(0, 16); + NavigableMap> shardFollowStats = new TreeMap<>(); + for (int i = 0; i < numIndices; i++) { + String index = randomAlphaOfLength(4); + int numShards = randomIntBetween(0, 5); + List stats = new ArrayList<>(numShards); + shardFollowStats.put(index, stats); + for (int j = 0; j < numShards; j++) { + final int count = randomIntBetween(0, 16); + final NavigableMap> readExceptions = new TreeMap<>(); + for (long k = 0; k < count; k++) { + readExceptions.put(k, new Tuple<>(randomIntBetween(0, Integer.MAX_VALUE), + new ElasticsearchException(new IllegalStateException("index [" + k + "]")))); + } + + stats.add(new ShardFollowStats( + randomAlphaOfLength(4), + randomAlphaOfLength(4), + randomAlphaOfLength(4), + randomInt(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomIntBetween(0, Integer.MAX_VALUE), + randomIntBetween(0, Integer.MAX_VALUE), + randomIntBetween(0, Integer.MAX_VALUE), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomLong(), + readExceptions, + randomBoolean() ? new ElasticsearchException("fatal error") : null)); + } + } + return new IndicesFollowStats(shardFollowStats); + } + +} 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 fea6ad0fbd720..42bd600a224f8 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 @@ -33,10 +33,14 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; +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.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternResponse.Pattern; +import org.elasticsearch.client.ccr.IndicesFollowStats; import org.elasticsearch.client.ccr.PauseFollowRequest; import org.elasticsearch.client.ccr.PutAutoFollowPatternRequest; import org.elasticsearch.client.ccr.PutFollowRequest; @@ -568,6 +572,57 @@ public void onFailure(Exception e) { } } + public void testGetCCRStats() throws Exception { + RestHighLevelClient client = highLevelClient(); + + // tag::ccr-get-stats-request + CcrStatsRequest request = + new CcrStatsRequest(); // <1> + // end::ccr-get-stats-request + + // tag::ccr-get-stats-execute + CcrStatsResponse response = client.ccr() + .getCcrStats(request, RequestOptions.DEFAULT); + // end::ccr-get-stats-execute + + // tag::ccr-get-stats-response + IndicesFollowStats indicesFollowStats = + response.getIndicesFollowStats(); // <1> + AutoFollowStats autoFollowStats = + response.getAutoFollowStats(); // <2> + // end::ccr-get-stats-response + + // tag::ccr-get-stats-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(CcrStatsResponse response) { // <1> + IndicesFollowStats indicesFollowStats = + response.getIndicesFollowStats(); + AutoFollowStats autoFollowStats = + response.getAutoFollowStats(); + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::ccr-get-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-stats-execute-async + client.ccr().getCcrStatsAsync(request, + RequestOptions.DEFAULT, listener); // <1> + // end::ccr-get-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_stats.asciidoc b/docs/java-rest/high-level/ccr/get_stats.asciidoc new file mode 100644 index 0000000000000..28c9e107a09f2 --- /dev/null +++ b/docs/java-rest/high-level/ccr/get_stats.asciidoc @@ -0,0 +1,37 @@ +-- +:api: ccr-get-stats +:request: CcrStatsRequest +:response: CcrStatsResponse +-- + +[id="{upid}-{api}"] +=== Get CCR Stats API + + +[id="{upid}-{api}-request"] +==== Request + +The Get CCR Stats API allows you to get statistics about index following and auto following. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> The request accepts no parameters. + +[id="{upid}-{api}-response"] +==== Response + +The returned +{response}+ always includes index follow statistics of all follow indices and +auto follow statistics. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> The follow stats of active follower indices. +<2> The auto follow stats of the cluster that has been queried. + +include::../execution.asciidoc[] + + diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 661ce78fe80a5..a8d5e580f840c 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -473,6 +473,7 @@ The Java High Level REST Client supports the following CCR APIs: * <<{upid}-ccr-put-auto-follow-pattern>> * <<{upid}-ccr-delete-auto-follow-pattern>> * <<{upid}-ccr-get-auto-follow-pattern>> +* <<{upid}-ccr-get-stats>> include::ccr/put_follow.asciidoc[] include::ccr/pause_follow.asciidoc[] @@ -481,6 +482,7 @@ include::ccr/unfollow.asciidoc[] 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[] == Index Lifecycle Management APIs