Skip to content

Commit 750a81d

Browse files
committed
Add new x-pack endpoints to asynchronously track the progress of a search
1 parent 0b9a9b4 commit 750a81d

File tree

43 files changed

+2579
-43
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2579
-43
lines changed

server/src/main/java/org/elasticsearch/action/search/SearchRequest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
* @see org.elasticsearch.client.Client#search(SearchRequest)
5757
* @see SearchResponse
5858
*/
59-
public final class SearchRequest extends ActionRequest implements IndicesRequest.Replaceable {
59+
public class SearchRequest extends ActionRequest implements IndicesRequest.Replaceable {
6060

6161
private static final ToXContent.Params FORMAT_PARAMS = new ToXContent.MapParams(Collections.singletonMap("pretty", "false"));
6262

server/src/main/java/org/elasticsearch/action/search/SearchRequestBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public SearchRequestBuilder setVersion(boolean version) {
213213
sourceBuilder().version(version);
214214
return this;
215215
}
216-
216+
217217
/**
218218
* Should each {@link org.elasticsearch.search.SearchHit} be returned with the
219219
* sequence number and primary term of the last modification of the document.

server/src/main/java/org/elasticsearch/client/node/NodeClient.java

-38
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@
2323
import org.elasticsearch.action.ActionRequest;
2424
import org.elasticsearch.action.ActionResponse;
2525
import org.elasticsearch.action.ActionType;
26-
import org.elasticsearch.action.search.SearchAction;
27-
import org.elasticsearch.action.search.SearchRequest;
28-
import org.elasticsearch.action.search.SearchResponse;
29-
import org.elasticsearch.action.search.SearchTask;
30-
import org.elasticsearch.action.search.SearchProgressActionListener;
31-
import org.elasticsearch.action.search.TransportSearchAction;
3226
import org.elasticsearch.action.support.TransportAction;
3327
import org.elasticsearch.client.Client;
3428
import org.elasticsearch.client.support.AbstractClient;
@@ -108,38 +102,6 @@ > Task executeLocally(ActionType<Response> action, Request request, TaskListener
108102
listener::onResponse, listener::onFailure);
109103
}
110104

111-
/**
112-
* Execute a {@link SearchRequest} locally and track the progress of the request through
113-
* a {@link SearchProgressActionListener}.
114-
*/
115-
public SearchTask executeSearchLocally(SearchRequest request, SearchProgressActionListener listener) {
116-
// we cannot track the progress if remote cluster requests are splitted.
117-
request.setCcsMinimizeRoundtrips(false);
118-
TransportSearchAction action = (TransportSearchAction) actions.get(SearchAction.INSTANCE);
119-
SearchTask task = (SearchTask) taskManager.register("transport", action.actionName, request);
120-
task.setProgressListener(listener);
121-
action.execute(task, request, new ActionListener<>() {
122-
@Override
123-
public void onResponse(SearchResponse response) {
124-
try {
125-
taskManager.unregister(task);
126-
} finally {
127-
listener.onResponse(response);
128-
}
129-
}
130-
131-
@Override
132-
public void onFailure(Exception e) {
133-
try {
134-
taskManager.unregister(task);
135-
} finally {
136-
listener.onFailure(e);
137-
}
138-
}
139-
});
140-
return task;
141-
}
142-
143105
/**
144106
* The id of the local {@link DiscoveryNode}. Useful for generating task ids from tasks returned by
145107
* {@link #executeLocally(ActionType, ActionRequest, TaskListener)}.

server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r
162162
searchRequest.routing(request.param("routing"));
163163
searchRequest.preference(request.param("preference"));
164164
searchRequest.indicesOptions(IndicesOptions.fromRequest(request, searchRequest.indicesOptions()));
165-
searchRequest.setCcsMinimizeRoundtrips(request.paramAsBoolean("ccs_minimize_roundtrips", true));
165+
searchRequest.setCcsMinimizeRoundtrips(request.paramAsBoolean("ccs_minimize_roundtrips", searchRequest.isCcsMinimizeRoundtrips()));
166166

167167
checkRestTotalHits(request, searchRequest);
168168
}

server/src/test/java/org/elasticsearch/action/search/SearchProgressActionListenerIT.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.elasticsearch.search.builder.SearchSourceBuilder;
3030
import org.elasticsearch.search.sort.FieldSortBuilder;
3131
import org.elasticsearch.search.sort.SortOrder;
32+
import org.elasticsearch.tasks.Task;
33+
import org.elasticsearch.tasks.TaskId;
3234
import org.elasticsearch.test.ESSingleNodeTestCase;
3335

3436
import java.util.ArrayList;
@@ -37,6 +39,7 @@
3739
import java.util.Comparator;
3840
import java.util.List;
3941
import java.util.Locale;
42+
import java.util.Map;
4043
import java.util.concurrent.CountDownLatch;
4144
import java.util.concurrent.atomic.AtomicInteger;
4245
import java.util.concurrent.atomic.AtomicReference;
@@ -189,7 +192,14 @@ public void onFailure(Exception e) {
189192
throw new AssertionError();
190193
}
191194
};
192-
client.executeSearchLocally(request, listener);
195+
client.executeLocally(SearchAction.INSTANCE, new SearchRequest(request) {
196+
@Override
197+
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) {
198+
SearchTask task = (SearchTask) super.createTask(id, type, action, parentTaskId, headers);
199+
task.setProgressListener(listener);
200+
return task;
201+
}
202+
}, listener);
193203
latch.await();
194204

195205
assertThat(shardsListener.get(), equalTo(expectedShards));

test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/DoSection.java

+1
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ private String formatStatusCodeMessage(ClientYamlTestResponse restTestResponse,
345345
private static Map<String, Tuple<String, org.hamcrest.Matcher<Integer>>> catches = new HashMap<>();
346346

347347
static {
348+
catches.put("not_modified", tuple("304", equalTo(304)));
348349
catches.put("bad_request", tuple("400", equalTo(400)));
349350
catches.put("unauthorized", tuple("401", equalTo(401)));
350351
catches.put("forbidden", tuple("403", equalTo(403)));
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
evaluationDependsOn(xpackModule('core'))
8+
9+
apply plugin: 'elasticsearch.esplugin'
10+
esplugin {
11+
name 'x-pack-async-search'
12+
description 'A module which allows to track the progress of a search asynchronously.'
13+
classname 'org.elasticsearch.xpack.search.AsyncSearch'
14+
extendedPlugins = ['x-pack-core']
15+
}
16+
archivesBaseName = 'x-pack-async-search'
17+
18+
compileJava.options.compilerArgs << "-Xlint:-rawtypes"
19+
compileTestJava.options.compilerArgs << "-Xlint:-rawtypes"
20+
21+
22+
dependencies {
23+
compileOnly project(":server")
24+
25+
compileOnly project(path: xpackModule('core'), configuration: 'default')
26+
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
27+
testCompile project(path: xpackModule('ilm'))
28+
}
29+
30+
integTest.enabled = false
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import org.elasticsearch.gradle.test.RestIntegTestTask
2+
3+
apply plugin: 'elasticsearch.build'
4+
test.enabled = false
5+
6+
dependencies {
7+
compile project(':test:framework')
8+
}
9+
10+
subprojects {
11+
project.tasks.withType(RestIntegTestTask) {
12+
final File xPackResources = new File(xpackProject('plugin').projectDir, 'src/test/resources')
13+
project.copyRestSpec.from(xPackResources) {
14+
include 'rest-api-spec/api/**'
15+
}
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import org.elasticsearch.gradle.test.RestIntegTestTask
2+
3+
apply plugin: 'elasticsearch.testclusters'
4+
apply plugin: 'elasticsearch.standalone-test'
5+
6+
dependencies {
7+
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
8+
testCompile project(path: xpackModule('async-search'), configuration: 'runtime')
9+
}
10+
11+
task restTest(type: RestIntegTestTask) {
12+
mustRunAfter(precommit)
13+
}
14+
15+
testClusters.restTest {
16+
testDistribution = 'DEFAULT'
17+
setting 'xpack.ml.enabled', 'false'
18+
setting 'xpack.monitoring.enabled', 'false'
19+
setting 'xpack.security.enabled', 'true'
20+
user username: 'async-search-user', password: 'async-search-password'
21+
}
22+
23+
check.dependsOn restTest
24+
test.enabled = false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.search;
8+
9+
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
10+
import org.elasticsearch.common.settings.SecureString;
11+
import org.elasticsearch.common.settings.Settings;
12+
import org.elasticsearch.common.util.concurrent.ThreadContext;
13+
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
14+
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
15+
16+
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
17+
18+
public class AsyncSearchRestIT extends ESClientYamlSuiteTestCase {
19+
20+
public AsyncSearchRestIT(final ClientYamlTestCandidate testCandidate) {
21+
super(testCandidate);
22+
}
23+
24+
@ParametersFactory
25+
public static Iterable<Object[]> parameters() throws Exception {
26+
return ESClientYamlSuiteTestCase.createParameters();
27+
}
28+
29+
@Override
30+
protected Settings restClientSettings() {
31+
final String userAuthHeaderValue = basicAuthHeaderValue("async-search-user",
32+
new SecureString("async-search-password".toCharArray()));
33+
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", userAuthHeaderValue).build();
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
"Async search":
3+
- do:
4+
indices.create:
5+
index: test-1
6+
7+
- do:
8+
indices.create:
9+
index: test-2
10+
11+
- do:
12+
indices.create:
13+
index: test-3
14+
15+
- do:
16+
index:
17+
index: test-2
18+
body: { max: 2 }
19+
20+
- do:
21+
index:
22+
index: test-1
23+
body: { max: 1 }
24+
25+
- do:
26+
index:
27+
index: test-3
28+
body: { max: 3 }
29+
30+
- do:
31+
indices.refresh: {}
32+
33+
- do:
34+
async_search.submit:
35+
index: test-*
36+
batched_reduce_size: 2
37+
body:
38+
query:
39+
match_all: {}
40+
aggs:
41+
1:
42+
max:
43+
field: max
44+
sort: max
45+
46+
- set: { id: id }
47+
48+
- do:
49+
async_search.get:
50+
id: "$id"
51+
wait_for_completion: 10s
52+
53+
- set: { version: version }
54+
- match: { version: 4 }
55+
- is_false: partial_response
56+
- length: { response.hits.hits: 3 }
57+
- match: { response.hits.hits.0._source.max: 1 }
58+
- match: { response.aggregations.1.value: 3.0 }
59+
60+
- do:
61+
catch: not_modified
62+
async_search.get:
63+
id: "$id"
64+
last_version: "$version"
65+
66+
- is_false: partial_response
67+
- is_false: response
68+
69+
- do:
70+
async_search.delete:
71+
id: "$id"
72+
73+
- match: { acknowledged: true }
74+
75+
- do:
76+
catch: missing
77+
async_search.delete:
78+
id: "$id"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.search;
7+
8+
import org.elasticsearch.test.rest.ESRestTestCase;
9+
10+
public class AsyncSearchRestTestCase extends ESRestTestCase {
11+
@Override
12+
protected boolean preserveClusterUponCompletion() {
13+
return true;
14+
}
15+
}

0 commit comments

Comments
 (0)