Skip to content

Commit c75ac31

Browse files
authored
Add ability to associate an ID with tasks (#27764)
Adds support for capturing the X-Opaque-Id header from a REST request and storing it's value in the tasks that this request started. It works for all user-initiated tasks (not only search). Closes #23250 Usage: ``` $ curl -H "X-Opaque-Id: imotov" -H "foo:bar" "localhost:9200/_tasks?pretty&group_by=parents" { "tasks" : { "7qrTVbiDQKiZfubUP7DPkg:6998" : { "node" : "7qrTVbiDQKiZfubUP7DPkg", "id" : 6998, "type" : "transport", "action" : "cluster:monitor/tasks/lists", "start_time_in_millis" : 1513029940042, "running_time_in_nanos" : 266794, "cancellable" : false, "headers" : { "X-Opaque-Id" : "imotov" }, "children" : [ { "node" : "V-PuCjPhRp2ryuEsNw6V1g", "id" : 6088, "type" : "netty", "action" : "cluster:monitor/tasks/lists[n]", "start_time_in_millis" : 1513029940043, "running_time_in_nanos" : 67785, "cancellable" : false, "parent_task_id" : "7qrTVbiDQKiZfubUP7DPkg:6998", "headers" : { "X-Opaque-Id" : "imotov" } }, { "node" : "7qrTVbiDQKiZfubUP7DPkg", "id" : 6999, "type" : "direct", "action" : "cluster:monitor/tasks/lists[n]", "start_time_in_millis" : 1513029940043, "running_time_in_nanos" : 98754, "cancellable" : false, "parent_task_id" : "7qrTVbiDQKiZfubUP7DPkg:6998", "headers" : { "X-Opaque-Id" : "imotov" } } ] } } } ```
1 parent 6a5807a commit c75ac31

File tree

84 files changed

+627
-180
lines changed

Some content is hidden

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

84 files changed

+627
-180
lines changed

docs/reference/cluster/tasks.asciidoc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,71 @@ The following command will change the grouping to parent tasks:
195195
GET _tasks?group_by=parents
196196
--------------------------------------------------
197197
// CONSOLE
198+
199+
The grouping can be disabled by specifying `none` as a `group_by` parameter:
200+
201+
[source,js]
202+
--------------------------------------------------
203+
GET _tasks?group_by=none
204+
--------------------------------------------------
205+
// CONSOLE
206+
207+
[float]
208+
=== Identifying running tasks
209+
210+
The `X-Opaque-Id` header, when provided on the HTTP request header, is going to be returned as a header in the response as well as
211+
in the `headers` field for in the task information. This allows to track certain calls, or associate certain tasks with
212+
a the client that started them:
213+
214+
[source,sh]
215+
--------------------------------------------------
216+
curl -i -H "X-Opaque-Id: 123456" "http://localhost:9200/_tasks?group_by=parents"
217+
--------------------------------------------------
218+
// NOTCONSOLE
219+
220+
The result will look similar to the following:
221+
222+
[source,js]
223+
--------------------------------------------------
224+
HTTP/1.1 200 OK
225+
X-Opaque-Id: 123456 <1>
226+
content-type: application/json; charset=UTF-8
227+
content-length: 831
228+
229+
{
230+
"tasks" : {
231+
"u5lcZHqcQhu-rUoFaqDphA:45" : {
232+
"node" : "u5lcZHqcQhu-rUoFaqDphA",
233+
"id" : 45,
234+
"type" : "transport",
235+
"action" : "cluster:monitor/tasks/lists",
236+
"start_time_in_millis" : 1513823752749,
237+
"running_time_in_nanos" : 293139,
238+
"cancellable" : false,
239+
"headers" : {
240+
"X-Opaque-Id" : "123456" <2>
241+
},
242+
"children" : [
243+
{
244+
"node" : "u5lcZHqcQhu-rUoFaqDphA",
245+
"id" : 46,
246+
"type" : "direct",
247+
"action" : "cluster:monitor/tasks/lists[n]",
248+
"start_time_in_millis" : 1513823752750,
249+
"running_time_in_nanos" : 92133,
250+
"cancellable" : false,
251+
"parent_task_id" : "u5lcZHqcQhu-rUoFaqDphA:45",
252+
"headers" : {
253+
"X-Opaque-Id" : "123456" <3>
254+
}
255+
}
256+
]
257+
}
258+
}
259+
}
260+
--------------------------------------------------
261+
// NOTCONSOLE
262+
263+
<1> id as a part of the response header
264+
<2> id for the tasks that was initiated by the REST request
265+
<3> the child task of the task initiated by the REST request

modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import org.junit.Before;
8282

8383
import java.util.ArrayList;
84+
import java.util.Collections;
8485
import java.util.HashMap;
8586
import java.util.IdentityHashMap;
8687
import java.util.Iterator;
@@ -123,6 +124,7 @@ public class AsyncBulkByScrollActionTests extends ESTestCase {
123124
private SearchRequest firstSearchRequest;
124125
private PlainActionFuture<BulkByScrollResponse> listener;
125126
private String scrollId;
127+
private ThreadPool threadPool;
126128
private TaskManager taskManager;
127129
private BulkByScrollTask testTask;
128130
private WorkerBulkByScrollTaskState worker;
@@ -141,7 +143,8 @@ public void setupForTest() {
141143
testRequest = new DummyAbstractBulkByScrollRequest(firstSearchRequest);
142144
listener = new PlainActionFuture<>();
143145
scrollId = null;
144-
taskManager = new TaskManager(Settings.EMPTY);
146+
threadPool = new TestThreadPool(getClass().getName());
147+
taskManager = new TaskManager(Settings.EMPTY, threadPool, Collections.emptySet());
145148
testTask = (BulkByScrollTask) taskManager.register("don'tcare", "hereeither", testRequest);
146149
testTask.setWorker(testRequest.getRequestsPerSecond(), null);
147150
worker = testTask.getWorkerState();
@@ -159,8 +162,9 @@ private void setupClient(ThreadPool threadPool) {
159162
}
160163

161164
@After
162-
public void tearDownAndVerifyCommonStuff() {
165+
public void tearDownAndVerifyCommonStuff() throws Exception {
163166
client.close();
167+
terminate(threadPool);
164168
}
165169

166170
/**

modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.mockito.ArgumentCaptor;
3333

3434
import java.util.ArrayList;
35+
import java.util.Collections;
3536
import java.util.List;
3637
import java.util.function.Consumer;
3738

@@ -53,7 +54,7 @@ public class TransportRethrottleActionTests extends ESTestCase {
5354
@Before
5455
public void createTask() {
5556
slices = between(2, 50);
56-
task = new BulkByScrollTask(1, "test_type", "test_action", "test", TaskId.EMPTY_TASK_ID);
57+
task = new BulkByScrollTask(1, "test_type", "test_action", "test", TaskId.EMPTY_TASK_ID, Collections.emptyMap());
5758
task.setWorkerCount(slices);
5859
}
5960

@@ -101,7 +102,8 @@ public void testRethrottleSuccessfulResponse() {
101102
List<BulkByScrollTask.StatusOrException> sliceStatuses = new ArrayList<>(slices);
102103
for (int i = 0; i < slices; i++) {
103104
BulkByScrollTask.Status status = believeableInProgressStatus(i);
104-
tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId())));
105+
tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId()),
106+
Collections.emptyMap()));
105107
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
106108
}
107109
rethrottleTestCase(slices,
@@ -121,7 +123,8 @@ public void testRethrottleWithSomeSucceeded() {
121123
List<TaskInfo> tasks = new ArrayList<>();
122124
for (int i = succeeded; i < slices; i++) {
123125
BulkByScrollTask.Status status = believeableInProgressStatus(i);
124-
tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId())));
126+
tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId()),
127+
Collections.emptyMap()));
125128
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
126129
}
127130
rethrottleTestCase(slices - succeeded,

modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected Version getCurrentVersion() {
7373
}
7474
};
7575
MockTransportService mockTransportService =
76-
MockTransportService.createNewService(Settings.EMPTY, transport, version, threadPool, clusterSettings);
76+
MockTransportService.createNewService(Settings.EMPTY, transport, version, threadPool, clusterSettings, Collections.emptySet());
7777
mockTransportService.start();
7878
return mockTransportService;
7979
}

plugins/transport-nio/src/test/java/org/elasticsearch/transport/nio/SimpleNioTransportTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ protected Version getCurrentVersion() {
7777
}
7878
};
7979
MockTransportService mockTransportService =
80-
MockTransportService.createNewService(Settings.EMPTY, transport, version, threadPool, clusterSettings);
80+
MockTransportService.createNewService(Settings.EMPTY, transport, version, threadPool, clusterSettings, Collections.emptySet());
8181
mockTransportService.start();
8282
return mockTransportService;
8383
}

qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,44 @@
212212
field3: value
213213
- match: { hits.total: 1 }
214214
- match: { hits.hits.0._id: q3 }
215+
216+
---
217+
"Create a task result record in the old cluster":
218+
- do:
219+
indices.create:
220+
index: reindexed_index
221+
body:
222+
settings:
223+
index:
224+
number_of_replicas: 0
225+
- do:
226+
bulk:
227+
refresh: true
228+
body:
229+
- '{"index": {"_index": "reindexed_index", "_type": "doc"}}'
230+
- '{"f1": "1"}'
231+
- '{"index": {"_index": "reindexed_index", "_type": "doc"}}'
232+
- '{"f1": "2"}'
233+
- '{"index": {"_index": "reindexed_index", "_type": "doc"}}'
234+
- '{"f1": "3"}'
235+
- '{"index": {"_index": "reindexed_index", "_type": "doc"}}'
236+
- '{"f1": "4"}'
237+
- '{"index": {"_index": "reindexed_index", "_type": "doc"}}'
238+
- '{"f1": "5"}'
239+
240+
- do:
241+
reindex:
242+
wait_for_completion: false
243+
body:
244+
source:
245+
index: reindexed_index
246+
size: 1
247+
dest:
248+
index: reindexed_index_copy
249+
- match: {task: '/.+:\d+/'}
250+
- set: {task: task}
251+
252+
- do:
253+
tasks.get:
254+
wait_for_completion: true
255+
task_id: $task

qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,42 @@
126126
field3: value
127127
- match: { hits.total: 1 }
128128
- match: { hits.hits.0._id: q3 }
129+
130+
---
131+
"Find a task result record from the old cluster":
132+
- do:
133+
search:
134+
index: .tasks
135+
body:
136+
query:
137+
match_all: {}
138+
- match: { hits.total: 1 }
139+
- match: { hits.hits.0._id: '/.+:\d+/' }
140+
- set: {hits.hits.0._id: task_id}
141+
142+
- do:
143+
tasks.get:
144+
wait_for_completion: true
145+
task_id: $task_id
146+
147+
- is_false: node_failures
148+
- is_true: task
149+
150+
- do:
151+
headers: { "X-Opaque-Id": "Reindexing Again" }
152+
reindex:
153+
wait_for_completion: false
154+
body:
155+
source:
156+
index: reindexed_index_copy
157+
size: 1
158+
dest:
159+
index: reindexed_index_another_copy
160+
- match: { task: '/.+:\d+/' }
161+
- set: { task: task_id }
162+
163+
- do:
164+
tasks.get:
165+
wait_for_completion: true
166+
task_id: $task_id
167+
- match: { task.headers.X-Opaque-Id: "Reindexing Again" }

rest-api-spec/src/main/resources/rest-api-spec/api/tasks.list.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"group_by": {
3535
"type" : "enum",
3636
"description": "Group tasks by nodes or parent/child relationships",
37-
"options" : ["nodes", "parents"],
37+
"options" : ["nodes", "parents", "none"],
3838
"default" : "nodes"
3939
}
4040

rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,19 @@
1717
group_by: parents
1818

1919
- is_true: tasks
20+
21+
---
22+
"tasks_list headers":
23+
- skip:
24+
version: " - 6.99.99"
25+
reason: task headers has been added in 7.0.0
26+
27+
- do:
28+
headers: { "X-Opaque-Id": "That is me" }
29+
tasks.list:
30+
actions: "cluster:monitor/tasks/lists"
31+
group_by: none
32+
33+
- is_true: tasks
34+
- match: { tasks.0.headers.X-Opaque-Id: "That is me" }
35+

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
import org.elasticsearch.rest.action.search.RestMultiSearchAction;
313313
import org.elasticsearch.rest.action.search.RestSearchAction;
314314
import org.elasticsearch.rest.action.search.RestSearchScrollAction;
315+
import org.elasticsearch.tasks.TaskManager;
315316
import org.elasticsearch.threadpool.ThreadPool;
316317
import org.elasticsearch.usage.UsageService;
317318

@@ -324,6 +325,7 @@
324325
import java.util.function.Supplier;
325326
import java.util.function.UnaryOperator;
326327
import java.util.stream.Collectors;
328+
import java.util.stream.Stream;
327329

328330
import static java.util.Collections.unmodifiableMap;
329331

@@ -362,7 +364,10 @@ public ActionModule(boolean transportClient, Settings settings, IndexNameExpress
362364
actionFilters = setupActionFilters(actionPlugins);
363365
autoCreateIndex = transportClient ? null : new AutoCreateIndex(settings, clusterSettings, indexNameExpressionResolver);
364366
destructiveOperations = new DestructiveOperations(settings, clusterSettings);
365-
Set<String> headers = actionPlugins.stream().flatMap(p -> p.getRestHeaders().stream()).collect(Collectors.toSet());
367+
Set<String> headers = Stream.concat(
368+
actionPlugins.stream().flatMap(p -> p.getRestHeaders().stream()),
369+
Stream.of("X-Opaque-Id")
370+
).collect(Collectors.toSet());
366371
UnaryOperator<RestHandler> restWrapper = null;
367372
for (ActionPlugin plugin : actionPlugins) {
368373
UnaryOperator<RestHandler> newRestWrapper = plugin.getRestHandlerWrapper(threadPool.getThreadContext());

server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/cancel/TransportCancelTasksAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
* Transport action that can be used to cancel currently running cancellable tasks.
5757
* <p>
5858
* For a task to be cancellable it has to return an instance of
59-
* {@link CancellableTask} from {@link TransportRequest#createTask(long, String, String, TaskId)}
59+
* {@link CancellableTask} from {@link TransportRequest#createTask}
6060
*/
6161
public class TransportCancelTasksAction extends TransportTasksAction<CancellableTask, CancelTasksRequest, CancelTasksResponse, TaskInfo> {
6262

server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,21 @@ public XContentBuilder toXContentGroupedByParents(XContentBuilder builder, Param
186186
return builder;
187187
}
188188

189+
/**
190+
* Presents a flat list of tasks
191+
*/
192+
public XContentBuilder toXContentGroupedByNone(XContentBuilder builder, Params params) throws IOException {
193+
toXContentCommon(builder, params);
194+
builder.startArray("tasks");
195+
for (TaskInfo taskInfo : getTasks()) {
196+
builder.startObject();
197+
taskInfo.toXContent(builder, params);
198+
builder.endObject();
199+
}
200+
builder.endArray();
201+
return builder;
202+
}
203+
189204
@Override
190205
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
191206
builder.startObject();

server/src/main/java/org/elasticsearch/action/index/IndexRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public class IndexRequest extends ReplicatedWriteRequest<IndexRequest> implement
7777
/**
7878
* Max length of the source document to include into toString()
7979
*
80-
* @see ReplicationRequest#createTask(long, java.lang.String, java.lang.String, org.elasticsearch.tasks.TaskId)
80+
* @see ReplicationRequest#createTask
8181
*/
8282
static final int MAX_SOURCE_LENGTH_IN_TOSTRING = 2048;
8383

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.io.IOException;
3939
import java.util.Arrays;
4040
import java.util.Collections;
41+
import java.util.Map;
4142
import java.util.Objects;
4243

4344
import static org.elasticsearch.action.ValidateActions.addValidationError;
@@ -428,9 +429,9 @@ public boolean isSuggestOnly() {
428429
}
429430

430431
@Override
431-
public Task createTask(long id, String type, String action, TaskId parentTaskId) {
432+
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) {
432433
// generating description in a lazy way since source can be quite big
433-
return new SearchTask(id, type, action, null, parentTaskId) {
434+
return new SearchTask(id, type, action, null, parentTaskId, headers) {
434435
@Override
435436
public String getDescription() {
436437
StringBuilder sb = new StringBuilder();

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.elasticsearch.tasks.TaskId;
3333

3434
import java.io.IOException;
35+
import java.util.Map;
3536
import java.util.Objects;
3637

3738
import static org.elasticsearch.action.ValidateActions.addValidationError;
@@ -117,8 +118,8 @@ public void readFrom(StreamInput in) throws IOException {
117118
}
118119

119120
@Override
120-
public Task createTask(long id, String type, String action, TaskId parentTaskId) {
121-
return new SearchTask(id, type, action, getDescription(), parentTaskId);
121+
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) {
122+
return new SearchTask(id, type, action, getDescription(), parentTaskId, headers);
122123
}
123124

124125
@Override

0 commit comments

Comments
 (0)