|
@@ -7,6 +7,7 @@
|
|
|
|
|
|
package org.elasticsearch.xpack.transform.transforms;
|
|
|
|
|
|
+import org.elasticsearch.action.ActionListener;
|
|
|
import org.elasticsearch.client.internal.Client;
|
|
|
import org.elasticsearch.cluster.ClusterName;
|
|
|
import org.elasticsearch.cluster.ClusterState;
|
|
@@ -24,6 +25,7 @@ import org.elasticsearch.cluster.routing.UnassignedInfo;
|
|
|
import org.elasticsearch.cluster.service.ClusterService;
|
|
|
import org.elasticsearch.common.settings.ClusterSettings;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
+import org.elasticsearch.common.util.concurrent.EsExecutors;
|
|
|
import org.elasticsearch.core.TimeValue;
|
|
|
import org.elasticsearch.index.Index;
|
|
|
import org.elasticsearch.index.IndexVersion;
|
|
@@ -31,33 +33,74 @@ import org.elasticsearch.index.shard.ShardId;
|
|
|
import org.elasticsearch.indices.TestIndexNameExpressionResolver;
|
|
|
import org.elasticsearch.persistent.PersistentTasksCustomMetadata;
|
|
|
import org.elasticsearch.persistent.PersistentTasksCustomMetadata.Assignment;
|
|
|
+import org.elasticsearch.tasks.TaskId;
|
|
|
import org.elasticsearch.tasks.TaskManager;
|
|
|
import org.elasticsearch.test.ESTestCase;
|
|
|
+import org.elasticsearch.threadpool.TestThreadPool;
|
|
|
import org.elasticsearch.threadpool.ThreadPool;
|
|
|
+import org.elasticsearch.xpack.core.indexing.IndexerState;
|
|
|
import org.elasticsearch.xpack.core.transform.TransformConfigVersion;
|
|
|
+import org.elasticsearch.xpack.core.transform.transforms.AuthorizationState;
|
|
|
+import org.elasticsearch.xpack.core.transform.transforms.TransformConfig;
|
|
|
+import org.elasticsearch.xpack.core.transform.transforms.TransformConfigTests;
|
|
|
+import org.elasticsearch.xpack.core.transform.transforms.TransformState;
|
|
|
import org.elasticsearch.xpack.core.transform.transforms.TransformTaskParams;
|
|
|
+import org.elasticsearch.xpack.core.transform.transforms.TransformTaskState;
|
|
|
import org.elasticsearch.xpack.core.transform.transforms.persistence.TransformInternalIndexConstants;
|
|
|
import org.elasticsearch.xpack.transform.DefaultTransformExtension;
|
|
|
import org.elasticsearch.xpack.transform.Transform;
|
|
|
import org.elasticsearch.xpack.transform.TransformServices;
|
|
|
import org.elasticsearch.xpack.transform.checkpoint.TransformCheckpointService;
|
|
|
import org.elasticsearch.xpack.transform.notifications.TransformAuditor;
|
|
|
-import org.elasticsearch.xpack.transform.persistence.IndexBasedTransformConfigManager;
|
|
|
+import org.elasticsearch.xpack.transform.persistence.InMemoryTransformConfigManager;
|
|
|
+import org.elasticsearch.xpack.transform.persistence.TransformConfigManager;
|
|
|
import org.elasticsearch.xpack.transform.persistence.TransformInternalIndexTests;
|
|
|
import org.elasticsearch.xpack.transform.transforms.scheduling.TransformScheduler;
|
|
|
+import org.junit.AfterClass;
|
|
|
+import org.junit.BeforeClass;
|
|
|
|
|
|
import java.time.Clock;
|
|
|
import java.util.ArrayList;
|
|
|
-import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
+import java.util.concurrent.Executor;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.ArgumentMatchers.anyInt;
|
|
|
+import static org.mockito.ArgumentMatchers.argThat;
|
|
|
+import static org.mockito.ArgumentMatchers.isNull;
|
|
|
+import static org.mockito.Mockito.doAnswer;
|
|
|
import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
import static org.mockito.Mockito.when;
|
|
|
|
|
|
public class TransformPersistentTasksExecutorTests extends ESTestCase {
|
|
|
+ private static ThreadPool threadPool;
|
|
|
+
|
|
|
+ @BeforeClass
|
|
|
+ public static void setUpThreadPool() {
|
|
|
+ threadPool = new TestThreadPool(TransformPersistentTasksExecutorTests.class.getSimpleName()) {
|
|
|
+ @Override
|
|
|
+ public ExecutorService executor(String name) {
|
|
|
+ return EsExecutors.DIRECT_EXECUTOR_SERVICE;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ScheduledCancellable schedule(Runnable command, TimeValue delay, Executor name) {
|
|
|
+ command.run();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ @AfterClass
|
|
|
+ public static void tearDownThreadPool() {
|
|
|
+ terminate(threadPool);
|
|
|
+ }
|
|
|
|
|
|
public void testNodeVersionAssignment() {
|
|
|
DiscoveryNodes.Builder nodes = buildNodes(false, true, true, true, true);
|
|
@@ -262,6 +305,88 @@ public class TransformPersistentTasksExecutorTests extends ESTestCase {
|
|
|
assertEquals(indexToRemove, result.get(0));
|
|
|
}
|
|
|
|
|
|
+ public void testNodeOperation() {
|
|
|
+ var transformsConfigManager = new InMemoryTransformConfigManager();
|
|
|
+ var transformScheduler = new TransformScheduler(Clock.systemUTC(), threadPool, Settings.EMPTY, TimeValue.ZERO);
|
|
|
+ var taskExecutor = buildTaskExecutor(transformServices(transformsConfigManager, transformScheduler));
|
|
|
+
|
|
|
+ var transformId = "testNodeOperation";
|
|
|
+ var params = mockTaskParams(transformId);
|
|
|
+
|
|
|
+ putTransformConfiguration(transformsConfigManager, transformId);
|
|
|
+ var task = mockTransformTask();
|
|
|
+ taskExecutor.nodeOperation(task, params, mock());
|
|
|
+
|
|
|
+ verify(task).start(isNull(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void putTransformConfiguration(TransformConfigManager configManager, String transformId) {
|
|
|
+ configManager.putTransformConfiguration(
|
|
|
+ TransformConfigTests.randomTransformConfig(transformId, TimeValue.timeValueMillis(1), TransformConfigVersion.CURRENT),
|
|
|
+ ActionListener.<Boolean>noop().delegateResponse((l, e) -> fail(e))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testNodeOperationStartupRetry() throws Exception {
|
|
|
+ var failFirstCall = new AtomicBoolean(true);
|
|
|
+ var transformsConfigManager = new InMemoryTransformConfigManager() {
|
|
|
+ @Override
|
|
|
+ public void getTransformConfiguration(String transformId, ActionListener<TransformConfig> resultListener) {
|
|
|
+ if (failFirstCall.compareAndSet(true, false)) {
|
|
|
+ resultListener.onFailure(new IllegalStateException("Failing first call."));
|
|
|
+ } else {
|
|
|
+ super.getTransformConfiguration(transformId, resultListener);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var transformScheduler = new TransformScheduler(Clock.systemUTC(), threadPool, fastRetry(), TimeValue.ZERO);
|
|
|
+ var taskExecutor = buildTaskExecutor(transformServices(transformsConfigManager, transformScheduler));
|
|
|
+
|
|
|
+ var transformId = "testNodeOperationStartupRetry";
|
|
|
+ var params = mockTaskParams(transformId);
|
|
|
+ putTransformConfiguration(transformsConfigManager, transformId);
|
|
|
+
|
|
|
+ var task = mockTransformTask();
|
|
|
+ taskExecutor.nodeOperation(task, params, mock());
|
|
|
+
|
|
|
+ // skip waiting for the scheduler to run the task a second time and just rerun it now
|
|
|
+ transformScheduler.scheduleNow(transformId);
|
|
|
+
|
|
|
+ // verify the retry listener set the state to TransformTaskState.STARTED + IndexerState.STOPPED
|
|
|
+ verify(task).persistStateToClusterState(argThat(state -> {
|
|
|
+ assertThat(TransformTaskState.STARTED, equalTo(state.getTaskState()));
|
|
|
+ assertThat(IndexerState.STOPPED, equalTo(state.getIndexerState()));
|
|
|
+ return true;
|
|
|
+ }), any());
|
|
|
+ verify(task).start(isNull(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ private Settings fastRetry() {
|
|
|
+ // must be >= [1s]
|
|
|
+ return Settings.builder().put(Transform.SCHEDULER_FREQUENCY.getKey(), TimeValue.timeValueSeconds(1)).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private TransformTaskParams mockTaskParams(String transformId) {
|
|
|
+ var params = mock(TransformTaskParams.class);
|
|
|
+ when(params.getId()).thenReturn(transformId);
|
|
|
+ when(params.getFrequency()).thenReturn(TimeValue.timeValueSeconds(1));
|
|
|
+ return params;
|
|
|
+ }
|
|
|
+
|
|
|
+ private TransformTask mockTransformTask() {
|
|
|
+ var task = mock(TransformTask.class);
|
|
|
+ when(task.setAuthState(any(AuthorizationState.class))).thenReturn(task);
|
|
|
+ when(task.setNumFailureRetries(anyInt())).thenReturn(task);
|
|
|
+ when(task.getParentTaskId()).thenReturn(TaskId.EMPTY_TASK_ID);
|
|
|
+ when(task.getContext()).thenReturn(mock());
|
|
|
+ doAnswer(a -> fail(a.getArgument(0, Throwable.class))).when(task).fail(any(Throwable.class), any(String.class), any());
|
|
|
+ when(task.getState()).thenReturn(
|
|
|
+ new TransformState(TransformTaskState.STOPPED, IndexerState.STOPPED, null, 0, null, null, null, false, null)
|
|
|
+ );
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
private void addIndices(Metadata.Builder metadata, RoutingTable.Builder routingTable) {
|
|
|
List<String> indices = new ArrayList<>();
|
|
|
indices.add(TransformInternalIndexConstants.AUDIT_INDEX);
|
|
@@ -415,23 +540,20 @@ public class TransformPersistentTasksExecutorTests extends ESTestCase {
|
|
|
csBuilder.metadata(metadata);
|
|
|
|
|
|
return csBuilder.build();
|
|
|
-
|
|
|
}
|
|
|
|
|
|
private TransformPersistentTasksExecutor buildTaskExecutor() {
|
|
|
- ClusterService clusterService = mock(ClusterService.class);
|
|
|
- Client client = mock(Client.class);
|
|
|
- TransformAuditor mockAuditor = mock(TransformAuditor.class);
|
|
|
- IndexBasedTransformConfigManager transformsConfigManager = new IndexBasedTransformConfigManager(
|
|
|
- clusterService,
|
|
|
- TestIndexNameExpressionResolver.newInstance(),
|
|
|
- client,
|
|
|
- xContentRegistry()
|
|
|
+ var transformServices = transformServices(
|
|
|
+ new InMemoryTransformConfigManager(),
|
|
|
+ new TransformScheduler(Clock.systemUTC(), threadPool, Settings.EMPTY, TimeValue.ZERO)
|
|
|
);
|
|
|
- Clock clock = Clock.systemUTC();
|
|
|
- ThreadPool threadPool = mock(ThreadPool.class);
|
|
|
- TransformCheckpointService transformCheckpointService = new TransformCheckpointService(
|
|
|
- clock,
|
|
|
+ return buildTaskExecutor(transformServices);
|
|
|
+ }
|
|
|
+
|
|
|
+ private TransformServices transformServices(TransformConfigManager configManager, TransformScheduler scheduler) {
|
|
|
+ var mockAuditor = mock(TransformAuditor.class);
|
|
|
+ var transformCheckpointService = new TransformCheckpointService(
|
|
|
+ Clock.systemUTC(),
|
|
|
Settings.EMPTY,
|
|
|
new ClusterService(
|
|
|
Settings.EMPTY,
|
|
@@ -439,28 +561,29 @@ public class TransformPersistentTasksExecutorTests extends ESTestCase {
|
|
|
null,
|
|
|
(TaskManager) null
|
|
|
),
|
|
|
- transformsConfigManager,
|
|
|
+ configManager,
|
|
|
mockAuditor
|
|
|
);
|
|
|
- TransformServices transformServices = new TransformServices(
|
|
|
- transformsConfigManager,
|
|
|
- transformCheckpointService,
|
|
|
- mockAuditor,
|
|
|
- new TransformScheduler(Clock.systemUTC(), threadPool, Settings.EMPTY, TimeValue.ZERO)
|
|
|
- );
|
|
|
-
|
|
|
- ClusterSettings cSettings = new ClusterSettings(Settings.EMPTY, Collections.singleton(Transform.NUM_FAILURE_RETRIES_SETTING));
|
|
|
- when(clusterService.getClusterSettings()).thenReturn(cSettings);
|
|
|
- when(clusterService.state()).thenReturn(TransformInternalIndexTests.randomTransformClusterState());
|
|
|
+ return new TransformServices(configManager, transformCheckpointService, mockAuditor, scheduler);
|
|
|
+ }
|
|
|
|
|
|
+ private TransformPersistentTasksExecutor buildTaskExecutor(TransformServices transformServices) {
|
|
|
return new TransformPersistentTasksExecutor(
|
|
|
- client,
|
|
|
+ mock(Client.class),
|
|
|
transformServices,
|
|
|
threadPool,
|
|
|
- clusterService,
|
|
|
+ clusterService(),
|
|
|
Settings.EMPTY,
|
|
|
new DefaultTransformExtension(),
|
|
|
TestIndexNameExpressionResolver.newInstance()
|
|
|
);
|
|
|
}
|
|
|
+
|
|
|
+ private ClusterService clusterService() {
|
|
|
+ var clusterService = mock(ClusterService.class);
|
|
|
+ var cSettings = new ClusterSettings(Settings.EMPTY, Set.of(Transform.NUM_FAILURE_RETRIES_SETTING));
|
|
|
+ when(clusterService.getClusterSettings()).thenReturn(cSettings);
|
|
|
+ when(clusterService.state()).thenReturn(TransformInternalIndexTests.randomTransformClusterState());
|
|
|
+ return clusterService;
|
|
|
+ }
|
|
|
}
|