|
@@ -31,6 +31,7 @@ import org.elasticsearch.xpack.core.ml.MlMetadata;
|
|
|
import org.elasticsearch.xpack.core.ml.action.DeleteExpiredDataAction;
|
|
|
import org.elasticsearch.xpack.core.ml.action.DeleteJobAction;
|
|
|
import org.elasticsearch.xpack.core.ml.action.GetJobsAction;
|
|
|
+import org.elasticsearch.xpack.core.ml.action.ResetJobAction;
|
|
|
import org.elasticsearch.xpack.core.ml.job.config.Job;
|
|
|
import org.junit.After;
|
|
|
import org.junit.Before;
|
|
@@ -38,8 +39,10 @@ import org.mockito.Mockito;
|
|
|
import org.mockito.stubbing.Answer;
|
|
|
|
|
|
import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.function.Supplier;
|
|
|
|
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
|
import static org.mockito.ArgumentMatchers.same;
|
|
@@ -79,29 +82,21 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
doAnswer(withResponse(new GetJobsAction.Response(new QueryPage<>(Collections.emptyList(), 0, new ParseField(""))))).when(client)
|
|
|
.execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
|
|
|
- int triggerCount = randomIntBetween(2, 4);
|
|
|
- CountDownLatch latch = new CountDownLatch(triggerCount);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client)) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ int triggerCount = randomIntBetween(1, 3);
|
|
|
+ executeMaintenanceTriggers(triggerCount);
|
|
|
|
|
|
- verify(client, times(triggerCount - 1)).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
- verify(client, times(triggerCount - 1)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
- verify(mlAssignmentNotifier, times(triggerCount - 1)).auditUnassignedMlTasks(any(), any());
|
|
|
+ verify(client, times(triggerCount)).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
+ verify(client, times(2 * triggerCount)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ verify(mlAssignmentNotifier, times(triggerCount)).auditUnassignedMlTasks(any(), any());
|
|
|
}
|
|
|
|
|
|
public void testScheduledTriggeringWhileUpgradeModeIsEnabled() throws InterruptedException {
|
|
|
when(clusterService.state()).thenReturn(createClusterState(true));
|
|
|
|
|
|
- int triggerCount = randomIntBetween(2, 4);
|
|
|
- CountDownLatch latch = new CountDownLatch(triggerCount);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client)) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ int triggerCount = randomIntBetween(1, 3);
|
|
|
+ executeMaintenanceTriggers(triggerCount);
|
|
|
|
|
|
- verify(clusterService, times(triggerCount - 1)).state();
|
|
|
+ verify(clusterService, times(triggerCount)).state();
|
|
|
verifyNoMoreInteractions(client, clusterService, mlAssignmentNotifier);
|
|
|
}
|
|
|
|
|
@@ -143,11 +138,7 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
public void testNoAnomalyDetectionTasksWhenDisabled() throws InterruptedException {
|
|
|
when(clusterService.state()).thenReturn(createClusterState(false));
|
|
|
|
|
|
- CountDownLatch latch = new CountDownLatch(2);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client, false, randomBoolean(), randomBoolean())) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ executeMaintenanceTriggers(1, false, randomBoolean(), randomBoolean());
|
|
|
|
|
|
verify(client, never()).threadPool();
|
|
|
verify(client, never()).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
@@ -160,15 +151,11 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
doAnswer(deleteExpiredDataAnswer).when(client).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
doAnswer(getJobsAnswer).when(client).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
|
|
|
- CountDownLatch latch = new CountDownLatch(2);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client)) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ executeMaintenanceTriggers(1);
|
|
|
|
|
|
- verify(client, Mockito.atLeast(2)).threadPool();
|
|
|
- verify(client, Mockito.atLeast(1)).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
- verify(client, Mockito.atLeast(1)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ verify(client, times(3)).threadPool();
|
|
|
+ verify(client, times(1)).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
+ verify(client, times(2)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
verify(mlAssignmentNotifier, Mockito.atLeast(1)).auditUnassignedMlTasks(any(), any());
|
|
|
}
|
|
|
|
|
@@ -202,14 +189,10 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
.when(client)
|
|
|
.execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
|
|
|
- CountDownLatch latch = new CountDownLatch(2);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client)) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ executeMaintenanceTriggers(1);
|
|
|
|
|
|
- verify(client, times(3)).threadPool();
|
|
|
- verify(client).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ verify(client, times(4)).threadPool();
|
|
|
+ verify(client, times(2)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
verify(client).execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
verify(client).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
verify(mlAssignmentNotifier).auditUnassignedMlTasks(any(), any());
|
|
@@ -240,14 +223,10 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
).execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
doAnswer(withResponse(AcknowledgedResponse.of(deleted))).when(client).execute(same(DeleteJobAction.INSTANCE), any(), any());
|
|
|
|
|
|
- CountDownLatch latch = new CountDownLatch(2);
|
|
|
- try (MlDailyMaintenanceService service = createService(latch, client)) {
|
|
|
- service.start();
|
|
|
- latch.await(5, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
+ executeMaintenanceTriggers(1);
|
|
|
|
|
|
- verify(client, times(4)).threadPool();
|
|
|
- verify(client).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ verify(client, times(5)).threadPool();
|
|
|
+ verify(client, times(2)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
verify(client).execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
verify(client).execute(same(DeleteJobAction.INSTANCE), any(), any());
|
|
|
verify(client).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
@@ -255,29 +234,98 @@ public class MlDailyMaintenanceServiceTests extends ESTestCase {
|
|
|
verifyNoMoreInteractions(client, mlAssignmentNotifier);
|
|
|
}
|
|
|
|
|
|
- private MlDailyMaintenanceService createService(CountDownLatch latch, Client client) {
|
|
|
- return createService(latch, client, true, true, true);
|
|
|
+ public void testJobInResettingState_doesNotHaveResetTask() throws InterruptedException {
|
|
|
+ testJobInResettingState(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testJobInResettingState_hasResetTask() throws InterruptedException {
|
|
|
+ testJobInResettingState(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void testJobInResettingState(boolean hasResetTask) throws InterruptedException {
|
|
|
+ String jobId = "job-in-state-resetting";
|
|
|
+ when(clusterService.state()).thenReturn(createClusterState(false));
|
|
|
+ doAnswer(withResponse(new DeleteExpiredDataAction.Response(true))).when(client)
|
|
|
+ .execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
+ Job job = mock(Job.class);
|
|
|
+ when(job.getId()).thenReturn(jobId);
|
|
|
+ when(job.isDeleting()).thenReturn(false);
|
|
|
+ when(job.isResetting()).thenReturn(true);
|
|
|
+ doAnswer(withResponse(new GetJobsAction.Response(new QueryPage<>(List.of(job), 1, new ParseField(""))))).when(client)
|
|
|
+ .execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ List<TaskInfo> tasks = hasResetTask
|
|
|
+ ? List.of(
|
|
|
+ new TaskInfo(
|
|
|
+ new TaskId("test", 123),
|
|
|
+ "test",
|
|
|
+ "test",
|
|
|
+ ResetJobAction.NAME,
|
|
|
+ "job-" + jobId,
|
|
|
+ null,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ true,
|
|
|
+ false,
|
|
|
+ new TaskId("test", 456),
|
|
|
+ Collections.emptyMap()
|
|
|
+ )
|
|
|
+ )
|
|
|
+ : List.of();
|
|
|
+ doAnswer(withResponse(new ListTasksResponse(tasks, List.of(), List.of()))).when(client)
|
|
|
+ .execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
+ doAnswer(withResponse(AcknowledgedResponse.of(true))).when(client).execute(same(ResetJobAction.INSTANCE), any(), any());
|
|
|
+
|
|
|
+ executeMaintenanceTriggers(1);
|
|
|
+
|
|
|
+ verify(client, times(hasResetTask ? 4 : 5)).threadPool();
|
|
|
+ verify(client, times(2)).execute(same(GetJobsAction.INSTANCE), any(), any());
|
|
|
+ verify(client).execute(same(TransportListTasksAction.TYPE), any(), any());
|
|
|
+ if (hasResetTask == false) {
|
|
|
+ verify(client).execute(same(ResetJobAction.INSTANCE), any(), any());
|
|
|
+ }
|
|
|
+ verify(client).execute(same(DeleteExpiredDataAction.INSTANCE), any(), any());
|
|
|
+ verify(mlAssignmentNotifier).auditUnassignedMlTasks(any(), any());
|
|
|
+ verifyNoMoreInteractions(client, mlAssignmentNotifier);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void executeMaintenanceTriggers(int triggerCount) throws InterruptedException {
|
|
|
+ executeMaintenanceTriggers(triggerCount, true, true, true);
|
|
|
}
|
|
|
|
|
|
- private MlDailyMaintenanceService createService(
|
|
|
- CountDownLatch latch,
|
|
|
- Client client,
|
|
|
+ private void executeMaintenanceTriggers(
|
|
|
+ int triggerCount,
|
|
|
boolean isAnomalyDetectionEnabled,
|
|
|
boolean isDataFrameAnalyticsEnabled,
|
|
|
boolean isNlpEnabled
|
|
|
- ) {
|
|
|
- return new MlDailyMaintenanceService(Settings.EMPTY, threadPool, client, clusterService, mlAssignmentNotifier, () -> {
|
|
|
- // We need to be careful that an unexpected iteration doesn't get squeezed in by the maintenance threadpool in
|
|
|
- // between the latch getting counted down to zero and the main test thread stopping the maintenance service.
|
|
|
- // This could happen if the main test thread happens to be waiting for a CPU for the whole 100ms after the
|
|
|
- // latch counts down to zero.
|
|
|
- if (latch.getCount() > 0) {
|
|
|
- latch.countDown();
|
|
|
- return TimeValue.timeValueMillis(100);
|
|
|
- } else {
|
|
|
- return TimeValue.timeValueHours(1);
|
|
|
- }
|
|
|
- }, isAnomalyDetectionEnabled, isDataFrameAnalyticsEnabled, isNlpEnabled);
|
|
|
+ ) throws InterruptedException {
|
|
|
+ // The scheduleProvider is called upon scheduling. The latch waits for (triggerCount + 1)
|
|
|
+ // schedules to happen, which means that the maintenance task is executed triggerCount
|
|
|
+ // times. The first triggerCount invocations of the scheduleProvider return 100ms, which
|
|
|
+ // is the time between the executed maintenance tasks.
|
|
|
+ // After that, maintenance task (triggerCount + 1) is scheduled after 100sec, the latch is
|
|
|
+ // released, the service is closed, and the method returns. Task (triggerCount + 1) is
|
|
|
+ // therefore never executed.
|
|
|
+ CountDownLatch latch = new CountDownLatch(triggerCount + 1);
|
|
|
+ Supplier<TimeValue> scheduleProvider = () -> {
|
|
|
+ latch.countDown();
|
|
|
+ return TimeValue.timeValueMillis(latch.getCount() > 0 ? 100 : 100_000);
|
|
|
+ };
|
|
|
+ try (
|
|
|
+ MlDailyMaintenanceService service = new MlDailyMaintenanceService(
|
|
|
+ Settings.EMPTY,
|
|
|
+ threadPool,
|
|
|
+ client,
|
|
|
+ clusterService,
|
|
|
+ mlAssignmentNotifier,
|
|
|
+ scheduleProvider,
|
|
|
+ isAnomalyDetectionEnabled,
|
|
|
+ isDataFrameAnalyticsEnabled,
|
|
|
+ isNlpEnabled
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ service.start();
|
|
|
+ latch.await(5, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static ClusterState createClusterState(boolean isUpgradeMode) {
|