@@ -1428,7 +1428,7 @@ private static class ShardSnapshotUpdatesSequencer {
14281428 private static final SubscribableListener <Void > ALWAYS_PROCEED = SubscribableListener .newSucceeded (null );
14291429
14301430 private SubscribableListener <Void > listenerFor (String snapshot , String index ) {
1431- if ("last-snapshot" .equals (snapshot )) {
1431+ if ("last-snapshot" .equals (snapshot ) || "first-snapshot" . equals ( snapshot ) ) {
14321432 return ALWAYS_PROCEED ;
14331433 }
14341434
@@ -1513,10 +1513,21 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15131513 );
15141514 }
15151515 }
1516+ })
1517+ .andThen (l -> {
1518+ // Create the first snapshot as source of the clone
1519+ client .admin ()
1520+ .cluster ()
1521+ .prepareCreateSnapshot (TEST_REQUEST_TIMEOUT , repoName , "first-snapshot" )
1522+ .setIndices ("index-0" , indexToDelete )
1523+ .setPartial (false )
1524+ .setWaitForCompletion (true )
1525+ .execute (l .map (v -> null ));
15161526 });
15171527
15181528 // Start some snapshots such that snapshot-{i} contains index-{i} and index-{snapshotCount} so that we can control the order in
15191529 // which they finalize by controlling the order in which the shard snapshot updates are processed
1530+ final var cloneFuture = new PlainActionFuture <AcknowledgedResponse >();
15201531 for (int i = 0 ; i < snapshotCount ; i ++) {
15211532 final var snapshotName = "snapshot-" + i ;
15221533 final var indexName = "index-" + i ;
@@ -1528,10 +1539,25 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15281539 .setPartial (true )
15291540 .execute (stepListener .map (createSnapshotResponse -> null ))
15301541 );
1542+ if (i == 0 ) {
1543+ // Insert a clone between snapshot-0 and snapshot-1 and it finalizes after snapshot-1 because it will be blocked on index-0
1544+ testListener = testListener .andThen (stepListener -> {
1545+ client .admin ()
1546+ .cluster ()
1547+ .prepareCloneSnapshot (TEST_REQUEST_TIMEOUT , repoName , "first-snapshot" , "clone" )
1548+ .setIndices ("index-0" , indexToDelete )
1549+ .execute (cloneFuture );
1550+ ClusterServiceUtils .addTemporaryStateListener (
1551+ masterClusterService ,
1552+ clusterState -> SnapshotsInProgress .get (clusterState )
1553+ .asStream ()
1554+ .anyMatch (e -> e .snapshot ().getSnapshotId ().getName ().equals ("clone" ) && e .isClone ())
1555+ ).addListener (stepListener .map (v -> null ));
1556+ });
1557+ }
15311558 }
15321559
15331560 testListener = testListener
1534-
15351561 // wait for the target index to complete in snapshot-1
15361562 .andThen (l -> {
15371563 sequencer .releaseBlock ("snapshot-0" , indexToDelete );
@@ -1541,6 +1567,7 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15411567 masterClusterService ,
15421568 clusterState -> SnapshotsInProgress .get (clusterState )
15431569 .asStream ()
1570+ .filter (e -> e .isClone () == false )
15441571 .mapToLong (
15451572 e -> e .shards ()
15461573 .entrySet ()
@@ -1569,6 +1596,8 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15691596
15701597 // wait for all the other snapshots to complete
15711598 .andThen (l -> {
1599+ // Clone is yet to be finalized
1600+ assertTrue (SnapshotsInProgress .get (masterClusterService .state ()).asStream ().anyMatch (SnapshotsInProgress .Entry ::isClone ));
15721601 for (int i = 0 ; i < snapshotCount ; i ++) {
15731602 sequencer .releaseBlock ("snapshot-" + i , indexToDelete );
15741603 sequencer .releaseBlock ("snapshot-" + i , "index-" + i );
@@ -1577,16 +1606,26 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15771606 .addListener (l .map (v -> null ));
15781607 })
15791608 .andThen (l -> {
1609+ final var snapshotNames = Stream .concat (
1610+ Stream .of ("clone" ),
1611+ IntStream .range (0 , snapshotCount ).mapToObj (i -> "snapshot-" + i )
1612+ ).toArray (String []::new );
1613+
15801614 client .admin ()
15811615 .cluster ()
15821616 .prepareGetSnapshots (TEST_REQUEST_TIMEOUT , repoName )
1583- .setSnapshots (IntStream . range ( 0 , snapshotCount ). mapToObj ( i -> "snapshot-" + i ). toArray ( String []:: new ) )
1617+ .setSnapshots (snapshotNames )
15841618 .execute (ActionTestUtils .assertNoFailureListener (getSnapshotsResponse -> {
15851619 for (final var snapshot : getSnapshotsResponse .getSnapshots ()) {
15861620 assertThat (snapshot .state (), is (SnapshotState .SUCCESS ));
15871621 final String snapshotName = snapshot .snapshot ().getSnapshotId ().getName ();
1588- // Does not contain the deleted index in the snapshot
1589- assertThat (snapshot .indices (), contains ("index-" + snapshotName .charAt (snapshotName .length () - 1 )));
1622+ if ("clone" .equals (snapshotName )) {
1623+ // Clone is not affected by index deletion
1624+ assertThat (snapshot .indices (), containsInAnyOrder ("index-0" , indexToDelete ));
1625+ } else {
1626+ // Does not contain the deleted index in the snapshot
1627+ assertThat (snapshot .indices (), contains ("index-" + snapshotName .charAt (snapshotName .length () - 1 )));
1628+ }
15901629 }
15911630 l .onResponse (null );
15921631 }));
@@ -1599,6 +1638,7 @@ public void testDeleteIndexBetweenSuccessAndFinalization() {
15991638 testListener .isDone ()
16001639 );
16011640 safeAwait (testListener ); // shouldn't throw
1641+ assertTrue (cloneFuture .isDone ());
16021642 }
16031643
16041644 @ TestLogging (reason = "testing logging at INFO level" , value = "org.elasticsearch.snapshots.SnapshotsService:INFO" )
0 commit comments