|
@@ -8,9 +8,14 @@ package org.elasticsearch.xpack.eql.analysis;
|
|
|
import org.elasticsearch.action.ActionListener;
|
|
import org.elasticsearch.action.ActionListener;
|
|
|
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
|
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
|
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
|
|
|
|
+import org.elasticsearch.action.search.SearchAction;
|
|
|
|
|
+import org.elasticsearch.action.search.SearchRequest;
|
|
|
|
|
+import org.elasticsearch.action.search.SearchRequestBuilder;
|
|
|
|
|
+import org.elasticsearch.action.search.SearchResponse;
|
|
|
import org.elasticsearch.client.Client;
|
|
import org.elasticsearch.client.Client;
|
|
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
|
|
import org.elasticsearch.tasks.TaskCancelledException;
|
|
import org.elasticsearch.tasks.TaskCancelledException;
|
|
|
|
|
+import org.elasticsearch.tasks.TaskId;
|
|
|
import org.elasticsearch.test.ESTestCase;
|
|
import org.elasticsearch.test.ESTestCase;
|
|
|
import org.elasticsearch.xpack.eql.action.EqlSearchRequest;
|
|
import org.elasticsearch.xpack.eql.action.EqlSearchRequest;
|
|
|
import org.elasticsearch.xpack.eql.action.EqlSearchResponse;
|
|
import org.elasticsearch.xpack.eql.action.EqlSearchResponse;
|
|
@@ -19,6 +24,7 @@ import org.elasticsearch.xpack.eql.execution.PlanExecutor;
|
|
|
import org.elasticsearch.xpack.eql.plugin.TransportEqlSearchAction;
|
|
import org.elasticsearch.xpack.eql.plugin.TransportEqlSearchAction;
|
|
|
import org.elasticsearch.xpack.ql.index.IndexResolver;
|
|
import org.elasticsearch.xpack.ql.index.IndexResolver;
|
|
|
import org.elasticsearch.xpack.ql.type.DefaultDataTypeRegistry;
|
|
import org.elasticsearch.xpack.ql.type.DefaultDataTypeRegistry;
|
|
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
import org.mockito.stubbing.Answer;
|
|
import org.mockito.stubbing.Answer;
|
|
|
|
|
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
@@ -48,7 +54,7 @@ public class CancellationTests extends ESTestCase {
|
|
|
IndexResolver indexResolver = new IndexResolver(client, randomAlphaOfLength(10), DefaultDataTypeRegistry.INSTANCE);
|
|
IndexResolver indexResolver = new IndexResolver(client, randomAlphaOfLength(10), DefaultDataTypeRegistry.INSTANCE);
|
|
|
PlanExecutor planExecutor = new PlanExecutor(client, indexResolver, new NamedWriteableRegistry(Collections.emptyList()));
|
|
PlanExecutor planExecutor = new PlanExecutor(client, indexResolver, new NamedWriteableRegistry(Collections.emptyList()));
|
|
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
|
- TransportEqlSearchAction.operation(planExecutor, task, new EqlSearchRequest().query("foo where blah"), "", "",
|
|
|
|
|
|
|
+ TransportEqlSearchAction.operation(planExecutor, task, new EqlSearchRequest().query("foo where blah"), "", "", "node_id",
|
|
|
new ActionListener<>() {
|
|
new ActionListener<>() {
|
|
|
@Override
|
|
@Override
|
|
|
public void onResponse(EqlSearchResponse eqlSearchResponse) {
|
|
public void onResponse(EqlSearchResponse eqlSearchResponse) {
|
|
@@ -64,18 +70,13 @@ public class CancellationTests extends ESTestCase {
|
|
|
});
|
|
});
|
|
|
countDownLatch.await();
|
|
countDownLatch.await();
|
|
|
verify(task, times(1)).isCancelled();
|
|
verify(task, times(1)).isCancelled();
|
|
|
|
|
+ verify(task, times(1)).getId();
|
|
|
|
|
+ verify(client, times(1)).settings();
|
|
|
|
|
+ verify(client, times(1)).threadPool();
|
|
|
verifyNoMoreInteractions(client, task);
|
|
verifyNoMoreInteractions(client, task);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public void testCancellationBeforeSearch() throws InterruptedException {
|
|
|
|
|
- Client client = mock(Client.class);
|
|
|
|
|
-
|
|
|
|
|
- AtomicBoolean cancelled = new AtomicBoolean(false);
|
|
|
|
|
- EqlSearchTask task = mock(EqlSearchTask.class);
|
|
|
|
|
- when(task.isCancelled()).then(invocationOnMock -> cancelled.get());
|
|
|
|
|
-
|
|
|
|
|
- String[] indices = new String[]{"endgame"};
|
|
|
|
|
-
|
|
|
|
|
|
|
+ private Map<String, Map<String, FieldCapabilities>> fields(String[] indices) {
|
|
|
FieldCapabilities fooField =
|
|
FieldCapabilities fooField =
|
|
|
new FieldCapabilities("foo", "integer", true, true, indices, null, null, emptyMap());
|
|
new FieldCapabilities("foo", "integer", true, true, indices, null, null, emptyMap());
|
|
|
FieldCapabilities categoryField =
|
|
FieldCapabilities categoryField =
|
|
@@ -86,10 +87,24 @@ public class CancellationTests extends ESTestCase {
|
|
|
fields.put(fooField.getName(), singletonMap(fooField.getName(), fooField));
|
|
fields.put(fooField.getName(), singletonMap(fooField.getName(), fooField));
|
|
|
fields.put(categoryField.getName(), singletonMap(categoryField.getName(), categoryField));
|
|
fields.put(categoryField.getName(), singletonMap(categoryField.getName(), categoryField));
|
|
|
fields.put(timestampField.getName(), singletonMap(timestampField.getName(), timestampField));
|
|
fields.put(timestampField.getName(), singletonMap(timestampField.getName(), timestampField));
|
|
|
|
|
+ return fields;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void testCancellationBeforeSearch() throws InterruptedException {
|
|
|
|
|
+ Client client = mock(Client.class);
|
|
|
|
|
+
|
|
|
|
|
+ AtomicBoolean cancelled = new AtomicBoolean(false);
|
|
|
|
|
+ EqlSearchTask task = mock(EqlSearchTask.class);
|
|
|
|
|
+ String nodeId = randomAlphaOfLength(10);
|
|
|
|
|
+ long taskId = randomNonNegativeLong();
|
|
|
|
|
+ when(task.isCancelled()).then(invocationOnMock -> cancelled.get());
|
|
|
|
|
+ when(task.getId()).thenReturn(taskId);
|
|
|
|
|
+
|
|
|
|
|
+ String[] indices = new String[]{"endgame"};
|
|
|
|
|
|
|
|
FieldCapabilitiesResponse fieldCapabilitiesResponse = mock(FieldCapabilitiesResponse.class);
|
|
FieldCapabilitiesResponse fieldCapabilitiesResponse = mock(FieldCapabilitiesResponse.class);
|
|
|
when(fieldCapabilitiesResponse.getIndices()).thenReturn(indices);
|
|
when(fieldCapabilitiesResponse.getIndices()).thenReturn(indices);
|
|
|
- when(fieldCapabilitiesResponse.get()).thenReturn(fields);
|
|
|
|
|
|
|
+ when(fieldCapabilitiesResponse.get()).thenReturn(fields(indices));
|
|
|
doAnswer((Answer<Void>) invocation -> {
|
|
doAnswer((Answer<Void>) invocation -> {
|
|
|
@SuppressWarnings("unchecked")
|
|
@SuppressWarnings("unchecked")
|
|
|
ActionListener<FieldCapabilitiesResponse> listener = (ActionListener<FieldCapabilitiesResponse>) invocation.getArguments()[1];
|
|
ActionListener<FieldCapabilitiesResponse> listener = (ActionListener<FieldCapabilitiesResponse>) invocation.getArguments()[1];
|
|
@@ -103,7 +118,71 @@ public class CancellationTests extends ESTestCase {
|
|
|
PlanExecutor planExecutor = new PlanExecutor(client, indexResolver, new NamedWriteableRegistry(Collections.emptyList()));
|
|
PlanExecutor planExecutor = new PlanExecutor(client, indexResolver, new NamedWriteableRegistry(Collections.emptyList()));
|
|
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
|
TransportEqlSearchAction.operation(planExecutor, task, new EqlSearchRequest().indices("endgame")
|
|
TransportEqlSearchAction.operation(planExecutor, task, new EqlSearchRequest().indices("endgame")
|
|
|
- .query("process where foo==3"), "", "", new ActionListener<>() {
|
|
|
|
|
|
|
+ .query("process where foo==3"), "", "", nodeId, new ActionListener<>() {
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void onResponse(EqlSearchResponse eqlSearchResponse) {
|
|
|
|
|
+ fail("Shouldn't be here");
|
|
|
|
|
+ countDownLatch.countDown();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void onFailure(Exception e) {
|
|
|
|
|
+ assertThat(e, instanceOf(TaskCancelledException.class));
|
|
|
|
|
+ countDownLatch.countDown();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ countDownLatch.await();
|
|
|
|
|
+ verify(client).fieldCaps(any(), any());
|
|
|
|
|
+ verify(task, times(2)).isCancelled();
|
|
|
|
|
+ verify(task, times(1)).getId();
|
|
|
|
|
+ verify(client, times(1)).settings();
|
|
|
|
|
+ verify(client, times(1)).threadPool();
|
|
|
|
|
+ verifyNoMoreInteractions(client, task);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void testCancellationDuringSearch() throws InterruptedException {
|
|
|
|
|
+ Client client = mock(Client.class);
|
|
|
|
|
+
|
|
|
|
|
+ EqlSearchTask task = mock(EqlSearchTask.class);
|
|
|
|
|
+ String nodeId = randomAlphaOfLength(10);
|
|
|
|
|
+ long taskId = randomNonNegativeLong();
|
|
|
|
|
+ when(task.isCancelled()).thenReturn(false);
|
|
|
|
|
+ when(task.getId()).thenReturn(taskId);
|
|
|
|
|
+
|
|
|
|
|
+ String[] indices = new String[]{"endgame"};
|
|
|
|
|
+
|
|
|
|
|
+ // Emulation of field capabilities
|
|
|
|
|
+ FieldCapabilitiesResponse fieldCapabilitiesResponse = mock(FieldCapabilitiesResponse.class);
|
|
|
|
|
+ when(fieldCapabilitiesResponse.getIndices()).thenReturn(indices);
|
|
|
|
|
+ when(fieldCapabilitiesResponse.get()).thenReturn(fields(indices));
|
|
|
|
|
+ doAnswer((Answer<Void>) invocation -> {
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ ActionListener<FieldCapabilitiesResponse> listener = (ActionListener<FieldCapabilitiesResponse>) invocation.getArguments()[1];
|
|
|
|
|
+ listener.onResponse(fieldCapabilitiesResponse);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }).when(client).fieldCaps(any(), any());
|
|
|
|
|
+
|
|
|
|
|
+ // Emulation of search cancellation
|
|
|
|
|
+ ArgumentCaptor<SearchRequest> searchRequestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
|
|
|
|
+ when(client.prepareSearch(any())).thenReturn(new SearchRequestBuilder(client, SearchAction.INSTANCE).setIndices(indices));
|
|
|
|
|
+ doAnswer((Answer<Void>) invocation -> {
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ SearchRequest request = (SearchRequest) invocation.getArguments()[1];
|
|
|
|
|
+ TaskId parentTask = request.getParentTask();
|
|
|
|
|
+ assertNotNull(parentTask);
|
|
|
|
|
+ assertEquals(taskId, parentTask.getId());
|
|
|
|
|
+ assertEquals(nodeId, parentTask.getNodeId());
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ ActionListener<SearchResponse> listener = (ActionListener<SearchResponse>) invocation.getArguments()[2];
|
|
|
|
|
+ listener.onFailure(new TaskCancelledException("cancelled"));
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }).when(client).execute(any(), searchRequestCaptor.capture(), any());
|
|
|
|
|
+
|
|
|
|
|
+ IndexResolver indexResolver = new IndexResolver(client, randomAlphaOfLength(10), DefaultDataTypeRegistry.INSTANCE);
|
|
|
|
|
+ PlanExecutor planExecutor = new PlanExecutor(client, indexResolver, new NamedWriteableRegistry(Collections.emptyList()));
|
|
|
|
|
+ CountDownLatch countDownLatch = new CountDownLatch(1);
|
|
|
|
|
+ TransportEqlSearchAction.operation(planExecutor, task, new EqlSearchRequest().indices("endgame")
|
|
|
|
|
+ .query("process where foo==3"), "", "", nodeId, new ActionListener<>() {
|
|
|
@Override
|
|
@Override
|
|
|
public void onResponse(EqlSearchResponse eqlSearchResponse) {
|
|
public void onResponse(EqlSearchResponse eqlSearchResponse) {
|
|
|
fail("Shouldn't be here");
|
|
fail("Shouldn't be here");
|
|
@@ -117,8 +196,13 @@ public class CancellationTests extends ESTestCase {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
countDownLatch.await();
|
|
countDownLatch.await();
|
|
|
|
|
+ // Final verification to ensure no more interaction
|
|
|
verify(client).fieldCaps(any(), any());
|
|
verify(client).fieldCaps(any(), any());
|
|
|
|
|
+ verify(client).execute(any(), any(), any());
|
|
|
verify(task, times(2)).isCancelled();
|
|
verify(task, times(2)).isCancelled();
|
|
|
|
|
+ verify(task, times(1)).getId();
|
|
|
|
|
+ verify(client, times(1)).settings();
|
|
|
|
|
+ verify(client, times(1)).threadPool();
|
|
|
verifyNoMoreInteractions(client, task);
|
|
verifyNoMoreInteractions(client, task);
|
|
|
}
|
|
}
|
|
|
|
|
|