Backport of async search changes (#53976)

* Get Async Search: omit _clusters section when empty (#53907)

The _clusters section is omitted by the search API whenever no remote clusters are searched. Async search should do the same, but Get Async Search returns a deserialized response, hence a weird `_clusters` section with all values set to `0` gets returned instead. In fact the recreated Clusters object is not the same object as the EMPTY constant, yet it has the same content.

This commit addresses this by changing the comparison in the `toXContent` method to not print out the section if the number of total clusters is `0`.

* Async search: remove version from response (#53960)

The goal of the version field was to quickly show when you can expect to find something new in the search response, compared to when nothing has changed. This can also be done by looking at the `_shards` section and `num_reduce_phases` returned with the search response. In fact when there has been one or more additional reduction of the results, you can expect new results in the search response. Otherwise, the `_shards` section could notify of additional failures of shards that have completed the query, but that is not a guarantee that their results will be exposed (only when the following partial reduction is performed their results will be available).

That said this commit clarifies this in the docs and removes the version field from the async search response

* Async Search: replicas to auto expand from 0 to 1 (#53964)

This way single node clusters that are green don't go yellow once async search is used, while
all the others still have one replica.

* [DOCS] address timing issue in async search docs tests (#53910)

The docs snippets for submit async search have proven difficult to test as it is not possible to guarantee that you get a response that is not final, even when providing `wait_for_completion=0`. In the docs we want to show though a proper long-running query, and its first response should be partial rather than final.

With this commit we adapt the docs snippets to show a partial response, and replace under the hood all that's needed to make the snippets tests succeed when we get a final response. Also, increased the timeout so we always get a final response.

Closes #53887
Closes #53891
This commit is contained in:
Luca Cavanna 2020-03-23 19:13:31 +01:00 committed by GitHub
parent 965af3a68b
commit 932a7e3112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 92 deletions

View File

@ -41,7 +41,6 @@ import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpect
public class AsyncSearchResponse implements ToXContentObject { public class AsyncSearchResponse implements ToXContentObject {
@Nullable @Nullable
private final String id; private final String id;
private final int version;
@Nullable @Nullable
private final SearchResponse searchResponse; private final SearchResponse searchResponse;
@Nullable @Nullable
@ -55,15 +54,13 @@ public class AsyncSearchResponse implements ToXContentObject {
/** /**
* Creates an {@link AsyncSearchResponse} with the arguments that are always present in the server response * Creates an {@link AsyncSearchResponse} with the arguments that are always present in the server response
*/ */
AsyncSearchResponse(int version, AsyncSearchResponse(boolean isPartial,
boolean isPartial, boolean isRunning,
boolean isRunning, long startTimeMillis,
long startTimeMillis, long expirationTimeMillis,
long expirationTimeMillis, @Nullable String id,
@Nullable String id, @Nullable SearchResponse searchResponse,
@Nullable SearchResponse searchResponse, @Nullable ElasticsearchException error) {
@Nullable ElasticsearchException error) {
this.version = version;
this.isPartial = isPartial; this.isPartial = isPartial;
this.isRunning = isRunning; this.isRunning = isRunning;
this.startTimeMillis = startTimeMillis; this.startTimeMillis = startTimeMillis;
@ -81,13 +78,6 @@ public class AsyncSearchResponse implements ToXContentObject {
return id; return id;
} }
/**
* Returns the version of this response.
*/
public int getVersion() {
return version;
}
/** /**
* Returns the current {@link SearchResponse} or <code>null</code> if not available. * Returns the current {@link SearchResponse} or <code>null</code> if not available.
* *
@ -145,7 +135,6 @@ public class AsyncSearchResponse implements ToXContentObject {
if (id != null) { if (id != null) {
builder.field("id", id); builder.field("id", id);
} }
builder.field("version", version);
builder.field("is_partial", isPartial); builder.field("is_partial", isPartial);
builder.field("is_running", isRunning); builder.field("is_running", isRunning);
builder.field("start_time_in_millis", startTimeMillis); builder.field("start_time_in_millis", startTimeMillis);
@ -165,7 +154,6 @@ public class AsyncSearchResponse implements ToXContentObject {
} }
public static final ParseField ID_FIELD = new ParseField("id"); public static final ParseField ID_FIELD = new ParseField("id");
public static final ParseField VERSION_FIELD = new ParseField("version");
public static final ParseField IS_PARTIAL_FIELD = new ParseField("is_partial"); public static final ParseField IS_PARTIAL_FIELD = new ParseField("is_partial");
public static final ParseField IS_RUNNING_FIELD = new ParseField("is_running"); public static final ParseField IS_RUNNING_FIELD = new ParseField("is_running");
public static final ParseField START_TIME_FIELD = new ParseField("start_time_in_millis"); public static final ParseField START_TIME_FIELD = new ParseField("start_time_in_millis");
@ -176,16 +164,14 @@ public class AsyncSearchResponse implements ToXContentObject {
public static final ConstructingObjectParser<AsyncSearchResponse, Void> PARSER = new ConstructingObjectParser<>( public static final ConstructingObjectParser<AsyncSearchResponse, Void> PARSER = new ConstructingObjectParser<>(
"submit_async_search_response", true, "submit_async_search_response", true,
args -> new AsyncSearchResponse( args -> new AsyncSearchResponse(
(int) args[0], (boolean) args[0],
(boolean) args[1], (boolean) args[1],
(boolean) args[2], (long) args[2],
(long) args[3], (long) args[3],
(long) args[4], (String) args[4],
(String) args[5], (SearchResponse) args[5],
(SearchResponse) args[6], (ElasticsearchException) args[6]));
(ElasticsearchException) args[7]));
static { static {
PARSER.declareInt(constructorArg(), VERSION_FIELD);
PARSER.declareBoolean(constructorArg(), IS_PARTIAL_FIELD); PARSER.declareBoolean(constructorArg(), IS_PARTIAL_FIELD);
PARSER.declareBoolean(constructorArg(), IS_RUNNING_FIELD); PARSER.declareBoolean(constructorArg(), IS_RUNNING_FIELD);
PARSER.declareLong(constructorArg(), START_TIME_FIELD); PARSER.declareLong(constructorArg(), START_TIME_FIELD);
@ -203,7 +189,7 @@ public class AsyncSearchResponse implements ToXContentObject {
return SearchResponse.innerFromXContent(p); return SearchResponse.innerFromXContent(p);
} }
public static AsyncSearchResponse fromXContent(XContentParser parser) throws IOException { public static AsyncSearchResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null); return PARSER.apply(parser, null);
} }

View File

@ -40,7 +40,6 @@ public class AsyncSearchIT extends ESRestHighLevelClientTestCase {
// 15 sec should be enough to make sure we always complete right away // 15 sec should be enough to make sure we always complete right away
request.setWaitForCompletion(new TimeValue(15, TimeUnit.SECONDS)); request.setWaitForCompletion(new TimeValue(15, TimeUnit.SECONDS));
AsyncSearchResponse response = highLevelClient().asyncSearch().submitAsyncSearch(request, RequestOptions.DEFAULT); AsyncSearchResponse response = highLevelClient().asyncSearch().submitAsyncSearch(request, RequestOptions.DEFAULT);
assertTrue(response.getVersion() >= 0);
assertFalse(response.isPartial()); assertFalse(response.isPartial());
assertTrue(response.getStartTime() > 0); assertTrue(response.getStartTime() > 0);
assertTrue(response.getExpirationTime() > 0); assertTrue(response.getExpirationTime() > 0);

View File

@ -36,7 +36,6 @@ public class AsyncSearchResponseTests
@Override @Override
protected org.elasticsearch.xpack.core.search.action.AsyncSearchResponse createServerTestInstance(XContentType xContentType) { protected org.elasticsearch.xpack.core.search.action.AsyncSearchResponse createServerTestInstance(XContentType xContentType) {
int version = randomIntBetween(0, Integer.MAX_VALUE);
boolean isPartial = randomBoolean(); boolean isPartial = randomBoolean();
boolean isRunning = randomBoolean(); boolean isRunning = randomBoolean();
long startTimeMillis = randomLongBetween(0, Long.MAX_VALUE); long startTimeMillis = randomLongBetween(0, Long.MAX_VALUE);
@ -48,7 +47,7 @@ public class AsyncSearchResponseTests
: new SearchResponse(InternalSearchResponse.empty(), randomAlphaOfLength(10), 1, 1, 0, randomIntBetween(0, 10000), : new SearchResponse(InternalSearchResponse.empty(), randomAlphaOfLength(10), 1, 1, 0, randomIntBetween(0, 10000),
ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY); ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY);
org.elasticsearch.xpack.core.search.action.AsyncSearchResponse testResponse = org.elasticsearch.xpack.core.search.action.AsyncSearchResponse testResponse =
new org.elasticsearch.xpack.core.search.action.AsyncSearchResponse(id, version, searchResponse, error, isPartial, isRunning, new org.elasticsearch.xpack.core.search.action.AsyncSearchResponse(id, searchResponse, error, isPartial, isRunning,
startTimeMillis, expirationTimeMillis); startTimeMillis, expirationTimeMillis);
return testResponse; return testResponse;
} }
@ -62,7 +61,6 @@ public class AsyncSearchResponseTests
protected void assertInstances(org.elasticsearch.xpack.core.search.action.AsyncSearchResponse expected, AsyncSearchResponse parsed) { protected void assertInstances(org.elasticsearch.xpack.core.search.action.AsyncSearchResponse expected, AsyncSearchResponse parsed) {
assertNotSame(parsed, expected); assertNotSame(parsed, expected);
assertEquals(expected.getId(), parsed.getId()); assertEquals(expected.getId(), parsed.getId());
assertEquals(expected.getVersion(), parsed.getVersion());
assertEquals(expected.isRunning(), parsed.isRunning()); assertEquals(expected.isRunning(), parsed.isRunning());
assertEquals(expected.isPartial(), parsed.isPartial()); assertEquals(expected.isPartial(), parsed.isPartial());
assertEquals(expected.getStartTime(), parsed.getStartTime()); assertEquals(expected.getStartTime(), parsed.getStartTime());

View File

@ -30,9 +30,8 @@ POST /sales*/_async_search?size=0
} }
} }
-------------------------------------------------- --------------------------------------------------
// TEST[skip:"AwaitsFix https://github.com/elastic/elasticsearch/issues/53891"]
// TEST[setup:sales] // TEST[setup:sales]
// TEST[s/size=0/size=0&wait_for_completion=0/] // TEST[s/size=0/size=0&wait_for_completion=10s&clean_on_completion=false/]
The response contains an identifier of the search being executed. The response contains an identifier of the search being executed.
You can use this ID to later retrieve the search's final results. You can use this ID to later retrieve the search's final results.
@ -43,7 +42,6 @@ results are returned as part of the <<search-api-response-body,`response`>> obje
-------------------------------------------------- --------------------------------------------------
{ {
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=", <1> "id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=", <1>
"version" : 0,
"is_partial" : true, <2> "is_partial" : true, <2>
"is_running" : true, <3> "is_running" : true, <3>
"start_time_in_millis" : 1583945890986, "start_time_in_millis" : 1583945890986,
@ -70,12 +68,17 @@ results are returned as part of the <<search-api-response-body,`response`>> obje
} }
-------------------------------------------------- --------------------------------------------------
// TESTRESPONSE[s/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=/$body.id/] // TESTRESPONSE[s/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=/$body.id/]
// TESTRESPONSE[s/"is_partial" : true/"is_partial": $body.is_partial/]
// TESTRESPONSE[s/"is_running" : true/"is_running": $body.is_running/]
// TESTRESPONSE[s/1583945890986/$body.start_time_in_millis/] // TESTRESPONSE[s/1583945890986/$body.start_time_in_millis/]
// TESTRESPONSE[s/1584377890986/$body.expiration_time_in_millis/] // TESTRESPONSE[s/1584377890986/$body.expiration_time_in_millis/]
// TESTRESPONSE[s/"took" : 1122/"took": $body.response.took/] // TESTRESPONSE[s/"took" : 1122/"took": $body.response.took/]
// TESTRESPONSE[s/"num_reduce_phases" : 0,//]
// TESTRESPONSE[s/"total" : 562/"total": $body.response._shards.total/] // TESTRESPONSE[s/"total" : 562/"total": $body.response._shards.total/]
// TESTRESPONSE[s/"successful" : 3/"successful": $body.response._shards.successful/] // TESTRESPONSE[s/"successful" : 3/"successful": $body.response._shards.successful/]
// TESTRESPONSE[s/"value" : 157483/"value": $body.response.hits.total.value/] // TESTRESPONSE[s/"value" : 157483/"value": $body.response.hits.total.value/]
// TESTRESPONSE[s/"relation" : "gte"/"relation": $body.response.hits.total.relation/]
// TESTRESPONSE[s/"hits" : \[ \]\n\s\s\s\s\}/"hits" : \[\]},"aggregations": $body.response.aggregations/]
<1> Identifier of the async search that can be used to monitor its progress, retrieve its results, and/or delete it. <1> Identifier of the async search that can be used to monitor its progress, retrieve its results, and/or delete it.
<2> Whether the returned search results are partial or final <2> Whether the returned search results are partial or final
@ -135,17 +138,16 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
-------------------------------------------------- --------------------------------------------------
{ {
"id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=", "id" : "FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsdzoxMDc=",
"version" : 2, <1> "is_partial" : true, <1>
"is_partial" : true, <2> "is_running" : true, <2>
"is_running" : true, <3>
"start_time_in_millis" : 1583945890986, "start_time_in_millis" : 1583945890986,
"expiration_time_in_millis" : 1584377890986, <4> "expiration_time_in_millis" : 1584377890986, <3>
"response" : { "response" : {
"took" : 12144, "took" : 12144,
"timed_out" : false, "timed_out" : false,
"num_reduce_phases" : 38, "num_reduce_phases" : 46, <4>
"_shards" : { "_shards" : {
"total" : 562, "total" : 562, <5>
"successful" : 188, "successful" : 188,
"skipped" : 0, "skipped" : 0,
"failed" : 0 "failed" : 0
@ -158,7 +160,7 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
"max_score" : null, "max_score" : null,
"hits" : [ ] "hits" : [ ]
}, },
"aggregations" : { <5> "aggregations" : { <6>
"sale_date" : { "sale_date" : {
"buckets" : [] "buckets" : []
} }
@ -176,17 +178,18 @@ GET /_async_search/FmRldE8zREVEUzA2ZVpUeGs2ejJFUFEaMkZ5QTVrSTZSaVN3WlNFVmtlWHJsd
// TESTRESPONSE[s/"successful" : 188/"successful": $body.response._shards.successful/] // TESTRESPONSE[s/"successful" : 188/"successful": $body.response._shards.successful/]
// TESTRESPONSE[s/"value" : 456433/"value": $body.response.hits.total.value/] // TESTRESPONSE[s/"value" : 456433/"value": $body.response.hits.total.value/]
// TESTRESPONSE[s/"buckets" : \[\]/"buckets": $body.response.aggregations.sale_date.buckets/] // TESTRESPONSE[s/"buckets" : \[\]/"buckets": $body.response.aggregations.sale_date.buckets/]
// TESTRESPONSE[s/"num_reduce_phases" : 38,//] // TESTRESPONSE[s/"num_reduce_phases" : 46,//]
<1> The returned `version` is useful to identify whether the response contains <1> Whether the returned search results are partial or final
additional results compared to previously obtained responses. If the version <2> Whether the search is still being executed or it has completed
stays the same, no new results have become available, otherwise a higher version <3> When the async search will expire
number indicates that more shards have completed their execution of the query <4> Indicates how many reduction of the results have been performed. If this
and their partial results are also included in the response. number increases compared to the last retrieved results, you can expect
<2> Whether the returned search results are partial or final additional results included in the search response
<3> Whether the search is still being executed or it has completed <5> Indicates how many shards have executed the query. Note that in order for
<4> When the async search will expire shard results to be included in the search response, they need to be reduced
<5> Partial aggregations results, coming from the shards that have already first.
<6> Partial aggregations results, coming from the shards that have already
completed the execution of the query. completed the execution of the query.
The `wait_for_completion` parameter, which defaults to `1`, can also be provided The `wait_for_completion` parameter, which defaults to `1`, can also be provided

View File

@ -420,9 +420,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
} }
private Clusters(StreamInput in) throws IOException { private Clusters(StreamInput in) throws IOException {
this.total = in.readVInt(); this(in.readVInt(), in.readVInt(), in.readVInt());
this.successful = in.readVInt();
this.skipped = in.readVInt();
} }
@Override @Override
@ -434,7 +432,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (this != EMPTY) { if (total > 0) {
builder.startObject(_CLUSTERS_FIELD.getPreferredName()); builder.startObject(_CLUSTERS_FIELD.getPreferredName());
builder.field(TOTAL_FIELD.getPreferredName(), total); builder.field(TOTAL_FIELD.getPreferredName(), total);
builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful); builder.field(SUCCESSFUL_FIELD.getPreferredName(), successful);

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text; import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
@ -292,4 +293,13 @@ public class SearchResponseTests extends ESTestCase {
assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards()); assertEquals(searchResponse.getSkippedShards(), deserialized.getSkippedShards());
assertEquals(searchResponse.getClusters(), deserialized.getClusters()); assertEquals(searchResponse.getClusters(), deserialized.getClusters());
} }
public void testToXContentEmptyClusters() throws IOException {
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), null, 1, 1, 0, 1,
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
SearchResponse deserialized = copyWriteable(searchResponse, namedWriteableRegistry, SearchResponse::new, Version.CURRENT);
XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent());
deserialized.getClusters().toXContent(builder, ToXContent.EMPTY_PARAMS);
assertEquals(0, Strings.toString(builder).length());
}
} }

View File

@ -66,14 +66,14 @@ class AsyncSearchIndexService {
public static final String EXPIRATION_TIME_FIELD = "expiration_time"; public static final String EXPIRATION_TIME_FIELD = "expiration_time";
public static final String RESULT_FIELD = "result"; public static final String RESULT_FIELD = "result";
public static Settings settings() { private static Settings settings() {
return Settings.builder() return Settings.builder()
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1) .put(IndexMetaData.SETTING_AUTO_EXPAND_REPLICAS, "0-1")
.build(); .build();
} }
public static XContentBuilder mappings() throws IOException { private static XContentBuilder mappings() throws IOException {
XContentBuilder builder = jsonBuilder() XContentBuilder builder = jsonBuilder()
.startObject() .startObject()
.startObject(SINGLE_MAPPING_NAME) .startObject(SINGLE_MAPPING_NAME)

View File

@ -40,7 +40,6 @@ class MutableSearchResponse {
private final AtomicArray<ShardSearchFailure> shardFailures; private final AtomicArray<ShardSearchFailure> shardFailures;
private final Supplier<InternalAggregation.ReduceContext> aggReduceContextSupplier; private final Supplier<InternalAggregation.ReduceContext> aggReduceContextSupplier;
private int version;
private boolean isPartial; private boolean isPartial;
private boolean isFinalReduce; private boolean isFinalReduce;
private int successfulShards; private int successfulShards;
@ -63,7 +62,6 @@ class MutableSearchResponse {
this.skippedShards = skippedShards; this.skippedShards = skippedShards;
this.clusters = clusters; this.clusters = clusters;
this.aggReduceContextSupplier = aggReduceContextSupplier; this.aggReduceContextSupplier = aggReduceContextSupplier;
this.version = 0;
this.shardFailures = totalShards == -1 ? null : new AtomicArray<>(totalShards-skippedShards); this.shardFailures = totalShards == -1 ? null : new AtomicArray<>(totalShards-skippedShards);
this.isPartial = true; this.isPartial = true;
this.sections = totalShards == -1 ? null : new InternalSearchResponse( this.sections = totalShards == -1 ? null : new InternalSearchResponse(
@ -83,7 +81,6 @@ class MutableSearchResponse {
throw new IllegalStateException("received partial response out of order: " throw new IllegalStateException("received partial response out of order: "
+ newSections.getNumReducePhases() + " < " + sections.getNumReducePhases()); + newSections.getNumReducePhases() + " < " + sections.getNumReducePhases());
} }
++ version;
this.successfulShards = successfulShards; this.successfulShards = successfulShards;
this.sections = newSections; this.sections = newSections;
this.isPartial = true; this.isPartial = true;
@ -96,7 +93,6 @@ class MutableSearchResponse {
*/ */
synchronized void updateFinalResponse(int successfulShards, SearchResponseSections newSections) { synchronized void updateFinalResponse(int successfulShards, SearchResponseSections newSections) {
failIfFrozen(); failIfFrozen();
++ version;
this.successfulShards = successfulShards; this.successfulShards = successfulShards;
this.sections = newSections; this.sections = newSections;
this.isPartial = false; this.isPartial = false;
@ -110,7 +106,6 @@ class MutableSearchResponse {
*/ */
synchronized void updateWithFailure(Exception exc) { synchronized void updateWithFailure(Exception exc) {
failIfFrozen(); failIfFrozen();
++ version;
this.isPartial = true; this.isPartial = true;
this.failure = ElasticsearchException.guessRootCauses(exc)[0]; this.failure = ElasticsearchException.guessRootCauses(exc)[0];
this.frozen = true; this.frozen = true;
@ -147,7 +142,7 @@ class MutableSearchResponse {
} else { } else {
resp = null; resp = null;
} }
return new AsyncSearchResponse(task.getSearchId().getEncoded(), version, resp, failure, isPartial, return new AsyncSearchResponse(task.getSearchId().getEncoded(), resp, failure, isPartial,
frozen == false, task.getStartTime(), expirationTime); frozen == false, task.getStartTime(), expirationTime);
} }
@ -159,7 +154,7 @@ class MutableSearchResponse {
private ShardSearchFailure[] buildShardFailures() { private ShardSearchFailure[] buildShardFailures() {
if (shardFailures == null) { if (shardFailures == null) {
return new ShardSearchFailure[0]; return ShardSearchFailure.EMPTY_ARRAY;
} }
List<ShardSearchFailure> failures = new ArrayList<>(); List<ShardSearchFailure> failures = new ArrayList<>();
for (int i = 0; i < shardFailures.length(); i++) { for (int i = 0; i < shardFailures.length(); i++) {

View File

@ -5,6 +5,10 @@
*/ */
package org.elasticsearch.xpack.search; package org.elasticsearch.xpack.search;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -17,6 +21,7 @@ import org.junit.Before;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.assertEqualResponses; import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.assertEqualResponses;
import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.randomAsyncSearchResponse; import static org.elasticsearch.xpack.search.AsyncSearchResponseTests.randomAsyncSearchResponse;
@ -100,4 +105,15 @@ public class AsyncSearchIndexServiceTests extends ESSingleNodeTestCase {
assertFalse(indexService.ensureAuthenticatedUserIsSame(original, runAsDiffType)); assertFalse(indexService.ensureAuthenticatedUserIsSame(original, runAsDiffType));
assertFalse(indexService.ensureAuthenticatedUserIsSame(threadContext.getHeaders(), runAsDiffType)); assertFalse(indexService.ensureAuthenticatedUserIsSame(threadContext.getHeaders(), runAsDiffType));
} }
public void testSettings() throws ExecutionException, InterruptedException {
PlainActionFuture<Void> future = PlainActionFuture.newFuture();
indexService.createIndexIfNecessary(future);
future.get();
GetIndexResponse getIndexResponse = client().admin().indices().getIndex(
new GetIndexRequest().indices(AsyncSearchIndexService.INDEX)).actionGet();
Settings settings = getIndexResponse.getSettings().get(AsyncSearchIndexService.INDEX);
assertEquals("1", settings.get(IndexMetaData.SETTING_NUMBER_OF_SHARDS));
assertEquals("0-1", settings.get(IndexMetaData.SETTING_AUTO_EXPAND_REPLICAS));
}
} }

View File

@ -91,15 +91,15 @@ public class AsyncSearchResponseTests extends ESTestCase {
int rand = randomIntBetween(0, 2); int rand = randomIntBetween(0, 2);
switch (rand) { switch (rand) {
case 0: case 0:
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), randomBoolean(), return new AsyncSearchResponse(searchId, randomBoolean(),
randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong()); randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
case 1: case 1:
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), searchResponse, null, return new AsyncSearchResponse(searchId, searchResponse, null,
randomBoolean(), randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong()); randomBoolean(), randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong());
case 2: case 2:
return new AsyncSearchResponse(searchId, randomIntBetween(0, Integer.MAX_VALUE), searchResponse, return new AsyncSearchResponse(searchId, searchResponse,
new ElasticsearchException(new IOException("boum")), randomBoolean(), randomBoolean(), new ElasticsearchException(new IOException("boum")), randomBoolean(), randomBoolean(),
randomNonNegativeLong(), randomNonNegativeLong()); randomNonNegativeLong(), randomNonNegativeLong());
@ -120,7 +120,6 @@ public class AsyncSearchResponseTests extends ESTestCase {
static void assertEqualResponses(AsyncSearchResponse expected, AsyncSearchResponse actual) { static void assertEqualResponses(AsyncSearchResponse expected, AsyncSearchResponse actual) {
assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getVersion(), actual.getVersion());
assertEquals(expected.status(), actual.status()); assertEquals(expected.status(), actual.status());
assertEquals(expected.getFailure() == null, actual.getFailure() == null); assertEquals(expected.getFailure() == null, actual.getFailure() == null);
assertEquals(expected.isRunning(), actual.isRunning()); assertEquals(expected.isRunning(), actual.isRunning());

View File

@ -25,7 +25,6 @@ import static org.elasticsearch.rest.RestStatus.OK;
public class AsyncSearchResponse extends ActionResponse implements StatusToXContentObject { public class AsyncSearchResponse extends ActionResponse implements StatusToXContentObject {
@Nullable @Nullable
private final String id; private final String id;
private final int version;
@Nullable @Nullable
private final SearchResponse searchResponse; private final SearchResponse searchResponse;
@Nullable @Nullable
@ -40,19 +39,17 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
* Creates an {@link AsyncSearchResponse} with meta-information only (not-modified). * Creates an {@link AsyncSearchResponse} with meta-information only (not-modified).
*/ */
public AsyncSearchResponse(String id, public AsyncSearchResponse(String id,
int version,
boolean isPartial, boolean isPartial,
boolean isRunning, boolean isRunning,
long startTimeMillis, long startTimeMillis,
long expirationTimeMillis) { long expirationTimeMillis) {
this(id, version, null, null, isPartial, isRunning, startTimeMillis, expirationTimeMillis); this(id, null, null, isPartial, isRunning, startTimeMillis, expirationTimeMillis);
} }
/** /**
* Creates a new {@link AsyncSearchResponse} * Creates a new {@link AsyncSearchResponse}
* *
* @param id The id of the search for further retrieval, <code>null</code> if not stored. * @param id The id of the search for further retrieval, <code>null</code> if not stored.
* @param version The version number of this response.
* @param searchResponse The actual search response. * @param searchResponse The actual search response.
* @param error The error if the search failed, <code>null</code> if the search is running * @param error The error if the search failed, <code>null</code> if the search is running
* or has completed without failure. * or has completed without failure.
@ -61,7 +58,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
* @param startTimeMillis The start date of the search in milliseconds since epoch. * @param startTimeMillis The start date of the search in milliseconds since epoch.
*/ */
public AsyncSearchResponse(String id, public AsyncSearchResponse(String id,
int version,
SearchResponse searchResponse, SearchResponse searchResponse,
ElasticsearchException error, ElasticsearchException error,
boolean isPartial, boolean isPartial,
@ -69,7 +65,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
long startTimeMillis, long startTimeMillis,
long expirationTimeMillis) { long expirationTimeMillis) {
this.id = id; this.id = id;
this.version = version;
this.error = error; this.error = error;
this.searchResponse = searchResponse; this.searchResponse = searchResponse;
this.isPartial = isPartial; this.isPartial = isPartial;
@ -80,7 +75,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
public AsyncSearchResponse(StreamInput in) throws IOException { public AsyncSearchResponse(StreamInput in) throws IOException {
this.id = in.readOptionalString(); this.id = in.readOptionalString();
this.version = in.readVInt();
this.error = in.readOptionalWriteable(ElasticsearchException::new); this.error = in.readOptionalWriteable(ElasticsearchException::new);
this.searchResponse = in.readOptionalWriteable(SearchResponse::new); this.searchResponse = in.readOptionalWriteable(SearchResponse::new);
this.isPartial = in.readBoolean(); this.isPartial = in.readBoolean();
@ -92,7 +86,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalString(id); out.writeOptionalString(id);
out.writeVInt(version);
out.writeOptionalWriteable(error); out.writeOptionalWriteable(error);
out.writeOptionalWriteable(searchResponse); out.writeOptionalWriteable(searchResponse);
out.writeBoolean(isPartial); out.writeBoolean(isPartial);
@ -102,7 +95,7 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
} }
public AsyncSearchResponse clone(String id) { public AsyncSearchResponse clone(String id) {
return new AsyncSearchResponse(id, version, searchResponse, error, isPartial, false, startTimeMillis, expirationTimeMillis); return new AsyncSearchResponse(id, searchResponse, error, isPartial, false, startTimeMillis, expirationTimeMillis);
} }
/** /**
@ -113,13 +106,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
return id; return id;
} }
/**
* Returns the version of this response.
*/
public int getVersion() {
return version;
}
/** /**
* Returns the current {@link SearchResponse} or <code>null</code> if not available. * Returns the current {@link SearchResponse} or <code>null</code> if not available.
* *
@ -189,7 +175,6 @@ public class AsyncSearchResponse extends ActionResponse implements StatusToXCont
if (id != null) { if (id != null) {
builder.field("id", id); builder.field("id", id);
} }
builder.field("version", version);
builder.field("is_partial", isPartial); builder.field("is_partial", isPartial);
builder.field("is_running", isRunning); builder.field("is_running", isRunning);
builder.field("start_time_in_millis", startTimeMillis); builder.field("start_time_in_millis", startTimeMillis);

View File

@ -54,7 +54,6 @@
sort: max sort: max
- is_false: id - is_false: id
- match: { version: 6 }
- match: { is_partial: false } - match: { is_partial: false }
- length: { response.hits.hits: 3 } - length: { response.hits.hits: 3 }
- match: { response.hits.hits.0._source.max: 1 } - match: { response.hits.hits.0._source.max: 1 }
@ -73,8 +72,6 @@
field: max field: max
sort: max sort: max
- set: { id: id }
- match: { version: 6 }
- match: { is_partial: false } - match: { is_partial: false }
- length: { response.hits.hits: 3 } - length: { response.hits.hits: 3 }
- match: { response.hits.hits.0._source.max: 1 } - match: { response.hits.hits.0._source.max: 1 }
@ -96,8 +93,8 @@
sort: max sort: max
- set: { id: id } - set: { id: id }
- match: { version: 6 }
- match: { is_partial: false } - match: { is_partial: false }
- is_false: response._clusters
- length: { response.hits.hits: 3 } - length: { response.hits.hits: 3 }
- match: { response.hits.hits.0._source.max: 1 } - match: { response.hits.hits.0._source.max: 1 }
- match: { response.aggregations.max#max.value: 3.0 } - match: { response.aggregations.max#max.value: 3.0 }
@ -106,8 +103,8 @@
async_search.get: async_search.get:
id: "$id" id: "$id"
- match: { version: 6 }
- match: { is_partial: false } - match: { is_partial: false }
- is_false: response._clusters
- length: { response.hits.hits: 3 } - length: { response.hits.hits: 3 }
- match: { response.hits.hits.0._source.max: 1 } - match: { response.hits.hits.0._source.max: 1 }
- match: { response.aggregations.max.value: 3.0 } - match: { response.aggregations.max.value: 3.0 }
@ -118,8 +115,8 @@
id: "$id" id: "$id"
typed_keys: true typed_keys: true
- match: { version: 6 }
- match: { is_partial: false } - match: { is_partial: false }
- is_false: response._clusters
- length: { response.hits.hits: 3 } - length: { response.hits.hits: 3 }
- match: { response.hits.hits.0._source.max: 1 } - match: { response.hits.hits.0._source.max: 1 }
- match: { response.aggregations.max#max.value: 3.0 } - match: { response.aggregations.max#max.value: 3.0 }