diff --git a/docs/changelog/86514.yaml b/docs/changelog/86514.yaml new file mode 100644 index 000000000000..69b1fa9e6880 --- /dev/null +++ b/docs/changelog/86514.yaml @@ -0,0 +1,5 @@ +pr: 86514 +summary: Make snapshot deletes not block the repository during data blob deletes +area: Snapshot/Restore +type: enhancement +issues: [] diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index 89706c813eb7..a1a4a52b2a27 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -29,6 +29,7 @@ import org.elasticsearch.repositories.FinalizeSnapshotContext; import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.repositories.RepositoryException; import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.threadpool.Scheduler; @@ -275,12 +276,42 @@ class S3Repository extends MeteredBlobStoreRepository { Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { - if (SnapshotsService.useShardGenerations(repositoryMetaVersion) == false) { - listener = delayedListener(listener); + final SnapshotDeleteListener wrappedListener; + if (SnapshotsService.useShardGenerations(repositoryMetaVersion)) { + wrappedListener = listener; + } else { + wrappedListener = new SnapshotDeleteListener() { + @Override + public void onDone() { + listener.onDone(); + } + + @Override + public void onRepositoryDataWritten(RepositoryData repositoryData) { + logCooldownInfo(); + final Scheduler.Cancellable existing = finalizationFuture.getAndSet(threadPool.schedule(() -> { + final Scheduler.Cancellable cancellable = finalizationFuture.getAndSet(null); + assert cancellable != null; + listener.onRepositoryDataWritten(repositoryData); + }, coolDown, ThreadPool.Names.SNAPSHOT)); + assert existing == null : "Already have an ongoing finalization " + finalizationFuture; + } + + @Override + public void onFailure(Exception e) { + logCooldownInfo(); + final Scheduler.Cancellable existing = finalizationFuture.getAndSet(threadPool.schedule(() -> { + final Scheduler.Cancellable cancellable = finalizationFuture.getAndSet(null); + assert cancellable != null; + listener.onFailure(e); + }, coolDown, ThreadPool.Names.SNAPSHOT)); + assert existing == null : "Already have an ongoing finalization " + finalizationFuture; + } + }; } - super.deleteSnapshots(snapshotIds, repositoryStateId, repositoryMetaVersion, listener); + super.deleteSnapshots(snapshotIds, repositoryStateId, repositoryMetaVersion, wrappedListener); } /** diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java index 9dc84e99c314..771cb87e34f3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java @@ -20,12 +20,10 @@ import org.elasticsearch.cluster.metadata.RepositoryMetadata; import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.RepositoryConflictException; import org.elasticsearch.repositories.RepositoryException; import org.elasticsearch.repositories.RepositoryVerificationException; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.snapshots.mockstore.MockRepository; import org.elasticsearch.test.ESIntegTestCase; import java.nio.file.Path; @@ -270,7 +268,7 @@ public class RepositoriesIT extends AbstractSnapshotIntegTestCase { final String snapshot1 = "test-snap1"; client().admin().cluster().prepareCreateSnapshot(repo, snapshot1).setWaitForCompletion(true).get(); String blockedNode = internalCluster().getMasterName(); - ((MockRepository) internalCluster().getInstance(RepositoriesService.class, blockedNode).repository(repo)).blockOnDataFiles(); + blockMasterOnWriteIndexFile(repo); logger.info("--> start deletion of snapshot"); ActionFuture future = client().admin().cluster().prepareDeleteSnapshot(repo, snapshot1).execute(); logger.info("--> waiting for block to kick in on node [{}]", blockedNode); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java index be7d5b951495..d03d5a297aab 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java @@ -802,12 +802,28 @@ public class SnapshotStressTestsIT extends AbstractSnapshotIntegTestCase { .cluster() .prepareCleanupRepository(trackedRepository.repositoryName) .execute(mustSucceed(cleanupRepositoryResponse -> { - Releasables.close(releaseAll); - logger.info("--> completed cleanup of [{}]", trackedRepository.repositoryName); final RepositoryCleanupResult result = cleanupRepositoryResponse.result(); - assertThat(Strings.toString(result), result.blobs(), equalTo(0L)); - assertThat(Strings.toString(result), result.bytes(), equalTo(0L)); - startCleaner(); + if (result.bytes() > 0L || result.blobs() > 0L) { + // we could legitimately run into dangling blobs as the result of a shard snapshot failing half-way + // through the snapshot because of a concurrent index-close or -delete. The second round of cleanup on + // the same repository however must always fully remove any dangling blobs since we block all concurrent + // operations on the repository here + client.admin() + .cluster() + .prepareCleanupRepository(trackedRepository.repositoryName) + .execute(mustSucceed(secondCleanupRepositoryResponse -> { + final RepositoryCleanupResult secondCleanupResult = secondCleanupRepositoryResponse.result(); + assertThat(Strings.toString(secondCleanupResult), secondCleanupResult.blobs(), equalTo(0L)); + assertThat(Strings.toString(secondCleanupResult), secondCleanupResult.bytes(), equalTo(0L)); + Releasables.close(releaseAll); + logger.info("--> completed second cleanup of [{}]", trackedRepository.repositoryName); + startCleaner(); + })); + } else { + Releasables.close(releaseAll); + logger.info("--> completed cleanup of [{}]", trackedRepository.repositoryName); + startCleaner(); + } })); startedCleanup = true; diff --git a/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java b/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java index 70d9cf85d03f..3c1be35809ec 100644 --- a/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java @@ -22,6 +22,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; import org.elasticsearch.index.store.Store; import org.elasticsearch.indices.recovery.RecoveryState; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import java.io.IOException; @@ -77,7 +78,7 @@ public class FilterRepository implements Repository { Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { in.deleteSnapshots(snapshotIds, repositoryStateId, repositoryMetaVersion, listener); } diff --git a/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java b/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java index 1f11def3c801..85ddc157cbdd 100644 --- a/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java @@ -21,6 +21,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; import org.elasticsearch.index.store.Store; import org.elasticsearch.indices.recovery.RecoveryState; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import java.io.IOException; @@ -84,7 +85,7 @@ public class InvalidRepository extends AbstractLifecycleComponent implements Rep Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { listener.onFailure(createCreationException()); } diff --git a/server/src/main/java/org/elasticsearch/repositories/Repository.java b/server/src/main/java/org/elasticsearch/repositories/Repository.java index cbc7652677cc..d1f354269636 100644 --- a/server/src/main/java/org/elasticsearch/repositories/Repository.java +++ b/server/src/main/java/org/elasticsearch/repositories/Repository.java @@ -23,6 +23,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; import org.elasticsearch.index.store.Store; import org.elasticsearch.indices.recovery.RecoveryState; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.threadpool.ThreadPool; @@ -149,7 +150,7 @@ public interface Repository extends LifecycleComponent { Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ); /** diff --git a/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java b/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java index 70f764dd9b5e..0479eedea090 100644 --- a/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java @@ -21,6 +21,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; import org.elasticsearch.index.store.Store; import org.elasticsearch.indices.recovery.RecoveryState; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import java.io.IOException; @@ -82,7 +83,7 @@ public class UnknownTypeRepository extends AbstractLifecycleComponent implements Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { listener.onFailure(createUnknownTypeException()); } diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 9e9f39089440..59a889cf75c6 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -106,6 +106,7 @@ import org.elasticsearch.repositories.ShardGenerations; import org.elasticsearch.repositories.ShardSnapshotResult; import org.elasticsearch.repositories.SnapshotShardContext; import org.elasticsearch.snapshots.AbortedSnapshotException; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotException; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; @@ -807,7 +808,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { if (isReadOnly()) { listener.onFailure(new RepositoryException(metadata.name(), "cannot delete snapshot from a readonly repository")); @@ -906,9 +907,8 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp Map rootBlobs, RepositoryData repositoryData, Version repoMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { - if (SnapshotsService.useShardGenerations(repoMetaVersion)) { // First write the new shard state metadata (with the removed snapshot) and compute deletion targets final StepListener> writeShardMetaDataAndComputeDeletesStep = new StepListener<>(); @@ -937,11 +937,9 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp }, listener::onFailure); // Once we have updated the repository, run the clean-ups writeUpdatedRepoDataStep.whenComplete(updatedRepoData -> { + listener.onRepositoryDataWritten(updatedRepoData); // Run unreferenced blobs cleanup in parallel to shard-level snapshot deletion - final ActionListener afterCleanupsListener = new GroupedActionListener<>( - ActionListener.wrap(() -> listener.onResponse(updatedRepoData)), - 2 - ); + final ActionListener afterCleanupsListener = new GroupedActionListener<>(ActionListener.wrap(listener::onDone), 2); cleanupUnlinkedRootAndIndicesBlobs(snapshotIds, foundIndices, rootBlobs, updatedRepoData, afterCleanupsListener); asyncCleanupUnlinkedShardLevelBlobs( repositoryData, @@ -955,10 +953,10 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp final RepositoryData updatedRepoData = repositoryData.removeSnapshots(snapshotIds, ShardGenerations.EMPTY); writeIndexGen(updatedRepoData, repositoryStateId, repoMetaVersion, Function.identity(), ActionListener.wrap(newRepoData -> { // Run unreferenced blobs cleanup in parallel to shard-level snapshot deletion - final ActionListener afterCleanupsListener = new GroupedActionListener<>( - ActionListener.wrap(() -> listener.onResponse(newRepoData)), - 2 - ); + final ActionListener afterCleanupsListener = new GroupedActionListener<>(ActionListener.wrap(() -> { + listener.onRepositoryDataWritten(newRepoData); + listener.onDone(); + }), 2); cleanupUnlinkedRootAndIndicesBlobs(snapshotIds, foundIndices, rootBlobs, newRepoData, afterCleanupsListener); final StepListener> writeMetaAndComputeDeletesStep = new StepListener<>(); writeUpdatedShardMetaDataAndComputeDeletes(snapshotIds, repositoryData, false, writeMetaAndComputeDeletesStep); @@ -2627,8 +2625,8 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp final long startTime = threadPool.absoluteTimeInMillis(); try { final ShardGeneration generation = snapshotStatus.generation(); - logger.debug("[{}] [{}] snapshot to [{}] [{}] ...", shardId, snapshotId, metadata.name(), generation); final BlobContainer shardContainer = shardContainer(context.indexId(), shardId); + logger.debug("[{}][{}] snapshot to [{}][{}][{}] ...", shardId, snapshotId, metadata.name(), context.indexId(), generation); final Set blobs; if (generation == null) { try { diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotDeleteListener.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotDeleteListener.java new file mode 100644 index 000000000000..cc3d3cd9603a --- /dev/null +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotDeleteListener.java @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +package org.elasticsearch.snapshots; + +import org.elasticsearch.repositories.RepositoryData; + +public interface SnapshotDeleteListener { + + /** + * Invoked once a snapshot has been fully deleted from the repository. + */ + void onDone(); + + /** + * Invoked once the updated {@link RepositoryData} has been written to the repository. + * + * @param repositoryData updated repository data + */ + void onRepositoryDataWritten(RepositoryData repositoryData); + + /** + * Invoked if writing updated {@link RepositoryData} to the repository failed. Once {@link #onRepositoryDataWritten(RepositoryData)} has + * been invoked this method will never be invoked. + * + * @param e exception during metadata steps of snapshot delete + */ + void onFailure(Exception e); +} diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 5c1e0db7fd3b..81ea17eb7b8a 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -65,6 +65,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.Tuple; @@ -2407,7 +2408,11 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus // delete now, we can avoid reaching to the repository and can complete the deletion. // TODO we should complete the deletion and resolve the listeners of SnapshotDeletionsInProgress with no snapshot sooner, // that would save some cluster state updates. - removeSnapshotDeletionFromClusterState(deleteEntry, null, repositoryData); + removeSnapshotDeletionFromClusterState( + deleteEntry, + repositoryData, + listeners -> completeListenersIgnoringException(listeners, null) + ); return; } repositoriesService.repository(deleteEntry.repository()) @@ -2415,10 +2420,51 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus snapshotIds, repositoryData.getGenId(), minCompatibleVersion(minNodeVersion, repositoryData, snapshotIds), - ActionListener.wrap(updatedRepoData -> { - logger.info("snapshots {} deleted", snapshotIds); - removeSnapshotDeletionFromClusterState(deleteEntry, null, updatedRepoData); - }, ex -> removeSnapshotDeletionFromClusterState(deleteEntry, ex, repositoryData)) + new SnapshotDeleteListener() { + + private final ListenableFuture doneFuture = new ListenableFuture<>(); + + @Override + public void onDone() { + logger.info("snapshots {} deleted", snapshotIds); + doneFuture.onResponse(null); + } + + @Override + public void onRepositoryDataWritten(RepositoryData updatedRepoData) { + removeSnapshotDeletionFromClusterState( + deleteEntry, + updatedRepoData, + listeners -> doneFuture.addListener(new ActionListener<>() { + @Override + public void onResponse(Void unused) { + completeListenersIgnoringException(listeners, null); + } + + @Override + public void onFailure(Exception e) { + // this should never be called, once updated repository metadata has been written to the + // repository and the delete been removed from the cluster state, we ignore any further failures + // and always complete the delete successfully + assert false : e; + } + }) + ); + } + + @Override + public void onFailure(Exception e) { + submitUnbatchedTask( + "remove snapshot deletion metadata after failed delete", + new RemoveSnapshotDeletionAndContinueTask(deleteEntry, repositoryData) { + @Override + protected void handleListeners(List> deleteListeners) { + failListenersIgnoringException(deleteListeners, e); + } + } + ); + } + } ); } } @@ -2427,54 +2473,41 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus * Removes a {@link SnapshotDeletionsInProgress.Entry} from {@link SnapshotDeletionsInProgress} in the cluster state after it executed * on the repository. * - * @param deleteEntry delete entry to remove from the cluster state - * @param failure failure encountered while executing the delete on the repository or {@code null} if the delete executed - * successfully + * @param deleteEntry delete entry to remove from the cluster state * @param repositoryData current {@link RepositoryData} for the repository we just ran the delete on. + * @param listenersHandler consumer that gets passed a list of all listeners that had their delete entry successfully removed from the + * cluster state */ private void removeSnapshotDeletionFromClusterState( final SnapshotDeletionsInProgress.Entry deleteEntry, - @Nullable final Exception failure, - final RepositoryData repositoryData + final RepositoryData repositoryData, + final Consumer>> listenersHandler ) { - final ClusterStateUpdateTask clusterStateUpdateTask; - if (failure == null) { - // If we didn't have a failure during the snapshot delete we will remove all snapshot ids that the delete successfully removed - // from the repository from enqueued snapshot delete entries during the cluster state update. After the cluster state update we - // resolve the delete listeners with the latest repository data from after the delete. - clusterStateUpdateTask = new RemoveSnapshotDeletionAndContinueTask(deleteEntry, repositoryData) { - @Override - protected SnapshotDeletionsInProgress filterDeletions(SnapshotDeletionsInProgress deletions) { - final SnapshotDeletionsInProgress updatedDeletions = deletionsWithoutSnapshots( - deletions, - deleteEntry.getSnapshots(), - deleteEntry.repository() - ); - return updatedDeletions == null ? deletions : updatedDeletions; - } + // We remove all snapshot ids that the delete successfully removed from the repository from enqueued snapshot delete entries during + // the cluster state update. After the cluster state update we pass the list of listeners that had their entry removed from the + // cluster state to the given handler + submitUnbatchedTask("remove snapshot deletion metadata", new RemoveSnapshotDeletionAndContinueTask(deleteEntry, repositoryData) { + @Override + protected SnapshotDeletionsInProgress filterDeletions(SnapshotDeletionsInProgress deletions) { + final SnapshotDeletionsInProgress updatedDeletions = deletionsWithoutSnapshots( + deletions, + deleteEntry.getSnapshots(), + deleteEntry.repository() + ); + return updatedDeletions == null ? deletions : updatedDeletions; + } - @Override - protected void handleListeners(List> deleteListeners) { - assert repositoryData.getSnapshotIds().stream().noneMatch(deleteEntry.getSnapshots()::contains) - : "Repository data contained snapshot ids " - + repositoryData.getSnapshotIds() - + " that should should been deleted by [" - + deleteEntry - + "]"; - completeListenersIgnoringException(deleteListeners, null); - } - }; - } else { - // The delete failed to execute on the repository. We remove it from the cluster state and then fail all listeners associated - // with it. - clusterStateUpdateTask = new RemoveSnapshotDeletionAndContinueTask(deleteEntry, repositoryData) { - @Override - protected void handleListeners(List> deleteListeners) { - failListenersIgnoringException(deleteListeners, failure); - } - }; - } - submitUnbatchedTask("remove snapshot deletion metadata", clusterStateUpdateTask); + @Override + protected void handleListeners(List> deleteListeners) { + assert repositoryData.getSnapshotIds().stream().noneMatch(deleteEntry.getSnapshots()::contains) + : "Repository data contained snapshot ids " + + repositoryData.getSnapshotIds() + + " that should should been deleted by [" + + deleteEntry + + "]"; + listenersHandler.accept(deleteListeners); + } + }); } /** @@ -2558,9 +2591,8 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus @Override public final void clusterStateProcessed(ClusterState oldState, ClusterState newState) { - final List> deleteListeners; repositoryOperations.finishDeletion(deleteEntry.uuid()); - deleteListeners = snapshotDeletionListeners.remove(deleteEntry.uuid()); + final List> deleteListeners = snapshotDeletionListeners.remove(deleteEntry.uuid()); handleListeners(deleteListeners); if (newFinalizations.isEmpty()) { if (readyDeletions.isEmpty()) { diff --git a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java index 0d5977ab9a23..97e5afcd21f8 100644 --- a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.index.store.Store; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.indices.recovery.RecoveryState; import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; @@ -350,9 +351,9 @@ public class RepositoriesServiceTests extends ESTestCase { Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { - listener.onResponse(null); + listener.onFailure(new UnsupportedOperationException()); } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java b/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java index 74a7ad403fa4..29f67b577897 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java @@ -28,6 +28,7 @@ import org.elasticsearch.repositories.ShardGeneration; import org.elasticsearch.repositories.ShardGenerations; import org.elasticsearch.repositories.ShardSnapshotResult; import org.elasticsearch.repositories.SnapshotShardContext; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import java.util.Collection; @@ -103,9 +104,9 @@ public abstract class RestoreOnlyRepository extends AbstractLifecycleComponent i Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { - listener.onResponse(null); + listener.onFailure(new UnsupportedOperationException()); } @Override diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java index c5cfbe9979fc..0bdf9af7509a 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java @@ -74,6 +74,7 @@ import org.elasticsearch.repositories.ShardSnapshotResult; import org.elasticsearch.repositories.SnapshotShardContext; import org.elasticsearch.repositories.blobstore.FileRestoreContext; import org.elasticsearch.snapshots.Snapshot; +import org.elasticsearch.snapshots.SnapshotDeleteListener; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotState; @@ -305,9 +306,9 @@ public class CcrRepository extends AbstractLifecycleComponent implements Reposit Collection snapshotIds, long repositoryStateId, Version repositoryMetaVersion, - ActionListener listener + SnapshotDeleteListener listener ) { - throw new UnsupportedOperationException("Unsupported for repository of type: " + TYPE); + listener.onFailure(new UnsupportedOperationException("Unsupported for repository of type: " + TYPE)); } @Override