From 337fd75f67347d90d275e582ce796c7e36f93d84 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 21 Jan 2019 13:09:36 +0100 Subject: [PATCH 01/27] Adapt SearchRequest constructor --- .../action/search/SearchRequest.java | 63 +++++++++++-------- .../action/search/SearchRequestTests.java | 22 ++++--- .../TransportSearchActionSingleNodeTests.java | 11 ++-- .../search/RandomSearchRequestGenerator.java | 12 +--- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 69b090fb89a5a..05803a12b4225 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -105,21 +105,7 @@ public SearchRequest() { * Constructs a new search request from the provided search request */ public SearchRequest(SearchRequest searchRequest) { - this.allowPartialSearchResults = searchRequest.allowPartialSearchResults; - this.batchedReduceSize = searchRequest.batchedReduceSize; - this.indices = searchRequest.indices; - this.indicesOptions = searchRequest.indicesOptions; - this.maxConcurrentShardRequests = searchRequest.maxConcurrentShardRequests; - this.preference = searchRequest.preference; - this.preFilterShardSize = searchRequest.preFilterShardSize; - this.requestCache = searchRequest.requestCache; - this.routing = searchRequest.routing; - this.scroll = searchRequest.scroll; - this.searchType = searchRequest.searchType; - this.source = searchRequest.source; - this.types = searchRequest.types; - this.localClusterAlias = searchRequest.localClusterAlias; - this.absoluteStartMillis = searchRequest.absoluteStartMillis; + this(searchRequest, searchRequest.indices, searchRequest.localClusterAlias, searchRequest.absoluteStartMillis); } /** @@ -143,16 +129,39 @@ public SearchRequest(String[] indices, SearchSourceBuilder source) { } /** - * Creates a new search request by providing the alias of the cluster where it will be executed, as well as the current time in - * milliseconds from the epoch time. Used when a {@link SearchRequest} is created and executed as part of a cross-cluster search - * request performing local reduction on each cluster. The coordinating CCS node provides the alias to prefix index names with in - * the returned search results, and the current time to be used on the remote clusters to ensure that the same value is used. + * Creates a new search request by providing the search request to copy all fields from, the indices to search against, + * the alias of the cluster where it will be executed, as well as the start time in milliseconds from the epoch time. + * Used when a {@link SearchRequest} is created and executed as part of a cross-cluster search request performing local reduction + * on each cluster. The coordinating CCS node provides the original search request, the indices to search against as well as the + * alias to prefix index names with in the returned search results, and the absolute start time to be used on the remote clusters + * to ensure that the same value is used. */ - SearchRequest(String localClusterAlias, long absoluteStartMillis) { - this.localClusterAlias = Objects.requireNonNull(localClusterAlias, "cluster alias must not be null"); + static SearchRequest withLocalReduction(SearchRequest originalSearchRequest, String[] indices, + String localClusterAlias, long absoluteStartMillis) { + Objects.requireNonNull(originalSearchRequest, "search request must not be null"); + validateIndices(indices); + Objects.requireNonNull(localClusterAlias, "cluster alias must not be null"); if (absoluteStartMillis < 0) { throw new IllegalArgumentException("absoluteStartMillis must not be negative but was [" + absoluteStartMillis + "]"); } + return new SearchRequest(originalSearchRequest, indices, localClusterAlias, absoluteStartMillis); + } + + private SearchRequest(SearchRequest searchRequest, String[] indices, String localClusterAlias, long absoluteStartMillis) { + this.allowPartialSearchResults = searchRequest.allowPartialSearchResults; + this.batchedReduceSize = searchRequest.batchedReduceSize; + this.indices = indices; + this.indicesOptions = searchRequest.indicesOptions; + this.maxConcurrentShardRequests = searchRequest.maxConcurrentShardRequests; + this.preference = searchRequest.preference; + this.preFilterShardSize = searchRequest.preFilterShardSize; + this.requestCache = searchRequest.requestCache; + this.routing = searchRequest.routing; + this.scroll = searchRequest.scroll; + this.searchType = searchRequest.searchType; + this.source = searchRequest.source; + this.types = searchRequest.types; + this.localClusterAlias = localClusterAlias; this.absoluteStartMillis = absoluteStartMillis; } @@ -257,8 +266,8 @@ String getLocalClusterAlias() { /** * Returns the current time in milliseconds from the time epoch, to be used for the execution of this search request. Used to * ensure that the same value, determined by the coordinating node, is used on all nodes involved in the execution of the search - * request. When created through {@link #SearchRequest(String, long)}, this method returns the provided current time, otherwise - * it will return {@link System#currentTimeMillis()}. + * request. When created through {@link #withLocalReduction(SearchRequest, String[], String, long)}, this method returns the provided + * current time, otherwise it will return {@link System#currentTimeMillis()}. * */ long getOrCreateAbsoluteStartMillis() { @@ -270,12 +279,16 @@ long getOrCreateAbsoluteStartMillis() { */ @Override public SearchRequest indices(String... indices) { + validateIndices(indices); + this.indices = indices; + return this; + } + + private static void validateIndices(String... indices) { Objects.requireNonNull(indices, "indices must not be null"); for (String index : indices) { Objects.requireNonNull(index, "index must not be null"); } - this.indices = indices; - return this; } @Override diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 91f6c0c09cd20..7902e735ca493 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -22,12 +22,12 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.ArrayUtils; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.AbstractSearchTestCase; -import org.elasticsearch.search.RandomSearchRequestGenerator; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.rescore.QueryRescorerBuilder; @@ -48,19 +48,23 @@ public class SearchRequestTests extends AbstractSearchTestCase { @Override protected SearchRequest createSearchRequest() throws IOException { + SearchRequest request = super.createSearchRequest(); if (randomBoolean()) { - return super.createSearchRequest(); + return request; } //clusterAlias and absoluteStartMillis do not have public getters/setters hence we randomize them only in this test specifically. - SearchRequest searchRequest = new SearchRequest(randomAlphaOfLengthBetween(5, 10), randomNonNegativeLong()); - RandomSearchRequestGenerator.randomSearchRequest(searchRequest, this::createSearchSourceBuilder); - return searchRequest; + return SearchRequest.withLocalReduction(request, request.indices(), + randomAlphaOfLengthBetween(5, 10), randomNonNegativeLong()); } - public void testClusterAliasValidation() { - expectThrows(NullPointerException.class, () -> new SearchRequest(null, 0)); - expectThrows(IllegalArgumentException.class, () -> new SearchRequest("", -1)); - SearchRequest searchRequest = new SearchRequest("", 0); + public void testWithLocalReduction() { + expectThrows(NullPointerException.class, () -> SearchRequest.withLocalReduction(null, Strings.EMPTY_ARRAY, "", 0)); + SearchRequest request = new SearchRequest(); + expectThrows(NullPointerException.class, () -> SearchRequest.withLocalReduction(request, null, "", 0)); + expectThrows(NullPointerException.class, () -> SearchRequest.withLocalReduction(request, new String[]{null}, "", 0)); + expectThrows(NullPointerException.class, () -> SearchRequest.withLocalReduction(request, Strings.EMPTY_ARRAY, null, 0)); + expectThrows(IllegalArgumentException.class, () -> SearchRequest.withLocalReduction(request, Strings.EMPTY_ARRAY, "", -1)); + SearchRequest searchRequest = SearchRequest.withLocalReduction(request, Strings.EMPTY_ARRAY, "", 0); assertNull(searchRequest.validate()); } diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionSingleNodeTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionSingleNodeTests.java index 19bd76ec09da2..8fd75c5fd673d 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionSingleNodeTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionSingleNodeTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; +import org.elasticsearch.common.Strings; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; @@ -41,7 +42,7 @@ public void testLocalClusterAlias() { assertEquals(RestStatus.CREATED, indexResponse.status()); { - SearchRequest searchRequest = new SearchRequest("local", nowInMillis); + SearchRequest searchRequest = SearchRequest.withLocalReduction(new SearchRequest(), Strings.EMPTY_ARRAY, "local", nowInMillis); SearchResponse searchResponse = client().search(searchRequest).actionGet(); assertEquals(1, searchResponse.getHits().getTotalHits().value); SearchHit[] hits = searchResponse.getHits().getHits(); @@ -52,7 +53,7 @@ public void testLocalClusterAlias() { assertEquals("1", hit.getId()); } { - SearchRequest searchRequest = new SearchRequest("", nowInMillis); + SearchRequest searchRequest = SearchRequest.withLocalReduction(new SearchRequest(), Strings.EMPTY_ARRAY, "", nowInMillis); SearchResponse searchResponse = client().search(searchRequest).actionGet(); assertEquals(1, searchResponse.getHits().getTotalHits().value); SearchHit[] hits = searchResponse.getHits().getHits(); @@ -93,19 +94,19 @@ public void testAbsoluteStartMillis() { assertEquals(0, searchResponse.getTotalShards()); } { - SearchRequest searchRequest = new SearchRequest("", 0); + SearchRequest searchRequest = SearchRequest.withLocalReduction(new SearchRequest(), Strings.EMPTY_ARRAY, "", 0); SearchResponse searchResponse = client().search(searchRequest).actionGet(); assertEquals(2, searchResponse.getHits().getTotalHits().value); } { - SearchRequest searchRequest = new SearchRequest("", 0); + SearchRequest searchRequest = SearchRequest.withLocalReduction(new SearchRequest(), Strings.EMPTY_ARRAY, "", 0); searchRequest.indices(""); SearchResponse searchResponse = client().search(searchRequest).actionGet(); assertEquals(1, searchResponse.getHits().getTotalHits().value); assertEquals("test-1970.01.01", searchResponse.getHits().getHits()[0].getIndex()); } { - SearchRequest searchRequest = new SearchRequest("", 0); + SearchRequest searchRequest = SearchRequest.withLocalReduction(new SearchRequest(), Strings.EMPTY_ARRAY, "", 0); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); RangeQueryBuilder rangeQuery = new RangeQueryBuilder("date"); rangeQuery.gte("1970-01-01"); diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 6ec2732aaf915..0954e21f8813e 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -84,17 +84,7 @@ private RandomSearchRequestGenerator() {} * {@link #randomSearchSourceBuilder(Supplier, Supplier, Supplier, Supplier, Supplier)}. */ public static SearchRequest randomSearchRequest(Supplier randomSearchSourceBuilder) { - return randomSearchRequest(new SearchRequest(), randomSearchSourceBuilder); - } - - /** - * Set random fields to the provided search request. - * - * @param searchRequest the search request - * @param randomSearchSourceBuilder builds a random {@link SearchSourceBuilder}. You can use - * {@link #randomSearchSourceBuilder(Supplier, Supplier, Supplier, Supplier, Supplier)}. - */ - public static SearchRequest randomSearchRequest(SearchRequest searchRequest, Supplier randomSearchSourceBuilder) { + SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); From f100b464fec7951ec405cd316866d51a0266a275 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 21 Jan 2019 13:09:52 +0100 Subject: [PATCH 02/27] add default from and size public constants --- .../main/java/org/elasticsearch/search/SearchService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java index 5e2758eb5b83c..c218f848da15e 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchService.java +++ b/server/src/main/java/org/elasticsearch/search/SearchService.java @@ -148,6 +148,8 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv public static final Setting MAX_OPEN_SCROLL_CONTEXT = Setting.intSetting("search.max_open_scroll_context", 500, 0, Property.Dynamic, Property.NodeScope); + public static final int DEFAULT_SIZE = 10; + public static final int DEFAULT_FROM = 0; private final ThreadPool threadPool; @@ -606,10 +608,10 @@ final SearchContext createContext(ShardSearchRequest request) throws IOException // if the from and size are still not set, default them if (context.from() == -1) { - context.from(0); + context.from(DEFAULT_FROM); } if (context.size() == -1) { - context.size(10); + context.size(DEFAULT_SIZE); } // pre process From f3daf695928548654c4dd10c1766c3e0a1e9e8ef Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 21 Jan 2019 13:10:29 +0100 Subject: [PATCH 03/27] [TEST] add search handler to test transport --- .../CrossClusterSearchUnavailableClusterIT.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index efac9ac220573..e280b1d2d1a05 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -22,6 +22,7 @@ import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; +import org.apache.lucene.search.TotalHits; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsAction; @@ -32,9 +33,11 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchScrollRequest; +import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; @@ -49,6 +52,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; @@ -107,6 +112,14 @@ private static MockTransportService startTransport( channel.sendResponse(new ClusterSearchShardsResponse(new ClusterSearchShardsGroup[0], knownNodes.toArray(new DiscoveryNode[0]), Collections.emptyMap())); }); + newService.registerRequestHandler(SearchAction.NAME, ThreadPool.Names.SAME, SearchRequest::new, + (request, channel, task) -> { + InternalSearchResponse response = new InternalSearchResponse(new SearchHits(new SearchHit[0], + new TotalHits(0, TotalHits.Relation.EQUAL_TO), Float.NaN), InternalAggregations.EMPTY, null, null, false, null, 1); + SearchResponse searchResponse = new SearchResponse(response, null, 1, 1, 0, 100, ShardSearchFailure.EMPTY_ARRAY, + SearchResponse.Clusters.EMPTY); + channel.sendResponse(searchResponse); + }); newService.registerRequestHandler(ClusterStateAction.NAME, ThreadPool.Names.SAME, ClusterStateRequest::new, (request, channel, task) -> { DiscoveryNodes.Builder builder = DiscoveryNodes.builder(); From e05725e0c17ad2591c6b3a19dca4dde2835fe5af Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 21 Jan 2019 13:11:14 +0100 Subject: [PATCH 04/27] add comment on registering same cluster multiple times --- .../org/elasticsearch/action/search/SearchResponseMerger.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index b146d42c0d2e6..0e996577fd3da 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -257,6 +257,8 @@ private static void setShardIndex(Map shards, List to FieldDocAndSearchHit fieldDocAndSearchHit = (FieldDocAndSearchHit) scoreDoc; //When hits come from the indices with same names on multiple clusters and same shard identifier, we rely on such indices //to have a different uuid across multiple clusters. That's how they will get a different shardIndex. + //In case the same remote cluster is registered twice using different aliases, a search across such clusters that are the + //same and hold the same documents will make an assertion in lucene fail (see TopDocs#tieBreakLessThan line 86). ShardId shardId = fieldDocAndSearchHit.searchHit.getShard().getShardId(); fieldDocAndSearchHit.shardIndex = shards.get(shardId); } From eae918b7fca1d2f3cf6ef0fc88d3936421893c0f Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 21 Jan 2019 13:40:16 +0100 Subject: [PATCH 05/27] wip --- .../action/search/TransportSearchAction.java | 130 +++++++++++++++--- 1 file changed, 109 insertions(+), 21 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 30e030eca7376..0884737476a6f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -49,6 +49,7 @@ import org.elasticsearch.search.SearchService; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.internal.AliasFilter; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.RemoteClusterAware; @@ -190,8 +191,8 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< new SearchTimeProvider(searchRequest.getOrCreateAbsoluteStartMillis(), relativeStartNanos, System::nanoTime); ActionListener rewriteListener = ActionListener.wrap(source -> { if (source != searchRequest.source()) { - // only set it if it changed - we don't allow null values to be set but it might be already null be we want to catch - // situations when it possible due to a bug changes to null + // only set it if it changed - we don't allow null values to be set but it might be already null. this way we catch + // situations when source is rewritten to null due to a bug searchRequest.source(source); } final ClusterState clusterState = clusterService.state(); @@ -199,26 +200,70 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< searchRequest.indices(), idx -> indexNameExpressionResolver.hasIndexOrAlias(idx, clusterState)); OriginalIndices localIndices = remoteClusterIndices.remove(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); if (remoteClusterIndices.isEmpty()) { - executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(), - (clusterName, nodeId) -> null, clusterState, Collections.emptyMap(), listener, SearchResponse.Clusters.EMPTY); + executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - AtomicInteger skippedClusters = new AtomicInteger(0); - collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, - remoteClusterIndices, remoteClusterService, threadPool, - ActionListener.wrap( - searchShardsResponses -> { - List remoteShardIterators = new ArrayList<>(); - Map remoteAliasFilters = new HashMap<>(); - BiFunction clusterNodeLookup = processRemoteShards( - searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); - int localClusters = localIndices == null ? 0 : 1; - int totalClusters = remoteClusterIndices.size() + localClusters; - int successfulClusters = searchShardsResponses.size() + localClusters; - executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, - remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, - new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get())); - }, - listener::onFailure)); + //TODO discuss the heuristic and add flag to opt-out of local reduction? + boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null + && source.collapse().getInnerHits().isEmpty() == false; + if (searchRequest.scroll() == null && collapseWithInnerHits == false) { + final int from; + final int size; + final int trackTotalHitsUpTo; + if (source == null) { + from = SearchService.DEFAULT_FROM; + size = SearchService.DEFAULT_SIZE; + trackTotalHitsUpTo = SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO; + } else { + from = source.from() == -1 ? SearchService.DEFAULT_FROM : source.from(); + size = source.size() == -1 ? SearchService.DEFAULT_SIZE : source.size(); + trackTotalHitsUpTo = source.trackTotalHitsUpTo(); + //here we modify the original source so we can re-use it by setting it to each outgoing search request + source.from(0); + source.size(from + size); + } + + SearchResponseMerger searchResponseMerger = new SearchResponseMerger(from, size, trackTotalHitsUpTo, + timeProvider, searchService::createReduceContext); + + final AtomicReference exceptions = new AtomicReference<>(); + final CountDown countDown = new CountDown(remoteClusterIndices.size() + (localIndices == null ? 0 : 1)); + for (Map.Entry entry : remoteClusterIndices.entrySet()) { + String clusterAlias = entry.getKey(); + OriginalIndices indices = entry.getValue(); + SearchRequest ccsSearchRequest = SearchRequest.withLocalReduction(searchRequest, indices.indices(), + clusterAlias, timeProvider.getAbsoluteStartMillis()); + ActionListener ccsListener = createCCSListener( + e -> new RemoteTransportException("error while communicating with remote cluster [" + clusterAlias + "]", e), + countDown, exceptions, searchResponseMerger, listener); + Client remoteClusterClient = remoteClusterService.getRemoteClusterClient(threadPool, clusterAlias); + remoteClusterClient.search(ccsSearchRequest, ccsListener); + } + if (localIndices != null) { + ActionListener ccsListener = createCCSListener(e -> e, countDown, exceptions, + searchResponseMerger, listener); + SearchRequest ccsLocalSearchRequest = SearchRequest.withLocalReduction(searchRequest, localIndices.indices(), + RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); + executeLocalSearch(task, timeProvider, ccsLocalSearchRequest, localIndices, clusterState, ccsListener); + } + } else { + AtomicInteger skippedClusters = new AtomicInteger(0); + collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, + remoteClusterIndices, remoteClusterService, threadPool, + ActionListener.wrap( + searchShardsResponses -> { + List remoteShardIterators = new ArrayList<>(); + Map remoteAliasFilters = new HashMap<>(); + BiFunction clusterNodeLookup = processRemoteShards( + searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); + int localClusters = localIndices == null ? 0 : 1; + int totalClusters = remoteClusterIndices.size() + localClusters; + int successfulClusters = searchShardsResponses.size() + localClusters; + executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, + remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, + new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get())); + }, + listener::onFailure)); + } } }, listener::onFailure); if (searchRequest.source() == null) { @@ -281,6 +326,49 @@ private void maybeFinish() { } } + private ActionListener createCCSListener(Function exceptionWrapper, + CountDown countDown, + AtomicReference exceptions, + SearchResponseMerger searchResponseMerger, + ActionListener originalListener) { + return ActionListener.wrap( + response -> { + searchResponseMerger.add(response); + //TODO test when merge fails + if (countDown.countDown()) { + Exception exception = exceptions.get(); + if (exception == null) { + //originalListener.onResponse(searchResponseMerger.getMergedResponse()); + } else { + //TODO here we need to support skip_unavailable + originalListener.onFailure(exceptions.get()); + } + } + }, + e -> { + Exception exception = exceptionWrapper.apply(e); + if (exceptions.compareAndSet(null, exception) == false) { + exception = exceptions.accumulateAndGet(exception, (previous, current) -> { + current.addSuppressed(previous); + return current; + }); + } + //it can happen that onResponse throws and calls onFailure, then countDown + //is not enough or we may never notify the listener of the failure + if (countDown.isCountedDown() || countDown.countDown()) { + originalListener.onFailure(exception); + } + } + ); + } + + + private void executeLocalSearch(Task task, SearchTimeProvider timeProvider, SearchRequest searchRequest, OriginalIndices localIndices, + ClusterState clusterState, ActionListener listener) { + executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(), + (clusterName, nodeId) -> null, clusterState, Collections.emptyMap(), listener, SearchResponse.Clusters.EMPTY); + } + static BiFunction processRemoteShards(Map searchShardsResponses, Map remoteIndicesByCluster, List remoteShardIterators, From 2703d7483a1b14ba521bd84edc2c14e49fb38180 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 22 Jan 2019 12:29:15 +0100 Subject: [PATCH 06/27] add search request ccs_execution_mode option --- .../client/RequestConverters.java | 3 ++ .../client/RequestConvertersTests.java | 6 ++- .../modules/cross-cluster-search.asciidoc | 38 +++++++++++++++ docs/reference/search/request-body.asciidoc | 5 ++ .../mustache/MultiSearchTemplateRequest.java | 1 - .../resources/rest-api-spec/api/msearch.json | 6 +++ .../rest-api-spec/api/msearch_template.json | 6 +++ .../resources/rest-api-spec/api/search.json | 6 +++ .../rest-api-spec/api/search_template.json | 6 +++ .../action/search/MultiSearchRequest.java | 9 ++++ .../action/search/SearchRequest.java | 47 ++++++++++++++++++- .../action/search/RestMultiSearchAction.java | 3 +- .../rest/action/search/RestSearchAction.java | 3 ++ .../search/MultiSearchRequestTests.java | 12 ++--- .../action/search/SearchRequestTests.java | 13 ++++- .../search/RandomSearchRequestGenerator.java | 3 ++ 16 files changed, 155 insertions(+), 12 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 7a6a562e4fb7f..b88d9e52b034b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -398,6 +398,9 @@ private static void addSearchRequestParams(Params params, SearchRequest searchRe params.withPreference(searchRequest.preference()); params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); + if (searchRequest.getCCSExecutionMode() != null) { + params.putParam("ccs_execution_mode", searchRequest.getCCSExecutionMode().toString()); + } if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index f31c562332687..79f2d95e44673 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -1239,7 +1239,7 @@ public void testMultiSearch() throws IOException { requests.add(searchRequest); }; MultiSearchRequest.readMultiLineFormat(new BytesArray(EntityUtils.toByteArray(request.getEntity())), - REQUEST_BODY_CONTENT_TYPE.xContent(), consumer, null, multiSearchRequest.indicesOptions(), null, null, null, + REQUEST_BODY_CONTENT_TYPE.xContent(), consumer, null, multiSearchRequest.indicesOptions(), null, null, null, null, xContentRegistry(), true); assertEquals(requests, multiSearchRequest.requests()); } @@ -1862,6 +1862,10 @@ private static void setRandomSearchParams(SearchRequest searchRequest, searchRequest.scroll(randomTimeValue()); expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); } + if (randomBoolean()) { + searchRequest.setCCSExecutionMode(randomFrom(SearchRequest.CCSExecutionMode.values())); + expectedParams.put("ccs_execution_mode", searchRequest.getCCSExecutionMode().toString()); + } } static void setRandomIndicesOptions(Consumer setter, Supplier getter, diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index 61b0bb50aedc7..cb56c0b1c40b2 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -273,3 +273,41 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> // TESTRESPONSE[s/"_score": 1/"_score": "$body.hits.hits.0._score"/] // TESTRESPONSE[s/"_score": 2/"_score": "$body.hits.hits.1._score"/] <1> The `clusters` section indicates that one cluster was unavailable and got skipped + +[float] +[[ccs-execution-mode]] +=== Execution mode + +When searching across remote clusters, there are two possible execution modes: + +- `one_request_per_cluster`: the coordinating node sends one search +request to each cluster. Each cluster performs the search independently, +reducing and fetching results which are then returned to the caller. +Once the CCS node has received all the responses, it performs another +reduction and returns the relevant results back to the user. This execution +mode is beneficial when there is network latency between the coordinating node +and the remote clusters involved, which is typically the case. A single request +is sent to each remote cluster, at the cost of retrieving `from` + `size` +already fetched results from each one of them. + +- `one_request_per_shard`: the coordinating node sends a +<> request to each remote cluster, to collect +information about their corresponding remote indices involved in the search +request and the shards where their data is located. Once each cluster has +responded to such request, the search executes as if all shards were part of +the same cluster. The coordinating node sends one request to each shard +involved, each shard executes the query and returns its own results which are +then reduced (and fetched, depending on the +<>) by the coordinating node. This +execution mode is beneficial whenever there is very low network latency between +the coordinating node and the remote clusters involved. + +`one_request_per_cluster` is the default execution mode, but it does not +support scroll requests, as well as requests where inner hits are requested +as part of field collapsing, in which cases `one_request_per_shard` is +automatically used instead. The <> supports +the `ccs_execution_mode` parameter which allows to select the execution mode. + +Note that all the communication between the nodes, regardless of which cluster +they belong to and the selected execution mode, happens through the +<>. diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index dac7622aab8ed..fa438af86bfaf 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -113,6 +113,11 @@ And here is a sample response: reduce the memory overhead per search request if the potential number of shards in the request can be large. +`ccs_execution_mode`:: + + The cross-cluster search execution mode. Can be `one_request_per_shard` or + `prefer_one_request_per_cluster`. Defaults to `prefer_one_request_per_cluster`. + See <> for more. Out of the above, the `search_type`, `request_cache` and the `allow_partial_search_results` diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java index a685c3ba5ba7c..c80f99484a947 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MultiSearchTemplateRequest.java @@ -65,7 +65,6 @@ public MultiSearchTemplateRequest add(SearchTemplateRequest request) { return this; } - /** * Returns the amount of search requests specified in this multi search requests are allowed to be ran concurrently. */ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index 13a6005c9a189..fa41c51df8d0a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -43,6 +43,12 @@ "type" : "boolean", "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false + }, + "ccs_execution_mode": { + "type" : "enum", + "options" : ["one_request_per_shard","one_request_per_cluster"], + "default" : "one_request_per_cluster", + "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index e49cc2083f929..35f21d485162e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -33,6 +33,12 @@ "type" : "boolean", "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false + }, + "ccs_execution_mode": { + "type" : "enum", + "options" : ["one_request_per_shard","one_request_per_cluster"], + "default" : "one_request_per_cluster", + "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index 9ac02b1214a2f..de69e89c8a2ae 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -24,6 +24,12 @@ "type" : "boolean", "description" : "Specify whether wildcard and prefix queries should be analyzed (default: false)" }, + "ccs_execution_mode": { + "type" : "enum", + "options" : ["one_request_per_shard","one_request_per_cluster"], + "default" : "one_request_per_cluster", + "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" + }, "default_operator": { "type" : "enum", "options" : ["AND","OR"], diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index 528ee905e7ee8..cd15e684c9679 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -67,6 +67,12 @@ "type" : "boolean", "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false + }, + "ccs_execution_mode": { + "type" : "enum", + "options" : ["one_request_per_shard","one_request_per_cluster"], + "default" : "one_request_per_cluster", + "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } }, diff --git a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java index a574c0559804b..e7b9becbe4493 100644 --- a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java @@ -173,6 +173,7 @@ public static void readMultiLineFormat(BytesReference data, String[] types, String routing, String searchType, + String ccsExecutionMode, NamedXContentRegistry registry, boolean allowExplicitIndex) throws IOException { int from = 0; @@ -205,6 +206,9 @@ public static void readMultiLineFormat(BytesReference data, if (searchType != null) { searchRequest.searchType(searchType); } + if (ccsExecutionMode != null) { + searchRequest.setCCSExecutionMode(ccsExecutionMode); + } IndicesOptions defaultOptions = searchRequest.indicesOptions(); // now parse the action if (nextMarker - from > 0) { @@ -226,6 +230,8 @@ public static void readMultiLineFormat(BytesReference data, searchRequest.types(nodeStringArrayValue(value)); } else if ("search_type".equals(entry.getKey()) || "searchType".equals(entry.getKey())) { searchRequest.searchType(nodeStringValue(value, null)); + } else if ("ccs_execution_mode".equals(entry.getKey()) || "ccsExecutionMode".equals(entry.getKey())) { + searchRequest.setCCSExecutionMode(nodeStringValue(value, null)); } else if ("request_cache".equals(entry.getKey()) || "requestCache".equals(entry.getKey())) { searchRequest.requestCache(nodeBooleanValue(value, entry.getKey())); } else if ("preference".equals(entry.getKey())) { @@ -327,6 +333,9 @@ public static void writeSearchRequestParams(SearchRequest request, XContentBuild if (request.searchType() != null) { xContentBuilder.field("search_type", request.searchType().name().toLowerCase(Locale.ROOT)); } + if (request.getCCSExecutionMode() != null) { + xContentBuilder.field("ccs_execution_mode", request.getCCSExecutionMode().toString()); + } if (request.requestCache() != null) { xContentBuilder.field("request_cache", request.requestCache()); } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 05803a12b4225..18e0705f12929 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -38,6 +38,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -92,6 +93,8 @@ public final class SearchRequest extends ActionRequest implements IndicesRequest private String[] types = Strings.EMPTY_ARRAY; + private CCSExecutionMode ccsExecutionMode; + public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled(); private IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS; @@ -150,6 +153,7 @@ static SearchRequest withLocalReduction(SearchRequest originalSearchRequest, Str private SearchRequest(SearchRequest searchRequest, String[] indices, String localClusterAlias, long absoluteStartMillis) { this.allowPartialSearchResults = searchRequest.allowPartialSearchResults; this.batchedReduceSize = searchRequest.batchedReduceSize; + this.ccsExecutionMode = searchRequest.ccsExecutionMode; this.indices = indices; this.indicesOptions = searchRequest.indicesOptions; this.maxConcurrentShardRequests = searchRequest.maxConcurrentShardRequests; @@ -199,6 +203,9 @@ public SearchRequest(StreamInput in) throws IOException { localClusterAlias = null; absoluteStartMillis = DEFAULT_ABSOLUTE_START_MILLIS; } + if (in.getVersion().onOrAfter(Version.V_7_0_0)) { + ccsExecutionMode = in.readEnum(CCSExecutionMode.class); + } } @Override @@ -225,6 +232,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVLong(absoluteStartMillis); } } + if (out.getVersion().onOrAfter(Version.V_7_0_0)) { + out.writeEnum(ccsExecutionMode); + } } @Override @@ -301,6 +311,18 @@ public SearchRequest indicesOptions(IndicesOptions indicesOptions) { return this; } + public void setCCSExecutionMode(CCSExecutionMode ccsExecutionMode) { + this.ccsExecutionMode = Objects.requireNonNull(ccsExecutionMode, "ccsExecutionMode must not be null"); + } + + public void setCCSExecutionMode(String ccsExecutionMode) { + this.ccsExecutionMode = CCSExecutionMode.fromString(ccsExecutionMode); + } + + public CCSExecutionMode getCCSExecutionMode() { + return this.ccsExecutionMode; + } + /** * The document types to execute the search against. Defaults to be executed against * all types. @@ -592,14 +614,15 @@ public boolean equals(Object o) { Objects.equals(indicesOptions, that.indicesOptions) && Objects.equals(allowPartialSearchResults, that.allowPartialSearchResults) && Objects.equals(localClusterAlias, that.localClusterAlias) && - absoluteStartMillis == that.absoluteStartMillis; + absoluteStartMillis == that.absoluteStartMillis && + ccsExecutionMode == that.ccsExecutionMode; } @Override public int hashCode() { return Objects.hash(searchType, Arrays.hashCode(indices), routing, preference, source, requestCache, scroll, Arrays.hashCode(types), indicesOptions, batchedReduceSize, maxConcurrentShardRequests, preFilterShardSize, - allowPartialSearchResults, localClusterAlias, absoluteStartMillis); + allowPartialSearchResults, localClusterAlias, absoluteStartMillis, ccsExecutionMode); } @Override @@ -619,6 +642,26 @@ public String toString() { ", allowPartialSearchResults=" + allowPartialSearchResults + ", localClusterAlias=" + localClusterAlias + ", getOrCreateAbsoluteStartMillis=" + absoluteStartMillis + + ", ccsExecutionMode=" + ccsExecutionMode + ", source=" + source + '}'; } + + public enum CCSExecutionMode { + ONE_REQUEST_PER_SHARD, + PREFER_ONE_REQUEST_PER_CLUSTER; + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } + + public static CCSExecutionMode fromString(String executionMode) { + for (CCSExecutionMode value : CCSExecutionMode.values()) { + if (value.name().toLowerCase(Locale.ROOT).equals(executionMode)) { + return value; + } + } + throw new IllegalArgumentException("unknown ccs_execution_mode: [" + executionMode + "]"); + } + } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index 49bebe053a3f9..ace4f7de12e81 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -147,13 +147,14 @@ public static void parseMultiLineRequest(RestRequest request, IndicesOptions ind String[] indices = Strings.splitStringByCommaToArray(request.param("index")); String[] types = Strings.splitStringByCommaToArray(request.param("type")); String searchType = request.param("search_type"); + String ccsExecutionMode = request.param("ccs_execution_mode"); String routing = request.param("routing"); final Tuple sourceTuple = request.contentOrSourceParam(); final XContent xContent = sourceTuple.v1().xContent(); final BytesReference data = sourceTuple.v2(); MultiSearchRequest.readMultiLineFormat(data, xContent, consumer, indices, indicesOptions, types, routing, - searchType, request.getXContentRegistry(), allowExplicitIndex); + searchType, ccsExecutionMode, request.getXContentRegistry(), allowExplicitIndex); } @Override diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index da773efed580d..2c3d706ed631b 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -173,6 +173,9 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r searchRequest.routing(request.param("routing")); searchRequest.preference(request.param("preference")); searchRequest.indicesOptions(IndicesOptions.fromRequest(request, searchRequest.indicesOptions())); + if (request.hasParam("ccs_execution_mode")) { + searchRequest.setCCSExecutionMode(request.param("ccs_execution_mode")); + } checkRestTotalHits(request, searchRequest); } diff --git a/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java index bd12f46564bac..da22ce4c96c1a 100644 --- a/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java @@ -180,7 +180,7 @@ public void testSimpleAdd4() throws Exception { assertThat(request.requests().get(2).routing(), equalTo("123")); } - public void testResponseErrorToXContent() throws IOException { + public void testResponseErrorToXContent() { long tookInMillis = randomIntBetween(1, 1000); MultiSearchResponse response = new MultiSearchResponse( new MultiSearchResponse.Item[] { @@ -262,12 +262,12 @@ public void testMultiLineSerialization() throws IOException { parsedRequest.add(r); }; MultiSearchRequest.readMultiLineFormat(new BytesArray(originalBytes), xContentType.xContent(), - consumer, null, null, null, null, null, xContentRegistry(), true); + consumer, null, null, null, null, null, null, xContentRegistry(), true); assertEquals(originalRequest, parsedRequest); } } - public void testEqualsAndHashcode() throws IOException { + public void testEqualsAndHashcode() { checkEqualsAndHashCode(createMultiSearchRequest(), MultiSearchRequestTests::copyRequest, MultiSearchRequestTests::mutate); } @@ -282,7 +282,7 @@ private static MultiSearchRequest mutate(MultiSearchRequest searchRequest) throw return mutation; } - private static MultiSearchRequest copyRequest(MultiSearchRequest request) throws IOException { + private static MultiSearchRequest copyRequest(MultiSearchRequest request) { MultiSearchRequest copy = new MultiSearchRequest(); if (request.maxConcurrentSearchRequests() > 0) { copy.maxConcurrentSearchRequests(request.maxConcurrentSearchRequests()); @@ -294,7 +294,7 @@ private static MultiSearchRequest copyRequest(MultiSearchRequest request) throws return copy; } - private static MultiSearchRequest createMultiSearchRequest() throws IOException { + private static MultiSearchRequest createMultiSearchRequest() { int numSearchRequest = randomIntBetween(1, 128); MultiSearchRequest request = new MultiSearchRequest(); for (int j = 0; j < numSearchRequest; j++) { @@ -321,7 +321,7 @@ private static MultiSearchRequest createMultiSearchRequest() throws IOException return request; } - private static SearchRequest createSimpleSearchRequest() throws IOException { + private static SearchRequest createSimpleSearchRequest() { return randomSearchRequest(() -> { // No need to return a very complex SearchSourceBuilder here, that is tested elsewhere SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 7902e735ca493..9abe2f5712198 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -76,10 +76,15 @@ public void testSerialization() throws Exception { assertNotSame(deserializedRequest, searchRequest); } - public void testClusterAliasSerialization() throws IOException { + public void testRandomVersionSerialization() throws IOException { SearchRequest searchRequest = createSearchRequest(); Version version = VersionUtils.randomVersion(random()); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new, version); + if (version.before(Version.V_7_0_0)) { + assertEquals(SearchRequest.CCSExecutionMode.ONE_REQUEST_PER_SHARD, deserializedRequest.getCCSExecutionMode()); + } else { + assertEquals(searchRequest.getCCSExecutionMode(), deserializedRequest.getCCSExecutionMode()); + } if (version.before(Version.V_6_7_0)) { assertNull(deserializedRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(deserializedRequest); @@ -97,6 +102,7 @@ public void testReadFromPre6_7_0() throws IOException { assertArrayEquals(new String[]{"index"}, searchRequest.indices()); assertNull(searchRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(searchRequest); + assertEquals(SearchRequest.CCSExecutionMode.ONE_REQUEST_PER_SHARD, searchRequest.getCCSExecutionMode()); } } @@ -135,6 +141,9 @@ public void testIllegalArguments() { e = expectThrows(NullPointerException.class, () -> searchRequest.scroll((TimeValue)null)); assertEquals("keepAlive must not be null", e.getMessage()); + + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> searchRequest.setCCSExecutionMode("whatever")); + assertEquals("unknown ccs_execution_mode: [whatever]", iae.getMessage()); } public void testValidate() throws IOException { @@ -219,6 +228,8 @@ private SearchRequest mutate(SearchRequest searchRequest) { mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); + mutators.add(() -> mutation.setCCSExecutionMode(randomValueOtherThan(searchRequest.getCCSExecutionMode(), + () -> randomFrom(SearchRequest.CCSExecutionMode.values())))); randomFrom(mutators).run(); return mutation; } diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 0954e21f8813e..7e1a85f1e936a 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -86,6 +86,9 @@ private RandomSearchRequestGenerator() {} public static SearchRequest randomSearchRequest(Supplier randomSearchSourceBuilder) { SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); + if (randomBoolean()) { + searchRequest.setCCSExecutionMode(randomFrom(SearchRequest.CCSExecutionMode.values())); + } if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); } From c9b737b91e9c5b18c0f6ba3f189eb66762a2612f Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 23 Jan 2019 21:59:40 +0100 Subject: [PATCH 07/27] done --- .../client/RequestConvertersTests.java | 3 +- .../modules/cross-cluster-search.asciidoc | 14 +- .../MultiSearchTemplateResponseTests.java | 20 ++- ...rossClusterSearchUnavailableClusterIT.java | 7 + .../test/multi_cluster/10_basic.yml | 33 +++++ .../test/multi_cluster/40_scroll.yml | 1 + .../test/multi_cluster/70_skip_shards.yml | 4 + .../action/search/CCSExecutionMode.java | 44 ++++++ .../action/search/SearchRequest.java | 86 ++++++------ .../action/search/SearchResponse.java | 39 ++++-- .../action/search/SearchResponseMerger.java | 88 ++++++++++-- .../action/search/TransportSearchAction.java | 131 +++++++++++------- .../common/io/stream/StreamInput.java | 9 +- .../common/io/stream/StreamOutput.java | 14 +- .../search/MultiSearchResponseTests.java | 7 +- .../action/search/SearchRequestTests.java | 42 +++++- .../action/search/SearchResponseTests.java | 57 ++++---- .../common/io/stream/BytesStreamsTests.java | 11 +- .../search/RandomSearchRequestGenerator.java | 3 +- 19 files changed, 453 insertions(+), 160 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 79f2d95e44673..8940c29c373d9 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -41,6 +41,7 @@ import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.MultiGetRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.CCSExecutionMode; import org.elasticsearch.action.search.ClearScrollRequest; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; @@ -1863,7 +1864,7 @@ private static void setRandomSearchParams(SearchRequest searchRequest, expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); } if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(SearchRequest.CCSExecutionMode.values())); + searchRequest.setCCSExecutionMode(randomFrom(CCSExecutionMode.values())); expectedParams.put("ccs_execution_mode", searchRequest.getCCSExecutionMode().toString()); } } diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index cb56c0b1c40b2..d57bdf85e21f9 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -72,6 +72,7 @@ GET /cluster_one:twitter/_search "skipped": 0 }, "_clusters": { + "execution_mode": "one_request_per_cluster", "total": 1, "successful": 1, "skipped": 0 @@ -137,6 +138,7 @@ will be prefixed with their remote cluster name: "skipped": 0 }, "_clusters": { + "execution_mode": "one_request_per_cluster", "total": 2, "successful": 2, "skipped": 0 @@ -229,6 +231,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> "skipped": 0 }, "_clusters": { <1> + "execution_mode": "one_request_per_cluster", "total": 3, "successful": 2, "skipped": 1 @@ -302,11 +305,12 @@ then reduced (and fetched, depending on the execution mode is beneficial whenever there is very low network latency between the coordinating node and the remote clusters involved. -`one_request_per_cluster` is the default execution mode, but it does not -support scroll requests, as well as requests where inner hits are requested -as part of field collapsing, in which cases `one_request_per_shard` is -automatically used instead. The <> supports -the `ccs_execution_mode` parameter which allows to select the execution mode. +By default `one_request_per_cluster` is used when possible, unless a scroll +is provided, or unless inner hits are requested as part of field collapsing: +The <> supports the `ccs_execution_mode` +parameter which allows to select the execution mode if needed. The execution +mode that was used for the request is returned as part of the `_clusters` +section. Note that all the communication between the nodes, regardless of which cluster they belong to and the selected execution mode, happens through the diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java index 2c67dd4709bc9..1ec412e6f2ebf 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.search.CCSExecutionMode; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.Strings; @@ -50,7 +51,7 @@ protected MultiSearchTemplateResponse createTestInstance() { int successfulShards = randomIntBetween(0, totalShards); int skippedShards = totalShards - successfulShards; InternalSearchResponse internalSearchResponse = InternalSearchResponse.empty(); - SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalShards, successfulShards, skippedShards); + SearchResponse.Clusters clusters = randomClusters(); SearchTemplateResponse searchTemplateResponse = new SearchTemplateResponse(); SearchResponse searchResponse = new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, skippedShards, tookInMillis, ShardSearchFailure.EMPTY_ARRAY, clusters); @@ -59,7 +60,14 @@ protected MultiSearchTemplateResponse createTestInstance() { } return new MultiSearchTemplateResponse(items, overallTookInMillis); } - + + private static SearchResponse.Clusters randomClusters() { + int totalClusters = randomIntBetween(0, 10); + int successfulClusters = randomIntBetween(0, totalClusters); + int skippedClusters = totalClusters - successfulClusters; + CCSExecutionMode executionMode = randomFrom(CCSExecutionMode.values()); + return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); + } private static MultiSearchTemplateResponse createTestInstanceWithFailures() { int numItems = randomIntBetween(0, 128); @@ -67,14 +75,13 @@ private static MultiSearchTemplateResponse createTestInstanceWithFailures() { MultiSearchTemplateResponse.Item[] items = new MultiSearchTemplateResponse.Item[numItems]; for (int i = 0; i < numItems; i++) { if (randomBoolean()) { - // Creating a minimal response is OK, because SearchResponse self - // is tested elsewhere. + // Creating a minimal response is OK, because SearchResponse is tested elsewhere. long tookInMillis = randomNonNegativeLong(); int totalShards = randomIntBetween(1, Integer.MAX_VALUE); int successfulShards = randomIntBetween(0, totalShards); int skippedShards = totalShards - successfulShards; InternalSearchResponse internalSearchResponse = InternalSearchResponse.empty(); - SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalShards, successfulShards, skippedShards); + SearchResponse.Clusters clusters = randomClusters(); SearchTemplateResponse searchTemplateResponse = new SearchTemplateResponse(); SearchResponse searchResponse = new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, skippedShards, tookInMillis, ShardSearchFailure.EMPTY_ARRAY, clusters); @@ -133,6 +140,5 @@ public void testFromXContentWithFailures() throws IOException { AbstractXContentTestCase.testFromXContent(NUMBER_OF_TEST_RUNS, instanceSupplier, supportsUnknownFields, Strings.EMPTY_ARRAY, getRandomFieldsExcludeFilterWhenResultHasErrors(), this::createParser, this::doParseInstance, this::assertEqualInstances, assertToXContentEquivalence, ToXContent.EMPTY_PARAMS); - } - + } } diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index e280b1d2d1a05..a3aa3080a23ee 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -33,6 +33,7 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.CCSExecutionMode; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -164,6 +165,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -172,6 +174,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -181,6 +184,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -199,6 +203,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -207,6 +212,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -216,6 +222,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); + assertEquals(CCSExecutionMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 5acf84139bbf4..786ebb2a99cc9 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -36,6 +36,10 @@ terms: field: f1.keyword + - match: {_clusters.total: 2} + - match: {_clusters.successful: 2} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 5 } - match: { hits.total: 11 } - gte: { hits.hits.0._seq_no: 0 } @@ -59,6 +63,10 @@ terms: field: f1.keyword + - match: {_clusters.total: 2} + - match: {_clusters.successful: 2} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -76,6 +84,10 @@ terms: field: f1.keyword + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -93,6 +105,7 @@ terms: field: f1.keyword + - is_false: _clusters - match: { _shards.total: 2 } - match: { hits.total: 5} - match: { hits.hits.0._index: "test_index"} @@ -122,6 +135,10 @@ rest_total_hits_as_int: true index: test_remote_cluster:test_index + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -148,6 +165,10 @@ rest_total_hits_as_int: true index: "*:test_index" + - match: {_clusters.total: 2} + - match: {_clusters.successful: 2} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -159,6 +180,10 @@ rest_total_hits_as_int: true index: my_remote_cluster:aliased_test_index + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -172,6 +197,10 @@ rest_total_hits_as_int: true index: my_remote_cluster:aliased_test_index,my_remote_cluster:field_caps_index_1 + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 4 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -185,6 +214,10 @@ rest_total_hits_as_int: true index: "my_remote_cluster:single_doc_index" + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_cluster"} - match: { _shards.total: 1 } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index 6a7fe3c5356c0..1e61785ac3347 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -15,6 +15,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} + - match: {_clusters.execution_mode: "one_request_per_shard"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 81c09da54c085..723c6dfba308c 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -29,10 +29,12 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 + ccs_execution_mode: "one_request_per_shard" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2016-02-01", "lt": "2018-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} + - match: {_clusters.execution_mode: "one_request_per_shard"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} @@ -45,10 +47,12 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 + ccs_execution_mode: "one_request_per_shard" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2015-02-01", "lt": "2016-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} + - match: {_clusters.execution_mode: "one_request_per_shard"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java new file mode 100644 index 0000000000000..190c9509d8eee --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java @@ -0,0 +1,44 @@ +/* + * 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.action.search; + +import java.util.Locale; + +/** + * The execution modes when executing a cross-cluster search request + */ +public enum CCSExecutionMode { + ONE_REQUEST_PER_SHARD, + ONE_REQUEST_PER_CLUSTER; + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } + + public static CCSExecutionMode fromString(String executionMode) { + for (CCSExecutionMode value : CCSExecutionMode.values()) { + if (value.name().toLowerCase(Locale.ROOT).equals(executionMode)) { + return value; + } + } + throw new IllegalArgumentException("unknown ccs_execution_mode: [" + executionMode + "]"); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 18e0705f12929..ebb1a86254ad5 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -38,7 +38,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; -import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -204,7 +203,7 @@ public SearchRequest(StreamInput in) throws IOException { absoluteStartMillis = DEFAULT_ABSOLUTE_START_MILLIS; } if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - ccsExecutionMode = in.readEnum(CCSExecutionMode.class); + ccsExecutionMode = in.readOptionalEnum(CCSExecutionMode.class); } } @@ -233,32 +232,46 @@ public void writeTo(StreamOutput out) throws IOException { } } if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeEnum(ccsExecutionMode); + out.writeOptionalEnum(ccsExecutionMode); } } @Override public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; - final Scroll scroll = scroll(); - if (source != null && source.trackTotalHits() == false && scroll != null) { - validationException = - addValidationError("disabling [track_total_hits] is not allowed in a scroll context", validationException); - } - if (source != null && source.from() > 0 && scroll != null) { - validationException = - addValidationError("using [from] is not allowed in a scroll context", validationException); - } - if (requestCache != null && requestCache && scroll != null) { - validationException = - addValidationError("[request_cache] cannot be used in a scroll context", validationException); - } - if (source != null && source.size() == 0 && scroll != null) { - validationException = addValidationError("[size] cannot be [0] in a scroll context", validationException); + boolean scroll = scroll() != null; + if (scroll) { + if (source != null) { + if (source.trackTotalHits() == false) { + validationException = + addValidationError("disabling [track_total_hits] is not allowed in a scroll context", validationException); + } + if (source.from() > 0) { + validationException = + addValidationError("using [from] is not allowed in a scroll context", validationException); + } + if (source.size() == 0) { + validationException = addValidationError("[size] cannot be [0] in a scroll context", validationException); + } + if (source.rescores() != null && source.rescores().isEmpty() == false) { + validationException = + addValidationError("using [rescore] is not allowed in a scroll context", validationException); + } + } + if (requestCache != null && requestCache) { + validationException = + addValidationError("[request_cache] cannot be used in a scroll context", validationException); + } + if (ccsExecutionMode == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + validationException = addValidationError("[ccs_execution_mode] cannot be [" + CCSExecutionMode.ONE_REQUEST_PER_CLUSTER + + "] in a scroll context", validationException); + } } - if (source != null && source.rescores() != null && source.rescores().isEmpty() == false && scroll != null) { - validationException = - addValidationError("using [rescore] is not allowed in a scroll context", validationException); + boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null + && source.collapse().getInnerHits().isEmpty() == false; + if (collapseWithInnerHits && ccsExecutionMode == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + validationException = addValidationError("[ccs_execution_mode] cannot be [" + CCSExecutionMode.ONE_REQUEST_PER_CLUSTER + + "] when inner hits are requested as part of field collapsing", validationException); } return validationException; } @@ -311,14 +324,26 @@ public SearchRequest indicesOptions(IndicesOptions indicesOptions) { return this; } + /** + * Sets the execution mode (as a {@link CCSExecutionMode}) for cross-cluster search requests + */ public void setCCSExecutionMode(CCSExecutionMode ccsExecutionMode) { this.ccsExecutionMode = Objects.requireNonNull(ccsExecutionMode, "ccsExecutionMode must not be null"); } + /** + * Sets the execution mode (as a string) for cross-cluster search requests + */ public void setCCSExecutionMode(String ccsExecutionMode) { this.ccsExecutionMode = CCSExecutionMode.fromString(ccsExecutionMode); } + /** + * Returns the execution mode for cross-cluster search request. When not set {@link CCSExecutionMode#ONE_REQUEST_PER_CLUSTER} is used + * whenever possible. In case a scroll is provided or inner hits are requested as part of field collapsing, + * {@link CCSExecutionMode#ONE_REQUEST_PER_SHARD} is used instead. + */ + @Nullable public CCSExecutionMode getCCSExecutionMode() { return this.ccsExecutionMode; } @@ -645,23 +670,4 @@ public String toString() { ", ccsExecutionMode=" + ccsExecutionMode + ", source=" + source + '}'; } - - public enum CCSExecutionMode { - ONE_REQUEST_PER_SHARD, - PREFER_ONE_REQUEST_PER_CLUSTER; - - @Override - public String toString() { - return name().toLowerCase(Locale.ROOT); - } - - public static CCSExecutionMode fromString(String executionMode) { - for (CCSExecutionMode value : CCSExecutionMode.values()) { - if (value.name().toLowerCase(Locale.ROOT).equals(executionMode)) { - return value; - } - } - throw new IllegalArgumentException("unknown ccs_execution_mode: [" + executionMode + "]"); - } - } } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 0273d5e58219a..5b0f81273f8f6 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -111,7 +111,6 @@ public Aggregations getAggregations() { return internalResponse.aggregations(); } - public Suggest getSuggest() { return internalResponse.suggest(); } @@ -323,6 +322,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio int successful = -1; int total = -1; int skipped = -1; + CCSExecutionMode executionMode = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -333,6 +333,8 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio total = parser.intValue(); } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); + } else if (Clusters.EXECUTION_MODE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + executionMode = CCSExecutionMode.fromString(parser.text()); } else { parser.skipChildren(); } @@ -340,7 +342,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio parser.skipChildren(); } } - clusters = new Clusters(total, successful, skipped); + clusters = new Clusters(total, successful, skipped, executionMode); } else { parser.skipChildren(); } @@ -349,7 +351,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio SearchResponseSections searchResponseSections = new SearchResponseSections(hits, aggs, suggest, timedOut, terminatedEarly, profile, numReducePhases); return new SearchResponse(searchResponseSections, scrollId, totalShards, successfulShards, skippedShards, tookInMillis, - failures.toArray(new ShardSearchFailure[failures.size()]), clusters); + failures.toArray(ShardSearchFailure.EMPTY_ARRAY), clusters); } @Override @@ -407,18 +409,20 @@ public String toString() { */ public static class Clusters implements ToXContent, Writeable { - public static final Clusters EMPTY = new Clusters(0, 0, 0); + public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); static final ParseField SKIPPED_FIELD = new ParseField("skipped"); static final ParseField TOTAL_FIELD = new ParseField("total"); + static final ParseField EXECUTION_MODE_FIELD = new ParseField("execution_mode"); private final int total; private final int successful; private final int skipped; + private final CCSExecutionMode executionMode; - public Clusters(int total, int successful, int skipped) { + public Clusters(int total, int successful, int skipped, CCSExecutionMode executionMode) { assert total >= 0 && successful >= 0 && skipped >= 0 : "total: " + total + " successful: " + successful + " skipped: " + skipped; assert successful <= total && skipped == total - successful @@ -426,12 +430,18 @@ public Clusters(int total, int successful, int skipped) { this.total = total; this.successful = successful; this.skipped = skipped; + this.executionMode = Objects.requireNonNull(executionMode); } private Clusters(StreamInput in) throws IOException { this.total = in.readVInt(); this.successful = in.readVInt(); this.skipped = in.readVInt(); + if (in.getVersion().onOrAfter(Version.V_7_0_0)) { + this.executionMode = in.readOptionalEnum(CCSExecutionMode.class); + } else { + this.executionMode = null; + } } @Override @@ -439,12 +449,16 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVInt(total); out.writeVInt(successful); out.writeVInt(skipped); + if (out.getVersion().onOrAfter(Version.V_7_0_0)) { + out.writeOptionalEnum(executionMode); + } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (this != EMPTY) { builder.startObject(_CLUSTERS_FIELD.getPreferredName()); + builder.field(EXECUTION_MODE_FIELD.getPreferredName(), executionMode == null ? null : executionMode.toString()); builder.field(TOTAL_FIELD.getPreferredName(), total); builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); builder.field(SKIPPED_FIELD.getPreferredName(), skipped); @@ -474,6 +488,13 @@ public int getSkipped() { return skipped; } + /** + * Returns the execution mode used for the execution of this cross-cluster search request + */ + public CCSExecutionMode getExecutionMode() { + return executionMode; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -485,17 +506,19 @@ public boolean equals(Object o) { Clusters clusters = (Clusters) o; return total == clusters.total && successful == clusters.successful && - skipped == clusters.skipped; + skipped == clusters.skipped && + executionMode == clusters.executionMode; } @Override public int hashCode() { - return Objects.hash(total, successful, skipped); + return Objects.hash(total, successful, skipped, executionMode); } @Override public String toString() { - return "Clusters{total=" + total + ", successful=" + successful + ", skipped=" + skipped + '}'; + return "Clusters{execution_mode=" + executionMode + ", total=" + total + + ", successful=" + successful + ", skipped=" + skipped + '}'; } } } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index 0e996577fd3da..49c5654966284 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -29,6 +29,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.search.TransportSearchAction.SearchTimeProvider; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; +import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; @@ -101,12 +102,34 @@ void add(SearchResponse searchResponse) { searchResponses.add(searchResponse); } + int numResponses() { + return searchResponses.size(); + } + /** * Returns the merged response. To be called once all responses have been added through {@link #add(SearchResponse)} * so that all responses are merged into a single one. */ SearchResponse getMergedResponse(Clusters clusters) { - assert searchResponses.size() > 1; + //if the search is only across remote clusters, none of them are available, and all of them have skip_unavailable set to true, + //we end up calling merge without anything to merge, we just return an empty search response + if (searchResponses.size() == 0) { + SearchHits searchHits = new SearchHits(new SearchHit[0], new TotalHits(0L, TotalHits.Relation.EQUAL_TO), Float.NaN); + InternalSearchResponse internalSearchResponse = new InternalSearchResponse(searchHits, + InternalAggregations.EMPTY, null, null, false, null, 0); + return new SearchResponse(internalSearchResponse, null, 0, 0, 0, searchTimeProvider.buildTookInMillis(), + ShardSearchFailure.EMPTY_ARRAY, clusters); + } + if (searchResponses.size() == 1) { + SearchResponse response = searchResponses.get(0); + SearchProfileShardResults profileShardResults = new SearchProfileShardResults(response.getProfileResults()); + InternalSearchResponse internalSearchResponse = new InternalSearchResponse(response.getHits(), + (InternalAggregations)response.getAggregations(), response.getSuggest(), profileShardResults, response.isTimedOut(), + response.isTerminatedEarly(), response.getNumReducePhases()); + return new SearchResponse(internalSearchResponse, response.getScrollId(), response.getTotalShards(), + response.getSuccessfulShards(), response.getSkippedShards(), searchTimeProvider.buildTookInMillis(), + response.getShardFailures(), clusters); + } int totalShards = 0; int skippedShards = 0; int successfulShards = 0; @@ -115,7 +138,7 @@ SearchResponse getMergedResponse(Clusters clusters) { List failures = new ArrayList<>(); Map profileResults = new HashMap<>(); List aggs = new ArrayList<>(); - Map shards = new TreeMap<>(); + Map shards = new TreeMap<>(); List topDocsList = new ArrayList<>(searchResponses.size()); Map> groupedSuggestions = new HashMap<>(); Boolean trackTotalHits = null; @@ -210,7 +233,7 @@ private ShardId extractShardId(ShardSearchFailure failure) { } }; - private static TopDocs searchHitsToTopDocs(SearchHits searchHits, TotalHits totalHits, Map shards) { + private static TopDocs searchHitsToTopDocs(SearchHits searchHits, TotalHits totalHits, Map shards) { SearchHit[] hits = searchHits.getHits(); ScoreDoc[] scoreDocs = new ScoreDoc[hits.length]; final TopDocs topDocs; @@ -228,7 +251,8 @@ private static TopDocs searchHitsToTopDocs(SearchHits searchHits, TotalHits tota for (int i = 0; i < hits.length; i++) { SearchHit hit = hits[i]; - ShardId shardId = hit.getShard().getShardId(); + SearchShardTarget shard = hit.getShard(); + ShardIdAndClusterAlias shardId = new ShardIdAndClusterAlias(shard.getShardId(), shard.getClusterAlias()); shards.putIfAbsent(shardId, null); final SortField[] sortFields = searchHits.getSortFields(); final Object[] sortValues; @@ -246,21 +270,17 @@ private static TopDocs searchHitsToTopDocs(SearchHits searchHits, TotalHits tota return topDocs; } - private static void setShardIndex(Map shards, List topDocsList) { + private static void setShardIndex(Map shards, List topDocsList) { int shardIndex = 0; - for (Map.Entry shard : shards.entrySet()) { + for (Map.Entry shard : shards.entrySet()) { shard.setValue(shardIndex++); } //and go through all the scoreDocs from each cluster and set their corresponding shardIndex for (TopDocs topDocs : topDocsList) { for (ScoreDoc scoreDoc : topDocs.scoreDocs) { FieldDocAndSearchHit fieldDocAndSearchHit = (FieldDocAndSearchHit) scoreDoc; - //When hits come from the indices with same names on multiple clusters and same shard identifier, we rely on such indices - //to have a different uuid across multiple clusters. That's how they will get a different shardIndex. - //In case the same remote cluster is registered twice using different aliases, a search across such clusters that are the - //same and hold the same documents will make an assertion in lucene fail (see TopDocs#tieBreakLessThan line 86). - ShardId shardId = fieldDocAndSearchHit.searchHit.getShard().getShardId(); - fieldDocAndSearchHit.shardIndex = shards.get(shardId); + SearchShardTarget shard = fieldDocAndSearchHit.searchHit.getShard(); + fieldDocAndSearchHit.shardIndex = shards.get(new ShardIdAndClusterAlias(shard.getShardId(), shard.getClusterAlias())); } } } @@ -296,4 +316,48 @@ private static final class FieldDocAndSearchHit extends FieldDoc { this.searchHit = searchHit; } } + + /** + * This class is used instead of plain {@link ShardId} to support the scenario where the same remote cluster is registered twice using + * different aliases. In that case searching across the same cluster twice would make an assertion in lucene fail + * (see TopDocs#tieBreakLessThan line 86). Generally, indices with same names on different clusters have different index uuids which + * make their ShardIds different, which is not the case if the index is really the same one from the same cluster, in which case we + * need to look at the cluster alias and make sure to assign a different shardIndex based on that. + */ + private static final class ShardIdAndClusterAlias implements Comparable { + private final ShardId shardId; + private final String clusterAlias; + + ShardIdAndClusterAlias(ShardId shardId, String clusterAlias) { + this.shardId = shardId; + this.clusterAlias = clusterAlias; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ShardIdAndClusterAlias that = (ShardIdAndClusterAlias) o; + return shardId.equals(that.shardId) && + clusterAlias.equals(that.clusterAlias); + } + + @Override + public int hashCode() { + return Objects.hash(shardId, clusterAlias); + } + + @Override + public int compareTo(ShardIdAndClusterAlias o) { + int shardIdCompareTo = shardId.compareTo(o.shardId); + if (shardIdCompareTo != 0) { + return shardIdCompareTo; + } + return clusterAlias.compareTo(o.clusterAlias); + } + } } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 0884737476a6f..b9eb82c905113 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -202,10 +202,35 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - //TODO discuss the heuristic and add flag to opt-out of local reduction? - boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null - && source.collapse().getInnerHits().isEmpty() == false; - if (searchRequest.scroll() == null && collapseWithInnerHits == false) { + CCSExecutionMode executionMode = searchRequest.getCCSExecutionMode(); + if (executionMode == null) { + boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null + && source.collapse().getInnerHits().isEmpty() == false; + boolean scroll = searchRequest.scroll() != null; + executionMode = collapseWithInnerHits || scroll ? CCSExecutionMode.ONE_REQUEST_PER_SHARD + : CCSExecutionMode.ONE_REQUEST_PER_CLUSTER; + } + if (executionMode == CCSExecutionMode.ONE_REQUEST_PER_SHARD) { + AtomicInteger skippedClusters = new AtomicInteger(0); + collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), + skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, + ActionListener.wrap( + searchShardsResponses -> { + List remoteShardIterators = new ArrayList<>(); + Map remoteAliasFilters = new HashMap<>(); + BiFunction clusterNodeLookup = processRemoteShards( + searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); + int localClusters = localIndices == null ? 0 : 1; + int totalClusters = remoteClusterIndices.size() + localClusters; + int successfulClusters = searchShardsResponses.size() + localClusters; + executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, + remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, + new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), + CCSExecutionMode.ONE_REQUEST_PER_SHARD)); + }, + listener::onFailure)); + } else { + //TODO make this a method and test it? final int from; final int size; final int trackTotalHitsUpTo; @@ -221,48 +246,32 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< source.from(0); source.size(from + size); } - + //TODO add some yaml test to check the execution mode paramater SearchResponseMerger searchResponseMerger = new SearchResponseMerger(from, size, trackTotalHitsUpTo, timeProvider, searchService::createReduceContext); - + AtomicInteger skippedClusters = new AtomicInteger(0); final AtomicReference exceptions = new AtomicReference<>(); - final CountDown countDown = new CountDown(remoteClusterIndices.size() + (localIndices == null ? 0 : 1)); + int totalClusters = remoteClusterIndices.size() + (localIndices == null ? 0 : 1); + final CountDown countDown = new CountDown(totalClusters); for (Map.Entry entry : remoteClusterIndices.entrySet()) { String clusterAlias = entry.getKey(); + boolean skipUnavailable = remoteClusterService.isSkipUnavailable(clusterAlias); OriginalIndices indices = entry.getValue(); SearchRequest ccsSearchRequest = SearchRequest.withLocalReduction(searchRequest, indices.indices(), clusterAlias, timeProvider.getAbsoluteStartMillis()); - ActionListener ccsListener = createCCSListener( + ActionListener ccsListener = createCCSListener(skipUnavailable, e -> new RemoteTransportException("error while communicating with remote cluster [" + clusterAlias + "]", e), - countDown, exceptions, searchResponseMerger, listener); + countDown, skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); Client remoteClusterClient = remoteClusterService.getRemoteClusterClient(threadPool, clusterAlias); remoteClusterClient.search(ccsSearchRequest, ccsListener); } if (localIndices != null) { - ActionListener ccsListener = createCCSListener(e -> e, countDown, exceptions, - searchResponseMerger, listener); + ActionListener ccsListener = createCCSListener(false, e -> e, countDown, skippedClusters, + exceptions, searchResponseMerger, totalClusters, listener); SearchRequest ccsLocalSearchRequest = SearchRequest.withLocalReduction(searchRequest, localIndices.indices(), RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); executeLocalSearch(task, timeProvider, ccsLocalSearchRequest, localIndices, clusterState, ccsListener); } - } else { - AtomicInteger skippedClusters = new AtomicInteger(0); - collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, - remoteClusterIndices, remoteClusterService, threadPool, - ActionListener.wrap( - searchShardsResponses -> { - List remoteShardIterators = new ArrayList<>(); - Map remoteAliasFilters = new HashMap<>(); - BiFunction clusterNodeLookup = processRemoteShards( - searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); - int localClusters = localIndices == null ? 0 : 1; - int totalClusters = remoteClusterIndices.size() + localClusters; - int successfulClusters = searchShardsResponses.size() + localClusters; - executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, - remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, - new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get())); - }, - listener::onFailure)); } } }, listener::onFailure); @@ -326,43 +335,61 @@ private void maybeFinish() { } } - private ActionListener createCCSListener(Function exceptionWrapper, + //TODO unify the two listeners? + private ActionListener createCCSListener(boolean skipUnavailable, + Function exceptionWrapper, CountDown countDown, + AtomicInteger skippedClusters, AtomicReference exceptions, SearchResponseMerger searchResponseMerger, + int totalClusters, ActionListener originalListener) { - return ActionListener.wrap( - response -> { - searchResponseMerger.add(response); - //TODO test when merge fails + return new ActionListener() { + @Override + public void onResponse(SearchResponse searchResponse) { + searchResponseMerger.add(searchResponse); + maybeFinish(); + } + + @Override + public void onFailure(Exception e) { + if (skipUnavailable) { + skippedClusters.incrementAndGet(); + } else { + Exception exception = exceptionWrapper.apply(e); + if (exceptions.compareAndSet(null, exception) == false) { + exceptions.accumulateAndGet(exception, (previous, current) -> { + current.addSuppressed(previous); + return current; + }); + } + } + maybeFinish(); + } + + private void maybeFinish() { if (countDown.countDown()) { Exception exception = exceptions.get(); if (exception == null) { - //originalListener.onResponse(searchResponseMerger.getMergedResponse()); + SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), + skippedClusters.get(), CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + SearchResponse response; + try { + //TODO test when merge breaks + response = searchResponseMerger.getMergedResponse(clusters); + } catch(Exception e) { + originalListener.onFailure(e); + return; + } + originalListener.onResponse(response); } else { - //TODO here we need to support skip_unavailable originalListener.onFailure(exceptions.get()); } } - }, - e -> { - Exception exception = exceptionWrapper.apply(e); - if (exceptions.compareAndSet(null, exception) == false) { - exception = exceptions.accumulateAndGet(exception, (previous, current) -> { - current.addSuppressed(previous); - return current; - }); - } - //it can happen that onResponse throws and calls onFailure, then countDown - //is not enough or we may never notify the listener of the failure - if (countDown.isCountedDown() || countDown.countDown()) { - originalListener.onFailure(exception); - } } - ); + }; } - private void executeLocalSearch(Task task, SearchTimeProvider timeProvider, SearchRequest searchRequest, OriginalIndices localIndices, ClusterState clusterState, ActionListener listener) { executeSearch((SearchTask)task, timeProvider, searchRequest, localIndices, Collections.emptyList(), diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 723de8fd5da31..02933fc961882 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -999,7 +999,14 @@ public List readNamedWriteableList(Class catego } /** - * Reads an enum with type E that was serialized based on the value of it's ordinal + * Reads an optional enum with type E that was serialized based on the value of its ordinal + */ + public > E readOptionalEnum(Class enumClass) throws IOException { + return readBoolean() ? readEnum(enumClass) : null; + } + + /** + * Reads an enum with type E that was serialized based on the value of its ordinal */ public > E readEnum(Class enumClass) throws IOException { int ordinal = readVInt(); diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java index e9709de1a44a3..83e6d38f50f9a 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java @@ -1082,7 +1082,19 @@ public void writeNamedWriteableList(List list) throws } /** - * Writes an enum with type E that by serialized it based on it's ordinal value + * Writes an optional enum with type E based on its ordinal value + */ + public > void writeOptionalEnum(@Nullable E enumValue) throws IOException { + if (enumValue == null) { + writeBoolean(false); + } else { + writeBoolean(true); + writeEnum(enumValue); + } + } + + /** + * Writes an enum with type E based on its ordinal value */ public > void writeEnum(E enumValue) throws IOException { writeVInt(enumValue.ordinal()); diff --git a/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java index 4bd4406d81cca..d91a4eaf02288 100644 --- a/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java @@ -46,8 +46,8 @@ protected MultiSearchResponse createTestInstance() { int totalShards = randomIntBetween(1, Integer.MAX_VALUE); int successfulShards = randomIntBetween(0, totalShards); int skippedShards = totalShards - successfulShards; + SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); InternalSearchResponse internalSearchResponse = InternalSearchResponse.empty(); - SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalShards, successfulShards, skippedShards); SearchResponse searchResponse = new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, skippedShards, tookInMillis, ShardSearchFailure.EMPTY_ARRAY, clusters); items[i] = new MultiSearchResponse.Item(searchResponse, null); @@ -60,14 +60,13 @@ private static MultiSearchResponse createTestInstanceWithFailures() { MultiSearchResponse.Item[] items = new MultiSearchResponse.Item[numItems]; for (int i = 0; i < numItems; i++) { if (randomBoolean()) { - // Creating a minimal response is OK, because SearchResponse self - // is tested elsewhere. + // Creating a minimal response is OK, because SearchResponse is tested elsewhere. long tookInMillis = randomNonNegativeLong(); int totalShards = randomIntBetween(1, Integer.MAX_VALUE); int successfulShards = randomIntBetween(0, totalShards); int skippedShards = totalShards - successfulShards; + SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); InternalSearchResponse internalSearchResponse = InternalSearchResponse.empty(); - SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalShards, successfulShards, skippedShards); SearchResponse searchResponse = new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, skippedShards, tookInMillis, ShardSearchFailure.EMPTY_ARRAY, clusters); items[i] = new MultiSearchResponse.Item(searchResponse, null); diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 9abe2f5712198..44750e9a4678c 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -26,10 +26,12 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.ArrayUtils; +import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.AbstractSearchTestCase; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.rescore.QueryRescorerBuilder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; @@ -81,7 +83,7 @@ public void testRandomVersionSerialization() throws IOException { Version version = VersionUtils.randomVersion(random()); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new, version); if (version.before(Version.V_7_0_0)) { - assertEquals(SearchRequest.CCSExecutionMode.ONE_REQUEST_PER_SHARD, deserializedRequest.getCCSExecutionMode()); + assertNull(deserializedRequest.getCCSExecutionMode()); } else { assertEquals(searchRequest.getCCSExecutionMode(), deserializedRequest.getCCSExecutionMode()); } @@ -102,7 +104,7 @@ public void testReadFromPre6_7_0() throws IOException { assertArrayEquals(new String[]{"index"}, searchRequest.indices()); assertNull(searchRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(searchRequest); - assertEquals(SearchRequest.CCSExecutionMode.ONE_REQUEST_PER_SHARD, searchRequest.getCCSExecutionMode()); + assertNull(searchRequest.getCCSExecutionMode()); } } @@ -161,6 +163,9 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().trackTotalHits(false); + if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -173,6 +178,9 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().from(10); + if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -183,6 +191,9 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder().size(0)); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); + if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -194,11 +205,36 @@ public void testValidate() throws IOException { searchRequest.source().addRescorer(new QueryRescorerBuilder(QueryBuilders.matchAllQuery())); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); + if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); assertEquals("using [rescore] is not allowed in a scroll context", validationErrors.validationErrors().get(0)); } + { + SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder()); + searchRequest.scroll(new TimeValue(1000)); + searchRequest.requestCache(false); + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + ActionRequestValidationException validationErrors = searchRequest.validate(); + assertNotNull(validationErrors); + assertEquals(1, validationErrors.validationErrors().size()); + assertEquals("[ccs_execution_mode] cannot be [one_request_per_cluster] in a scroll context", + validationErrors.validationErrors().get(0)); + } + { + SearchRequest searchRequest = createSearchRequest().source( + new SearchSourceBuilder().collapse(new CollapseBuilder("field").setInnerHits(new InnerHitBuilder()))); + searchRequest.scroll((Scroll)null); + searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + ActionRequestValidationException validationErrors = searchRequest.validate(); + assertNotNull(validationErrors); + assertEquals(validationErrors.validationErrors().toString(), 1, validationErrors.validationErrors().size()); + assertEquals("[ccs_execution_mode] cannot be [one_request_per_cluster] " + + "when inner hits are requested as part of field collapsing", validationErrors.validationErrors().get(0)); + } } public void testCopyConstructor() throws IOException { @@ -229,7 +265,7 @@ private SearchRequest mutate(SearchRequest searchRequest) { () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); mutators.add(() -> mutation.setCCSExecutionMode(randomValueOtherThan(searchRequest.getCCSExecutionMode(), - () -> randomFrom(SearchRequest.CCSExecutionMode.values())))); + () -> randomFrom(CCSExecutionMode.values())))); randomFrom(mutators).run(); return mutation; } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index f07be38765f66..831faf521a4fc 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -20,13 +20,11 @@ package org.elasticsearch.action.search; import org.apache.lucene.search.TotalHits; +import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.BytesStreamOutput; -import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.text.Text; import org.elasticsearch.common.xcontent.NamedXContentRegistry; @@ -48,6 +46,7 @@ import org.elasticsearch.search.suggest.SuggestTests; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalAggregationTestCase; +import org.elasticsearch.test.VersionUtils; import org.junit.After; import org.junit.Before; @@ -132,7 +131,8 @@ static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters); + CCSExecutionMode executionMode = randomFrom(CCSExecutionMode.values()); + return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); } /** @@ -246,7 +246,8 @@ public void testToXContent() { new InternalSearchResponse( new SearchHits(hits, new TotalHits(100, TotalHits.Relation.EQUAL_TO), 1.5f), null, null, null, false, null, 1 ), - null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, new SearchResponse.Clusters(5, 3, 2)); + null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, + new SearchResponse.Clusters(5, 3, 2, CCSExecutionMode.ONE_REQUEST_PER_SHARD)); StringBuilder expectedString = new StringBuilder(); expectedString.append("{"); { @@ -261,7 +262,8 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("{\"total\":5,"); + expectedString.append("{\"execution_mode\":\"one_request_per_shard\","); + expectedString.append("\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); } @@ -279,24 +281,31 @@ public void testToXContent() { public void testSerialization() throws IOException { SearchResponse searchResponse = createTestItem(false); - BytesStreamOutput bytesStreamOutput = new BytesStreamOutput(); - searchResponse.writeTo(bytesStreamOutput); - try (StreamInput in = new NamedWriteableAwareStreamInput( - StreamInput.wrap(bytesStreamOutput.bytes().toBytesRef().bytes), namedWriteableRegistry)) { - SearchResponse serialized = new SearchResponse(); - serialized.readFrom(in); - if (searchResponse.getHits().getTotalHits() == null) { - assertNull(serialized.getHits().getTotalHits()); - } else { - assertEquals(searchResponse.getHits().getTotalHits().value, serialized.getHits().getTotalHits().value); - assertEquals(searchResponse.getHits().getTotalHits().relation, serialized.getHits().getTotalHits().relation); - } - assertEquals(searchResponse.getHits().getHits().length, serialized.getHits().getHits().length); - assertEquals(searchResponse.getNumReducePhases(), serialized.getNumReducePhases()); - assertEquals(searchResponse.getFailedShards(), serialized.getFailedShards()); - assertEquals(searchResponse.getTotalShards(), serialized.getTotalShards()); - assertEquals(searchResponse.getSkippedShards(), serialized.getSkippedShards()); - assertEquals(searchResponse.getClusters(), serialized.getClusters()); + SearchResponse deserialized = copyStreamable(searchResponse, namedWriteableRegistry, SearchResponse::new, Version.CURRENT); + if (searchResponse.getHits().getTotalHits() == null) { + assertNull(deserialized.getHits().getTotalHits()); + } else { + assertEquals(searchResponse.getHits().getTotalHits().value, deserialized.getHits().getTotalHits().value); + assertEquals(searchResponse.getHits().getTotalHits().relation, deserialized.getHits().getTotalHits().relation); } + assertEquals(searchResponse.getHits().getHits().length, deserialized.getHits().getHits().length); + assertEquals(searchResponse.getNumReducePhases(), deserialized.getNumReducePhases()); + assertEquals(searchResponse.getFailedShards(), deserialized.getFailedShards()); + assertEquals(searchResponse.getTotalShards(), deserialized.getTotalShards()); + assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards()); + assertEquals(searchResponse.getClusters(), deserialized.getClusters()); + } + + public void testSerializationPre7_0_0() throws IOException { + SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), null, 1, 1, 0, 100L, + ShardSearchFailure.EMPTY_ARRAY, randomClusters()); + Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_1_0, VersionUtils.getPreviousVersion(Version.V_7_0_0)); + SearchResponse deserialized = copyStreamable(searchResponse, namedWriteableRegistry, SearchResponse::new, version); + SearchResponse.Clusters clusters = searchResponse.getClusters(); + SearchResponse.Clusters deserializedClusters = deserialized.getClusters(); + assertEquals(clusters.getSkipped(), deserializedClusters.getSkipped()); + assertEquals(clusters.getSuccessful(), deserializedClusters.getSuccessful()); + assertEquals(clusters.getTotal(), deserializedClusters.getTotal()); + assertNull(deserializedClusters.getExecutionMode()); } } diff --git a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java index adfe90755dd03..0e18b49eea83e 100644 --- a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java +++ b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java @@ -813,7 +813,16 @@ public void testInvalidEnum() throws IOException { assertEquals(0, input.available()); } - private void assertEqualityAfterSerialize(TimeValue value, int expectedSize) throws IOException { + public void testOptionalEnum() throws IOException { + TestEnum value = randomFrom(TestEnum.values()); + BytesStreamOutput output = new BytesStreamOutput(); + output.writeOptionalEnum(value); + StreamInput input = output.bytes().streamInput(); + assertEquals(value, input.readOptionalEnum(TestEnum.class)); + assertEquals(0, input.available()); + } + + private static void assertEqualityAfterSerialize(TimeValue value, int expectedSize) throws IOException { BytesStreamOutput out = new BytesStreamOutput(); out.writeTimeValue(value); assertEquals(expectedSize, out.size()); diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 7e1a85f1e936a..a15d8b68cc737 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -19,6 +19,7 @@ package org.elasticsearch.search; +import org.elasticsearch.action.search.CCSExecutionMode; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.IndicesOptions; @@ -87,7 +88,7 @@ public static SearchRequest randomSearchRequest(Supplier ra SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(SearchRequest.CCSExecutionMode.values())); + searchRequest.setCCSExecutionMode(randomFrom(CCSExecutionMode.values())); } if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); From a6404e314cc529b2fdfb86c7cc4b93237374bca7 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 10:13:54 +0100 Subject: [PATCH 08/27] import --- .../org/elasticsearch/action/search/SearchResponseMerger.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index 49c5654966284..e3db8a9b64f94 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -29,7 +29,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.search.TransportSearchAction.SearchTimeProvider; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; -import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; From 0b064a9733b85b7158caa6f797c34685d0479c27 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 11:29:53 +0100 Subject: [PATCH 09/27] progress --- .../action/search/SearchResponseMerger.java | 10 --- .../action/search/TransportSearchAction.java | 4 +- .../search/SearchResponseMergerTests.java | 79 ++++++++++++++++++- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index e3db8a9b64f94..472097d287f3e 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -119,16 +119,6 @@ SearchResponse getMergedResponse(Clusters clusters) { return new SearchResponse(internalSearchResponse, null, 0, 0, 0, searchTimeProvider.buildTookInMillis(), ShardSearchFailure.EMPTY_ARRAY, clusters); } - if (searchResponses.size() == 1) { - SearchResponse response = searchResponses.get(0); - SearchProfileShardResults profileShardResults = new SearchProfileShardResults(response.getProfileResults()); - InternalSearchResponse internalSearchResponse = new InternalSearchResponse(response.getHits(), - (InternalAggregations)response.getAggregations(), response.getSuggest(), profileShardResults, response.isTimedOut(), - response.isTerminatedEarly(), response.getNumReducePhases()); - return new SearchResponse(internalSearchResponse, response.getScrollId(), response.getTotalShards(), - response.getSuccessfulShards(), response.getSkippedShards(), searchTimeProvider.buildTookInMillis(), - response.getShardFailures(), clusters); - } int totalShards = 0; int skippedShards = 0; int successfulShards = 0; diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index b9eb82c905113..4fbe99b96fae9 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -245,8 +245,10 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< //here we modify the original source so we can re-use it by setting it to each outgoing search request source.from(0); source.size(from + size); + //TODO when searching only against a remote cluster, we could ask directly for the final number of results and let + //the remote cluster do a final reduction, yet that is not possible as we are providing a localClusterAlias which + //will automatically make the local reduction non final } - //TODO add some yaml test to check the execution mode paramater SearchResponseMerger searchResponseMerger = new SearchResponseMerger(from, size, trackTotalHitsUpTo, timeProvider, searchService::createReduceContext); AtomicInteger skippedClusters = new AtomicInteger(0); diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java index d02b712eaaef3..2c8d4d4d21e23 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java @@ -73,7 +73,7 @@ public class SearchResponseMergerTests extends ESTestCase { @Before public void init() { - numResponses = randomIntBetween(2, 10); + numResponses = randomIntBetween(1, 10); executorService = Executors.newFixedThreadPool(numResponses); } @@ -87,7 +87,7 @@ private void addResponse(SearchResponseMerger searchResponseMerger, SearchRespon private void awaitResponsesAdded() throws InterruptedException { executorService.shutdown(); - executorService.awaitTermination(5, TimeUnit.SECONDS); + assertTrue(executorService.awaitTermination(5, TimeUnit.SECONDS)); } public void testMergeTookInMillis() throws InterruptedException { @@ -137,6 +137,7 @@ public void testMergeShardFailures() throws InterruptedException { addResponse(merger, searchResponse); } awaitResponsesAdded(); + assertEquals(numResponses, merger.numResponses()); SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); SearchResponse mergedResponse = merger.getMergedResponse(clusters); assertSame(clusters, mergedResponse.getClusters()); @@ -170,6 +171,7 @@ public void testMergeShardFailuresNullShardId() throws InterruptedException { addResponse(merger, searchResponse); } awaitResponsesAdded(); + assertEquals(numResponses, merger.numResponses()); ShardSearchFailure[] shardFailures = merger.getMergedResponse(SearchResponse.Clusters.EMPTY).getShardFailures(); assertThat(Arrays.asList(shardFailures), containsInAnyOrder(expectedFailures.toArray(ShardSearchFailure.EMPTY_ARRAY))); } @@ -189,6 +191,7 @@ public void testMergeProfileResults() throws InterruptedException { addResponse(merger, searchResponse); } awaitResponsesAdded(); + assertEquals(numResponses, merger.numResponses()); SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); SearchResponse mergedResponse = merger.getMergedResponse(clusters); assertSame(clusters, mergedResponse.getClusters()); @@ -221,6 +224,7 @@ public void testMergeSuggestions() throws InterruptedException { addResponse(searchResponseMerger, searchResponse); } awaitResponsesAdded(); + assertEquals(numResponses, searchResponseMerger.numResponses()); SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); SearchResponse mergedResponse = searchResponseMerger.getMergedResponse(clusters); assertSame(clusters, mergedResponse.getClusters()); @@ -267,6 +271,7 @@ public void testMergeAggs() throws InterruptedException { addResponse(searchResponseMerger, searchResponse); } awaitResponsesAdded(); + assertEquals(numResponses, searchResponseMerger.numResponses()); SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); SearchResponse mergedResponse = searchResponseMerger.getMergedResponse(clusters); assertSame(clusters, mergedResponse.getClusters()); @@ -334,7 +339,7 @@ public void testMergeSearchHits() throws InterruptedException { Iterator> indicesIterator = randomRealisticIndices(numIndices, numResponses).entrySet().iterator(); for (int i = 0; i < numResponses; i++) { Map.Entry entry = indicesIterator.next(); - String clusterAlias = entry.getKey().equals(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY) ? null : entry.getKey(); + String clusterAlias = entry.getKey(); Index[] indices = entry.getValue(); int total = randomIntBetween(1, 1000); expectedTotal += total; @@ -386,7 +391,7 @@ public void testMergeSearchHits() throws InterruptedException { } awaitResponsesAdded(); - + assertEquals(numResponses, searchResponseMerger.numResponses()); final SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); SearchResponse searchResponse = searchResponseMerger.getMergedResponse(clusters); @@ -434,6 +439,72 @@ public void testMergeSearchHits() throws InterruptedException { } } + public void testMergeSearchHitsSameCluster() throws InterruptedException { + //this test verifies that we can search against docs coming from the same cluster, + // in case it is registered twice with different aliases. + final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> 0); + SearchResponseMerger merger = new SearchResponseMerger(0, 10, Integer.MAX_VALUE, timeProvider, flag -> null); + ShardId shardId = new ShardId("index", "index-uuid", 0); + for (int i = 0; i < numResponses; i++) { + SearchHit hit = new SearchHit(0); + SearchShardTarget shardTarget = new SearchShardTarget(randomAlphaOfLengthBetween(3, 8), shardId, + "cluster-" + i, OriginalIndices.NONE); + hit.shard(shardTarget); + hit.score(10F); + SearchHits searchHits = new SearchHits(new SearchHit[]{hit}, new TotalHits(1, TotalHits.Relation.EQUAL_TO), 10F); + InternalSearchResponse internalSearchResponse = new InternalSearchResponse(searchHits, InternalAggregations.EMPTY, + null, null, false, null, 1); + merger.add(new SearchResponse(internalSearchResponse, null, 1, 1, 0, 100L, ShardSearchFailure.EMPTY_ARRAY, + SearchResponse.Clusters.EMPTY)); + } + awaitResponsesAdded(); + assertEquals(numResponses, merger.numResponses()); + SearchResponse.Clusters clusters = new SearchResponse.Clusters(numResponses, numResponses, 0, + CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + SearchResponse merged = merger.getMergedResponse(clusters); + assertSame(clusters, merged.getClusters()); + SearchHits searchHits = merged.getHits(); + assertNotNull(searchHits.getTotalHits()); + assertEquals(TotalHits.Relation.EQUAL_TO, searchHits.getTotalHits().relation); + assertEquals(numResponses, searchHits.getTotalHits().value); + assertEquals(10F, searchHits.getMaxScore(), 0F); + assertEquals(numResponses, searchHits.getHits().length); + for (int i = 0; i < numResponses; i++) { + SearchHit searchHit = searchHits.getHits()[i]; + assertEquals(0, searchHit.docId()); + assertEquals(10F, searchHit.getScore(), 0F); + assertEquals(shardId, searchHit.getShard().getShardId()); + assertEquals("cluster-" + i, searchHit.getShard().getClusterAlias()); + } + } + + public void testMergeNoResponsesAdded() { + long currentRelativeTime = randomLong(); + final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> currentRelativeTime); + SearchResponseMerger merger = new SearchResponseMerger(0, 10, Integer.MAX_VALUE, timeProvider, flag -> null); + SearchResponse.Clusters clusters = SearchResponseTests.randomClusters(); + assertEquals(0, merger.numResponses()); + SearchResponse response = merger.getMergedResponse(clusters); + assertSame(clusters, response.getClusters()); + assertEquals(TimeUnit.NANOSECONDS.toMillis(currentRelativeTime), response.getTook().millis()); + assertEquals(0, response.getTotalShards()); + assertEquals(0, response.getSuccessfulShards()); + assertEquals(0, response.getSkippedShards()); + assertEquals(0, response.getFailedShards()); + assertEquals(0, response.getNumReducePhases()); + assertFalse(response.isTimedOut()); + assertNotNull(response.getHits().getTotalHits()); + assertEquals(0, response.getHits().getTotalHits().value); + assertEquals(0, response.getHits().getHits().length); + assertEquals(TotalHits.Relation.EQUAL_TO, response.getHits().getTotalHits().relation); + assertNull(response.getScrollId()); + assertSame(InternalAggregations.EMPTY, response.getAggregations()); + assertNull(response.getSuggest()); + assertEquals(0, response.getProfileResults().size()); + assertNull(response.isTerminatedEarly()); + assertEquals(0, response.getShardFailures().length); + } + private static Tuple randomTrackTotalHits() { switch(randomIntBetween(0, 2)) { case 0: From 650e876ff59fc6ff2ee3283f0f8bcd959bc34433 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 15:31:47 +0100 Subject: [PATCH 10/27] fix --- .../modules/cross-cluster-search.asciidoc | 3 + .../test/multi_cluster/10_basic.yml | 1 + .../test/multi_cluster/70_skip_shards.yml | 2 + .../action/search/SearchResponseMerger.java | 33 +++++++--- .../search/SearchResponseMergerTests.java | 60 ++++++------------- 5 files changed, 49 insertions(+), 50 deletions(-) diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index d57bdf85e21f9..ab3a42b4c9c48 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -65,6 +65,7 @@ GET /cluster_one:twitter/_search { "took": 150, "timed_out": false, + "num_reduce_phases": 2, "_shards": { "total": 1, "successful": 1, @@ -131,6 +132,7 @@ will be prefixed with their remote cluster name: { "took": 150, "timed_out": false, + "num_reduce_phases": 3, "_shards": { "total": 2, "successful": 2, @@ -224,6 +226,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> { "took": 150, "timed_out": false, + "num_reduce_phases": 3, "_shards": { "total": 2, "successful": 2, diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 786ebb2a99cc9..036b070102406 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -40,6 +40,7 @@ - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {num} - match: { _shards.total: 5 } - match: { hits.total: 11 } - gte: { hits.hits.0._seq_no: 0 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 723c6dfba308c..f565a55f1c541 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -34,6 +34,7 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} + - is_false: num_reduce_phases - match: {_clusters.execution_mode: "one_request_per_shard"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } @@ -52,6 +53,7 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} + - is_false: num_reduce_phases - match: {_clusters.execution_mode: "one_request_per_shard"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index 472097d287f3e..94f02303455af 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -39,6 +39,7 @@ import org.elasticsearch.search.profile.ProfileShardResult; import org.elasticsearch.search.profile.SearchProfileShardResults; import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.transport.RemoteClusterAware; import java.util.ArrayList; import java.util.Arrays; @@ -183,10 +184,11 @@ SearchResponse getMergedResponse(Clusters clusters) { Suggest suggest = groupedSuggestions.isEmpty() ? null : new Suggest(Suggest.reduce(groupedSuggestions)); InternalAggregations reducedAggs = InternalAggregations.reduce(aggs, reduceContextFunction.apply(true)); ShardSearchFailure[] shardFailures = failures.toArray(ShardSearchFailure.EMPTY_ARRAY); + SearchProfileShardResults profileShardResults = profileResults.isEmpty() ? null : new SearchProfileShardResults(profileResults); //make failures ordering consistent with ordinary search and CCS Arrays.sort(shardFailures, FAILURES_COMPARATOR); - InternalSearchResponse response = new InternalSearchResponse(mergedSearchHits, reducedAggs, suggest, - new SearchProfileShardResults(profileResults), topDocsStats.timedOut, topDocsStats.terminatedEarly, numReducePhases); + InternalSearchResponse response = new InternalSearchResponse(mergedSearchHits, reducedAggs, suggest, profileShardResults, + topDocsStats.timedOut, topDocsStats.terminatedEarly, numReducePhases); long tookInMillis = searchTimeProvider.buildTookInMillis(); return new SearchResponse(response, null, totalShards, successfulShards, skippedShards, tookInMillis, shardFailures, clusters); } @@ -260,16 +262,21 @@ private static TopDocs searchHitsToTopDocs(SearchHits searchHits, TotalHits tota } private static void setShardIndex(Map shards, List topDocsList) { - int shardIndex = 0; - for (Map.Entry shard : shards.entrySet()) { - shard.setValue(shardIndex++); + { + //assign a different shardIndex to each shard, based on their shardId natural ordering and their cluster alias + int shardIndex = 0; + for (Map.Entry shard : shards.entrySet()) { + shard.setValue(shardIndex++); + } } - //and go through all the scoreDocs from each cluster and set their corresponding shardIndex + //go through all the scoreDocs from each cluster and set their corresponding shardIndex for (TopDocs topDocs : topDocsList) { for (ScoreDoc scoreDoc : topDocs.scoreDocs) { FieldDocAndSearchHit fieldDocAndSearchHit = (FieldDocAndSearchHit) scoreDoc; SearchShardTarget shard = fieldDocAndSearchHit.searchHit.getShard(); - fieldDocAndSearchHit.shardIndex = shards.get(new ShardIdAndClusterAlias(shard.getShardId(), shard.getClusterAlias())); + ShardIdAndClusterAlias shardId = new ShardIdAndClusterAlias(shard.getShardId(), shard.getClusterAlias()); + assert shards.containsKey(shardId); + fieldDocAndSearchHit.shardIndex = shards.get(shardId); } } } @@ -346,7 +353,17 @@ public int compareTo(ShardIdAndClusterAlias o) { if (shardIdCompareTo != 0) { return shardIdCompareTo; } - return clusterAlias.compareTo(o.clusterAlias); + int clusterAliasCompareTo = clusterAlias.compareTo(o.clusterAlias); + if (clusterAliasCompareTo != 0) { + //TODO we may want to fix this, CCS returns remote results before local ones (TransportSearchAction#mergeShardsIterators) + if (clusterAlias.equals(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)) { + return 1; + } + if (o.clusterAlias.equals(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)) { + return -1; + } + } + return clusterAliasCompareTo; } } } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java index 2c8d4d4d21e23..712d6a60440fe 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java @@ -439,45 +439,6 @@ public void testMergeSearchHits() throws InterruptedException { } } - public void testMergeSearchHitsSameCluster() throws InterruptedException { - //this test verifies that we can search against docs coming from the same cluster, - // in case it is registered twice with different aliases. - final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> 0); - SearchResponseMerger merger = new SearchResponseMerger(0, 10, Integer.MAX_VALUE, timeProvider, flag -> null); - ShardId shardId = new ShardId("index", "index-uuid", 0); - for (int i = 0; i < numResponses; i++) { - SearchHit hit = new SearchHit(0); - SearchShardTarget shardTarget = new SearchShardTarget(randomAlphaOfLengthBetween(3, 8), shardId, - "cluster-" + i, OriginalIndices.NONE); - hit.shard(shardTarget); - hit.score(10F); - SearchHits searchHits = new SearchHits(new SearchHit[]{hit}, new TotalHits(1, TotalHits.Relation.EQUAL_TO), 10F); - InternalSearchResponse internalSearchResponse = new InternalSearchResponse(searchHits, InternalAggregations.EMPTY, - null, null, false, null, 1); - merger.add(new SearchResponse(internalSearchResponse, null, 1, 1, 0, 100L, ShardSearchFailure.EMPTY_ARRAY, - SearchResponse.Clusters.EMPTY)); - } - awaitResponsesAdded(); - assertEquals(numResponses, merger.numResponses()); - SearchResponse.Clusters clusters = new SearchResponse.Clusters(numResponses, numResponses, 0, - CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); - SearchResponse merged = merger.getMergedResponse(clusters); - assertSame(clusters, merged.getClusters()); - SearchHits searchHits = merged.getHits(); - assertNotNull(searchHits.getTotalHits()); - assertEquals(TotalHits.Relation.EQUAL_TO, searchHits.getTotalHits().relation); - assertEquals(numResponses, searchHits.getTotalHits().value); - assertEquals(10F, searchHits.getMaxScore(), 0F); - assertEquals(numResponses, searchHits.getHits().length); - for (int i = 0; i < numResponses; i++) { - SearchHit searchHit = searchHits.getHits()[i]; - assertEquals(0, searchHit.docId()); - assertEquals(10F, searchHit.getScore(), 0F); - assertEquals(shardId, searchHit.getShard().getShardId()); - assertEquals("cluster-" + i, searchHit.getShard().getClusterAlias()); - } - } - public void testMergeNoResponsesAdded() { long currentRelativeTime = randomLong(); final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> currentRelativeTime); @@ -570,8 +531,11 @@ private static Map randomRealisticIndices(int numIndices, int n for (int i = 0; i < numClusters; i++) { Index[] indices = new Index[indicesNames.length]; for (int j = 0; j < indices.length; j++) { - //Realistically clusters have the same indices with same names, but different uuid - indices[j] = new Index(indicesNames[j], randomAlphaOfLength(10)); + String indexName = indicesNames[j]; + //Realistically clusters have the same indices with same names, but different uuid. Yet it can happen that the same cluster + //is registered twice with different aliases and searched multiple times as part of the same search request. + String indexUuid = frequently() ? randomAlphaOfLength(10) : indexName; + indices[j] = new Index(indexName, indexUuid); } String clusterAlias; if (frequently() || indicesPerCluster.containsKey(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)) { @@ -622,10 +586,22 @@ public int compare(SearchHit a, SearchHit b) { } } } - int shardIdCompareTo = a.getShard().getShardId().compareTo(b.getShard().getShardId()); + SearchShardTarget aShard = a.getShard(); + SearchShardTarget bShard = b.getShard(); + int shardIdCompareTo = aShard.getShardId().compareTo(bShard.getShardId()); if (shardIdCompareTo != 0) { return shardIdCompareTo; } + int clusterAliasCompareTo = aShard.getClusterAlias().compareTo(bShard.getClusterAlias()); + if (clusterAliasCompareTo != 0) { + if (aShard.getClusterAlias().equals(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)) { + return 1; + } + if (bShard.getClusterAlias().equals(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)) { + return -1; + } + return clusterAliasCompareTo; + } return Integer.compare(a.docId(), b.docId()); } } From 74d52cb6fb91e07e3fd75a3dcb7e98887422a407 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 16:05:13 +0100 Subject: [PATCH 11/27] rename execution mode to reduce mode --- .../client/RequestConverters.java | 2 +- .../client/RequestConvertersTests.java | 6 +-- .../modules/cross-cluster-search.asciidoc | 2 +- docs/reference/search/request-body.asciidoc | 2 +- .../MultiSearchTemplateResponseTests.java | 4 +- ...rossClusterSearchUnavailableClusterIT.java | 14 +++---- .../test/multi_cluster/70_skip_shards.yml | 4 +- .../resources/rest-api-spec/api/msearch.json | 2 +- .../rest-api-spec/api/msearch_template.json | 2 +- .../resources/rest-api-spec/api/search.json | 2 +- .../rest-api-spec/api/search_template.json | 2 +- ...SExecutionMode.java => CCSReduceMode.java} | 8 ++-- .../action/search/MultiSearchRequest.java | 4 +- .../action/search/SearchRequest.java | 38 +++++++++---------- .../action/search/SearchResponse.java | 14 +++---- .../action/search/TransportSearchAction.java | 12 +++--- .../action/search/RestMultiSearchAction.java | 2 +- .../rest/action/search/RestSearchAction.java | 4 +- .../action/search/SearchRequestTests.java | 28 +++++++------- .../action/search/SearchResponseTests.java | 4 +- .../search/RandomSearchRequestGenerator.java | 4 +- 21 files changed, 80 insertions(+), 80 deletions(-) rename server/src/main/java/org/elasticsearch/action/search/{CCSExecutionMode.java => CCSReduceMode.java} (82%) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index b88d9e52b034b..a9a9fa2bfa6af 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -399,7 +399,7 @@ private static void addSearchRequestParams(Params params, SearchRequest searchRe params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); if (searchRequest.getCCSExecutionMode() != null) { - params.putParam("ccs_execution_mode", searchRequest.getCCSExecutionMode().toString()); + params.putParam("ccs_reduce_mode", searchRequest.getCCSExecutionMode().toString()); } if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 8940c29c373d9..ac233943e6e9e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -41,7 +41,7 @@ import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.MultiGetRequest; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.search.CCSExecutionMode; +import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.ClearScrollRequest; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; @@ -1864,8 +1864,8 @@ private static void setRandomSearchParams(SearchRequest searchRequest, expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); } if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(CCSExecutionMode.values())); - expectedParams.put("ccs_execution_mode", searchRequest.getCCSExecutionMode().toString()); + searchRequest.setCCSExecutionMode(randomFrom(CCSReduceMode.values())); + expectedParams.put("ccs_reduce_mode", searchRequest.getCCSExecutionMode().toString()); } } diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index ab3a42b4c9c48..4784eb88e7345 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -310,7 +310,7 @@ the coordinating node and the remote clusters involved. By default `one_request_per_cluster` is used when possible, unless a scroll is provided, or unless inner hits are requested as part of field collapsing: -The <> supports the `ccs_execution_mode` +The <> supports the `ccs_reduce_mode` parameter which allows to select the execution mode if needed. The execution mode that was used for the request is returned as part of the `_clusters` section. diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index fa438af86bfaf..404b968441b12 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -113,7 +113,7 @@ And here is a sample response: reduce the memory overhead per search request if the potential number of shards in the request can be large. -`ccs_execution_mode`:: +`ccs_reduce_mode`:: The cross-cluster search execution mode. Can be `one_request_per_shard` or `prefer_one_request_per_cluster`. Defaults to `prefer_one_request_per_cluster`. diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java index 1ec412e6f2ebf..a61fb821c58dd 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.search.CCSExecutionMode; +import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.Strings; @@ -65,7 +65,7 @@ private static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - CCSExecutionMode executionMode = randomFrom(CCSExecutionMode.values()); + CCSReduceMode executionMode = randomFrom(CCSReduceMode.values()); return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); } diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index a3aa3080a23ee..c980c45a20e70 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -33,7 +33,7 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.search.CCSExecutionMode; +import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -165,7 +165,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -174,7 +174,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -184,7 +184,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -203,7 +203,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -212,7 +212,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -222,7 +222,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSExecutionMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index f565a55f1c541..86975deb9db84 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -29,7 +29,7 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_execution_mode: "one_request_per_shard" + ccs_reduce_mode: "one_request_per_shard" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2016-02-01", "lt": "2018-02-01"} } } } - match: { hits.total: 1 } @@ -48,7 +48,7 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_execution_mode: "one_request_per_shard" + ccs_reduce_mode: "one_request_per_shard" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2015-02-01", "lt": "2016-02-01"} } } } - match: { hits.total: 1 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index fa41c51df8d0a..9ec2ff6d6cc9f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -44,7 +44,7 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_execution_mode": { + "ccs_reduce_mode": { "type" : "enum", "options" : ["one_request_per_shard","one_request_per_cluster"], "default" : "one_request_per_cluster", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index 35f21d485162e..a782aa88f608e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -34,7 +34,7 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_execution_mode": { + "ccs_reduce_mode": { "type" : "enum", "options" : ["one_request_per_shard","one_request_per_cluster"], "default" : "one_request_per_cluster", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index de69e89c8a2ae..026fc636fe3e5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -24,7 +24,7 @@ "type" : "boolean", "description" : "Specify whether wildcard and prefix queries should be analyzed (default: false)" }, - "ccs_execution_mode": { + "ccs_reduce_mode": { "type" : "enum", "options" : ["one_request_per_shard","one_request_per_cluster"], "default" : "one_request_per_cluster", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index cd15e684c9679..091cbf194d976 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -68,7 +68,7 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_execution_mode": { + "ccs_reduce_mode": { "type" : "enum", "options" : ["one_request_per_shard","one_request_per_cluster"], "default" : "one_request_per_cluster", diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java similarity index 82% rename from server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java rename to server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java index 190c9509d8eee..d71b70d036364 100644 --- a/server/src/main/java/org/elasticsearch/action/search/CCSExecutionMode.java +++ b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java @@ -24,7 +24,7 @@ /** * The execution modes when executing a cross-cluster search request */ -public enum CCSExecutionMode { +public enum CCSReduceMode { ONE_REQUEST_PER_SHARD, ONE_REQUEST_PER_CLUSTER; @@ -33,12 +33,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT); } - public static CCSExecutionMode fromString(String executionMode) { - for (CCSExecutionMode value : CCSExecutionMode.values()) { + public static CCSReduceMode fromString(String executionMode) { + for (CCSReduceMode value : CCSReduceMode.values()) { if (value.name().toLowerCase(Locale.ROOT).equals(executionMode)) { return value; } } - throw new IllegalArgumentException("unknown ccs_execution_mode: [" + executionMode + "]"); + throw new IllegalArgumentException("unknown ccs_reduce_mode: [" + executionMode + "]"); } } diff --git a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java index e7b9becbe4493..c7e0df8530dc6 100644 --- a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java @@ -230,7 +230,7 @@ public static void readMultiLineFormat(BytesReference data, searchRequest.types(nodeStringArrayValue(value)); } else if ("search_type".equals(entry.getKey()) || "searchType".equals(entry.getKey())) { searchRequest.searchType(nodeStringValue(value, null)); - } else if ("ccs_execution_mode".equals(entry.getKey()) || "ccsExecutionMode".equals(entry.getKey())) { + } else if ("ccs_reduce_mode".equals(entry.getKey()) || "ccsExecutionMode".equals(entry.getKey())) { searchRequest.setCCSExecutionMode(nodeStringValue(value, null)); } else if ("request_cache".equals(entry.getKey()) || "requestCache".equals(entry.getKey())) { searchRequest.requestCache(nodeBooleanValue(value, entry.getKey())); @@ -334,7 +334,7 @@ public static void writeSearchRequestParams(SearchRequest request, XContentBuild xContentBuilder.field("search_type", request.searchType().name().toLowerCase(Locale.ROOT)); } if (request.getCCSExecutionMode() != null) { - xContentBuilder.field("ccs_execution_mode", request.getCCSExecutionMode().toString()); + xContentBuilder.field("ccs_reduce_mode", request.getCCSExecutionMode().toString()); } if (request.requestCache() != null) { xContentBuilder.field("request_cache", request.requestCache()); diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index ebb1a86254ad5..de971de09fff9 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -92,7 +92,7 @@ public final class SearchRequest extends ActionRequest implements IndicesRequest private String[] types = Strings.EMPTY_ARRAY; - private CCSExecutionMode ccsExecutionMode; + private CCSReduceMode ccsReduceMode; public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled(); @@ -152,7 +152,7 @@ static SearchRequest withLocalReduction(SearchRequest originalSearchRequest, Str private SearchRequest(SearchRequest searchRequest, String[] indices, String localClusterAlias, long absoluteStartMillis) { this.allowPartialSearchResults = searchRequest.allowPartialSearchResults; this.batchedReduceSize = searchRequest.batchedReduceSize; - this.ccsExecutionMode = searchRequest.ccsExecutionMode; + this.ccsReduceMode = searchRequest.ccsReduceMode; this.indices = indices; this.indicesOptions = searchRequest.indicesOptions; this.maxConcurrentShardRequests = searchRequest.maxConcurrentShardRequests; @@ -203,7 +203,7 @@ public SearchRequest(StreamInput in) throws IOException { absoluteStartMillis = DEFAULT_ABSOLUTE_START_MILLIS; } if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - ccsExecutionMode = in.readOptionalEnum(CCSExecutionMode.class); + ccsReduceMode = in.readOptionalEnum(CCSReduceMode.class); } } @@ -232,7 +232,7 @@ public void writeTo(StreamOutput out) throws IOException { } } if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeOptionalEnum(ccsExecutionMode); + out.writeOptionalEnum(ccsReduceMode); } } @@ -262,15 +262,15 @@ public ActionRequestValidationException validate() { validationException = addValidationError("[request_cache] cannot be used in a scroll context", validationException); } - if (ccsExecutionMode == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - validationException = addValidationError("[ccs_execution_mode] cannot be [" + CCSExecutionMode.ONE_REQUEST_PER_CLUSTER + + if (ccsReduceMode == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.ONE_REQUEST_PER_CLUSTER + "] in a scroll context", validationException); } } boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; - if (collapseWithInnerHits && ccsExecutionMode == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - validationException = addValidationError("[ccs_execution_mode] cannot be [" + CCSExecutionMode.ONE_REQUEST_PER_CLUSTER + + if (collapseWithInnerHits && ccsReduceMode == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.ONE_REQUEST_PER_CLUSTER + "] when inner hits are requested as part of field collapsing", validationException); } return validationException; @@ -325,27 +325,27 @@ public SearchRequest indicesOptions(IndicesOptions indicesOptions) { } /** - * Sets the execution mode (as a {@link CCSExecutionMode}) for cross-cluster search requests + * Sets the execution mode (as a {@link CCSReduceMode}) for cross-cluster search requests */ - public void setCCSExecutionMode(CCSExecutionMode ccsExecutionMode) { - this.ccsExecutionMode = Objects.requireNonNull(ccsExecutionMode, "ccsExecutionMode must not be null"); + public void setCCSExecutionMode(CCSReduceMode ccsReduceMode) { + this.ccsReduceMode = Objects.requireNonNull(ccsReduceMode, "ccsReduceMode must not be null"); } /** * Sets the execution mode (as a string) for cross-cluster search requests */ public void setCCSExecutionMode(String ccsExecutionMode) { - this.ccsExecutionMode = CCSExecutionMode.fromString(ccsExecutionMode); + this.ccsReduceMode = CCSReduceMode.fromString(ccsExecutionMode); } /** - * Returns the execution mode for cross-cluster search request. When not set {@link CCSExecutionMode#ONE_REQUEST_PER_CLUSTER} is used + * Returns the execution mode for cross-cluster search request. When not set {@link CCSReduceMode#ONE_REQUEST_PER_CLUSTER} is used * whenever possible. In case a scroll is provided or inner hits are requested as part of field collapsing, - * {@link CCSExecutionMode#ONE_REQUEST_PER_SHARD} is used instead. + * {@link CCSReduceMode#ONE_REQUEST_PER_SHARD} is used instead. */ @Nullable - public CCSExecutionMode getCCSExecutionMode() { - return this.ccsExecutionMode; + public CCSReduceMode getCCSExecutionMode() { + return this.ccsReduceMode; } /** @@ -640,14 +640,14 @@ public boolean equals(Object o) { Objects.equals(allowPartialSearchResults, that.allowPartialSearchResults) && Objects.equals(localClusterAlias, that.localClusterAlias) && absoluteStartMillis == that.absoluteStartMillis && - ccsExecutionMode == that.ccsExecutionMode; + ccsReduceMode == that.ccsReduceMode; } @Override public int hashCode() { return Objects.hash(searchType, Arrays.hashCode(indices), routing, preference, source, requestCache, scroll, Arrays.hashCode(types), indicesOptions, batchedReduceSize, maxConcurrentShardRequests, preFilterShardSize, - allowPartialSearchResults, localClusterAlias, absoluteStartMillis, ccsExecutionMode); + allowPartialSearchResults, localClusterAlias, absoluteStartMillis, ccsReduceMode); } @Override @@ -667,7 +667,7 @@ public String toString() { ", allowPartialSearchResults=" + allowPartialSearchResults + ", localClusterAlias=" + localClusterAlias + ", getOrCreateAbsoluteStartMillis=" + absoluteStartMillis + - ", ccsExecutionMode=" + ccsExecutionMode + + ", ccsReduceMode=" + ccsReduceMode + ", source=" + source + '}'; } } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 5b0f81273f8f6..8fbc71607e5e2 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -322,7 +322,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio int successful = -1; int total = -1; int skipped = -1; - CCSExecutionMode executionMode = null; + CCSReduceMode executionMode = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -334,7 +334,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); } else if (Clusters.EXECUTION_MODE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - executionMode = CCSExecutionMode.fromString(parser.text()); + executionMode = CCSReduceMode.fromString(parser.text()); } else { parser.skipChildren(); } @@ -409,7 +409,7 @@ public String toString() { */ public static class Clusters implements ToXContent, Writeable { - public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduceMode.ONE_REQUEST_PER_CLUSTER); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); @@ -420,9 +420,9 @@ public static class Clusters implements ToXContent, Writeable { private final int total; private final int successful; private final int skipped; - private final CCSExecutionMode executionMode; + private final CCSReduceMode executionMode; - public Clusters(int total, int successful, int skipped, CCSExecutionMode executionMode) { + public Clusters(int total, int successful, int skipped, CCSReduceMode executionMode) { assert total >= 0 && successful >= 0 && skipped >= 0 : "total: " + total + " successful: " + successful + " skipped: " + skipped; assert successful <= total && skipped == total - successful @@ -438,7 +438,7 @@ private Clusters(StreamInput in) throws IOException { this.successful = in.readVInt(); this.skipped = in.readVInt(); if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - this.executionMode = in.readOptionalEnum(CCSExecutionMode.class); + this.executionMode = in.readOptionalEnum(CCSReduceMode.class); } else { this.executionMode = null; } @@ -491,7 +491,7 @@ public int getSkipped() { /** * Returns the execution mode used for the execution of this cross-cluster search request */ - public CCSExecutionMode getExecutionMode() { + public CCSReduceMode getExecutionMode() { return executionMode; } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 4fbe99b96fae9..3c39b829a50e9 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -202,15 +202,15 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSExecutionMode executionMode = searchRequest.getCCSExecutionMode(); + CCSReduceMode executionMode = searchRequest.getCCSExecutionMode(); if (executionMode == null) { boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; boolean scroll = searchRequest.scroll() != null; - executionMode = collapseWithInnerHits || scroll ? CCSExecutionMode.ONE_REQUEST_PER_SHARD - : CCSExecutionMode.ONE_REQUEST_PER_CLUSTER; + executionMode = collapseWithInnerHits || scroll ? CCSReduceMode.ONE_REQUEST_PER_SHARD + : CCSReduceMode.ONE_REQUEST_PER_CLUSTER; } - if (executionMode == CCSExecutionMode.ONE_REQUEST_PER_SHARD) { + if (executionMode == CCSReduceMode.ONE_REQUEST_PER_SHARD) { AtomicInteger skippedClusters = new AtomicInteger(0); collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, @@ -226,7 +226,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), - CCSExecutionMode.ONE_REQUEST_PER_SHARD)); + CCSReduceMode.ONE_REQUEST_PER_SHARD)); }, listener::onFailure)); } else { @@ -374,7 +374,7 @@ private void maybeFinish() { Exception exception = exceptions.get(); if (exception == null) { SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), - skippedClusters.get(), CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + skippedClusters.get(), CCSReduceMode.ONE_REQUEST_PER_CLUSTER); SearchResponse response; try { //TODO test when merge breaks diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index ace4f7de12e81..d8f169ebb545b 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -147,7 +147,7 @@ public static void parseMultiLineRequest(RestRequest request, IndicesOptions ind String[] indices = Strings.splitStringByCommaToArray(request.param("index")); String[] types = Strings.splitStringByCommaToArray(request.param("type")); String searchType = request.param("search_type"); - String ccsExecutionMode = request.param("ccs_execution_mode"); + String ccsExecutionMode = request.param("ccs_reduce_mode"); String routing = request.param("routing"); final Tuple sourceTuple = request.contentOrSourceParam(); diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 2c3d706ed631b..1efd32b1f5385 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -173,8 +173,8 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r searchRequest.routing(request.param("routing")); searchRequest.preference(request.param("preference")); searchRequest.indicesOptions(IndicesOptions.fromRequest(request, searchRequest.indicesOptions())); - if (request.hasParam("ccs_execution_mode")) { - searchRequest.setCCSExecutionMode(request.param("ccs_execution_mode")); + if (request.hasParam("ccs_reduce_mode")) { + searchRequest.setCCSExecutionMode(request.param("ccs_reduce_mode")); } checkRestTotalHits(request, searchRequest); } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 44750e9a4678c..d65343d1285e2 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -145,7 +145,7 @@ public void testIllegalArguments() { assertEquals("keepAlive must not be null", e.getMessage()); IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> searchRequest.setCCSExecutionMode("whatever")); - assertEquals("unknown ccs_execution_mode: [whatever]", iae.getMessage()); + assertEquals("unknown ccs_reduce_mode: [whatever]", iae.getMessage()); } public void testValidate() throws IOException { @@ -163,8 +163,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().trackTotalHits(false); - if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -178,8 +178,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().from(10); - if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -191,8 +191,8 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder().size(0)); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -205,8 +205,8 @@ public void testValidate() throws IOException { searchRequest.source().addRescorer(new QueryRescorerBuilder(QueryBuilders.matchAllQuery())); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSExecutionMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -217,22 +217,22 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder()); searchRequest.scroll(new TimeValue(1000)); searchRequest.requestCache(false); - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_CLUSTER); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); - assertEquals("[ccs_execution_mode] cannot be [one_request_per_cluster] in a scroll context", + assertEquals("[ccs_reduce_mode] cannot be [one_request_per_cluster] in a scroll context", validationErrors.validationErrors().get(0)); } { SearchRequest searchRequest = createSearchRequest().source( new SearchSourceBuilder().collapse(new CollapseBuilder("field").setInnerHits(new InnerHitBuilder()))); searchRequest.scroll((Scroll)null); - searchRequest.setCCSExecutionMode(CCSExecutionMode.ONE_REQUEST_PER_CLUSTER); + searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_CLUSTER); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(validationErrors.validationErrors().toString(), 1, validationErrors.validationErrors().size()); - assertEquals("[ccs_execution_mode] cannot be [one_request_per_cluster] " + + assertEquals("[ccs_reduce_mode] cannot be [one_request_per_cluster] " + "when inner hits are requested as part of field collapsing", validationErrors.validationErrors().get(0)); } } @@ -265,7 +265,7 @@ private SearchRequest mutate(SearchRequest searchRequest) { () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); mutators.add(() -> mutation.setCCSExecutionMode(randomValueOtherThan(searchRequest.getCCSExecutionMode(), - () -> randomFrom(CCSExecutionMode.values())))); + () -> randomFrom(CCSReduceMode.values())))); randomFrom(mutators).run(); return mutation; } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index 831faf521a4fc..b09c09530d895 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -131,7 +131,7 @@ static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - CCSExecutionMode executionMode = randomFrom(CCSExecutionMode.values()); + CCSReduceMode executionMode = randomFrom(CCSReduceMode.values()); return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); } @@ -247,7 +247,7 @@ public void testToXContent() { new SearchHits(hits, new TotalHits(100, TotalHits.Relation.EQUAL_TO), 1.5f), null, null, null, false, null, 1 ), null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, - new SearchResponse.Clusters(5, 3, 2, CCSExecutionMode.ONE_REQUEST_PER_SHARD)); + new SearchResponse.Clusters(5, 3, 2, CCSReduceMode.ONE_REQUEST_PER_SHARD)); StringBuilder expectedString = new StringBuilder(); expectedString.append("{"); { diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index a15d8b68cc737..c32bc3213c1d1 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -19,7 +19,7 @@ package org.elasticsearch.search; -import org.elasticsearch.action.search.CCSExecutionMode; +import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.IndicesOptions; @@ -88,7 +88,7 @@ public static SearchRequest randomSearchRequest(Supplier ra SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(CCSExecutionMode.values())); + searchRequest.setCCSExecutionMode(randomFrom(CCSReduceMode.values())); } if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); From c5d9dc55d45653772174f2b86f2bae0a297834de Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 16:06:55 +0100 Subject: [PATCH 12/27] rename reduce modes --- .../modules/cross-cluster-search.asciidoc | 12 +++++----- docs/reference/search/request-body.asciidoc | 4 ++-- ...rossClusterSearchUnavailableClusterIT.java | 12 +++++----- .../test/multi_cluster/10_basic.yml | 16 ++++++------- .../test/multi_cluster/40_scroll.yml | 2 +- .../test/multi_cluster/70_skip_shards.yml | 8 +++---- .../resources/rest-api-spec/api/msearch.json | 4 ++-- .../rest-api-spec/api/msearch_template.json | 4 ++-- .../resources/rest-api-spec/api/search.json | 4 ++-- .../rest-api-spec/api/search_template.json | 4 ++-- .../action/search/CCSReduceMode.java | 3 +-- .../action/search/SearchRequest.java | 12 +++++----- .../action/search/SearchResponse.java | 2 +- .../action/search/TransportSearchAction.java | 10 ++++---- .../action/search/SearchRequestTests.java | 24 +++++++++---------- .../action/search/SearchResponseTests.java | 4 ++-- 16 files changed, 62 insertions(+), 63 deletions(-) diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index 4784eb88e7345..1ae06576f9feb 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -73,7 +73,7 @@ GET /cluster_one:twitter/_search "skipped": 0 }, "_clusters": { - "execution_mode": "one_request_per_cluster", + "execution_mode": "remote", "total": 1, "successful": 1, "skipped": 0 @@ -140,7 +140,7 @@ will be prefixed with their remote cluster name: "skipped": 0 }, "_clusters": { - "execution_mode": "one_request_per_cluster", + "execution_mode": "remote", "total": 2, "successful": 2, "skipped": 0 @@ -234,7 +234,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> "skipped": 0 }, "_clusters": { <1> - "execution_mode": "one_request_per_cluster", + "execution_mode": "remote", "total": 3, "successful": 2, "skipped": 1 @@ -286,7 +286,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> When searching across remote clusters, there are two possible execution modes: -- `one_request_per_cluster`: the coordinating node sends one search +- `remote`: the coordinating node sends one search request to each cluster. Each cluster performs the search independently, reducing and fetching results which are then returned to the caller. Once the CCS node has received all the responses, it performs another @@ -296,7 +296,7 @@ and the remote clusters involved, which is typically the case. A single request is sent to each remote cluster, at the cost of retrieving `from` + `size` already fetched results from each one of them. -- `one_request_per_shard`: the coordinating node sends a +- `local`: the coordinating node sends a <> request to each remote cluster, to collect information about their corresponding remote indices involved in the search request and the shards where their data is located. Once each cluster has @@ -308,7 +308,7 @@ then reduced (and fetched, depending on the execution mode is beneficial whenever there is very low network latency between the coordinating node and the remote clusters involved. -By default `one_request_per_cluster` is used when possible, unless a scroll +By default `remote` is used when possible, unless a scroll is provided, or unless inner hits are requested as part of field collapsing: The <> supports the `ccs_reduce_mode` parameter which allows to select the execution mode if needed. The execution diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index 404b968441b12..5072062536df5 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -115,8 +115,8 @@ And here is a sample response: `ccs_reduce_mode`:: - The cross-cluster search execution mode. Can be `one_request_per_shard` or - `prefer_one_request_per_cluster`. Defaults to `prefer_one_request_per_cluster`. + The cross-cluster search execution mode. Can be `local` or + `prefer_remote`. Defaults to `prefer_remote`. See <> for more. diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index c980c45a20e70..c54b06352183d 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -165,7 +165,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -174,7 +174,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -184,7 +184,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.LOCAL, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -203,7 +203,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -212,7 +212,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_CLUSTER, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -222,7 +222,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.ONE_REQUEST_PER_SHARD, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.LOCAL, response.getClusters().getExecutionMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 036b070102406..f65b9f95a71be 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -39,7 +39,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: {num} - match: { _shards.total: 5 } - match: { hits.total: 11 } @@ -67,7 +67,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -88,7 +88,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -139,7 +139,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -169,7 +169,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -184,7 +184,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -201,7 +201,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 4 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -218,7 +218,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_cluster"} + - match: {_clusters.execution_mode: "remote"} - match: { _shards.total: 1 } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index 1e61785ac3347..a43fc68939f14 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -15,7 +15,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "one_request_per_shard"} + - match: {_clusters.execution_mode: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 86975deb9db84..6e50aac4c22c7 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -29,13 +29,13 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_reduce_mode: "one_request_per_shard" + ccs_reduce_mode: "local" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2016-02-01", "lt": "2018-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} - is_false: num_reduce_phases - - match: {_clusters.execution_mode: "one_request_per_shard"} + - match: {_clusters.execution_mode: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} @@ -48,13 +48,13 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_reduce_mode: "one_request_per_shard" + ccs_reduce_mode: "local" body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2015-02-01", "lt": "2016-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} - is_false: num_reduce_phases - - match: {_clusters.execution_mode: "one_request_per_shard"} + - match: {_clusters.execution_mode: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index 9ec2ff6d6cc9f..f0520e0eedd22 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -46,8 +46,8 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["one_request_per_shard","one_request_per_cluster"], - "default" : "one_request_per_cluster", + "options" : ["local","remote"], + "default" : "remote", "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index a782aa88f608e..90e3c80ab301a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -36,8 +36,8 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["one_request_per_shard","one_request_per_cluster"], - "default" : "one_request_per_cluster", + "options" : ["local","remote"], + "default" : "remote", "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index 026fc636fe3e5..d3a8cbcc086a1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -26,8 +26,8 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["one_request_per_shard","one_request_per_cluster"], - "default" : "one_request_per_cluster", + "options" : ["local","remote"], + "default" : "remote", "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" }, "default_operator": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index 091cbf194d976..03e58a3da36f7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -70,8 +70,8 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["one_request_per_shard","one_request_per_cluster"], - "default" : "one_request_per_cluster", + "options" : ["local","remote"], + "default" : "remote", "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" } } diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java index d71b70d036364..30e180aeda999 100644 --- a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java +++ b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java @@ -25,8 +25,7 @@ * The execution modes when executing a cross-cluster search request */ public enum CCSReduceMode { - ONE_REQUEST_PER_SHARD, - ONE_REQUEST_PER_CLUSTER; + LOCAL, REMOTE; @Override public String toString() { diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index de971de09fff9..158f0040233d3 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -262,15 +262,15 @@ public ActionRequestValidationException validate() { validationException = addValidationError("[request_cache] cannot be used in a scroll context", validationException); } - if (ccsReduceMode == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.ONE_REQUEST_PER_CLUSTER + + if (ccsReduceMode == CCSReduceMode.REMOTE) { + validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.REMOTE + "] in a scroll context", validationException); } } boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; - if (collapseWithInnerHits && ccsReduceMode == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.ONE_REQUEST_PER_CLUSTER + + if (collapseWithInnerHits && ccsReduceMode == CCSReduceMode.REMOTE) { + validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.REMOTE + "] when inner hits are requested as part of field collapsing", validationException); } return validationException; @@ -339,9 +339,9 @@ public void setCCSExecutionMode(String ccsExecutionMode) { } /** - * Returns the execution mode for cross-cluster search request. When not set {@link CCSReduceMode#ONE_REQUEST_PER_CLUSTER} is used + * Returns the execution mode for cross-cluster search request. When not set {@link CCSReduceMode#REMOTE} is used * whenever possible. In case a scroll is provided or inner hits are requested as part of field collapsing, - * {@link CCSReduceMode#ONE_REQUEST_PER_SHARD} is used instead. + * {@link CCSReduceMode#LOCAL} is used instead. */ @Nullable public CCSReduceMode getCCSExecutionMode() { diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 8fbc71607e5e2..bd00dae4a58ef 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -409,7 +409,7 @@ public String toString() { */ public static class Clusters implements ToXContent, Writeable { - public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduceMode.ONE_REQUEST_PER_CLUSTER); + public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduceMode.REMOTE); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 3c39b829a50e9..57d14c6768ac8 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -207,10 +207,10 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; boolean scroll = searchRequest.scroll() != null; - executionMode = collapseWithInnerHits || scroll ? CCSReduceMode.ONE_REQUEST_PER_SHARD - : CCSReduceMode.ONE_REQUEST_PER_CLUSTER; + executionMode = collapseWithInnerHits || scroll ? CCSReduceMode.LOCAL + : CCSReduceMode.REMOTE; } - if (executionMode == CCSReduceMode.ONE_REQUEST_PER_SHARD) { + if (executionMode == CCSReduceMode.LOCAL) { AtomicInteger skippedClusters = new AtomicInteger(0); collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, @@ -226,7 +226,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), - CCSReduceMode.ONE_REQUEST_PER_SHARD)); + CCSReduceMode.LOCAL)); }, listener::onFailure)); } else { @@ -374,7 +374,7 @@ private void maybeFinish() { Exception exception = exceptions.get(); if (exception == null) { SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), - skippedClusters.get(), CCSReduceMode.ONE_REQUEST_PER_CLUSTER); + skippedClusters.get(), CCSReduceMode.REMOTE); SearchResponse response; try { //TODO test when merge breaks diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index d65343d1285e2..a97b048952760 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -163,8 +163,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().trackTotalHits(false); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -178,8 +178,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().from(10); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -191,8 +191,8 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder().size(0)); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -205,8 +205,8 @@ public void testValidate() throws IOException { searchRequest.source().addRescorer(new QueryRescorerBuilder(QueryBuilders.matchAllQuery())); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.ONE_REQUEST_PER_CLUSTER) { - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_SHARD); + if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -217,22 +217,22 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder()); searchRequest.scroll(new TimeValue(1000)); searchRequest.requestCache(false); - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_CLUSTER); + searchRequest.setCCSExecutionMode(CCSReduceMode.REMOTE); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); - assertEquals("[ccs_reduce_mode] cannot be [one_request_per_cluster] in a scroll context", + assertEquals("[ccs_reduce_mode] cannot be [remote] in a scroll context", validationErrors.validationErrors().get(0)); } { SearchRequest searchRequest = createSearchRequest().source( new SearchSourceBuilder().collapse(new CollapseBuilder("field").setInnerHits(new InnerHitBuilder()))); searchRequest.scroll((Scroll)null); - searchRequest.setCCSExecutionMode(CCSReduceMode.ONE_REQUEST_PER_CLUSTER); + searchRequest.setCCSExecutionMode(CCSReduceMode.REMOTE); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(validationErrors.validationErrors().toString(), 1, validationErrors.validationErrors().size()); - assertEquals("[ccs_reduce_mode] cannot be [one_request_per_cluster] " + + assertEquals("[ccs_reduce_mode] cannot be [remote] " + "when inner hits are requested as part of field collapsing", validationErrors.validationErrors().get(0)); } } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index b09c09530d895..0dfa9039957e5 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -247,7 +247,7 @@ public void testToXContent() { new SearchHits(hits, new TotalHits(100, TotalHits.Relation.EQUAL_TO), 1.5f), null, null, null, false, null, 1 ), null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, - new SearchResponse.Clusters(5, 3, 2, CCSReduceMode.ONE_REQUEST_PER_SHARD)); + new SearchResponse.Clusters(5, 3, 2, CCSReduceMode.LOCAL)); StringBuilder expectedString = new StringBuilder(); expectedString.append("{"); { @@ -262,7 +262,7 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("{\"execution_mode\":\"one_request_per_shard\","); + expectedString.append("{\"execution_mode\":\"local\","); expectedString.append("\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); From 589d69ed811880d970d5c37a553423f19b269f8f Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 16:37:36 +0100 Subject: [PATCH 13/27] last renames and update docs --- .../client/RequestConverters.java | 4 +- .../client/RequestConvertersTests.java | 4 +- .../modules/cross-cluster-search.asciidoc | 66 +++++++++---------- docs/reference/search/request-body.asciidoc | 5 +- ...rossClusterSearchUnavailableClusterIT.java | 12 ++-- .../test/multi_cluster/10_basic.yml | 16 ++--- .../test/multi_cluster/40_scroll.yml | 2 +- .../test/multi_cluster/70_skip_shards.yml | 4 +- .../action/search/CCSReduceMode.java | 2 +- .../action/search/MultiSearchRequest.java | 8 +-- .../action/search/SearchRequest.java | 16 ++--- .../action/search/SearchResponse.java | 28 ++++---- .../action/search/TransportSearchAction.java | 2 +- .../rest/action/search/RestSearchAction.java | 2 +- .../action/search/SearchRequestTests.java | 30 ++++----- .../action/search/SearchResponseTests.java | 4 +- .../search/RandomSearchRequestGenerator.java | 2 +- 17 files changed, 103 insertions(+), 104 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index a9a9fa2bfa6af..a47ddf92ca0fa 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -398,8 +398,8 @@ private static void addSearchRequestParams(Params params, SearchRequest searchRe params.withPreference(searchRequest.preference()); params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); - if (searchRequest.getCCSExecutionMode() != null) { - params.putParam("ccs_reduce_mode", searchRequest.getCCSExecutionMode().toString()); + if (searchRequest.getCCSReduceMode() != null) { + params.putParam("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); } if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index ac233943e6e9e..5be82280688e3 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -1864,8 +1864,8 @@ private static void setRandomSearchParams(SearchRequest searchRequest, expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); } if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(CCSReduceMode.values())); - expectedParams.put("ccs_reduce_mode", searchRequest.getCCSExecutionMode().toString()); + searchRequest.setCCSReduceMode(randomFrom(CCSReduceMode.values())); + expectedParams.put("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); } } diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index 1ae06576f9feb..a03533e5dcd24 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -73,7 +73,7 @@ GET /cluster_one:twitter/_search "skipped": 0 }, "_clusters": { - "execution_mode": "remote", + "ccs_reduce_mode": "remote", "total": 1, "successful": 1, "skipped": 0 @@ -140,7 +140,7 @@ will be prefixed with their remote cluster name: "skipped": 0 }, "_clusters": { - "execution_mode": "remote", + "ccs_reduce_mode": "remote", "total": 2, "successful": 2, "skipped": 0 @@ -234,7 +234,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> "skipped": 0 }, "_clusters": { <1> - "execution_mode": "remote", + "ccs_reduce_mode": "remote", "total": 3, "successful": 2, "skipped": 1 @@ -281,40 +281,40 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> <1> The `clusters` section indicates that one cluster was unavailable and got skipped [float] -[[ccs-execution-mode]] -=== Execution mode +[[ccs-reduce-mode]] +=== Reduce mode -When searching across remote clusters, there are two possible execution modes: +When searching across remote clusters, there are two possible reduce modes: -- `remote`: the coordinating node sends one search -request to each cluster. Each cluster performs the search independently, -reducing and fetching results which are then returned to the caller. -Once the CCS node has received all the responses, it performs another -reduction and returns the relevant results back to the user. This execution -mode is beneficial when there is network latency between the coordinating node -and the remote clusters involved, which is typically the case. A single request -is sent to each remote cluster, at the cost of retrieving `from` + `size` -already fetched results from each one of them. +- `remote`: the coordinating node sends one search request to each cluster. +Each cluster performs the search independently, reducing and fetching results +which are then returned to the caller. Once the CCS node has received all the +responses, it performs another reduction and returns the relevant results back +to the user. The `remote` reduce mode is beneficial when there is network +latency between the coordinating node and the remote clusters involved, which +is typically the case. A single request is sent to each remote cluster, at the +cost of retrieving `from` + `size` already fetched results. -- `local`: the coordinating node sends a -<> request to each remote cluster, to collect -information about their corresponding remote indices involved in the search -request and the shards where their data is located. Once each cluster has -responded to such request, the search executes as if all shards were part of -the same cluster. The coordinating node sends one request to each shard -involved, each shard executes the query and returns its own results which are -then reduced (and fetched, depending on the -<>) by the coordinating node. This -execution mode is beneficial whenever there is very low network latency between -the coordinating node and the remote clusters involved. +- `local`: the coordinating node sends a <> +request to each remote cluster, in order to collect information about their +corresponding remote indices involved in the search request and the shards +where their data is located. Once each cluster has responded to such request, +the search executes as if all shards were part of the same cluster. The +coordinating node sends one request to each shard involved, each shard +executes the query and returns its own results which are then reduced (and +fetched, depending on the <>) by the +coordinating node. The `local` reduce mode is beneficial whenever there is very + low network latency between the coordinating node and the remote clusters + involved, as it treats all shards the same, at the cost of sending many + requests to each remote cluster. -By default `remote` is used when possible, unless a scroll -is provided, or unless inner hits are requested as part of field collapsing: -The <> supports the `ccs_reduce_mode` -parameter which allows to select the execution mode if needed. The execution -mode that was used for the request is returned as part of the `_clusters` -section. +By default `remote` is used whenever possible. In case a scroll is provided, +or inner hits are requested as part of field collapsing, `local` is used +instead. The <> supports the `ccs_reduce_mode` +parameter which allows to select the reduce mode if needed. The reduce mode +that was used for a cross-cluster search request is returned as part of the +`_clusters` section. Note that all the communication between the nodes, regardless of which cluster -they belong to and the selected execution mode, happens through the +they belong to and the selected reduce mode, happens through the <>. diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index 5072062536df5..3f5ba28c2c9e4 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -115,9 +115,8 @@ And here is a sample response: `ccs_reduce_mode`:: - The cross-cluster search execution mode. Can be `local` or - `prefer_remote`. Defaults to `prefer_remote`. - See <> for more. + The cross-cluster search reduce mode. Can be `local` or `remote`. + See <> for more. Out of the above, the `search_type`, `request_cache` and the `allow_partial_search_results` diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index c54b06352183d..bfcded260e9d7 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -165,7 +165,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -174,7 +174,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -184,7 +184,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.LOCAL, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.LOCAL, response.getClusters().getCCSReduceMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -203,7 +203,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -212,7 +212,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -222,7 +222,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.LOCAL, response.getClusters().getExecutionMode()); + assertEquals(CCSReduceMode.LOCAL, response.getClusters().getCCSReduceMode()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index f65b9f95a71be..83f387ece1780 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -39,7 +39,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: {num} - match: { _shards.total: 5 } - match: { hits.total: 11 } @@ -67,7 +67,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -88,7 +88,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -139,7 +139,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -169,7 +169,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -184,7 +184,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -201,7 +201,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 4 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -218,7 +218,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "remote"} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 1 } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index a43fc68939f14..a3e02a043d8fd 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -15,7 +15,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.execution_mode: "local"} + - match: {_clusters.ccs_reduce_mode: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 6e50aac4c22c7..d70cae02ac0f2 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -35,7 +35,7 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} - is_false: num_reduce_phases - - match: {_clusters.execution_mode: "local"} + - match: {_clusters.ccs_reduce_mode: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} @@ -54,7 +54,7 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} - is_false: num_reduce_phases - - match: {_clusters.execution_mode: "local"} + - match: {_clusters.ccs_reduce_mode: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java index 30e180aeda999..da69c8cecc01d 100644 --- a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java +++ b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java @@ -22,7 +22,7 @@ import java.util.Locale; /** - * The execution modes when executing a cross-cluster search request + * The reduce modes when executing a cross-cluster search request */ public enum CCSReduceMode { LOCAL, REMOTE; diff --git a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java index c7e0df8530dc6..f1e514018257f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java @@ -207,7 +207,7 @@ public static void readMultiLineFormat(BytesReference data, searchRequest.searchType(searchType); } if (ccsExecutionMode != null) { - searchRequest.setCCSExecutionMode(ccsExecutionMode); + searchRequest.setCCSReduceMode(ccsExecutionMode); } IndicesOptions defaultOptions = searchRequest.indicesOptions(); // now parse the action @@ -231,7 +231,7 @@ public static void readMultiLineFormat(BytesReference data, } else if ("search_type".equals(entry.getKey()) || "searchType".equals(entry.getKey())) { searchRequest.searchType(nodeStringValue(value, null)); } else if ("ccs_reduce_mode".equals(entry.getKey()) || "ccsExecutionMode".equals(entry.getKey())) { - searchRequest.setCCSExecutionMode(nodeStringValue(value, null)); + searchRequest.setCCSReduceMode(nodeStringValue(value, null)); } else if ("request_cache".equals(entry.getKey()) || "requestCache".equals(entry.getKey())) { searchRequest.requestCache(nodeBooleanValue(value, entry.getKey())); } else if ("preference".equals(entry.getKey())) { @@ -333,8 +333,8 @@ public static void writeSearchRequestParams(SearchRequest request, XContentBuild if (request.searchType() != null) { xContentBuilder.field("search_type", request.searchType().name().toLowerCase(Locale.ROOT)); } - if (request.getCCSExecutionMode() != null) { - xContentBuilder.field("ccs_reduce_mode", request.getCCSExecutionMode().toString()); + if (request.getCCSReduceMode() != null) { + xContentBuilder.field("ccs_reduce_mode", request.getCCSReduceMode().toString()); } if (request.requestCache() != null) { xContentBuilder.field("request_cache", request.requestCache()); diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 158f0040233d3..aac068247ee47 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -325,26 +325,26 @@ public SearchRequest indicesOptions(IndicesOptions indicesOptions) { } /** - * Sets the execution mode (as a {@link CCSReduceMode}) for cross-cluster search requests + * Sets the reduce mode (as a {@link CCSReduceMode}) for cross-cluster search requests */ - public void setCCSExecutionMode(CCSReduceMode ccsReduceMode) { + public void setCCSReduceMode(CCSReduceMode ccsReduceMode) { this.ccsReduceMode = Objects.requireNonNull(ccsReduceMode, "ccsReduceMode must not be null"); } /** - * Sets the execution mode (as a string) for cross-cluster search requests + * Sets the reduce mode (as a string) for cross-cluster search requests */ - public void setCCSExecutionMode(String ccsExecutionMode) { - this.ccsReduceMode = CCSReduceMode.fromString(ccsExecutionMode); + public void setCCSReduceMode(String ccsReduceMode) { + this.ccsReduceMode = CCSReduceMode.fromString(ccsReduceMode); } /** - * Returns the execution mode for cross-cluster search request. When not set {@link CCSReduceMode#REMOTE} is used - * whenever possible. In case a scroll is provided or inner hits are requested as part of field collapsing, + * Returns the reduce mode for cross-cluster search requests. When not set {@link CCSReduceMode#REMOTE} is used + * whenever possible; in case a scroll is provided or inner hits are requested as part of field collapsing, * {@link CCSReduceMode#LOCAL} is used instead. */ @Nullable - public CCSReduceMode getCCSExecutionMode() { + public CCSReduceMode getCCSReduceMode() { return this.ccsReduceMode; } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index bd00dae4a58ef..be57a31abe35a 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -333,7 +333,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio total = parser.intValue(); } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); - } else if (Clusters.EXECUTION_MODE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + } else if (Clusters.CCS_REDUCE_MODE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { executionMode = CCSReduceMode.fromString(parser.text()); } else { parser.skipChildren(); @@ -415,14 +415,14 @@ public static class Clusters implements ToXContent, Writeable { static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); static final ParseField SKIPPED_FIELD = new ParseField("skipped"); static final ParseField TOTAL_FIELD = new ParseField("total"); - static final ParseField EXECUTION_MODE_FIELD = new ParseField("execution_mode"); + static final ParseField CCS_REDUCE_MODE_FIELD = new ParseField("ccs_reduce_mode"); private final int total; private final int successful; private final int skipped; - private final CCSReduceMode executionMode; + private final CCSReduceMode ccsReduceMode; - public Clusters(int total, int successful, int skipped, CCSReduceMode executionMode) { + public Clusters(int total, int successful, int skipped, CCSReduceMode ccsReduceMode) { assert total >= 0 && successful >= 0 && skipped >= 0 : "total: " + total + " successful: " + successful + " skipped: " + skipped; assert successful <= total && skipped == total - successful @@ -430,7 +430,7 @@ public Clusters(int total, int successful, int skipped, CCSReduceMode executionM this.total = total; this.successful = successful; this.skipped = skipped; - this.executionMode = Objects.requireNonNull(executionMode); + this.ccsReduceMode = Objects.requireNonNull(ccsReduceMode); } private Clusters(StreamInput in) throws IOException { @@ -438,9 +438,9 @@ private Clusters(StreamInput in) throws IOException { this.successful = in.readVInt(); this.skipped = in.readVInt(); if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - this.executionMode = in.readOptionalEnum(CCSReduceMode.class); + this.ccsReduceMode = in.readOptionalEnum(CCSReduceMode.class); } else { - this.executionMode = null; + this.ccsReduceMode = null; } } @@ -450,7 +450,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVInt(successful); out.writeVInt(skipped); if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeOptionalEnum(executionMode); + out.writeOptionalEnum(ccsReduceMode); } } @@ -458,7 +458,7 @@ public void writeTo(StreamOutput out) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (this != EMPTY) { builder.startObject(_CLUSTERS_FIELD.getPreferredName()); - builder.field(EXECUTION_MODE_FIELD.getPreferredName(), executionMode == null ? null : executionMode.toString()); + builder.field(CCS_REDUCE_MODE_FIELD.getPreferredName(), ccsReduceMode == null ? null : ccsReduceMode.toString()); builder.field(TOTAL_FIELD.getPreferredName(), total); builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); builder.field(SKIPPED_FIELD.getPreferredName(), skipped); @@ -491,8 +491,8 @@ public int getSkipped() { /** * Returns the execution mode used for the execution of this cross-cluster search request */ - public CCSReduceMode getExecutionMode() { - return executionMode; + public CCSReduceMode getCCSReduceMode() { + return ccsReduceMode; } @Override @@ -507,17 +507,17 @@ public boolean equals(Object o) { return total == clusters.total && successful == clusters.successful && skipped == clusters.skipped && - executionMode == clusters.executionMode; + ccsReduceMode == clusters.ccsReduceMode; } @Override public int hashCode() { - return Objects.hash(total, successful, skipped, executionMode); + return Objects.hash(total, successful, skipped, ccsReduceMode); } @Override public String toString() { - return "Clusters{execution_mode=" + executionMode + ", total=" + total + + return "Clusters{ccs_reduce_mode=" + ccsReduceMode + ", total=" + total + ", successful=" + successful + ", skipped=" + skipped + '}'; } } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 57d14c6768ac8..430170452910f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -202,7 +202,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSReduceMode executionMode = searchRequest.getCCSExecutionMode(); + CCSReduceMode executionMode = searchRequest.getCCSReduceMode(); if (executionMode == null) { boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 1efd32b1f5385..8fa4f95192d33 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -174,7 +174,7 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r searchRequest.preference(request.param("preference")); searchRequest.indicesOptions(IndicesOptions.fromRequest(request, searchRequest.indicesOptions())); if (request.hasParam("ccs_reduce_mode")) { - searchRequest.setCCSExecutionMode(request.param("ccs_reduce_mode")); + searchRequest.setCCSReduceMode(request.param("ccs_reduce_mode")); } checkRestTotalHits(request, searchRequest); } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index a97b048952760..b7d2a472c7205 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -83,9 +83,9 @@ public void testRandomVersionSerialization() throws IOException { Version version = VersionUtils.randomVersion(random()); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new, version); if (version.before(Version.V_7_0_0)) { - assertNull(deserializedRequest.getCCSExecutionMode()); + assertNull(deserializedRequest.getCCSReduceMode()); } else { - assertEquals(searchRequest.getCCSExecutionMode(), deserializedRequest.getCCSExecutionMode()); + assertEquals(searchRequest.getCCSReduceMode(), deserializedRequest.getCCSReduceMode()); } if (version.before(Version.V_6_7_0)) { assertNull(deserializedRequest.getLocalClusterAlias()); @@ -104,7 +104,7 @@ public void testReadFromPre6_7_0() throws IOException { assertArrayEquals(new String[]{"index"}, searchRequest.indices()); assertNull(searchRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(searchRequest); - assertNull(searchRequest.getCCSExecutionMode()); + assertNull(searchRequest.getCCSReduceMode()); } } @@ -144,7 +144,7 @@ public void testIllegalArguments() { e = expectThrows(NullPointerException.class, () -> searchRequest.scroll((TimeValue)null)); assertEquals("keepAlive must not be null", e.getMessage()); - IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> searchRequest.setCCSExecutionMode("whatever")); + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> searchRequest.setCCSReduceMode("whatever")); assertEquals("unknown ccs_reduce_mode: [whatever]", iae.getMessage()); } @@ -163,8 +163,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().trackTotalHits(false); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); + if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -178,8 +178,8 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().from(10); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); + if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -191,8 +191,8 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder().size(0)); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); + if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -205,8 +205,8 @@ public void testValidate() throws IOException { searchRequest.source().addRescorer(new QueryRescorerBuilder(QueryBuilders.matchAllQuery())); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSExecutionMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSExecutionMode(CCSReduceMode.LOCAL); + if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { + searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); @@ -217,7 +217,7 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder()); searchRequest.scroll(new TimeValue(1000)); searchRequest.requestCache(false); - searchRequest.setCCSExecutionMode(CCSReduceMode.REMOTE); + searchRequest.setCCSReduceMode(CCSReduceMode.REMOTE); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -228,7 +228,7 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source( new SearchSourceBuilder().collapse(new CollapseBuilder("field").setInnerHits(new InnerHitBuilder()))); searchRequest.scroll((Scroll)null); - searchRequest.setCCSExecutionMode(CCSReduceMode.REMOTE); + searchRequest.setCCSReduceMode(CCSReduceMode.REMOTE); ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(validationErrors.validationErrors().toString(), 1, validationErrors.validationErrors().size()); @@ -264,7 +264,7 @@ private SearchRequest mutate(SearchRequest searchRequest) { mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); - mutators.add(() -> mutation.setCCSExecutionMode(randomValueOtherThan(searchRequest.getCCSExecutionMode(), + mutators.add(() -> mutation.setCCSReduceMode(randomValueOtherThan(searchRequest.getCCSReduceMode(), () -> randomFrom(CCSReduceMode.values())))); randomFrom(mutators).run(); return mutation; diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index 0dfa9039957e5..c520f356ce764 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -262,7 +262,7 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("{\"execution_mode\":\"local\","); + expectedString.append("{\"ccs_reduce_mode\":\"local\","); expectedString.append("\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); @@ -306,6 +306,6 @@ public void testSerializationPre7_0_0() throws IOException { assertEquals(clusters.getSkipped(), deserializedClusters.getSkipped()); assertEquals(clusters.getSuccessful(), deserializedClusters.getSuccessful()); assertEquals(clusters.getTotal(), deserializedClusters.getTotal()); - assertNull(deserializedClusters.getExecutionMode()); + assertNull(deserializedClusters.getCCSReduceMode()); } } diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index c32bc3213c1d1..c025b75d8fb28 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -88,7 +88,7 @@ public static SearchRequest randomSearchRequest(Supplier ra SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); if (randomBoolean()) { - searchRequest.setCCSExecutionMode(randomFrom(CCSReduceMode.values())); + searchRequest.setCCSReduceMode(randomFrom(CCSReduceMode.values())); } if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); From 56d845e1b2196730a16deaceaa15319fa3847c53 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 16:57:27 +0100 Subject: [PATCH 14/27] update spec --- rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json | 2 +- .../src/main/resources/rest-api-spec/api/msearch_template.json | 2 +- rest-api-spec/src/main/resources/rest-api-spec/api/search.json | 2 +- .../src/main/resources/rest-api-spec/api/search_template.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index f0520e0eedd22..5cf1741ac110f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -48,7 +48,7 @@ "type" : "enum", "options" : ["local","remote"], "default" : "remote", - "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index 90e3c80ab301a..08844e716fb3d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -38,7 +38,7 @@ "type" : "enum", "options" : ["local","remote"], "default" : "remote", - "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index d3a8cbcc086a1..e0c0080639a38 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -28,7 +28,7 @@ "type" : "enum", "options" : ["local","remote"], "default" : "remote", - "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" }, "default_operator": { "type" : "enum", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index 03e58a3da36f7..54e13a38ec5f6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -72,7 +72,7 @@ "type" : "enum", "options" : ["local","remote"], "default" : "remote", - "description" : "The cross-cluster search execution mode: fan out to all the shards or, when possible, send one search request to each remote cluster and perform local reduction" + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" } } }, From 01e9b276e4e574cf53bc6846b3094ca872a262de Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 21:57:19 +0100 Subject: [PATCH 15/27] add auto ccs_reduce_mode --- .../client/RequestConverters.java | 4 +--- .../client/RequestConvertersTests.java | 2 +- .../modules/cross-cluster-search.asciidoc | 11 ++++++---- .../resources/rest-api-spec/api/msearch.json | 6 +++--- .../rest-api-spec/api/msearch_template.json | 6 +++--- .../resources/rest-api-spec/api/search.json | 6 +++--- .../rest-api-spec/api/search_template.json | 6 +++--- .../action/search/CCSReduceMode.java | 21 ++++++++++++++++++- .../action/search/SearchRequest.java | 11 ++++------ .../action/search/TransportSearchAction.java | 9 ++++---- .../action/search/SearchRequestTests.java | 4 ++-- 11 files changed, 51 insertions(+), 35 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index a47ddf92ca0fa..2852f8da70163 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -398,9 +398,7 @@ private static void addSearchRequestParams(Params params, SearchRequest searchRe params.withPreference(searchRequest.preference()); params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); - if (searchRequest.getCCSReduceMode() != null) { - params.putParam("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); - } + params.putParam("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 5be82280688e3..b662afdd98163 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -1865,8 +1865,8 @@ private static void setRandomSearchParams(SearchRequest searchRequest, } if (randomBoolean()) { searchRequest.setCCSReduceMode(randomFrom(CCSReduceMode.values())); - expectedParams.put("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); } + expectedParams.put("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); } static void setRandomIndicesOptions(Consumer setter, Supplier getter, diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index a03533e5dcd24..81a1cf1b6bd44 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -284,7 +284,8 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> [[ccs-reduce-mode]] === Reduce mode -When searching across remote clusters, there are two possible reduce modes: +When searching across remote clusters, the execution path may differ depending +on the selected reduce mode: - `remote`: the coordinating node sends one search request to each cluster. Each cluster performs the search independently, reducing and fetching results @@ -308,12 +309,14 @@ coordinating node. The `local` reduce mode is beneficial whenever there is very involved, as it treats all shards the same, at the cost of sending many requests to each remote cluster. -By default `remote` is used whenever possible. In case a scroll is provided, +- `auto`: `remote` is used whenever possible. In case a scroll is provided, or inner hits are requested as part of field collapsing, `local` is used -instead. The <> supports the `ccs_reduce_mode` +instead. This is the default reduce mode. + +The <> supports the `ccs_reduce_mode` parameter which allows to select the reduce mode if needed. The reduce mode that was used for a cross-cluster search request is returned as part of the -`_clusters` section. +`_clusters` section, which will either be `remote` or `local`. Note that all the communication between the nodes, regardless of which cluster they belong to and the selected reduce mode, happens through the diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index 5cf1741ac110f..2f929be19b370 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -46,9 +46,9 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["local","remote"], - "default" : "remote", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" + "options" : ["local","remote","auto"], + "default" : "auto", + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index 08844e716fb3d..95c9dd83abdb8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -36,9 +36,9 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["local","remote"], - "default" : "remote", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" + "options" : ["local","remote","auto"], + "default" : "auto", + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index e0c0080639a38..1a3943815c7fa 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -26,9 +26,9 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["local","remote"], - "default" : "remote", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" + "options" : ["local","remote", "auto"], + "default" : "auto", + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" }, "default_operator": { "type" : "enum", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index 54e13a38ec5f6..87516a8161a3c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -70,9 +70,9 @@ }, "ccs_reduce_mode": { "type" : "enum", - "options" : ["local","remote"], - "default" : "remote", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster" + "options" : ["local","remote", "auto"], + "default" : "auto", + "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" } } }, diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java index da69c8cecc01d..3abfaa844ca09 100644 --- a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java +++ b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java @@ -25,7 +25,26 @@ * The reduce modes when executing a cross-cluster search request */ public enum CCSReduceMode { - LOCAL, REMOTE; + /** + * The coordinating node sends one search shards request to each remote cluster to collect information about the remote indices + * involved and their shards. From then on, the search executes as if all shards were part of the same cluster, meaning that + * each shard will receive a shard search request, execute the query, after which reduction happens in one go and relevant documents + * are fetched from their shards. To be preferred when network latency is very low between the coordinating node and the remote + * clusters. + */ + LOCAL, + /** + * The coordinating node sends one and only one search request to each cluster. Each cluster executes the search independently, + * performs non final reduction and returns `from` + `size` fetched hits to the coordinating node, which will perform a final + * reduction and merge the different search responses into one. To be preferred when there is network latency between the coordinating + * node and the remote clusters. + */ + REMOTE, + /** + * Default mode. {@link #REMOTE} is used whenever possible. When a scroll is provided or inner hits are requested as part of field + * collapsing, {@link #LOCAL} is used instead. + */ + AUTO; @Override public String toString() { diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index aac068247ee47..e8ac6bff04019 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -92,7 +92,7 @@ public final class SearchRequest extends ActionRequest implements IndicesRequest private String[] types = Strings.EMPTY_ARRAY; - private CCSReduceMode ccsReduceMode; + private CCSReduceMode ccsReduceMode = CCSReduceMode.AUTO; public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled(); @@ -203,7 +203,7 @@ public SearchRequest(StreamInput in) throws IOException { absoluteStartMillis = DEFAULT_ABSOLUTE_START_MILLIS; } if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - ccsReduceMode = in.readOptionalEnum(CCSReduceMode.class); + ccsReduceMode = in.readEnum(CCSReduceMode.class); } } @@ -232,7 +232,7 @@ public void writeTo(StreamOutput out) throws IOException { } } if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeOptionalEnum(ccsReduceMode); + out.writeEnum(ccsReduceMode); } } @@ -339,11 +339,8 @@ public void setCCSReduceMode(String ccsReduceMode) { } /** - * Returns the reduce mode for cross-cluster search requests. When not set {@link CCSReduceMode#REMOTE} is used - * whenever possible; in case a scroll is provided or inner hits are requested as part of field collapsing, - * {@link CCSReduceMode#LOCAL} is used instead. + * Returns the reduce mode for cross-cluster search requests */ - @Nullable public CCSReduceMode getCCSReduceMode() { return this.ccsReduceMode; } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 430170452910f..c46f5baed3b44 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -202,15 +202,14 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSReduceMode executionMode = searchRequest.getCCSReduceMode(); - if (executionMode == null) { + CCSReduceMode ccsReduceMode = searchRequest.getCCSReduceMode(); + if (ccsReduceMode == CCSReduceMode.AUTO) { boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null && source.collapse().getInnerHits().isEmpty() == false; boolean scroll = searchRequest.scroll() != null; - executionMode = collapseWithInnerHits || scroll ? CCSReduceMode.LOCAL - : CCSReduceMode.REMOTE; + ccsReduceMode = collapseWithInnerHits || scroll ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; } - if (executionMode == CCSReduceMode.LOCAL) { + if (ccsReduceMode == CCSReduceMode.LOCAL) { AtomicInteger skippedClusters = new AtomicInteger(0); collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index b7d2a472c7205..612896d6d8b1c 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -83,7 +83,7 @@ public void testRandomVersionSerialization() throws IOException { Version version = VersionUtils.randomVersion(random()); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new, version); if (version.before(Version.V_7_0_0)) { - assertNull(deserializedRequest.getCCSReduceMode()); + assertEquals(CCSReduceMode.AUTO, deserializedRequest.getCCSReduceMode()); } else { assertEquals(searchRequest.getCCSReduceMode(), deserializedRequest.getCCSReduceMode()); } @@ -104,7 +104,7 @@ public void testReadFromPre6_7_0() throws IOException { assertArrayEquals(new String[]{"index"}, searchRequest.indices()); assertNull(searchRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(searchRequest); - assertNull(searchRequest.getCCSReduceMode()); + assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); } } From 0e77e760e0804a0ce37150b22c69f480dfd9e235 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 22:11:39 +0100 Subject: [PATCH 16/27] fix failing xpack qa test and expand others --- .../test/multi_cluster/10_basic.yml | 29 +++++++++++++++++++ .../test/multi_cluster/40_scroll.yml | 9 ++++++ .../test/multi_cluster/60_skip_shards.yml | 2 ++ 3 files changed, 40 insertions(+) diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 1ebd18ccaa3ac..dcd9420ed4249 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -97,6 +97,10 @@ teardown: terms: field: f1.keyword + - match: {_clusters.total: 2} + - match: {_clusters.successful: 2} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -115,6 +119,10 @@ teardown: terms: field: f1.keyword + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -134,6 +142,10 @@ teardown: terms: field: f1.keyword + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -152,6 +164,7 @@ teardown: terms: field: f1.keyword + - is_false: _clusters - match: { _shards.total: 2 } - match: { hits.total: 5} - match: { hits.hits.0._index: "local_index"} @@ -182,6 +195,10 @@ teardown: rest_total_hits_as_int: true index: test_remote_cluster:test_index + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -193,6 +210,10 @@ teardown: rest_total_hits_as_int: true index: "*_remote_cluster:test_ind*" + - match: {_clusters.total: 2} + - match: {_clusters.successful: 2} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -205,6 +226,10 @@ teardown: rest_total_hits_as_int: true index: my_remote_cluster:aliased_test_index + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -219,6 +244,10 @@ teardown: rest_total_hits_as_int: true index: my_remote_cluster:secure_alias # TODO make this a wildcard once + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "remote"} - match: { _shards.total: 2 } - match: { hits.total: 1 } - is_true: hits.hits.0._source.secure diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index 6875df0847d1a..a586312cba7ea 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -52,6 +52,10 @@ teardown: query: match_all: {} + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } @@ -66,6 +70,7 @@ teardown: rest_total_hits_as_int: true body: { "scroll_id": "$scroll_id", "scroll": "1m"} + - is_false: _clusters - match: {hits.total: 6 } - length: {hits.hits: 2 } - match: {hits.hits.0._source.filter_field: 1 } @@ -100,6 +105,10 @@ teardown: query: match_all: {} + - match: {_clusters.total: 1} + - match: {_clusters.successful: 1} + - match: {_clusters.skipped: 0} + - match: {_clusters.ccs_reduce_mode: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml index e7842db70d263..dc43a98693de1 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml @@ -66,6 +66,7 @@ teardown: - do: headers: { Authorization: "Basic am9lOnMza3JpdA==" } search: + ccs_reduce_mode: local rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 @@ -83,6 +84,7 @@ teardown: - do: headers: { Authorization: "Basic am9lOnMza3JpdA==" } search: + ccs_reduce_mode: local rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 From bf3fdb75c10c90a0da8d6a422ce3234d67dbe6d2 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 24 Jan 2019 23:25:46 +0100 Subject: [PATCH 17/27] unify listeners and extract method that converts auto to an effective reduce mode --- .../action/search/SearchRequest.java | 9 + .../action/search/TransportSearchAction.java | 179 +++++++++--------- .../action/search/SearchRequestTests.java | 15 ++ 3 files changed, 115 insertions(+), 88 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index e8ac6bff04019..4aa9a21568d91 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -345,6 +345,15 @@ public CCSReduceMode getCCSReduceMode() { return this.ccsReduceMode; } + CCSReduceMode getEffectiveCCSReduceMode() { + if (ccsReduceMode == CCSReduceMode.AUTO) { + boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null + && source.collapse().getInnerHits().isEmpty() == false; + return collapseWithInnerHits || scroll != null ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; + } + return ccsReduceMode; + } + /** * The document types to execute the search against. Defaults to be executed against * all types. diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index c46f5baed3b44..3538d30eda42f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -202,13 +202,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSReduceMode ccsReduceMode = searchRequest.getCCSReduceMode(); - if (ccsReduceMode == CCSReduceMode.AUTO) { - boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null - && source.collapse().getInnerHits().isEmpty() == false; - boolean scroll = searchRequest.scroll() != null; - ccsReduceMode = collapseWithInnerHits || scroll ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; - } + CCSReduceMode ccsReduceMode = searchRequest.getEffectiveCCSReduceMode(); if (ccsReduceMode == CCSReduceMode.LOCAL) { AtomicInteger skippedClusters = new AtomicInteger(0); collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), @@ -246,7 +240,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< source.size(from + size); //TODO when searching only against a remote cluster, we could ask directly for the final number of results and let //the remote cluster do a final reduction, yet that is not possible as we are providing a localClusterAlias which - //will automatically make the local reduction non final + //will automatically make the reduction non final } SearchResponseMerger searchResponseMerger = new SearchResponseMerger(from, size, trackTotalHitsUpTo, timeProvider, searchService::createReduceContext); @@ -260,15 +254,14 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< OriginalIndices indices = entry.getValue(); SearchRequest ccsSearchRequest = SearchRequest.withLocalReduction(searchRequest, indices.indices(), clusterAlias, timeProvider.getAbsoluteStartMillis()); - ActionListener ccsListener = createCCSListener(skipUnavailable, - e -> new RemoteTransportException("error while communicating with remote cluster [" + clusterAlias + "]", e), - countDown, skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); + ActionListener ccsListener = createCCSListener(clusterAlias, skipUnavailable, countDown, + skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); Client remoteClusterClient = remoteClusterService.getRemoteClusterClient(threadPool, clusterAlias); remoteClusterClient.search(ccsSearchRequest, ccsListener); } if (localIndices != null) { - ActionListener ccsListener = createCCSListener(false, e -> e, countDown, skippedClusters, - exceptions, searchResponseMerger, totalClusters, listener); + ActionListener ccsListener = createCCSListener(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, + false, countDown, skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); SearchRequest ccsLocalSearchRequest = SearchRequest.withLocalReduction(searchRequest, localIndices.indices(), RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); executeLocalSearch(task, timeProvider, ccsLocalSearchRequest, localIndices, clusterState, ccsListener); @@ -289,7 +282,7 @@ static void collectSearchShards(IndicesOptions indicesOptions, String preference ThreadPool threadPool, ActionListener> listener) { final CountDown responsesCountDown = new CountDown(remoteIndicesByCluster.size()); final Map searchShardsResponses = new ConcurrentHashMap<>(); - final AtomicReference transportException = new AtomicReference<>(); + final AtomicReference exceptions = new AtomicReference<>(); for (Map.Entry entry : remoteIndicesByCluster.entrySet()) { final String clusterAlias = entry.getKey(); boolean skipUnavailable = remoteClusterService.isSkipUnavailable(clusterAlias); @@ -297,96 +290,39 @@ static void collectSearchShards(IndicesOptions indicesOptions, String preference final String[] indices = entry.getValue().indices(); ClusterSearchShardsRequest searchShardsRequest = new ClusterSearchShardsRequest(indices) .indicesOptions(indicesOptions).local(true).preference(preference).routing(routing); - clusterClient.admin().cluster().searchShards(searchShardsRequest, new ActionListener() { + clusterClient.admin().cluster().searchShards(searchShardsRequest, + new CCSActionListener>( + clusterAlias, skipUnavailable, responsesCountDown, skippedClusters, exceptions, listener) { @Override - public void onResponse(ClusterSearchShardsResponse response) { - searchShardsResponses.put(clusterAlias, response); - maybeFinish(); + void cumulateResponse(ClusterSearchShardsResponse clusterSearchShardsResponse) { + searchShardsResponses.put(clusterAlias, clusterSearchShardsResponse); } @Override - public void onFailure(Exception e) { - if (skipUnavailable) { - skippedClusters.incrementAndGet(); - } else { - RemoteTransportException exception = - new RemoteTransportException("error while communicating with remote cluster [" + clusterAlias + "]", e); - if (transportException.compareAndSet(null, exception) == false) { - transportException.accumulateAndGet(exception, (previous, current) -> { - current.addSuppressed(previous); - return current; - }); - } - } - maybeFinish(); - } - - private void maybeFinish() { - if (responsesCountDown.countDown()) { - RemoteTransportException exception = transportException.get(); - if (exception == null) { - listener.onResponse(searchShardsResponses); - } else { - listener.onFailure(transportException.get()); - } - } + Map getFinalResponse() { + return searchShardsResponses; } } ); } } - //TODO unify the two listeners? - private ActionListener createCCSListener(boolean skipUnavailable, - Function exceptionWrapper, - CountDown countDown, - AtomicInteger skippedClusters, - AtomicReference exceptions, - SearchResponseMerger searchResponseMerger, - int totalClusters, + private ActionListener createCCSListener(String clusterAlias, boolean skipUnavailable, CountDown countDown, + AtomicInteger skippedClusters, AtomicReference exceptions, + SearchResponseMerger searchResponseMerger, int totalClusters, ActionListener originalListener) { - return new ActionListener() { + return new CCSActionListener(clusterAlias, skipUnavailable, countDown, skippedClusters, + exceptions, originalListener) { @Override - public void onResponse(SearchResponse searchResponse) { + void cumulateResponse(SearchResponse searchResponse) { searchResponseMerger.add(searchResponse); - maybeFinish(); } @Override - public void onFailure(Exception e) { - if (skipUnavailable) { - skippedClusters.incrementAndGet(); - } else { - Exception exception = exceptionWrapper.apply(e); - if (exceptions.compareAndSet(null, exception) == false) { - exceptions.accumulateAndGet(exception, (previous, current) -> { - current.addSuppressed(previous); - return current; - }); - } - } - maybeFinish(); - } - - private void maybeFinish() { - if (countDown.countDown()) { - Exception exception = exceptions.get(); - if (exception == null) { - SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), - skippedClusters.get(), CCSReduceMode.REMOTE); - SearchResponse response; - try { - //TODO test when merge breaks - response = searchResponseMerger.getMergedResponse(clusters); - } catch(Exception e) { - originalListener.onFailure(e); - return; - } - originalListener.onResponse(response); - } else { - originalListener.onFailure(exceptions.get()); - } - } + SearchResponse getFinalResponse() { + SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), + skippedClusters.get(), CCSReduceMode.REMOTE); + return searchResponseMerger.getMergedResponse(clusters); } }; } @@ -607,4 +543,71 @@ private static void failIfOverShardCountLimit(ClusterService clusterService, int + "] to a greater value if you really want to query that many shards at the same time."); } } + + private abstract static class CCSActionListener implements ActionListener { + + private final String clusterAlias; + private final boolean skipUnavailable; + private final CountDown countDown; + private final AtomicInteger skippedClusters; + private final AtomicReference exceptions; + private final ActionListener originalListener; + + CCSActionListener(String clusterAlias, boolean skipUnavailable, CountDown countDown, AtomicInteger skippedClusters, + AtomicReference exceptions, ActionListener originalListener) { + this.clusterAlias = clusterAlias; + this.skipUnavailable = skipUnavailable; + this.countDown = countDown; + this.skippedClusters = skippedClusters; + this.exceptions = exceptions; + this.originalListener = originalListener; + } + + @Override + public final void onResponse(Response response) { + cumulateResponse(response); + maybeFinish(); + } + + abstract void cumulateResponse(Response response); + + @Override + public final void onFailure(Exception e) { + if (skipUnavailable) { + skippedClusters.incrementAndGet(); + } else { + Exception exception = e; + if (RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY.equals(clusterAlias) == false) { + exception = new RemoteTransportException("error while communicating with remote cluster [" + clusterAlias + "]", e); + } + if (exceptions.compareAndSet(null, exception) == false) { + exceptions.accumulateAndGet(exception, (previous, current) -> { + current.addSuppressed(previous); + return current; + }); + } + } + maybeFinish(); + } + + private void maybeFinish() { + if (countDown.countDown()) { + Exception exception = exceptions.get(); + if (exception == null) { + FinalResponse response; + try { + response = getFinalResponse(); + } catch(Exception e) { + originalListener.onFailure(e); + return; + } + originalListener.onResponse(response); + } else { + originalListener.onFailure(exceptions.get()); + } + } + } + + abstract FinalResponse getFinalResponse(); + } } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 612896d6d8b1c..1307e25470076 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -237,6 +237,21 @@ public void testValidate() throws IOException { } } + public void testGetEffectiveCCSReduceMode() throws IOException { + SearchRequest searchRequest = createSearchRequest(); + CCSReduceMode ccsReduceMode = randomFrom(CCSReduceMode.REMOTE, CCSReduceMode.LOCAL); + searchRequest.setCCSReduceMode(ccsReduceMode); + assertEquals(ccsReduceMode, searchRequest.getCCSReduceMode()); + assertEquals(ccsReduceMode, searchRequest.getEffectiveCCSReduceMode()); + + searchRequest.setCCSReduceMode(CCSReduceMode.AUTO); + assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); + boolean remoteReduce = searchRequest.scroll() == null && (searchRequest.source() == null + || searchRequest.source().collapse() == null || searchRequest.source().collapse().getInnerHits() == null + || searchRequest.source().collapse().getInnerHits().isEmpty()); + assertEquals(remoteReduce ? CCSReduceMode.REMOTE : CCSReduceMode.LOCAL, searchRequest.getEffectiveCCSReduceMode()); + } + public void testCopyConstructor() throws IOException { SearchRequest searchRequest = createSearchRequest(); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new); From ee25840f8f09406cce605e3447cebee0be3ac2b7 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Fri, 25 Jan 2019 14:48:21 +0100 Subject: [PATCH 18/27] adapt and expand unit tests --- .../action/search/SearchRequest.java | 9 --- .../action/search/SearchResponseMerger.java | 6 +- .../action/search/TransportSearchAction.java | 78 +++++++++++-------- .../action/search/SearchRequestTests.java | 15 ---- .../search/TransportSearchActionTests.java | 59 ++++++++++++++ 5 files changed, 109 insertions(+), 58 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index c8e3fad448813..8100f8ddd07a0 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -346,15 +346,6 @@ public CCSReduceMode getCCSReduceMode() { return this.ccsReduceMode; } - CCSReduceMode getEffectiveCCSReduceMode() { - if (ccsReduceMode == CCSReduceMode.AUTO) { - boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null - && source.collapse().getInnerHits().isEmpty() == false; - return collapseWithInnerHits || scroll != null ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; - } - return ccsReduceMode; - } - /** * The document types to execute the search against. Defaults to be executed against * all types. diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index 94f02303455af..db776bb901873 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -77,9 +77,9 @@ //from the remote clusters in the fetch phase. This would be identical to the removed QueryAndFetch strategy except that only the remote //cluster response would have the fetch results. final class SearchResponseMerger { - private final int from; - private final int size; - private final int trackTotalHitsUpTo; + final int from; + final int size; + final int trackTotalHitsUpTo; private final SearchTimeProvider searchTimeProvider; private final Function reduceContextFunction; private final List searchResponses = new CopyOnWriteArrayList<>(); diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 3538d30eda42f..29cd39da957d1 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -47,6 +47,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchService; +import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.search.internal.SearchContext; @@ -202,7 +203,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSReduceMode ccsReduceMode = searchRequest.getEffectiveCCSReduceMode(); + CCSReduceMode ccsReduceMode = resolveCCSReduceMode(searchRequest); if (ccsReduceMode == CCSReduceMode.LOCAL) { AtomicInteger skippedClusters = new AtomicInteger(0); collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), @@ -223,27 +224,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< }, listener::onFailure)); } else { - //TODO make this a method and test it? - final int from; - final int size; - final int trackTotalHitsUpTo; - if (source == null) { - from = SearchService.DEFAULT_FROM; - size = SearchService.DEFAULT_SIZE; - trackTotalHitsUpTo = SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO; - } else { - from = source.from() == -1 ? SearchService.DEFAULT_FROM : source.from(); - size = source.size() == -1 ? SearchService.DEFAULT_SIZE : source.size(); - trackTotalHitsUpTo = source.trackTotalHitsUpTo(); - //here we modify the original source so we can re-use it by setting it to each outgoing search request - source.from(0); - source.size(from + size); - //TODO when searching only against a remote cluster, we could ask directly for the final number of results and let - //the remote cluster do a final reduction, yet that is not possible as we are providing a localClusterAlias which - //will automatically make the reduction non final - } - SearchResponseMerger searchResponseMerger = new SearchResponseMerger(from, size, trackTotalHitsUpTo, - timeProvider, searchService::createReduceContext); + SearchResponseMerger searchResponseMerger = createSearchResponseMerger(source, timeProvider, searchService::createReduceContext); AtomicInteger skippedClusters = new AtomicInteger(0); final AtomicReference exceptions = new AtomicReference<>(); int totalClusters = remoteClusterIndices.size() + (localIndices == null ? 0 : 1); @@ -266,6 +247,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); executeLocalSearch(task, timeProvider, ccsLocalSearchRequest, localIndices, clusterState, ccsListener); } + } } }, listener::onFailure); @@ -277,6 +259,40 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< } } + static CCSReduceMode resolveCCSReduceMode(SearchRequest searchRequest) { + if (searchRequest.getCCSReduceMode() == CCSReduceMode.AUTO) { + SearchSourceBuilder source = searchRequest.source(); + boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null + && source.collapse().getInnerHits().isEmpty() == false; + return collapseWithInnerHits || searchRequest.scroll() != null ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; + } + return searchRequest.getCCSReduceMode(); + } + + static SearchResponseMerger createSearchResponseMerger(SearchSourceBuilder source, SearchTimeProvider timeProvider, + Function reduceContextFunction) { + final int from; + final int size; + final int trackTotalHitsUpTo; + if (source == null) { + from = SearchService.DEFAULT_FROM; + size = SearchService.DEFAULT_SIZE; + trackTotalHitsUpTo = SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO; + } else { + from = source.from() == -1 ? SearchService.DEFAULT_FROM : source.from(); + size = source.size() == -1 ? SearchService.DEFAULT_SIZE : source.size(); + trackTotalHitsUpTo = source.trackTotalHitsUpTo() == null + ? SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO : source.trackTotalHitsUpTo(); + //here we modify the original source so we can re-use it by setting it to each outgoing search request + source.from(0); + source.size(from + size); + //TODO when searching only against a remote cluster, we could ask directly for the final number of results and let + //the remote cluster do a final reduction, yet that is not possible as we are providing a localClusterAlias which + //will automatically make the reduction non final + } + return new SearchResponseMerger(from, size, trackTotalHitsUpTo, timeProvider, reduceContextFunction); + } + static void collectSearchShards(IndicesOptions indicesOptions, String preference, String routing, AtomicInteger skippedClusters, Map remoteIndicesByCluster, RemoteClusterService remoteClusterService, ThreadPool threadPool, ActionListener> listener) { @@ -294,12 +310,12 @@ static void collectSearchShards(IndicesOptions indicesOptions, String preference new CCSActionListener>( clusterAlias, skipUnavailable, responsesCountDown, skippedClusters, exceptions, listener) { @Override - void cumulateResponse(ClusterSearchShardsResponse clusterSearchShardsResponse) { + void innerOnResponse(ClusterSearchShardsResponse clusterSearchShardsResponse) { searchShardsResponses.put(clusterAlias, clusterSearchShardsResponse); } @Override - Map getFinalResponse() { + Map createFinalResponse() { return searchShardsResponses; } } @@ -307,19 +323,19 @@ Map getFinalResponse() { } } - private ActionListener createCCSListener(String clusterAlias, boolean skipUnavailable, CountDown countDown, + private static ActionListener createCCSListener(String clusterAlias, boolean skipUnavailable, CountDown countDown, AtomicInteger skippedClusters, AtomicReference exceptions, SearchResponseMerger searchResponseMerger, int totalClusters, ActionListener originalListener) { return new CCSActionListener(clusterAlias, skipUnavailable, countDown, skippedClusters, exceptions, originalListener) { @Override - void cumulateResponse(SearchResponse searchResponse) { + void innerOnResponse(SearchResponse searchResponse) { searchResponseMerger.add(searchResponse); } @Override - SearchResponse getFinalResponse() { + SearchResponse createFinalResponse() { SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), skippedClusters.get(), CCSReduceMode.REMOTE); return searchResponseMerger.getMergedResponse(clusters); @@ -565,11 +581,11 @@ private abstract static class CCSActionListener impleme @Override public final void onResponse(Response response) { - cumulateResponse(response); + innerOnResponse(response); maybeFinish(); } - abstract void cumulateResponse(Response response); + abstract void innerOnResponse(Response response); @Override public final void onFailure(Exception e) { @@ -596,7 +612,7 @@ private void maybeFinish() { if (exception == null) { FinalResponse response; try { - response = getFinalResponse(); + response = createFinalResponse(); } catch(Exception e) { originalListener.onFailure(e); return; @@ -608,6 +624,6 @@ private void maybeFinish() { } } - abstract FinalResponse getFinalResponse(); + abstract FinalResponse createFinalResponse(); } } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 1307e25470076..612896d6d8b1c 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -237,21 +237,6 @@ public void testValidate() throws IOException { } } - public void testGetEffectiveCCSReduceMode() throws IOException { - SearchRequest searchRequest = createSearchRequest(); - CCSReduceMode ccsReduceMode = randomFrom(CCSReduceMode.REMOTE, CCSReduceMode.LOCAL); - searchRequest.setCCSReduceMode(ccsReduceMode); - assertEquals(ccsReduceMode, searchRequest.getCCSReduceMode()); - assertEquals(ccsReduceMode, searchRequest.getEffectiveCCSReduceMode()); - - searchRequest.setCCSReduceMode(CCSReduceMode.AUTO); - assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); - boolean remoteReduce = searchRequest.scroll() == null && (searchRequest.source() == null - || searchRequest.source().collapse() == null || searchRequest.source().collapse().getInnerHits() == null - || searchRequest.source().collapse().getInnerHits().isEmpty()); - assertEquals(remoteReduce ? CCSReduceMode.REMOTE : CCSReduceMode.LOCAL, searchRequest.getEffectiveCCSReduceMode()); - } - public void testCopyConstructor() throws IOException { SearchRequest searchRequest = createSearchRequest(); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new); diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index 1b99beee65e81..c6234d496a3a2 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -40,7 +40,10 @@ import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.internal.AliasFilter; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; @@ -57,6 +60,7 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -485,4 +489,59 @@ public void onNodeDisconnected(DiscoveryNode node) { } } } + + public void testResolveCCSReduceMode() throws IOException { + SearchRequest searchRequest = new SearchRequestTests().createSearchRequest(); + CCSReduceMode ccsReduceMode = randomFrom(CCSReduceMode.REMOTE, CCSReduceMode.LOCAL); + searchRequest.setCCSReduceMode(ccsReduceMode); + assertEquals(ccsReduceMode, searchRequest.getCCSReduceMode()); + assertEquals(ccsReduceMode, TransportSearchAction.resolveCCSReduceMode(searchRequest)); + + searchRequest.setCCSReduceMode(CCSReduceMode.AUTO); + assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); + boolean remoteReduce = searchRequest.scroll() == null && (searchRequest.source() == null + || searchRequest.source().collapse() == null || searchRequest.source().collapse().getInnerHits() == null + || searchRequest.source().collapse().getInnerHits().isEmpty()); + assertEquals(remoteReduce ? CCSReduceMode.REMOTE : CCSReduceMode.LOCAL, TransportSearchAction.resolveCCSReduceMode(searchRequest)); + } + + public void testCreateSearchResponseMerger() { + TransportSearchAction.SearchTimeProvider timeProvider = new TransportSearchAction.SearchTimeProvider(0, 0, () -> 0); + Function reduceContext = flag -> null; + { + SearchSourceBuilder source = new SearchSourceBuilder(); + assertEquals(-1, source.size()); + assertEquals(-1, source.from()); + assertNull(source.trackTotalHitsUpTo()); + SearchResponseMerger merger = TransportSearchAction.createSearchResponseMerger(source, timeProvider, reduceContext); + assertEquals(0, merger.from); + assertEquals(10, merger.size); + assertEquals(SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO, merger.trackTotalHitsUpTo); + assertEquals(0, source.from()); + assertEquals(10, source.size()); + assertNull(source.trackTotalHitsUpTo()); + } + { + SearchResponseMerger merger = TransportSearchAction.createSearchResponseMerger(null, timeProvider, reduceContext); + assertEquals(0, merger.from); + assertEquals(10, merger.size); + assertEquals(SearchContext.DEFAULT_TRACK_TOTAL_HITS_UP_TO, merger.trackTotalHitsUpTo); + } + { + SearchSourceBuilder source = new SearchSourceBuilder(); + int originalFrom = randomIntBetween(0, 1000); + source.from(originalFrom); + int originalSize = randomIntBetween(0, 1000); + source.size(originalSize); + int trackTotalHitsUpTo = randomIntBetween(0, Integer.MAX_VALUE); + source.trackTotalHitsUpTo(trackTotalHitsUpTo); + SearchResponseMerger merger = TransportSearchAction.createSearchResponseMerger(source, timeProvider, reduceContext); + assertEquals(0, source.from()); + assertEquals(originalFrom + originalSize, source.size()); + assertEquals(trackTotalHitsUpTo, (int)source.trackTotalHitsUpTo()); + assertEquals(originalFrom, merger.from); + assertEquals(originalSize, merger.size); + assertEquals(trackTotalHitsUpTo, merger.trackTotalHitsUpTo); + } + } } From e554e2bfbab612732252e755c9a410c721c50e28 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Fri, 25 Jan 2019 16:29:47 +0100 Subject: [PATCH 19/27] extract ccsRemoteReduce method and add unit tests for it --- .../action/search/TransportSearchAction.java | 111 ++-- .../search/TransportSearchActionTests.java | 512 +++++++++++++----- .../RemoteClusterConnectionTests.java | 27 + 3 files changed, 472 insertions(+), 178 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 29cd39da957d1..3afad90cf411a 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -71,6 +71,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.LongSupplier; @@ -204,50 +205,32 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { CCSReduceMode ccsReduceMode = resolveCCSReduceMode(searchRequest); - if (ccsReduceMode == CCSReduceMode.LOCAL) { - AtomicInteger skippedClusters = new AtomicInteger(0); - collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), - skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, - ActionListener.wrap( - searchShardsResponses -> { - List remoteShardIterators = new ArrayList<>(); - Map remoteAliasFilters = new HashMap<>(); - BiFunction clusterNodeLookup = processRemoteShards( - searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); - int localClusters = localIndices == null ? 0 : 1; - int totalClusters = remoteClusterIndices.size() + localClusters; - int successfulClusters = searchShardsResponses.size() + localClusters; - executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, - remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, - new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), - CCSReduceMode.LOCAL)); - }, - listener::onFailure)); - } else { - SearchResponseMerger searchResponseMerger = createSearchResponseMerger(source, timeProvider, searchService::createReduceContext); - AtomicInteger skippedClusters = new AtomicInteger(0); - final AtomicReference exceptions = new AtomicReference<>(); - int totalClusters = remoteClusterIndices.size() + (localIndices == null ? 0 : 1); - final CountDown countDown = new CountDown(totalClusters); - for (Map.Entry entry : remoteClusterIndices.entrySet()) { - String clusterAlias = entry.getKey(); - boolean skipUnavailable = remoteClusterService.isSkipUnavailable(clusterAlias); - OriginalIndices indices = entry.getValue(); - SearchRequest ccsSearchRequest = SearchRequest.withLocalReduction(searchRequest, indices.indices(), - clusterAlias, timeProvider.getAbsoluteStartMillis()); - ActionListener ccsListener = createCCSListener(clusterAlias, skipUnavailable, countDown, - skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); - Client remoteClusterClient = remoteClusterService.getRemoteClusterClient(threadPool, clusterAlias); - remoteClusterClient.search(ccsSearchRequest, ccsListener); - } - if (localIndices != null) { - ActionListener ccsListener = createCCSListener(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, - false, countDown, skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); - SearchRequest ccsLocalSearchRequest = SearchRequest.withLocalReduction(searchRequest, localIndices.indices(), - RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); - executeLocalSearch(task, timeProvider, ccsLocalSearchRequest, localIndices, clusterState, ccsListener); - } - + switch(ccsReduceMode) { + case LOCAL: + AtomicInteger skippedClusters = new AtomicInteger(0); + collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), + skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, + ActionListener.wrap( + searchShardsResponses -> { + List remoteShardIterators = new ArrayList<>(); + Map remoteAliasFilters = new HashMap<>(); + BiFunction clusterNodeLookup = processRemoteShards( + searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); + int localClusters = localIndices == null ? 0 : 1; + int totalClusters = remoteClusterIndices.size() + localClusters; + int successfulClusters = searchShardsResponses.size() + localClusters; + executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, + remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, + new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), + CCSReduceMode.LOCAL)); + }, + listener::onFailure)); + break; + case REMOTE: + ccsRemoteReduce(searchRequest, localIndices, remoteClusterIndices, timeProvider, searchService::createReduceContext, + remoteClusterService, threadPool, listener, + (r, l) -> executeLocalSearch(task, timeProvider, r, localIndices, clusterState, l)); + break; } } }, listener::onFailure); @@ -259,6 +242,37 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< } } + static void ccsRemoteReduce(SearchRequest searchRequest, OriginalIndices localIndices, Map remoteIndices, + SearchTimeProvider timeProvider, Function reduceContext, + RemoteClusterService remoteClusterService, ThreadPool threadPool, ActionListener listener, + BiConsumer> localSearchConsumer) { + SearchResponseMerger searchResponseMerger = createSearchResponseMerger(searchRequest.source(), timeProvider, reduceContext); + AtomicInteger skippedClusters = new AtomicInteger(0); + final AtomicReference exceptions = new AtomicReference<>(); + int totalClusters = remoteIndices.size() + (localIndices == null ? 0 : 1); + final CountDown countDown = new CountDown(totalClusters); + for (Map.Entry entry : remoteIndices.entrySet()) { + String clusterAlias = entry.getKey(); + boolean skipUnavailable = remoteClusterService.isSkipUnavailable(clusterAlias); + OriginalIndices indices = entry.getValue(); + SearchRequest ccsSearchRequest = SearchRequest.withLocalReduction(searchRequest, indices.indices(), + clusterAlias, timeProvider.getAbsoluteStartMillis()); + ActionListener ccsListener = createCCSListener(clusterAlias, skipUnavailable, countDown, + skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); + Client remoteClusterClient = remoteClusterService.getRemoteClusterClient(threadPool, clusterAlias); + remoteClusterClient.search(ccsSearchRequest, ccsListener); + } + if (localIndices != null) { + ActionListener ccsListener = createCCSListener(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, + false, countDown, skippedClusters, exceptions, searchResponseMerger, totalClusters, listener); + //here we provide the empty string a cluster alias, which means no prefix in index name, + //but the coord node will perform non final reduce as it's not null. + SearchRequest ccsLocalSearchRequest = SearchRequest.withLocalReduction(searchRequest, localIndices.indices(), + RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY, timeProvider.getAbsoluteStartMillis()); + localSearchConsumer.accept(ccsLocalSearchRequest, ccsListener); + } + } + static CCSReduceMode resolveCCSReduceMode(SearchRequest searchRequest) { if (searchRequest.getCCSReduceMode() == CCSReduceMode.AUTO) { SearchSourceBuilder source = searchRequest.source(); @@ -350,9 +364,9 @@ private void executeLocalSearch(Task task, SearchTimeProvider timeProvider, Sear } static BiFunction processRemoteShards(Map searchShardsResponses, - Map remoteIndicesByCluster, - List remoteShardIterators, - Map aliasFilterMap) { + Map remoteIndicesByCluster, + List remoteShardIterators, + Map aliasFilterMap) { Map> clusterToNode = new HashMap<>(); for (Map.Entry entry : searchShardsResponses.entrySet()) { String clusterAlias = entry.getKey(); @@ -560,8 +574,7 @@ private static void failIfOverShardCountLimit(ClusterService clusterService, int } } - private abstract static class CCSActionListener implements ActionListener { - + abstract static class CCSActionListener implements ActionListener { private final String clusterAlias; private final boolean skipUnavailable; private final CountDown countDown; diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index c6234d496a3a2..bf7265251fa09 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -19,6 +19,8 @@ package org.elasticsearch.action.search; +import org.apache.lucene.search.TotalHits; +import org.apache.lucene.util.SetOnce; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.LatchedActionListener; @@ -34,15 +36,20 @@ import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.TestShardRouting; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.internal.AliasFilter; +import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; @@ -60,7 +67,6 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -324,165 +330,411 @@ public void close() { } } - private MockTransportService startTransport(String id, List knownNodes) { - return RemoteClusterConnectionTests.startTransport(id, knownNodes, Version.CURRENT, threadPool); - } - - public void testCollectSearchShards() throws Exception { - int numClusters = randomIntBetween(2, 10); + private MockTransportService[] startTransport(int numClusters, DiscoveryNode[] nodes, Map remoteIndices, + Settings.Builder settingsBuilder) { MockTransportService[] mockTransportServices = new MockTransportService[numClusters]; - DiscoveryNode[] nodes = new DiscoveryNode[numClusters]; - Map remoteIndicesByCluster = new HashMap<>(); - Settings.Builder builder = Settings.builder(); for (int i = 0; i < numClusters; i++) { List knownNodes = new CopyOnWriteArrayList<>(); - MockTransportService remoteSeedTransport = startTransport("node_remote" + i, knownNodes); + MockTransportService remoteSeedTransport = RemoteClusterConnectionTests.startTransport("node_remote" + i, knownNodes, + Version.CURRENT, threadPool); mockTransportServices[i] = remoteSeedTransport; DiscoveryNode remoteSeedNode = remoteSeedTransport.getLocalDiscoNode(); knownNodes.add(remoteSeedNode); nodes[i] = remoteSeedNode; - builder.put("cluster.remote.remote" + i + ".seeds", remoteSeedNode.getAddress().toString()); - remoteIndicesByCluster.put("remote" + i, new OriginalIndices(new String[]{"index"}, IndicesOptions.lenientExpandOpen())); + settingsBuilder.put("cluster.remote.remote" + i + ".seeds", remoteSeedNode.getAddress().toString()); + remoteIndices.put("remote" + i, new OriginalIndices(new String[]{"index"}, IndicesOptions.lenientExpandOpen())); } + return mockTransportServices; + } + + private static SearchResponse emptySearchResponse() { + InternalSearchResponse response = new InternalSearchResponse(new SearchHits(new SearchHit[0], + new TotalHits(0, TotalHits.Relation.EQUAL_TO), Float.NaN), InternalAggregations.EMPTY, null, null, false, null, 1); + return new SearchResponse(response, null, 1, 1, 0, 100, ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY); + } + + public void testCCSRemoteReduceMergeFails() throws Exception { + int numClusters = randomIntBetween(2, 10); + DiscoveryNode[] nodes = new DiscoveryNode[numClusters]; + Map remoteIndicesByCluster = new HashMap<>(); + Settings.Builder builder = Settings.builder(); + MockTransportService[] mockTransportServices = startTransport(numClusters, nodes, remoteIndicesByCluster, builder); Settings settings = builder.build(); + boolean local = randomBoolean(); + OriginalIndices localIndices = local ? new OriginalIndices(new String[]{"index"}, SearchRequest.DEFAULT_INDICES_OPTIONS) : null; + TransportSearchAction.SearchTimeProvider timeProvider = new TransportSearchAction.SearchTimeProvider(0, 0, () -> 0); + Function reduceContext = finalReduce -> null; + try (MockTransportService service = MockTransportService.createNewService(settings, Version.CURRENT, threadPool, null)) { + service.start(); + service.acceptIncomingRequests(); + RemoteClusterService remoteClusterService = service.getRemoteClusterService(); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.preference("null_target"); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference failure = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(r -> fail("no response expected"), failure::set), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); + } + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertNotNull(failure.get()); + //the intention here is not to test that we throw NPE, rather to trigger a situation that makes + //SearchResponseMerger#getMergedResponse fail unexpectedly and verify that the listener is properly notified with the NPE + assertThat(failure.get(), instanceOf(NullPointerException.class)); + assertEquals(0, service.getConnectionManager().size()); + } finally { + for (MockTransportService mockTransportService : mockTransportServices) { + mockTransportService.close(); + } + } + } - try { - try (MockTransportService service = MockTransportService.createNewService(settings, Version.CURRENT, threadPool, null)) { - service.start(); - service.acceptIncomingRequests(); - RemoteClusterService remoteClusterService = service.getRemoteClusterService(); - { - final CountDownLatch latch = new CountDownLatch(1); - AtomicReference> response = new AtomicReference<>(); - AtomicInteger skippedClusters = new AtomicInteger(); - TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, - remoteIndicesByCluster, remoteClusterService, threadPool, - new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); - awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(0, skippedClusters.get()); - assertNotNull(response.get()); - Map map = response.get(); - assertEquals(numClusters, map.size()); - for (int i = 0; i < numClusters; i++) { - String clusterAlias = "remote" + i; - assertTrue(map.containsKey(clusterAlias)); - ClusterSearchShardsResponse shardsResponse = map.get(clusterAlias); - assertEquals(1, shardsResponse.getNodes().length); - } + public void testCCSRemoteReduce() throws Exception { + int numClusters = randomIntBetween(2, 10); + DiscoveryNode[] nodes = new DiscoveryNode[numClusters]; + Map remoteIndicesByCluster = new HashMap<>(); + Settings.Builder builder = Settings.builder(); + MockTransportService[] mockTransportServices = startTransport(numClusters, nodes, remoteIndicesByCluster, builder); + Settings settings = builder.build(); + boolean local = randomBoolean(); + OriginalIndices localIndices = local ? new OriginalIndices(new String[]{"index"}, SearchRequest.DEFAULT_INDICES_OPTIONS) : null; + int totalClusters = numClusters + (local ? 1 : 0); + TransportSearchAction.SearchTimeProvider timeProvider = new TransportSearchAction.SearchTimeProvider(0, 0, () -> 0); + Function reduceContext = finalReduce -> null; + try (MockTransportService service = MockTransportService.createNewService(settings, Version.CURRENT, threadPool, null)) { + service.start(); + service.acceptIncomingRequests(); + RemoteClusterService remoteClusterService = service.getRemoteClusterService(); + { + SearchRequest searchRequest = new SearchRequest(); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference response = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(response::set, e -> fail("no failures expected")), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); } - { - final CountDownLatch latch = new CountDownLatch(1); - AtomicReference failure = new AtomicReference<>(); - AtomicInteger skippedClusters = new AtomicInteger(0); - TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), "index_not_found", null, skippedClusters, - remoteIndicesByCluster, remoteClusterService, threadPool, - new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch)); - awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(0, skippedClusters.get()); - assertNotNull(failure.get()); - assertThat(failure.get(), instanceOf(RemoteTransportException.class)); - RemoteTransportException remoteTransportException = (RemoteTransportException) failure.get(); - assertEquals(RestStatus.NOT_FOUND, remoteTransportException.status()); + awaitLatch(latch, 5, TimeUnit.SECONDS); + + SearchResponse searchResponse = response.get(); + assertEquals(0, searchResponse.getClusters().getSkipped()); + assertEquals(totalClusters, searchResponse.getClusters().getTotal()); + assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); + assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); + } + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.preference("index_not_found"); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference failure = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(r -> fail("no response expected"), failure::set), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); } + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertNotNull(failure.get()); + assertThat(failure.get(), instanceOf(RemoteTransportException.class)); + RemoteTransportException remoteTransportException = (RemoteTransportException) failure.get(); + assertEquals(RestStatus.NOT_FOUND, remoteTransportException.status()); + } - int numDisconnectedClusters = randomIntBetween(1, numClusters); - Set disconnectedNodes = new HashSet<>(numDisconnectedClusters); - Set disconnectedNodesIndices = new HashSet<>(numDisconnectedClusters); - while (disconnectedNodes.size() < numDisconnectedClusters) { - int i = randomIntBetween(0, numClusters - 1); - if (disconnectedNodes.add(nodes[i])) { - assertTrue(disconnectedNodesIndices.add(i)); - } + int numDisconnectedClusters = randomIntBetween(1, numClusters); + Set disconnectedNodes = new HashSet<>(numDisconnectedClusters); + Set disconnectedNodesIndices = new HashSet<>(numDisconnectedClusters); + while (disconnectedNodes.size() < numDisconnectedClusters) { + int i = randomIntBetween(0, numClusters - 1); + if (disconnectedNodes.add(nodes[i])) { + assertTrue(disconnectedNodesIndices.add(i)); } + } - CountDownLatch disconnectedLatch = new CountDownLatch(numDisconnectedClusters); - RemoteClusterServiceTests.addConnectionListener(remoteClusterService, new TransportConnectionListener() { - @Override - public void onNodeDisconnected(DiscoveryNode node) { - if (disconnectedNodes.remove(node)) { - disconnectedLatch.countDown(); - } + CountDownLatch disconnectedLatch = new CountDownLatch(numDisconnectedClusters); + RemoteClusterServiceTests.addConnectionListener(remoteClusterService, new TransportConnectionListener() { + @Override + public void onNodeDisconnected(DiscoveryNode node) { + if (disconnectedNodes.remove(node)) { + disconnectedLatch.countDown(); } - }); - for (DiscoveryNode disconnectedNode : disconnectedNodes) { - service.addFailToSendNoConnectRule(disconnectedNode.getAddress()); } + }); + for (DiscoveryNode disconnectedNode : disconnectedNodes) { + service.addFailToSendNoConnectRule(disconnectedNode.getAddress()); + } - { - final CountDownLatch latch = new CountDownLatch(1); - AtomicInteger skippedClusters = new AtomicInteger(0); - AtomicReference failure = new AtomicReference<>(); - TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, - remoteIndicesByCluster, remoteClusterService, threadPool, - new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch)); - awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(0, skippedClusters.get()); - assertNotNull(failure.get()); - assertThat(failure.get(), instanceOf(RemoteTransportException.class)); - assertThat(failure.get().getMessage(), containsString("error while communicating with remote cluster [")); - assertThat(failure.get().getCause(), instanceOf(NodeDisconnectedException.class)); + { + SearchRequest searchRequest = new SearchRequest(); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference failure = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(r -> fail("no response expected"), failure::set), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); } + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertNotNull(failure.get()); + assertThat(failure.get(), instanceOf(RemoteTransportException.class)); + RemoteTransportException remoteTransportException = (RemoteTransportException) failure.get(); + assertThat(failure.get().getMessage(), containsString("error while communicating with remote cluster [")); + assertThat(failure.get().getCause(), instanceOf(NodeDisconnectedException.class)); + } - //setting skip_unavailable to true for all the disconnected clusters will make the request succeed again - for (int i : disconnectedNodesIndices) { - RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); + //setting skip_unavailable to true for all the disconnected clusters will make the request succeed again + for (int i : disconnectedNodesIndices) { + RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); + } + + { + SearchRequest searchRequest = new SearchRequest(); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference response = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(response::set, e -> fail("no failures expected")), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); } + awaitLatch(latch, 5, TimeUnit.SECONDS); + + SearchResponse searchResponse = response.get(); + assertEquals(disconnectedNodesIndices.size(), searchResponse.getClusters().getSkipped()); + assertEquals(totalClusters, searchResponse.getClusters().getTotal()); + int successful = totalClusters - disconnectedNodesIndices.size(); + assertEquals(successful, searchResponse.getClusters().getSuccessful()); + assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(successful == 0 ? 0 : successful + 1, searchResponse.getNumReducePhases()); + } + + //give transport service enough time to realize that the node is down, and to notify the connection listeners + //so that RemoteClusterConnection is left with no connected nodes, hence it will retry connecting next + assertTrue(disconnectedLatch.await(5, TimeUnit.SECONDS)); - { - final CountDownLatch latch = new CountDownLatch(1); - AtomicInteger skippedClusters = new AtomicInteger(0); - AtomicReference> response = new AtomicReference<>(); - TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, - remoteIndicesByCluster, remoteClusterService, threadPool, - new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); - awaitLatch(latch, 5, TimeUnit.SECONDS); - assertNotNull(response.get()); - Map map = response.get(); - assertEquals(numClusters - disconnectedNodesIndices.size(), map.size()); - assertEquals(skippedClusters.get(), disconnectedNodesIndices.size()); - for (int i = 0; i < numClusters; i++) { - String clusterAlias = "remote" + i; - if (disconnectedNodesIndices.contains(i)) { - assertFalse(map.containsKey(clusterAlias)); - } else { - assertNotNull(map.get(clusterAlias)); - } + service.clearAllRules(); + if (randomBoolean()) { + for (int i : disconnectedNodesIndices) { + if (randomBoolean()) { + RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); } + + } + } + { + SearchRequest searchRequest = new SearchRequest(); + final CountDownLatch latch = new CountDownLatch(1); + SetOnce>> setOnce = new SetOnce<>(); + AtomicReference response = new AtomicReference<>(); + LatchedActionListener listener = new LatchedActionListener<>( + ActionListener.wrap(response::set, e -> fail("no failures expected")), latch); + TransportSearchAction.ccsRemoteReduce(searchRequest, localIndices, remoteIndicesByCluster, timeProvider, reduceContext, + remoteClusterService, threadPool, listener, (r, l) -> setOnce.set(Tuple.tuple(r, l))); + if (localIndices == null) { + assertNull(setOnce.get()); + } else { + Tuple> tuple = setOnce.get(); + assertEquals("", tuple.v1().getLocalClusterAlias()); + assertThat(tuple.v2(), instanceOf(TransportSearchAction.CCSActionListener.class)); + tuple.v2().onResponse(emptySearchResponse()); } + awaitLatch(latch, 5, TimeUnit.SECONDS); + + SearchResponse searchResponse = response.get(); + assertEquals(0, searchResponse.getClusters().getSkipped()); + assertEquals(totalClusters, searchResponse.getClusters().getTotal()); + assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); + assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); + } + assertEquals(0, service.getConnectionManager().size()); + } finally { + for (MockTransportService mockTransportService : mockTransportServices) { + mockTransportService.close(); + } + } + } - //give transport service enough time to realize that the node is down, and to notify the connection listeners - //so that RemoteClusterConnection is left with no connected nodes, hence it will retry connecting next - assertTrue(disconnectedLatch.await(5, TimeUnit.SECONDS)); + public void testCollectSearchShards() throws Exception { + int numClusters = randomIntBetween(2, 10); + DiscoveryNode[] nodes = new DiscoveryNode[numClusters]; + Map remoteIndicesByCluster = new HashMap<>(); + Settings.Builder builder = Settings.builder(); + MockTransportService[] mockTransportServices = startTransport(numClusters, nodes, remoteIndicesByCluster, builder); + Settings settings = builder.build(); + try (MockTransportService service = MockTransportService.createNewService(settings, Version.CURRENT, threadPool, null)) { + service.start(); + service.acceptIncomingRequests(); + RemoteClusterService remoteClusterService = service.getRemoteClusterService(); + { + final CountDownLatch latch = new CountDownLatch(1); + AtomicReference> response = new AtomicReference<>(); + AtomicInteger skippedClusters = new AtomicInteger(); + TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, + remoteIndicesByCluster, remoteClusterService, threadPool, + new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertEquals(0, skippedClusters.get()); + assertNotNull(response.get()); + Map map = response.get(); + assertEquals(numClusters, map.size()); + for (int i = 0; i < numClusters; i++) { + String clusterAlias = "remote" + i; + assertTrue(map.containsKey(clusterAlias)); + ClusterSearchShardsResponse shardsResponse = map.get(clusterAlias); + assertEquals(1, shardsResponse.getNodes().length); + } + } + { + final CountDownLatch latch = new CountDownLatch(1); + AtomicReference failure = new AtomicReference<>(); + AtomicInteger skippedClusters = new AtomicInteger(0); + TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), "index_not_found", null, skippedClusters, + remoteIndicesByCluster, remoteClusterService, threadPool, + new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch)); + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertEquals(0, skippedClusters.get()); + assertNotNull(failure.get()); + assertThat(failure.get(), instanceOf(RemoteTransportException.class)); + RemoteTransportException remoteTransportException = (RemoteTransportException) failure.get(); + assertEquals(RestStatus.NOT_FOUND, remoteTransportException.status()); + } - service.clearAllRules(); - if (randomBoolean()) { - for (int i : disconnectedNodesIndices) { - if (randomBoolean()) { - RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); - } + int numDisconnectedClusters = randomIntBetween(1, numClusters); + Set disconnectedNodes = new HashSet<>(numDisconnectedClusters); + Set disconnectedNodesIndices = new HashSet<>(numDisconnectedClusters); + while (disconnectedNodes.size() < numDisconnectedClusters) { + int i = randomIntBetween(0, numClusters - 1); + if (disconnectedNodes.add(nodes[i])) { + assertTrue(disconnectedNodesIndices.add(i)); + } + } + CountDownLatch disconnectedLatch = new CountDownLatch(numDisconnectedClusters); + RemoteClusterServiceTests.addConnectionListener(remoteClusterService, new TransportConnectionListener() { + @Override + public void onNodeDisconnected(DiscoveryNode node) { + if (disconnectedNodes.remove(node)) { + disconnectedLatch.countDown(); } } - { - final CountDownLatch latch = new CountDownLatch(1); - AtomicInteger skippedClusters = new AtomicInteger(0); - AtomicReference> response = new AtomicReference<>(); - TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, - remoteIndicesByCluster, remoteClusterService, threadPool, - new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); - awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(0, skippedClusters.get()); - assertNotNull(response.get()); - Map map = response.get(); - assertEquals(numClusters, map.size()); - for (int i = 0; i < numClusters; i++) { - String clusterAlias = "remote" + i; - assertTrue(map.containsKey(clusterAlias)); + }); + for (DiscoveryNode disconnectedNode : disconnectedNodes) { + service.addFailToSendNoConnectRule(disconnectedNode.getAddress()); + } + + { + final CountDownLatch latch = new CountDownLatch(1); + AtomicInteger skippedClusters = new AtomicInteger(0); + AtomicReference failure = new AtomicReference<>(); + TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, + remoteIndicesByCluster, remoteClusterService, threadPool, + new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch)); + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertEquals(0, skippedClusters.get()); + assertNotNull(failure.get()); + assertThat(failure.get(), instanceOf(RemoteTransportException.class)); + assertThat(failure.get().getMessage(), containsString("error while communicating with remote cluster [")); + assertThat(failure.get().getCause(), instanceOf(NodeDisconnectedException.class)); + } + + //setting skip_unavailable to true for all the disconnected clusters will make the request succeed again + for (int i : disconnectedNodesIndices) { + RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); + } + + { + final CountDownLatch latch = new CountDownLatch(1); + AtomicInteger skippedClusters = new AtomicInteger(0); + AtomicReference> response = new AtomicReference<>(); + TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, + remoteIndicesByCluster, remoteClusterService, threadPool, + new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertNotNull(response.get()); + Map map = response.get(); + assertEquals(numClusters - disconnectedNodesIndices.size(), map.size()); + assertEquals(skippedClusters.get(), disconnectedNodesIndices.size()); + for (int i = 0; i < numClusters; i++) { + String clusterAlias = "remote" + i; + if (disconnectedNodesIndices.contains(i)) { + assertFalse(map.containsKey(clusterAlias)); + } else { assertNotNull(map.get(clusterAlias)); } } - assertEquals(0, service.getConnectionManager().size()); } + + //give transport service enough time to realize that the node is down, and to notify the connection listeners + //so that RemoteClusterConnection is left with no connected nodes, hence it will retry connecting next + assertTrue(disconnectedLatch.await(5, TimeUnit.SECONDS)); + + service.clearAllRules(); + if (randomBoolean()) { + for (int i : disconnectedNodesIndices) { + if (randomBoolean()) { + RemoteClusterServiceTests.updateSkipUnavailable(remoteClusterService, "remote" + i, true); + } + + } + } + { + final CountDownLatch latch = new CountDownLatch(1); + AtomicInteger skippedClusters = new AtomicInteger(0); + AtomicReference> response = new AtomicReference<>(); + TransportSearchAction.collectSearchShards(IndicesOptions.lenientExpandOpen(), null, null, skippedClusters, + remoteIndicesByCluster, remoteClusterService, threadPool, + new LatchedActionListener<>(ActionListener.wrap(response::set, e -> fail("no failures expected")), latch)); + awaitLatch(latch, 5, TimeUnit.SECONDS); + assertEquals(0, skippedClusters.get()); + assertNotNull(response.get()); + Map map = response.get(); + assertEquals(numClusters, map.size()); + for (int i = 0; i < numClusters; i++) { + String clusterAlias = "remote" + i; + assertTrue(map.containsKey(clusterAlias)); + assertNotNull(map.get(clusterAlias)); + } + } + assertEquals(0, service.getConnectionManager().size()); } finally { for (MockTransportService mockTransportService : mockTransportServices) { mockTransportService.close(); @@ -490,8 +742,10 @@ public void onNodeDisconnected(DiscoveryNode node) { } } - public void testResolveCCSReduceMode() throws IOException { - SearchRequest searchRequest = new SearchRequestTests().createSearchRequest(); + public void testResolveCCSReduceMode() throws Exception { + SearchRequestTests searchRequestTests = new SearchRequestTests(); + searchRequestTests.setUp(); + SearchRequest searchRequest = searchRequestTests.createSearchRequest(); CCSReduceMode ccsReduceMode = randomFrom(CCSReduceMode.REMOTE, CCSReduceMode.LOCAL); searchRequest.setCCSReduceMode(ccsReduceMode); assertEquals(ccsReduceMode, searchRequest.getCCSReduceMode()); diff --git a/server/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java b/server/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java index 3ec2506da244e..9eddac80a17c0 100644 --- a/server/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java +++ b/server/src/test/java/org/elasticsearch/transport/RemoteClusterConnectionTests.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.transport; +import org.apache.lucene.search.TotalHits; import org.apache.lucene.store.AlreadyClosedException; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; @@ -29,6 +30,10 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; +import org.elasticsearch.action.search.SearchAction; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -47,6 +52,10 @@ import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.mocksocket.MockServerSocket; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.junit.annotations.TestLogging; @@ -130,6 +139,24 @@ public static MockTransportService startTransport( knownNodes.toArray(new DiscoveryNode[0]), Collections.emptyMap())); } }); + newService.registerRequestHandler(SearchAction.NAME, ThreadPool.Names.SAME, SearchRequest::new, + (request, channel, task) -> { + if ("index_not_found".equals(request.preference())) { + channel.sendResponse(new IndexNotFoundException("index")); + return; + } + SearchHits searchHits; + if ("null_target".equals(request.preference())) { + searchHits = new SearchHits(new SearchHit[] {new SearchHit(0)}, new TotalHits(1, TotalHits.Relation.EQUAL_TO), 1F); + } else { + searchHits = new SearchHits(new SearchHit[0], new TotalHits(0, TotalHits.Relation.EQUAL_TO), Float.NaN); + } + InternalSearchResponse response = new InternalSearchResponse(searchHits, + InternalAggregations.EMPTY, null, null, false, null, 1); + SearchResponse searchResponse = new SearchResponse(response, null, 1, 1, 0, 100, ShardSearchFailure.EMPTY_ARRAY, + SearchResponse.Clusters.EMPTY); + channel.sendResponse(searchResponse); + }); newService.registerRequestHandler(ClusterStateAction.NAME, ThreadPool.Names.SAME, ClusterStateRequest::new, (request, channel, task) -> { DiscoveryNodes.Builder builder = DiscoveryNodes.builder(); From 9fbff366990dd23f4a424c76033afbc1f0bc6536 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Fri, 25 Jan 2019 16:58:33 +0100 Subject: [PATCH 20/27] fix failing test --- .../elasticsearch/transport/RemoteClusterClientTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java index 3f85d927e9295..6e9c2e4eaf320 100644 --- a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java +++ b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java @@ -62,10 +62,10 @@ public void testConnectAndExecuteRequest() throws Exception { ClusterStateResponse clusterStateResponse = client.admin().cluster().prepareState().execute().get(); assertNotNull(clusterStateResponse); assertEquals("foo_bar_cluster", clusterStateResponse.getState().getClusterName().value()); - // also test a failure, there is no handler for search registered + // also test a failure, there is no handler for scroll registered ActionNotFoundTransportException ex = expectThrows(ActionNotFoundTransportException.class, - () -> client.prepareSearch().get()); - assertEquals("No handler for action [indices:data/read/search]", ex.getMessage()); + () -> client.prepareSearchScroll("").get()); + assertEquals("No handler for action [indices:data/read/scroll]", ex.getMessage()); } } } From 8c6783622730c8965785099d58cef4a2a0d63efe Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 29 Jan 2019 14:55:06 +0100 Subject: [PATCH 21/27] Rename ccs_reduce_mode to ccs_minimize_roundtrips and make it a boolean. Note that an enum is still returned in the _clusters section of the search response. --- .../client/RequestConverters.java | 2 +- .../client/RequestConvertersTests.java | 5 +- .../modules/cross-cluster-search.asciidoc | 72 ++++++++++--------- docs/reference/search/request-body.asciidoc | 7 +- .../MultiSearchTemplateResponseTests.java | 3 +- ...rossClusterSearchUnavailableClusterIT.java | 13 ++-- .../test/multi_cluster/10_basic.yml | 16 ++--- .../test/multi_cluster/40_scroll.yml | 2 +- .../test/multi_cluster/70_skip_shards.yml | 8 +-- .../resources/rest-api-spec/api/msearch.json | 9 ++- .../rest-api-spec/api/msearch_template.json | 9 ++- .../resources/rest-api-spec/api/search.json | 9 ++- .../rest-api-spec/api/search_template.json | 9 ++- .../action/search/CCSReduceMode.java | 62 ---------------- .../action/search/MultiSearchRequest.java | 14 ++-- .../action/search/SearchRequest.java | 44 ++++-------- .../action/search/SearchResponse.java | 57 ++++++++++----- .../action/search/TransportSearchAction.java | 62 +++++++--------- .../action/search/RestMultiSearchAction.java | 4 +- .../rest/action/search/RestSearchAction.java | 5 +- .../action/search/SearchRequestTests.java | 48 ++----------- .../action/search/SearchResponseTests.java | 10 +-- .../search/TransportSearchActionTests.java | 23 +----- .../search/RandomSearchRequestGenerator.java | 3 +- .../test/multi_cluster/10_basic.yml | 14 ++-- .../test/multi_cluster/40_scroll.yml | 4 +- .../test/multi_cluster/60_skip_shards.yml | 4 +- 27 files changed, 191 insertions(+), 327 deletions(-) delete mode 100644 server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 2852f8da70163..6f0d107644fc7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -398,7 +398,7 @@ private static void addSearchRequestParams(Params params, SearchRequest searchRe params.withPreference(searchRequest.preference()); params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); - params.putParam("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); + params.putParam("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index b662afdd98163..95971ad40ced0 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -41,7 +41,6 @@ import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.MultiGetRequest; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.ClearScrollRequest; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; @@ -1864,9 +1863,9 @@ private static void setRandomSearchParams(SearchRequest searchRequest, expectedParams.put("scroll", searchRequest.scroll().keepAlive().getStringRep()); } if (randomBoolean()) { - searchRequest.setCCSReduceMode(randomFrom(CCSReduceMode.values())); + searchRequest.setCcsMinimizeRoundtrips(randomBoolean()); } - expectedParams.put("ccs_reduce_mode", searchRequest.getCCSReduceMode().toString()); + expectedParams.put("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); } static void setRandomIndicesOptions(Consumer setter, Supplier getter, diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index 81a1cf1b6bd44..a7a4faabdd0c0 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -73,7 +73,7 @@ GET /cluster_one:twitter/_search "skipped": 0 }, "_clusters": { - "ccs_reduce_mode": "remote", + "ccs_reduction": "remote", "total": 1, "successful": 1, "skipped": 0 @@ -140,7 +140,7 @@ will be prefixed with their remote cluster name: "skipped": 0 }, "_clusters": { - "ccs_reduce_mode": "remote", + "ccs_reduction": "remote", "total": 2, "successful": 2, "skipped": 0 @@ -234,7 +234,7 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> "skipped": 0 }, "_clusters": { <1> - "ccs_reduce_mode": "remote", + "ccs_reduction": "remote", "total": 3, "successful": 2, "skipped": 1 @@ -281,42 +281,46 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> <1> The `clusters` section indicates that one cluster was unavailable and got skipped [float] -[[ccs-reduce-mode]] -=== Reduce mode +[[ccs-reduction]] +=== CCS reduction phase -When searching across remote clusters, the execution path may differ depending -on the selected reduce mode: +Cross-cluster search requests can be executed in two ways: -- `remote`: the coordinating node sends one search request to each cluster. -Each cluster performs the search independently, reducing and fetching results -which are then returned to the caller. Once the CCS node has received all the +- the CCS coordinating node minimizes network round-trips by sending one search +request to each cluster. Each cluster performs the search independently, +reducing and fetching results. Once the CCS node has received all the responses, it performs another reduction and returns the relevant results back -to the user. The `remote` reduce mode is beneficial when there is network -latency between the coordinating node and the remote clusters involved, which -is typically the case. A single request is sent to each remote cluster, at the -cost of retrieving `from` + `size` already fetched results. +to the user. This strategy is beneficial when there is network latency between +the CCS coordinating node and the remote clusters involved, which is typically +the case. A single request is sent to each remote cluster, at the cost of +retrieving `from` + `size` already fetched results. This is the default +strategy, used whenever possible. In case a scroll is provided, or inner hits +are requested as part of field collapsing, this strategy is not supported hence +network round-trips cannot be minimized and the following strategy is used +instead. -- `local`: the coordinating node sends a <> -request to each remote cluster, in order to collect information about their -corresponding remote indices involved in the search request and the shards -where their data is located. Once each cluster has responded to such request, -the search executes as if all shards were part of the same cluster. The -coordinating node sends one request to each shard involved, each shard -executes the query and returns its own results which are then reduced (and -fetched, depending on the <>) by the -coordinating node. The `local` reduce mode is beneficial whenever there is very - low network latency between the coordinating node and the remote clusters - involved, as it treats all shards the same, at the cost of sending many - requests to each remote cluster. +- the CCS coordinating node sends a <> request to +each remote cluster, in order to collect information about their corresponding +remote indices involved in the search request and the shards where their data +is located. Once each cluster has responded to such request, the search +executes as if all shards were part of the same cluster. The coordinating node +sends one request to each shard involved, each shard executes the query and +returns its own results which are then reduced (and fetched, depending on the +<>) by the CCS coordinating node. +This strategy may be beneficial whenever there is very low network latency +between the CCS coordinating node and the remote clusters involved, as it +treats all shards the same, at the cost of sending many requests to each remote +cluster, which is problematic in presence of network latency. -- `auto`: `remote` is used whenever possible. In case a scroll is provided, -or inner hits are requested as part of field collapsing, `local` is used -instead. This is the default reduce mode. - -The <> supports the `ccs_reduce_mode` -parameter which allows to select the reduce mode if needed. The reduce mode -that was used for a cross-cluster search request is returned as part of the -`_clusters` section, which will either be `remote` or `local`. +The <> supports the `ccs_minimize_roundtrips` +parameter, which defaults to `true` and can be set to `false` in case +minimizing network round-trips is not desirable. +Whenever a cross-cluster search request is executed, the `_clusters` section +returned as part of the search response holds the information on where the +reduction has happened: `remote` (meaning that minimizing round-trips was +enabled and possible with the provided request), or `local` (meaning that +minimizing round-trips was either disabled explicitly or not possible with +the provided request). Note that all the communication between the nodes, regardless of which cluster they belong to and the selected reduce mode, happens through the diff --git a/docs/reference/search/request-body.asciidoc b/docs/reference/search/request-body.asciidoc index 3f5ba28c2c9e4..120c4c6757599 100644 --- a/docs/reference/search/request-body.asciidoc +++ b/docs/reference/search/request-body.asciidoc @@ -113,10 +113,11 @@ And here is a sample response: reduce the memory overhead per search request if the potential number of shards in the request can be large. -`ccs_reduce_mode`:: +`ccs_minimize_roundtrips`:: - The cross-cluster search reduce mode. Can be `local` or `remote`. - See <> for more. + Defaults to `true`. Set to `false` to disable minimizing network round-trips + between the coordinating node and the remote clusters when executing + cross-cluster search requests. See <> for more. Out of the above, the `search_type`, `request_cache` and the `allow_partial_search_results` diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java index a61fb821c58dd..630b6ced0685a 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.script.mustache; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.Strings; @@ -65,7 +64,7 @@ private static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - CCSReduceMode executionMode = randomFrom(CCSReduceMode.values()); + SearchResponse.CCSReduction executionMode = randomFrom(SearchResponse.CCSReduction.values()); return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); } diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index bfcded260e9d7..60c2c27c28175 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -33,7 +33,6 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -165,7 +164,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -174,7 +173,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -184,7 +183,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.LOCAL, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.LOCAL, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -203,7 +202,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -212,7 +211,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.REMOTE, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -222,7 +221,7 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(CCSReduceMode.LOCAL, response.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.LOCAL, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 83f387ece1780..f18989a4bcb8a 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -39,7 +39,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: {num} - match: { _shards.total: 5 } - match: { hits.total: 11 } @@ -67,7 +67,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -88,7 +88,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -139,7 +139,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -169,7 +169,7 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -184,7 +184,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -201,7 +201,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 4 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -218,7 +218,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 1 } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index a3e02a043d8fd..bdd85512dacc9 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -15,7 +15,7 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "local"} + - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index d70cae02ac0f2..f01a429b4f5ab 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -29,13 +29,13 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_reduce_mode: "local" + ccs_minimize_roundtrips: false body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2016-02-01", "lt": "2018-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} - is_false: num_reduce_phases - - match: {_clusters.ccs_reduce_mode: "local"} + - match: {_clusters.ccs_reduction: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} @@ -48,13 +48,13 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_reduce_mode: "local" + ccs_minimize_roundstrips: false body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2015-02-01", "lt": "2016-02-01"} } } } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} - is_false: num_reduce_phases - - match: {_clusters.ccs_reduce_mode: "local"} + - match: {_clusters.ccs_reduction: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json index 2f929be19b370..398dcbd29515d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch.json @@ -44,11 +44,10 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_reduce_mode": { - "type" : "enum", - "options" : ["local","remote","auto"], - "default" : "auto", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" + "ccs_minimize_roundtrips": { + "type" : "boolean", + "description" : "Indicates whether network round-trips should be minimized as part of cross-cluster search requests execution", + "default" : "true" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json index 95c9dd83abdb8..e89f96e06960f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/msearch_template.json @@ -34,11 +34,10 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_reduce_mode": { - "type" : "enum", - "options" : ["local","remote","auto"], - "default" : "auto", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" + "ccs_minimize_roundtrips": { + "type" : "boolean", + "description" : "Indicates whether network round-trips should be minimized as part of cross-cluster search requests execution", + "default" : "true" } } }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index 1a3943815c7fa..f44c0f74b2c3d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -24,11 +24,10 @@ "type" : "boolean", "description" : "Specify whether wildcard and prefix queries should be analyzed (default: false)" }, - "ccs_reduce_mode": { - "type" : "enum", - "options" : ["local","remote", "auto"], - "default" : "auto", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" + "ccs_minimize_roundtrips": { + "type" : "boolean", + "description" : "Indicates whether network round-trips should be minimized as part of cross-cluster search requests execution", + "default" : "true" }, "default_operator": { "type" : "enum", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json index 87516a8161a3c..24b7fa135b331 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_template.json @@ -68,11 +68,10 @@ "description" : "Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default" : false }, - "ccs_reduce_mode": { - "type" : "enum", - "options" : ["local","remote", "auto"], - "default" : "auto", - "description" : "The cross-cluster search reduce mode: fan out to all the shards and perform one local reduce or send one search request to each remote cluster and perform remote reduction in each cluster, or remote whenever possible" + "ccs_minimize_roundtrips": { + "type" : "boolean", + "description" : "Indicates whether network round-trips should be minimized as part of cross-cluster search requests execution", + "default" : "true" } } }, diff --git a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java b/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java deleted file mode 100644 index 3abfaa844ca09..0000000000000 --- a/server/src/main/java/org/elasticsearch/action/search/CCSReduceMode.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.action.search; - -import java.util.Locale; - -/** - * The reduce modes when executing a cross-cluster search request - */ -public enum CCSReduceMode { - /** - * The coordinating node sends one search shards request to each remote cluster to collect information about the remote indices - * involved and their shards. From then on, the search executes as if all shards were part of the same cluster, meaning that - * each shard will receive a shard search request, execute the query, after which reduction happens in one go and relevant documents - * are fetched from their shards. To be preferred when network latency is very low between the coordinating node and the remote - * clusters. - */ - LOCAL, - /** - * The coordinating node sends one and only one search request to each cluster. Each cluster executes the search independently, - * performs non final reduction and returns `from` + `size` fetched hits to the coordinating node, which will perform a final - * reduction and merge the different search responses into one. To be preferred when there is network latency between the coordinating - * node and the remote clusters. - */ - REMOTE, - /** - * Default mode. {@link #REMOTE} is used whenever possible. When a scroll is provided or inner hits are requested as part of field - * collapsing, {@link #LOCAL} is used instead. - */ - AUTO; - - @Override - public String toString() { - return name().toLowerCase(Locale.ROOT); - } - - public static CCSReduceMode fromString(String executionMode) { - for (CCSReduceMode value : CCSReduceMode.values()) { - if (value.name().toLowerCase(Locale.ROOT).equals(executionMode)) { - return value; - } - } - throw new IllegalArgumentException("unknown ccs_reduce_mode: [" + executionMode + "]"); - } -} diff --git a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java index f1e514018257f..1dc2dc624b277 100644 --- a/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/MultiSearchRequest.java @@ -173,7 +173,7 @@ public static void readMultiLineFormat(BytesReference data, String[] types, String routing, String searchType, - String ccsExecutionMode, + Boolean ccsMinimizeRoundtrips, NamedXContentRegistry registry, boolean allowExplicitIndex) throws IOException { int from = 0; @@ -206,8 +206,8 @@ public static void readMultiLineFormat(BytesReference data, if (searchType != null) { searchRequest.searchType(searchType); } - if (ccsExecutionMode != null) { - searchRequest.setCCSReduceMode(ccsExecutionMode); + if (ccsMinimizeRoundtrips != null) { + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); } IndicesOptions defaultOptions = searchRequest.indicesOptions(); // now parse the action @@ -230,8 +230,8 @@ public static void readMultiLineFormat(BytesReference data, searchRequest.types(nodeStringArrayValue(value)); } else if ("search_type".equals(entry.getKey()) || "searchType".equals(entry.getKey())) { searchRequest.searchType(nodeStringValue(value, null)); - } else if ("ccs_reduce_mode".equals(entry.getKey()) || "ccsExecutionMode".equals(entry.getKey())) { - searchRequest.setCCSReduceMode(nodeStringValue(value, null)); + } else if ("ccs_minimize_roundtrips".equals(entry.getKey()) || "ccsMinimizeRoundtrips".equals(entry.getKey())) { + searchRequest.setCcsMinimizeRoundtrips(nodeBooleanValue(value)); } else if ("request_cache".equals(entry.getKey()) || "requestCache".equals(entry.getKey())) { searchRequest.requestCache(nodeBooleanValue(value, entry.getKey())); } else if ("preference".equals(entry.getKey())) { @@ -333,9 +333,7 @@ public static void writeSearchRequestParams(SearchRequest request, XContentBuild if (request.searchType() != null) { xContentBuilder.field("search_type", request.searchType().name().toLowerCase(Locale.ROOT)); } - if (request.getCCSReduceMode() != null) { - xContentBuilder.field("ccs_reduce_mode", request.getCCSReduceMode().toString()); - } + xContentBuilder.field("ccs_minimize_roundtrips", request.isCcsMinimizeRoundtrips()); if (request.requestCache() != null) { xContentBuilder.field("request_cache", request.requestCache()); } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 8100f8ddd07a0..55122b6806fd2 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -93,7 +93,7 @@ public final class SearchRequest extends ActionRequest implements IndicesRequest private String[] types = Strings.EMPTY_ARRAY; - private CCSReduceMode ccsReduceMode = CCSReduceMode.AUTO; + private boolean ccsMinimizeRoundtrips = true; public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled(); @@ -153,7 +153,7 @@ static SearchRequest withLocalReduction(SearchRequest originalSearchRequest, Str private SearchRequest(SearchRequest searchRequest, String[] indices, String localClusterAlias, long absoluteStartMillis) { this.allowPartialSearchResults = searchRequest.allowPartialSearchResults; this.batchedReduceSize = searchRequest.batchedReduceSize; - this.ccsReduceMode = searchRequest.ccsReduceMode; + this.ccsMinimizeRoundtrips = searchRequest.ccsMinimizeRoundtrips; this.indices = indices; this.indicesOptions = searchRequest.indicesOptions; this.maxConcurrentShardRequests = searchRequest.maxConcurrentShardRequests; @@ -204,7 +204,7 @@ public SearchRequest(StreamInput in) throws IOException { absoluteStartMillis = DEFAULT_ABSOLUTE_START_MILLIS; } if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - ccsReduceMode = in.readEnum(CCSReduceMode.class); + ccsMinimizeRoundtrips = in.readBoolean(); } } @@ -233,7 +233,7 @@ public void writeTo(StreamOutput out) throws IOException { } } if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeEnum(ccsReduceMode); + out.writeBoolean(ccsMinimizeRoundtrips); } } @@ -263,16 +263,6 @@ public ActionRequestValidationException validate() { validationException = addValidationError("[request_cache] cannot be used in a scroll context", validationException); } - if (ccsReduceMode == CCSReduceMode.REMOTE) { - validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.REMOTE + - "] in a scroll context", validationException); - } - } - boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null - && source.collapse().getInnerHits().isEmpty() == false; - if (collapseWithInnerHits && ccsReduceMode == CCSReduceMode.REMOTE) { - validationException = addValidationError("[ccs_reduce_mode] cannot be [" + CCSReduceMode.REMOTE + - "] when inner hits are requested as part of field collapsing", validationException); } return validationException; } @@ -326,24 +316,18 @@ public SearchRequest indicesOptions(IndicesOptions indicesOptions) { } /** - * Sets the reduce mode (as a {@link CCSReduceMode}) for cross-cluster search requests - */ - public void setCCSReduceMode(CCSReduceMode ccsReduceMode) { - this.ccsReduceMode = Objects.requireNonNull(ccsReduceMode, "ccsReduceMode must not be null"); - } - - /** - * Sets the reduce mode (as a string) for cross-cluster search requests + * Returns whether network round-trips should be minimized when executing cross-cluster search requests. + * Defaults to true. */ - public void setCCSReduceMode(String ccsReduceMode) { - this.ccsReduceMode = CCSReduceMode.fromString(ccsReduceMode); + public boolean isCcsMinimizeRoundtrips() { + return ccsMinimizeRoundtrips; } /** - * Returns the reduce mode for cross-cluster search requests + * Sets whether network round-trips should be minimized when executing cross-cluster search requests. Defaults to true. */ - public CCSReduceMode getCCSReduceMode() { - return this.ccsReduceMode; + public void setCcsMinimizeRoundtrips(boolean ccsMinimizeRoundtrips) { + this.ccsMinimizeRoundtrips = ccsMinimizeRoundtrips; } /** @@ -638,14 +622,14 @@ public boolean equals(Object o) { Objects.equals(allowPartialSearchResults, that.allowPartialSearchResults) && Objects.equals(localClusterAlias, that.localClusterAlias) && absoluteStartMillis == that.absoluteStartMillis && - ccsReduceMode == that.ccsReduceMode; + ccsMinimizeRoundtrips == that.ccsMinimizeRoundtrips; } @Override public int hashCode() { return Objects.hash(searchType, Arrays.hashCode(indices), routing, preference, source, requestCache, scroll, Arrays.hashCode(types), indicesOptions, batchedReduceSize, maxConcurrentShardRequests, preFilterShardSize, - allowPartialSearchResults, localClusterAlias, absoluteStartMillis, ccsReduceMode); + allowPartialSearchResults, localClusterAlias, absoluteStartMillis, ccsMinimizeRoundtrips); } @Override @@ -665,7 +649,7 @@ public String toString() { ", allowPartialSearchResults=" + allowPartialSearchResults + ", localClusterAlias=" + localClusterAlias + ", getOrCreateAbsoluteStartMillis=" + absoluteStartMillis + - ", ccsReduceMode=" + ccsReduceMode + + ", ccsMinimizeRoundtrips=" + ccsMinimizeRoundtrips + ", source=" + source + '}'; } } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index be57a31abe35a..20beadcfac16c 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -322,7 +323,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio int successful = -1; int total = -1; int skipped = -1; - CCSReduceMode executionMode = null; + CCSReduction ccsReduction = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -333,8 +334,8 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio total = parser.intValue(); } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); - } else if (Clusters.CCS_REDUCE_MODE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - executionMode = CCSReduceMode.fromString(parser.text()); + } else if (Clusters.CCS_REDUCTION_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + ccsReduction = CCSReduction.fromString(parser.text()); } else { parser.skipChildren(); } @@ -342,7 +343,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio parser.skipChildren(); } } - clusters = new Clusters(total, successful, skipped, executionMode); + clusters = new Clusters(total, successful, skipped, ccsReduction); } else { parser.skipChildren(); } @@ -409,20 +410,20 @@ public String toString() { */ public static class Clusters implements ToXContent, Writeable { - public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduceMode.REMOTE); + public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduction.REMOTE); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); static final ParseField SKIPPED_FIELD = new ParseField("skipped"); static final ParseField TOTAL_FIELD = new ParseField("total"); - static final ParseField CCS_REDUCE_MODE_FIELD = new ParseField("ccs_reduce_mode"); + static final ParseField CCS_REDUCTION_FIELD = new ParseField("ccs_reduction"); private final int total; private final int successful; private final int skipped; - private final CCSReduceMode ccsReduceMode; + private final CCSReduction ccsReduction; - public Clusters(int total, int successful, int skipped, CCSReduceMode ccsReduceMode) { + public Clusters(int total, int successful, int skipped, CCSReduction ccsReduction) { assert total >= 0 && successful >= 0 && skipped >= 0 : "total: " + total + " successful: " + successful + " skipped: " + skipped; assert successful <= total && skipped == total - successful @@ -430,7 +431,7 @@ public Clusters(int total, int successful, int skipped, CCSReduceMode ccsReduceM this.total = total; this.successful = successful; this.skipped = skipped; - this.ccsReduceMode = Objects.requireNonNull(ccsReduceMode); + this.ccsReduction = Objects.requireNonNull(ccsReduction); } private Clusters(StreamInput in) throws IOException { @@ -438,9 +439,9 @@ private Clusters(StreamInput in) throws IOException { this.successful = in.readVInt(); this.skipped = in.readVInt(); if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - this.ccsReduceMode = in.readOptionalEnum(CCSReduceMode.class); + this.ccsReduction = in.readOptionalEnum(CCSReduction.class); } else { - this.ccsReduceMode = null; + this.ccsReduction = null; } } @@ -450,7 +451,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVInt(successful); out.writeVInt(skipped); if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeOptionalEnum(ccsReduceMode); + out.writeOptionalEnum(ccsReduction); } } @@ -458,7 +459,7 @@ public void writeTo(StreamOutput out) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (this != EMPTY) { builder.startObject(_CLUSTERS_FIELD.getPreferredName()); - builder.field(CCS_REDUCE_MODE_FIELD.getPreferredName(), ccsReduceMode == null ? null : ccsReduceMode.toString()); + builder.field(CCS_REDUCTION_FIELD.getPreferredName(), ccsReduction == null ? null : ccsReduction.toString()); builder.field(TOTAL_FIELD.getPreferredName(), total); builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); builder.field(SKIPPED_FIELD.getPreferredName(), skipped); @@ -489,10 +490,10 @@ public int getSkipped() { } /** - * Returns the execution mode used for the execution of this cross-cluster search request + * Returns information about where the ccs reduction took place, either in each remote cluster or only in the coordinating node. */ - public CCSReduceMode getCCSReduceMode() { - return ccsReduceMode; + public CCSReduction getCCSReduction() { + return ccsReduction; } @Override @@ -507,18 +508,36 @@ public boolean equals(Object o) { return total == clusters.total && successful == clusters.successful && skipped == clusters.skipped && - ccsReduceMode == clusters.ccsReduceMode; + ccsReduction == clusters.ccsReduction; } @Override public int hashCode() { - return Objects.hash(total, successful, skipped, ccsReduceMode); + return Objects.hash(total, successful, skipped, ccsReduction); } @Override public String toString() { - return "Clusters{ccs_reduce_mode=" + ccsReduceMode + ", total=" + total + + return "Clusters{ccs_reduction=" + ccsReduction + ", total=" + total + ", successful=" + successful + ", skipped=" + skipped + '}'; } } + + public enum CCSReduction { + LOCAL, REMOTE; + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } + + private static CCSReduction fromString(String ccsReduction) { + for (CCSReduction value : CCSReduction.values()) { + if (value.name().toLowerCase(Locale.ROOT).equals(ccsReduction)) { + return value; + } + } + throw new IllegalArgumentException("unknown ccs_reduction: [" + ccsReduction + "]"); + } + } } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 3afad90cf411a..2d7b25ddb84db 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -204,33 +204,29 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - CCSReduceMode ccsReduceMode = resolveCCSReduceMode(searchRequest); - switch(ccsReduceMode) { - case LOCAL: - AtomicInteger skippedClusters = new AtomicInteger(0); - collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), - skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, - ActionListener.wrap( - searchShardsResponses -> { - List remoteShardIterators = new ArrayList<>(); - Map remoteAliasFilters = new HashMap<>(); - BiFunction clusterNodeLookup = processRemoteShards( - searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); - int localClusters = localIndices == null ? 0 : 1; - int totalClusters = remoteClusterIndices.size() + localClusters; - int successfulClusters = searchShardsResponses.size() + localClusters; - executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, - remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, - new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), - CCSReduceMode.LOCAL)); - }, - listener::onFailure)); - break; - case REMOTE: - ccsRemoteReduce(searchRequest, localIndices, remoteClusterIndices, timeProvider, searchService::createReduceContext, - remoteClusterService, threadPool, listener, - (r, l) -> executeLocalSearch(task, timeProvider, r, localIndices, clusterState, l)); - break; + if (searchRequest.isCcsMinimizeRoundtrips()) { + ccsRemoteReduce(searchRequest, localIndices, remoteClusterIndices, timeProvider, searchService::createReduceContext, + remoteClusterService, threadPool, listener, + (r, l) -> executeLocalSearch(task, timeProvider, r, localIndices, clusterState, l)); + } else { + AtomicInteger skippedClusters = new AtomicInteger(0); + collectSearchShards(searchRequest.indicesOptions(), searchRequest.preference(), searchRequest.routing(), + skippedClusters, remoteClusterIndices, remoteClusterService, threadPool, + ActionListener.wrap( + searchShardsResponses -> { + List remoteShardIterators = new ArrayList<>(); + Map remoteAliasFilters = new HashMap<>(); + BiFunction clusterNodeLookup = processRemoteShards( + searchShardsResponses, remoteClusterIndices, remoteShardIterators, remoteAliasFilters); + int localClusters = localIndices == null ? 0 : 1; + int totalClusters = remoteClusterIndices.size() + localClusters; + int successfulClusters = searchShardsResponses.size() + localClusters; + executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, + remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, + new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), + SearchResponse.CCSReduction.LOCAL)); + }, + listener::onFailure)); } } }, listener::onFailure); @@ -273,16 +269,6 @@ static void ccsRemoteReduce(SearchRequest searchRequest, OriginalIndices localIn } } - static CCSReduceMode resolveCCSReduceMode(SearchRequest searchRequest) { - if (searchRequest.getCCSReduceMode() == CCSReduceMode.AUTO) { - SearchSourceBuilder source = searchRequest.source(); - boolean collapseWithInnerHits = source != null && source.collapse() != null && source.collapse().getInnerHits() != null - && source.collapse().getInnerHits().isEmpty() == false; - return collapseWithInnerHits || searchRequest.scroll() != null ? CCSReduceMode.LOCAL : CCSReduceMode.REMOTE; - } - return searchRequest.getCCSReduceMode(); - } - static SearchResponseMerger createSearchResponseMerger(SearchSourceBuilder source, SearchTimeProvider timeProvider, Function reduceContextFunction) { final int from; @@ -351,7 +337,7 @@ void innerOnResponse(SearchResponse searchResponse) { @Override SearchResponse createFinalResponse() { SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), - skippedClusters.get(), CCSReduceMode.REMOTE); + skippedClusters.get(), SearchResponse.CCSReduction.REMOTE); return searchResponseMerger.getMergedResponse(clusters); } }; diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index d8f169ebb545b..05a20a0cc06b9 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -147,14 +147,14 @@ public static void parseMultiLineRequest(RestRequest request, IndicesOptions ind String[] indices = Strings.splitStringByCommaToArray(request.param("index")); String[] types = Strings.splitStringByCommaToArray(request.param("type")); String searchType = request.param("search_type"); - String ccsExecutionMode = request.param("ccs_reduce_mode"); + boolean ccsMinimizeRoundtrips = request.paramAsBoolean("ccs_minimize_roundtrips", true); String routing = request.param("routing"); final Tuple sourceTuple = request.contentOrSourceParam(); final XContent xContent = sourceTuple.v1().xContent(); final BytesReference data = sourceTuple.v2(); MultiSearchRequest.readMultiLineFormat(data, xContent, consumer, indices, indicesOptions, types, routing, - searchType, ccsExecutionMode, request.getXContentRegistry(), allowExplicitIndex); + searchType, ccsMinimizeRoundtrips, request.getXContentRegistry(), allowExplicitIndex); } @Override diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 28a04fe86909e..00c08a124f1e4 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -173,9 +173,8 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r searchRequest.routing(request.param("routing")); searchRequest.preference(request.param("preference")); searchRequest.indicesOptions(IndicesOptions.fromRequest(request, searchRequest.indicesOptions())); - if (request.hasParam("ccs_reduce_mode")) { - searchRequest.setCCSReduceMode(request.param("ccs_reduce_mode")); - } + searchRequest.setCcsMinimizeRoundtrips(request.paramAsBoolean("ccs_minimize_roundtrips", true)); + checkRestTotalHits(request, searchRequest); } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java index 612896d6d8b1c..1d2d59c60e2ae 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchRequestTests.java @@ -26,12 +26,10 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.ArrayUtils; -import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.AbstractSearchTestCase; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.rescore.QueryRescorerBuilder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; @@ -83,9 +81,9 @@ public void testRandomVersionSerialization() throws IOException { Version version = VersionUtils.randomVersion(random()); SearchRequest deserializedRequest = copyWriteable(searchRequest, namedWriteableRegistry, SearchRequest::new, version); if (version.before(Version.V_7_0_0)) { - assertEquals(CCSReduceMode.AUTO, deserializedRequest.getCCSReduceMode()); + assertTrue(deserializedRequest.isCcsMinimizeRoundtrips()); } else { - assertEquals(searchRequest.getCCSReduceMode(), deserializedRequest.getCCSReduceMode()); + assertEquals(searchRequest.isCcsMinimizeRoundtrips(), deserializedRequest.isCcsMinimizeRoundtrips()); } if (version.before(Version.V_6_7_0)) { assertNull(deserializedRequest.getLocalClusterAlias()); @@ -104,7 +102,7 @@ public void testReadFromPre6_7_0() throws IOException { assertArrayEquals(new String[]{"index"}, searchRequest.indices()); assertNull(searchRequest.getLocalClusterAlias()); assertAbsoluteStartMillisIsCurrentTime(searchRequest); - assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); + assertTrue(searchRequest.isCcsMinimizeRoundtrips()); } } @@ -143,9 +141,6 @@ public void testIllegalArguments() { e = expectThrows(NullPointerException.class, () -> searchRequest.scroll((TimeValue)null)); assertEquals("keepAlive must not be null", e.getMessage()); - - IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> searchRequest.setCCSReduceMode("whatever")); - assertEquals("unknown ccs_reduce_mode: [whatever]", iae.getMessage()); } public void testValidate() throws IOException { @@ -163,9 +158,6 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().trackTotalHits(false); - if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); - } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -178,9 +170,6 @@ public void testValidate() throws IOException { searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); searchRequest.source().from(10); - if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); - } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -191,9 +180,6 @@ public void testValidate() throws IOException { SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder().size(0)); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); - } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); @@ -205,36 +191,11 @@ public void testValidate() throws IOException { searchRequest.source().addRescorer(new QueryRescorerBuilder(QueryBuilders.matchAllQuery())); searchRequest.requestCache(false); searchRequest.scroll(new TimeValue(1000)); - if (searchRequest.getCCSReduceMode() == CCSReduceMode.REMOTE) { - searchRequest.setCCSReduceMode(CCSReduceMode.LOCAL); - } ActionRequestValidationException validationErrors = searchRequest.validate(); assertNotNull(validationErrors); assertEquals(1, validationErrors.validationErrors().size()); assertEquals("using [rescore] is not allowed in a scroll context", validationErrors.validationErrors().get(0)); } - { - SearchRequest searchRequest = createSearchRequest().source(new SearchSourceBuilder()); - searchRequest.scroll(new TimeValue(1000)); - searchRequest.requestCache(false); - searchRequest.setCCSReduceMode(CCSReduceMode.REMOTE); - ActionRequestValidationException validationErrors = searchRequest.validate(); - assertNotNull(validationErrors); - assertEquals(1, validationErrors.validationErrors().size()); - assertEquals("[ccs_reduce_mode] cannot be [remote] in a scroll context", - validationErrors.validationErrors().get(0)); - } - { - SearchRequest searchRequest = createSearchRequest().source( - new SearchSourceBuilder().collapse(new CollapseBuilder("field").setInnerHits(new InnerHitBuilder()))); - searchRequest.scroll((Scroll)null); - searchRequest.setCCSReduceMode(CCSReduceMode.REMOTE); - ActionRequestValidationException validationErrors = searchRequest.validate(); - assertNotNull(validationErrors); - assertEquals(validationErrors.validationErrors().toString(), 1, validationErrors.validationErrors().size()); - assertEquals("[ccs_reduce_mode] cannot be [remote] " + - "when inner hits are requested as part of field collapsing", validationErrors.validationErrors().get(0)); - } } public void testCopyConstructor() throws IOException { @@ -264,8 +225,7 @@ private SearchRequest mutate(SearchRequest searchRequest) { mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), () -> randomFrom(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH)))); mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), this::createSearchSourceBuilder))); - mutators.add(() -> mutation.setCCSReduceMode(randomValueOtherThan(searchRequest.getCCSReduceMode(), - () -> randomFrom(CCSReduceMode.values())))); + mutators.add(() -> mutation.setCcsMinimizeRoundtrips(searchRequest.isCcsMinimizeRoundtrips() == false)); randomFrom(mutators).run(); return mutation; } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index c520f356ce764..d47f488999d26 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -131,8 +131,8 @@ static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - CCSReduceMode executionMode = randomFrom(CCSReduceMode.values()); - return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); + SearchResponse.CCSReduction ccsReduction = randomFrom(SearchResponse.CCSReduction.values()); + return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, ccsReduction); } /** @@ -247,7 +247,7 @@ public void testToXContent() { new SearchHits(hits, new TotalHits(100, TotalHits.Relation.EQUAL_TO), 1.5f), null, null, null, false, null, 1 ), null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, - new SearchResponse.Clusters(5, 3, 2, CCSReduceMode.LOCAL)); + new SearchResponse.Clusters(5, 3, 2, SearchResponse.CCSReduction.LOCAL)); StringBuilder expectedString = new StringBuilder(); expectedString.append("{"); { @@ -262,7 +262,7 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("{\"ccs_reduce_mode\":\"local\","); + expectedString.append("{\"ccs_reduction\":\"local\","); expectedString.append("\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); @@ -306,6 +306,6 @@ public void testSerializationPre7_0_0() throws IOException { assertEquals(clusters.getSkipped(), deserializedClusters.getSkipped()); assertEquals(clusters.getSuccessful(), deserializedClusters.getSuccessful()); assertEquals(clusters.getTotal(), deserializedClusters.getTotal()); - assertNull(deserializedClusters.getCCSReduceMode()); + assertNull(deserializedClusters.getCCSReduction()); } } diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index bf7265251fa09..393348361947a 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -437,7 +437,7 @@ public void testCCSRemoteReduce() throws Exception { assertEquals(0, searchResponse.getClusters().getSkipped()); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); - assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); } { @@ -542,7 +542,7 @@ public void onNodeDisconnected(DiscoveryNode node) { assertEquals(totalClusters, searchResponse.getClusters().getTotal()); int successful = totalClusters - disconnectedNodesIndices.size(); assertEquals(successful, searchResponse.getClusters().getSuccessful()); - assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(successful == 0 ? 0 : successful + 1, searchResponse.getNumReducePhases()); } @@ -582,7 +582,7 @@ public void onNodeDisconnected(DiscoveryNode node) { assertEquals(0, searchResponse.getClusters().getSkipped()); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); - assertEquals(CCSReduceMode.REMOTE, searchResponse.getClusters().getCCSReduceMode()); + assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); } assertEquals(0, service.getConnectionManager().size()); @@ -742,23 +742,6 @@ public void onNodeDisconnected(DiscoveryNode node) { } } - public void testResolveCCSReduceMode() throws Exception { - SearchRequestTests searchRequestTests = new SearchRequestTests(); - searchRequestTests.setUp(); - SearchRequest searchRequest = searchRequestTests.createSearchRequest(); - CCSReduceMode ccsReduceMode = randomFrom(CCSReduceMode.REMOTE, CCSReduceMode.LOCAL); - searchRequest.setCCSReduceMode(ccsReduceMode); - assertEquals(ccsReduceMode, searchRequest.getCCSReduceMode()); - assertEquals(ccsReduceMode, TransportSearchAction.resolveCCSReduceMode(searchRequest)); - - searchRequest.setCCSReduceMode(CCSReduceMode.AUTO); - assertEquals(CCSReduceMode.AUTO, searchRequest.getCCSReduceMode()); - boolean remoteReduce = searchRequest.scroll() == null && (searchRequest.source() == null - || searchRequest.source().collapse() == null || searchRequest.source().collapse().getInnerHits() == null - || searchRequest.source().collapse().getInnerHits().isEmpty()); - assertEquals(remoteReduce ? CCSReduceMode.REMOTE : CCSReduceMode.LOCAL, TransportSearchAction.resolveCCSReduceMode(searchRequest)); - } - public void testCreateSearchResponseMerger() { TransportSearchAction.SearchTimeProvider timeProvider = new TransportSearchAction.SearchTimeProvider(0, 0, () -> 0); Function reduceContext = flag -> null; diff --git a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java index 98604b5bd1897..df554ea42de28 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java +++ b/test/framework/src/main/java/org/elasticsearch/search/RandomSearchRequestGenerator.java @@ -19,7 +19,6 @@ package org.elasticsearch.search; -import org.elasticsearch.action.search.CCSReduceMode; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.IndicesOptions; @@ -88,7 +87,7 @@ public static SearchRequest randomSearchRequest(Supplier ra SearchRequest searchRequest = new SearchRequest(); searchRequest.allowPartialSearchResults(true); if (randomBoolean()) { - searchRequest.setCCSReduceMode(randomFrom(CCSReduceMode.values())); + searchRequest.setCcsMinimizeRoundtrips(randomBoolean()); } if (randomBoolean()) { searchRequest.indices(generateRandomStringArray(10, 10, false, false)); diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index dcd9420ed4249..5b9a2be378f5f 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -100,7 +100,7 @@ teardown: - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -122,7 +122,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -145,7 +145,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -198,7 +198,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -213,7 +213,7 @@ teardown: - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -229,7 +229,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -247,7 +247,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "remote"} + - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 2 } - match: { hits.total: 1 } - is_true: hits.hits.0._source.secure diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index a586312cba7ea..c30c8dab0c278 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -55,7 +55,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "local"} + - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } @@ -108,7 +108,7 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduce_mode: "local"} + - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml index dc43a98693de1..d74e82edca7f0 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/60_skip_shards.yml @@ -66,7 +66,7 @@ teardown: - do: headers: { Authorization: "Basic am9lOnMza3JpdA==" } search: - ccs_reduce_mode: local + ccs_minimize_roundtrips: false rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 @@ -84,7 +84,7 @@ teardown: - do: headers: { Authorization: "Basic am9lOnMza3JpdA==" } search: - ccs_reduce_mode: local + ccs_minimize_roundtrips: false rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 From 9574278ca4b19328d5baeadb3855f379b54467bd Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 10:53:19 +0100 Subject: [PATCH 22/27] add assert that scrollId is never set in search response --- .../org/elasticsearch/action/search/SearchResponseMerger.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java index db776bb901873..567040246c50f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseMerger.java @@ -99,6 +99,7 @@ final class SearchResponseMerger { * That may change in the future as it's possible to introduce incremental merges as responses come in if necessary. */ void add(SearchResponse searchResponse) { + assert searchResponse.getScrollId() == null : "merging scroll results is not supported"; searchResponses.add(searchResponse); } From 27ae7dd9d94ee3f74a534ce4d2df6fce256390e7 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 16:09:20 +0100 Subject: [PATCH 23/27] remove ccs_reduction from _clusters section --- .../modules/cross-cluster-search.asciidoc | 9 ---- .../MultiSearchTemplateResponseTests.java | 3 +- ...rossClusterSearchUnavailableClusterIT.java | 6 --- .../test/multi_cluster/10_basic.yml | 8 --- .../test/multi_cluster/40_scroll.yml | 1 - .../test/multi_cluster/70_skip_shards.yml | 2 - .../action/search/SearchResponse.java | 54 +++---------------- .../action/search/TransportSearchAction.java | 5 +- .../common/io/stream/StreamInput.java | 7 --- .../common/io/stream/StreamOutput.java | 12 ----- .../action/search/SearchResponseTests.java | 20 +------ .../search/TransportSearchActionTests.java | 3 -- .../common/io/stream/BytesStreamsTests.java | 9 ---- .../test/multi_cluster/10_basic.yml | 7 --- .../test/multi_cluster/40_scroll.yml | 2 - 15 files changed, 11 insertions(+), 137 deletions(-) diff --git a/docs/reference/modules/cross-cluster-search.asciidoc b/docs/reference/modules/cross-cluster-search.asciidoc index a7a4faabdd0c0..b59f74198c3e8 100644 --- a/docs/reference/modules/cross-cluster-search.asciidoc +++ b/docs/reference/modules/cross-cluster-search.asciidoc @@ -73,7 +73,6 @@ GET /cluster_one:twitter/_search "skipped": 0 }, "_clusters": { - "ccs_reduction": "remote", "total": 1, "successful": 1, "skipped": 0 @@ -140,7 +139,6 @@ will be prefixed with their remote cluster name: "skipped": 0 }, "_clusters": { - "ccs_reduction": "remote", "total": 2, "successful": 2, "skipped": 0 @@ -234,7 +232,6 @@ GET /cluster_one:twitter,cluster_two:twitter,twitter/_search <1> "skipped": 0 }, "_clusters": { <1> - "ccs_reduction": "remote", "total": 3, "successful": 2, "skipped": 1 @@ -315,12 +312,6 @@ cluster, which is problematic in presence of network latency. The <> supports the `ccs_minimize_roundtrips` parameter, which defaults to `true` and can be set to `false` in case minimizing network round-trips is not desirable. -Whenever a cross-cluster search request is executed, the `_clusters` section -returned as part of the search response holds the information on where the -reduction has happened: `remote` (meaning that minimizing round-trips was -enabled and possible with the provided request), or `local` (meaning that -minimizing round-trips was either disabled explicitly or not possible with -the provided request). Note that all the communication between the nodes, regardless of which cluster they belong to and the selected reduce mode, happens through the diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java index 630b6ced0685a..dadaf7cb05a09 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MultiSearchTemplateResponseTests.java @@ -64,8 +64,7 @@ private static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - SearchResponse.CCSReduction executionMode = randomFrom(SearchResponse.CCSReduction.values()); - return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, executionMode); + return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters); } private static MultiSearchTemplateResponse createTestInstanceWithFailures() { diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index 60c2c27c28175..e280b1d2d1a05 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -164,7 +164,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -173,7 +172,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -183,7 +181,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(2, response.getClusters().getSuccessful()); assertEquals(0, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.LOCAL, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -202,7 +199,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } @@ -211,7 +207,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(1, response.getClusters().getTotal()); assertEquals(0, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.REMOTE, response.getClusters().getCCSReduction()); assertEquals(0, response.getHits().getTotalHits().value); } @@ -221,7 +216,6 @@ public void testSearchSkipUnavailable() throws IOException { assertEquals(2, response.getClusters().getTotal()); assertEquals(1, response.getClusters().getSuccessful()); assertEquals(1, response.getClusters().getSkipped()); - assertEquals(SearchResponse.CCSReduction.LOCAL, response.getClusters().getCCSReduction()); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index f18989a4bcb8a..4499a60bfe24a 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -39,7 +39,6 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: {num} - match: { _shards.total: 5 } - match: { hits.total: 11 } @@ -67,7 +66,6 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -88,7 +86,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -139,7 +136,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -169,7 +165,6 @@ - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -184,7 +179,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -201,7 +195,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 4 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -218,7 +211,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 1 } - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index bdd85512dacc9..6a7fe3c5356c0 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -15,7 +15,6 @@ - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index f01a429b4f5ab..6e348d2396b9e 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -35,7 +35,6 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "skip_shards_index"} - is_false: num_reduce_phases - - match: {_clusters.ccs_reduction: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} @@ -54,7 +53,6 @@ - match: { hits.total: 1 } - match: { hits.hits.0._index: "my_remote_cluster:single_doc_index"} - is_false: num_reduce_phases - - match: {_clusters.ccs_reduction: "local"} - match: { _shards.total: 2 } - match: { _shards.successful: 2 } - match: { _shards.skipped : 1} diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 20beadcfac16c..94726381fa28d 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -323,7 +322,6 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio int successful = -1; int total = -1; int skipped = -1; - CCSReduction ccsReduction = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -334,8 +332,6 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio total = parser.intValue(); } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); - } else if (Clusters.CCS_REDUCTION_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - ccsReduction = CCSReduction.fromString(parser.text()); } else { parser.skipChildren(); } @@ -343,7 +339,7 @@ static SearchResponse innerFromXContent(XContentParser parser) throws IOExceptio parser.skipChildren(); } } - clusters = new Clusters(total, successful, skipped, ccsReduction); + clusters = new Clusters(total, successful, skipped); } else { parser.skipChildren(); } @@ -410,20 +406,18 @@ public String toString() { */ public static class Clusters implements ToXContent, Writeable { - public static final Clusters EMPTY = new Clusters(0, 0, 0, CCSReduction.REMOTE); + public static final Clusters EMPTY = new Clusters(0, 0, 0); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); static final ParseField SKIPPED_FIELD = new ParseField("skipped"); static final ParseField TOTAL_FIELD = new ParseField("total"); - static final ParseField CCS_REDUCTION_FIELD = new ParseField("ccs_reduction"); private final int total; private final int successful; private final int skipped; - private final CCSReduction ccsReduction; - public Clusters(int total, int successful, int skipped, CCSReduction ccsReduction) { + public Clusters(int total, int successful, int skipped) { assert total >= 0 && successful >= 0 && skipped >= 0 : "total: " + total + " successful: " + successful + " skipped: " + skipped; assert successful <= total && skipped == total - successful @@ -431,18 +425,12 @@ public Clusters(int total, int successful, int skipped, CCSReduction ccsReductio this.total = total; this.successful = successful; this.skipped = skipped; - this.ccsReduction = Objects.requireNonNull(ccsReduction); } private Clusters(StreamInput in) throws IOException { this.total = in.readVInt(); this.successful = in.readVInt(); this.skipped = in.readVInt(); - if (in.getVersion().onOrAfter(Version.V_7_0_0)) { - this.ccsReduction = in.readOptionalEnum(CCSReduction.class); - } else { - this.ccsReduction = null; - } } @Override @@ -450,16 +438,12 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVInt(total); out.writeVInt(successful); out.writeVInt(skipped); - if (out.getVersion().onOrAfter(Version.V_7_0_0)) { - out.writeOptionalEnum(ccsReduction); - } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (this != EMPTY) { builder.startObject(_CLUSTERS_FIELD.getPreferredName()); - builder.field(CCS_REDUCTION_FIELD.getPreferredName(), ccsReduction == null ? null : ccsReduction.toString()); builder.field(TOTAL_FIELD.getPreferredName(), total); builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); builder.field(SKIPPED_FIELD.getPreferredName(), skipped); @@ -489,13 +473,6 @@ public int getSkipped() { return skipped; } - /** - * Returns information about where the ccs reduction took place, either in each remote cluster or only in the coordinating node. - */ - public CCSReduction getCCSReduction() { - return ccsReduction; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -507,37 +484,18 @@ public boolean equals(Object o) { Clusters clusters = (Clusters) o; return total == clusters.total && successful == clusters.successful && - skipped == clusters.skipped && - ccsReduction == clusters.ccsReduction; + skipped == clusters.skipped; } @Override public int hashCode() { - return Objects.hash(total, successful, skipped, ccsReduction); + return Objects.hash(total, successful, skipped); } @Override public String toString() { - return "Clusters{ccs_reduction=" + ccsReduction + ", total=" + total + + return "Clusters{total=" + total + ", successful=" + successful + ", skipped=" + skipped + '}'; } } - - public enum CCSReduction { - LOCAL, REMOTE; - - @Override - public String toString() { - return name().toLowerCase(Locale.ROOT); - } - - private static CCSReduction fromString(String ccsReduction) { - for (CCSReduction value : CCSReduction.values()) { - if (value.name().toLowerCase(Locale.ROOT).equals(ccsReduction)) { - return value; - } - } - throw new IllegalArgumentException("unknown ccs_reduction: [" + ccsReduction + "]"); - } - } } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index 2d7b25ddb84db..f1b1f68785b7e 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -223,8 +223,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< int successfulClusters = searchShardsResponses.size() + localClusters; executeSearch((SearchTask) task, timeProvider, searchRequest, localIndices, remoteShardIterators, clusterNodeLookup, clusterState, remoteAliasFilters, listener, - new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get(), - SearchResponse.CCSReduction.LOCAL)); + new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters.get())); }, listener::onFailure)); } @@ -337,7 +336,7 @@ void innerOnResponse(SearchResponse searchResponse) { @Override SearchResponse createFinalResponse() { SearchResponse.Clusters clusters = new SearchResponse.Clusters(totalClusters, searchResponseMerger.numResponses(), - skippedClusters.get(), SearchResponse.CCSReduction.REMOTE); + skippedClusters.get()); return searchResponseMerger.getMergedResponse(clusters); } }; diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index fcf6981732e0f..2de583b460f84 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -1008,13 +1008,6 @@ public List readNamedWriteableList(Class catego return builder; } - /** - * Reads an optional enum with type E that was serialized based on the value of its ordinal - */ - public > E readOptionalEnum(Class enumClass) throws IOException { - return readBoolean() ? readEnum(enumClass) : null; - } - /** * Reads an enum with type E that was serialized based on the value of its ordinal */ diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java index 4d028a9cc7720..175f800a7d8cf 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java @@ -1093,18 +1093,6 @@ public void writeNamedWriteableList(List list) throws } } - /** - * Writes an optional enum with type E based on its ordinal value - */ - public > void writeOptionalEnum(@Nullable E enumValue) throws IOException { - if (enumValue == null) { - writeBoolean(false); - } else { - writeBoolean(true); - writeEnum(enumValue); - } - } - /** * Writes an enum with type E based on its ordinal value */ diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index d47f488999d26..465195f92bddf 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -46,7 +46,6 @@ import org.elasticsearch.search.suggest.SuggestTests; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalAggregationTestCase; -import org.elasticsearch.test.VersionUtils; import org.junit.After; import org.junit.Before; @@ -131,8 +130,7 @@ static SearchResponse.Clusters randomClusters() { int totalClusters = randomIntBetween(0, 10); int successfulClusters = randomIntBetween(0, totalClusters); int skippedClusters = totalClusters - successfulClusters; - SearchResponse.CCSReduction ccsReduction = randomFrom(SearchResponse.CCSReduction.values()); - return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters, ccsReduction); + return new SearchResponse.Clusters(totalClusters, successfulClusters, skippedClusters); } /** @@ -247,7 +245,7 @@ public void testToXContent() { new SearchHits(hits, new TotalHits(100, TotalHits.Relation.EQUAL_TO), 1.5f), null, null, null, false, null, 1 ), null, 0, 0, 0, 0, ShardSearchFailure.EMPTY_ARRAY, - new SearchResponse.Clusters(5, 3, 2, SearchResponse.CCSReduction.LOCAL)); + new SearchResponse.Clusters(5, 3, 2)); StringBuilder expectedString = new StringBuilder(); expectedString.append("{"); { @@ -262,7 +260,6 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("{\"ccs_reduction\":\"local\","); expectedString.append("\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); @@ -295,17 +292,4 @@ public void testSerialization() throws IOException { assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards()); assertEquals(searchResponse.getClusters(), deserialized.getClusters()); } - - public void testSerializationPre7_0_0() throws IOException { - SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), null, 1, 1, 0, 100L, - ShardSearchFailure.EMPTY_ARRAY, randomClusters()); - Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_1_0, VersionUtils.getPreviousVersion(Version.V_7_0_0)); - SearchResponse deserialized = copyStreamable(searchResponse, namedWriteableRegistry, SearchResponse::new, version); - SearchResponse.Clusters clusters = searchResponse.getClusters(); - SearchResponse.Clusters deserializedClusters = deserialized.getClusters(); - assertEquals(clusters.getSkipped(), deserializedClusters.getSkipped()); - assertEquals(clusters.getSuccessful(), deserializedClusters.getSuccessful()); - assertEquals(clusters.getTotal(), deserializedClusters.getTotal()); - assertNull(deserializedClusters.getCCSReduction()); - } } diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index 393348361947a..2dbb1235bda29 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -437,7 +437,6 @@ public void testCCSRemoteReduce() throws Exception { assertEquals(0, searchResponse.getClusters().getSkipped()); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); - assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); } { @@ -542,7 +541,6 @@ public void onNodeDisconnected(DiscoveryNode node) { assertEquals(totalClusters, searchResponse.getClusters().getTotal()); int successful = totalClusters - disconnectedNodesIndices.size(); assertEquals(successful, searchResponse.getClusters().getSuccessful()); - assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(successful == 0 ? 0 : successful + 1, searchResponse.getNumReducePhases()); } @@ -582,7 +580,6 @@ public void onNodeDisconnected(DiscoveryNode node) { assertEquals(0, searchResponse.getClusters().getSkipped()); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); - assertEquals(SearchResponse.CCSReduction.REMOTE, searchResponse.getClusters().getCCSReduction()); assertEquals(totalClusters + 1, searchResponse.getNumReducePhases()); } assertEquals(0, service.getConnectionManager().size()); diff --git a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java index 467f119618c60..948e29d5d67de 100644 --- a/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java +++ b/server/src/test/java/org/elasticsearch/common/io/stream/BytesStreamsTests.java @@ -813,15 +813,6 @@ public void testInvalidEnum() throws IOException { assertEquals(0, input.available()); } - public void testOptionalEnum() throws IOException { - TestEnum value = randomFrom(TestEnum.values()); - BytesStreamOutput output = new BytesStreamOutput(); - output.writeOptionalEnum(value); - StreamInput input = output.bytes().streamInput(); - assertEquals(value, input.readOptionalEnum(TestEnum.class)); - assertEquals(0, input.available()); - } - private static void assertEqualityAfterSerialize(TimeValue value, int expectedSize) throws IOException { BytesStreamOutput out = new BytesStreamOutput(); out.writeTimeValue(value); diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml index 5b9a2be378f5f..fa8172697287e 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/10_basic.yml @@ -100,7 +100,6 @@ teardown: - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 5 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -122,7 +121,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -145,7 +143,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6} - match: { hits.hits.0._index: "my_remote_cluster:test_index"} @@ -198,7 +195,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 6 } - match: { hits.hits.0._index: "test_remote_cluster:test_index" } @@ -213,7 +209,6 @@ teardown: - match: {_clusters.total: 2} - match: {_clusters.successful: 2} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 6 } - match: { hits.total: 12 } @@ -229,7 +224,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 3 } - match: { hits.total: 2 } - match: { hits.hits.0._source.filter_field: 1 } @@ -247,7 +241,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "remote"} - match: { _shards.total: 2 } - match: { hits.total: 1 } - is_true: hits.hits.0._source.secure diff --git a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml index c30c8dab0c278..0026df4978075 100644 --- a/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml +++ b/x-pack/qa/multi-cluster-search-security/src/test/resources/rest-api-spec/test/multi_cluster/40_scroll.yml @@ -55,7 +55,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } @@ -108,7 +107,6 @@ teardown: - match: {_clusters.total: 1} - match: {_clusters.successful: 1} - match: {_clusters.skipped: 0} - - match: {_clusters.ccs_reduction: "local"} - set: {_scroll_id: scroll_id} - match: {hits.total: 6 } - length: {hits.hits: 4 } From 4d8266fb251cf1327994698c2000f8c9aef87928 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 16:43:58 +0100 Subject: [PATCH 24/27] fix selection between the two modes which got lost with the recent updates --- .../action/search/TransportSearchAction.java | 14 +++++- .../search/TransportSearchActionTests.java | 46 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index f1b1f68785b7e..48ae3f1249522 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -204,7 +204,7 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< if (remoteClusterIndices.isEmpty()) { executeLocalSearch(task, timeProvider, searchRequest, localIndices, clusterState, listener); } else { - if (searchRequest.isCcsMinimizeRoundtrips()) { + if (shouldMinimizeRoundtrips(searchRequest)) { ccsRemoteReduce(searchRequest, localIndices, remoteClusterIndices, timeProvider, searchService::createReduceContext, remoteClusterService, threadPool, listener, (r, l) -> executeLocalSearch(task, timeProvider, r, localIndices, clusterState, l)); @@ -237,6 +237,18 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< } } + static boolean shouldMinimizeRoundtrips(SearchRequest searchRequest) { + if (searchRequest.isCcsMinimizeRoundtrips() == false) { + return false; + } + if (searchRequest.scroll() != null) { + return false; + } + SearchSourceBuilder source = searchRequest.source(); + return source == null || source.collapse() == null || source.collapse().getInnerHits() == null || + source.collapse().getInnerHits().isEmpty(); + } + static void ccsRemoteReduce(SearchRequest searchRequest, OriginalIndices localIndices, Map remoteIndices, SearchTimeProvider timeProvider, Function reduceContext, RemoteClusterService remoteClusterService, ThreadPool threadPool, ActionListener listener, diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index 2dbb1235bda29..8a5859e200eac 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -39,15 +39,18 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.search.Scroll; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.search.internal.InternalSearchResponse; import org.elasticsearch.search.internal.SearchContext; @@ -778,4 +781,47 @@ public void testCreateSearchResponseMerger() { assertEquals(trackTotalHitsUpTo, merger.trackTotalHitsUpTo); } } + + public void testShouldMinimizeRoundtrips() throws Exception { + { + SearchRequest searchRequest = new SearchRequest(); + assertTrue(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + } + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder()); + assertTrue(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + } + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.scroll("5s"); + assertFalse(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + } + { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + searchRequest.source(source); + CollapseBuilder collapseBuilder = new CollapseBuilder("field"); + source.collapse(collapseBuilder); + collapseBuilder.setInnerHits(new InnerHitBuilder("inner")); + assertFalse(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + } + { + SearchRequestTests searchRequestTests = new SearchRequestTests(); + searchRequestTests.setUp(); + SearchRequest searchRequest = searchRequestTests.createSearchRequest(); + searchRequest.scroll((Scroll)null); + SearchSourceBuilder source = searchRequest.source(); + if (source != null) { + CollapseBuilder collapse = source.collapse(); + if (collapse != null) { + collapse.setInnerHits(Collections.emptyList()); + } + } + searchRequest.setCcsMinimizeRoundtrips(true); + assertTrue(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + searchRequest.setCcsMinimizeRoundtrips(false); + assertFalse(TransportSearchAction.shouldMinimizeRoundtrips(searchRequest)); + } + } } From 86b91cfdc4e8c503ea513dfb7e2f5a431b7c369b Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 16:47:44 +0100 Subject: [PATCH 25/27] revert minor change --- .../java/org/elasticsearch/action/search/SearchResponse.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 94726381fa28d..dd0d4de07d6f4 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -494,8 +494,7 @@ public int hashCode() { @Override public String toString() { - return "Clusters{total=" + total + - ", successful=" + successful + ", skipped=" + skipped + '}'; + return "Clusters{total=" + total + ", successful=" + successful + ", skipped=" + skipped + '}'; } } } From 2f1bbaf85f18950c45180b544bcd4422471c7515 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 18:51:24 +0100 Subject: [PATCH 26/27] fix failing test --- .../org/elasticsearch/action/search/SearchResponseTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index 465195f92bddf..18890e1339557 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -260,7 +260,7 @@ public void testToXContent() { } expectedString.append("\"_clusters\":"); { - expectedString.append("\"total\":5,"); + expectedString.append("{\"total\":5,"); expectedString.append("\"successful\":3,"); expectedString.append("\"skipped\":2},"); } From 91d5df8534b36f78a1afd8336bfb0f56eead0f7a Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 30 Jan 2019 18:52:28 +0100 Subject: [PATCH 27/27] fix parameter name --- .../rest-api-spec/test/multi_cluster/70_skip_shards.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 6e348d2396b9e..d1a5a273e1d0f 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -47,7 +47,7 @@ rest_total_hits_as_int: true index: "skip_shards_index,my_remote_cluster:single_doc_index" pre_filter_shard_size: 1 - ccs_minimize_roundstrips: false + ccs_minimize_roundtrips: false body: { "size" : 10, "query" : { "range" : { "created_at" : { "gte" : "2015-02-01", "lt": "2016-02-01"} } } } - match: { hits.total: 1 }