2929import org .elasticsearch .cluster .project .ProjectResolver ;
3030import org .elasticsearch .cluster .routing .allocation .DataTier ;
3131import org .elasticsearch .cluster .service .ClusterService ;
32+ import org .elasticsearch .cluster .service .ClusterStateTaskExecutorUtils ;
3233import org .elasticsearch .cluster .service .MasterServiceTaskQueue ;
3334import org .elasticsearch .common .compress .CompressedXContent ;
3435import org .elasticsearch .common .settings .IndexScopedSettings ;
5657import org .elasticsearch .xpack .core .downsample .DownsampleShardIndexerStatus ;
5758import org .elasticsearch .xpack .core .downsample .DownsampleShardPersistentTaskState ;
5859import org .elasticsearch .xpack .core .ilm .LifecycleSettings ;
60+ import org .hamcrest .Matchers ;
5961import org .junit .After ;
6062import org .junit .Before ;
6163import org .mockito .Answers ;
@@ -188,11 +190,12 @@ public void setUp() throws Exception {
188190 };
189191 doAnswer (mockBroadcastResponse ).when (indicesAdminClient ).refresh (any (), any ());
190192 doAnswer (mockBroadcastResponse ).when (indicesAdminClient ).forceMerge (any (), any ());
193+
191194 doAnswer (invocation -> {
192195 var updateTask = invocation .getArgument (1 , TransportDownsampleAction .DownsampleClusterStateUpdateTask .class );
193- updateTask .listener .onResponse (randomBoolean () ? AcknowledgedResponse .TRUE : AcknowledgedResponse . FALSE );
196+ updateTask .listener .onResponse (AcknowledgedResponse .TRUE );
194197 return null ;
195- }).when (taskQueue ).submitTask (startsWith ("update -downsample-metadata " ), any (), any ());
198+ }).when (taskQueue ).submitTask (startsWith ("create -downsample-index " ), any (), any ());
196199 when (indicesService .createIndexMapperServiceForValidation (any ())).thenReturn (mapperService );
197200 MappedFieldType timestampFieldMock = mock (MappedFieldType .class );
198201 when (timestampFieldMock .meta ()).thenReturn (Map .of ());
@@ -236,11 +239,6 @@ private void downsample(String mapping) throws IOException {
236239
237240 when (projectResolver .getProjectMetadata (any (ClusterState .class ))).thenReturn (projectMetadata );
238241
239- doAnswer (invocation -> {
240- var updateTask = invocation .getArgument (1 , TransportDownsampleAction .DownsampleClusterStateUpdateTask .class );
241- updateTask .listener .onResponse (AcknowledgedResponse .TRUE );
242- return null ;
243- }).when (taskQueue ).submitTask (startsWith ("create-downsample-index" ), any (), any ());
244242 Answer <Void > mockPersistentTask = invocation -> {
245243 ActionListener <PersistentTasksCustomMetadata .PersistentTask <?>> listener = invocation .getArgument (4 );
246244 PersistentTasksCustomMetadata .PersistentTask <?> task = mock (PersistentTasksCustomMetadata .PersistentTask .class );
@@ -260,6 +258,7 @@ private void downsample(String mapping) throws IOException {
260258 listener .onResponse (AcknowledgedResponse .TRUE );
261259 return null ;
262260 }).when (indicesAdminClient ).updateSettings (any (), any ());
261+ assertSuccessfulUpdateDownsampleStatus (clusterState );
263262
264263 PlainActionFuture <AcknowledgedResponse > listener = new PlainActionFuture <>();
265264 action .masterOperation (
@@ -298,6 +297,7 @@ public void testDownsamplingForceMergeWithShortCircuitAfterCreation() {
298297 .build ();
299298
300299 when (projectResolver .getProjectMetadata (any (ClusterState .class ))).thenReturn (projectMetadata );
300+ assertSuccessfulUpdateDownsampleStatus (clusterState );
301301
302302 PlainActionFuture <AcknowledgedResponse > listener = new PlainActionFuture <>();
303303 action .masterOperation (
@@ -359,6 +359,7 @@ public void downsampleWithShortCircuitDuringCreation(String mapping) throws IOEx
359359 )
360360 .build ()
361361 );
362+ assertSuccessfulUpdateDownsampleStatus (clusterService .state ());
362363
363364 PlainActionFuture <AcknowledgedResponse > listener = new PlainActionFuture <>();
364365 action .masterOperation (
@@ -377,6 +378,83 @@ public void downsampleWithShortCircuitDuringCreation(String mapping) throws IOEx
377378 verify (downsampleMetrics ).recordOperation (anyLong (), eq (DownsampleMetrics .ActionStatus .SUCCESS ));
378379 }
379380
381+ public void testDownsamplingWhenTargetIndexGetsDeleted () throws IOException {
382+ String mapping = switch (randomIntBetween (0 , 2 )) {
383+ case 0 -> NO_METADATA_MAPPING ;
384+ case 1 -> OTHER_METADATA_MAPPING ;
385+ default -> FORCE_MERGE_ENABLED_MAPPING ;
386+ };
387+ mockGetMapping (mapping );
388+ mockMergedMapping (mapping );
389+ var projectMetadata = ProjectMetadata .builder (projectId )
390+ .put (createSourceIndexMetadata (sourceIndex , primaryShards , replicaShards ))
391+ .build ();
392+
393+ var clusterState = ClusterState .builder (ClusterState .EMPTY_STATE )
394+ .putProjectMetadata (projectMetadata )
395+ .blocks (ClusterBlocks .builder ().addIndexBlock (projectId , sourceIndex , IndexMetadata .INDEX_WRITE_BLOCK ))
396+ .build ();
397+
398+ when (projectResolver .getProjectMetadata (any (ClusterState .class ))).thenReturn (projectMetadata );
399+
400+ Answer <Void > mockPersistentTask = invocation -> {
401+ ActionListener <PersistentTasksCustomMetadata .PersistentTask <?>> listener = invocation .getArgument (4 );
402+ PersistentTasksCustomMetadata .PersistentTask <?> task1 = mock (PersistentTasksCustomMetadata .PersistentTask .class );
403+ when (task1 .getId ()).thenReturn (randomAlphaOfLength (10 ));
404+ DownsampleShardPersistentTaskState runningTaskState = new DownsampleShardPersistentTaskState (
405+ DownsampleShardIndexerStatus .COMPLETED ,
406+ null
407+ );
408+ when (task1 .getState ()).thenReturn (runningTaskState );
409+ listener .onResponse (task1 );
410+ return null ;
411+ };
412+ doAnswer (mockPersistentTask ).when (persistentTaskService ).sendStartRequest (anyString (), anyString (), any (), any (), any ());
413+ doAnswer (mockPersistentTask ).when (persistentTaskService ).waitForPersistentTaskCondition (any (), anyString (), any (), any (), any ());
414+ doAnswer (invocation -> {
415+ var listener = invocation .getArgument (1 , TransportDownsampleAction .UpdateDownsampleIndexSettingsActionListener .class );
416+ listener .onResponse (AcknowledgedResponse .TRUE );
417+ return null ;
418+ }).when (indicesAdminClient ).updateSettings (any (), any ());
419+
420+ doAnswer (invocation -> {
421+ var updateTask = invocation .getArgument (1 , TransportDownsampleAction .DownsampleClusterStateUpdateTask .class );
422+ ClusterStateTaskExecutorUtils .executeHandlingResults (
423+ clusterState ,
424+ TransportDownsampleAction .STATE_UPDATE_TASK_EXECUTOR ,
425+ List .of (updateTask ),
426+ task1 -> {},
427+ TransportDownsampleAction .DownsampleClusterStateUpdateTask ::onFailure
428+ );
429+ return null ;
430+ }).when (taskQueue ).submitTask (startsWith ("update-downsample-metadata" ), any (), any ());
431+ IllegalStateException error = safeAwaitFailure (
432+ IllegalStateException .class ,
433+ AcknowledgedResponse .class ,
434+ listener -> action .masterOperation (
435+ task ,
436+ new DownsampleAction .Request (
437+ ESTestCase .TEST_REQUEST_TIMEOUT ,
438+ sourceIndex ,
439+ targetIndex ,
440+ TimeValue .ONE_HOUR ,
441+ new DownsampleConfig (new DateHistogramInterval ("5m" ))
442+ ),
443+ clusterState ,
444+ listener
445+ )
446+ );
447+ assertThat (
448+ error .getMessage (),
449+ Matchers .startsWith ("Failed to update downsample status because [" + targetIndex + "] does not exist" )
450+ );
451+ verify (downsampleMetrics , never ()).recordOperation (anyLong (), eq (DownsampleMetrics .ActionStatus .SUCCESS ));
452+ verify (downsampleMetrics ).recordOperation (anyLong (), eq (DownsampleMetrics .ActionStatus .FAILED ));
453+ verify (indicesAdminClient ).refresh (any (), any ());
454+ verify (indicesAdminClient , never ()).flush (any (), any ());
455+ verify (indicesAdminClient , never ()).forceMerge (any (), any ());
456+ }
457+
380458 private void mockGetMapping (String mapping ) {
381459 doAnswer (invocation -> {
382460 @ SuppressWarnings ("unchecked" )
@@ -532,4 +610,21 @@ public void testGetSupportedMetrics() {
532610 assertThat (supported .defaultMetric (), is ("max" ));
533611 assertThat (supported .supportedMetrics (), is (List .of (metricType .supportedAggs ())));
534612 }
613+
614+ private void assertSuccessfulUpdateDownsampleStatus (ClusterState clusterState ) {
615+ var projectMetadata = ProjectMetadata .builder (clusterState .metadata ().getProject (projectId ))
616+ .put (createSourceIndexMetadata (targetIndex , primaryShards , replicaShards ))
617+ .build ();
618+
619+ var updatedClusterState = ClusterState .builder (clusterState ).putProjectMetadata (projectMetadata ).build ();
620+ doAnswer (invocation -> {
621+ var updateTask = invocation .getArgument (1 , TransportDownsampleAction .DownsampleClusterStateUpdateTask .class );
622+ ClusterStateTaskExecutorUtils .executeAndAssertSuccessful (
623+ updatedClusterState ,
624+ TransportDownsampleAction .STATE_UPDATE_TASK_EXECUTOR ,
625+ List .of (updateTask )
626+ );
627+ return null ;
628+ }).when (taskQueue ).submitTask (startsWith ("update-downsample-metadata" ), any (), any ());
629+ }
535630}
0 commit comments