Skip to content

Commit be397b7

Browse files
Simplify Snapshot Delete Process (#47439)
We don't need to read the SnapshotInfo for a snapshot to determine the indices that need to be updated when it is deleted as the `RepositoryData` contains that information already. This PR makes it so the `RepositoryData` is used to determine which indices to update and also removes the special handling for deleting snapshot metadata and the CS snapshot blob and has those simply be deleted as part of the deleting of other unreferenced blobs in the last step of the delete. This makes the snapshot delete a little faster and more resilient by removing two RPC calls (the separate delete and the get). Also, this shortens the diff with #46250 as a side-effect.
1 parent 3c011aa commit be397b7

File tree

3 files changed

+30
-31
lines changed

3 files changed

+30
-31
lines changed

server/src/main/java/org/elasticsearch/repositories/RepositoryData.java

+14
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ public Map<String, IndexId> getIndices() {
123123
return indices;
124124
}
125125

126+
/**
127+
* Returns the list of {@link IndexId} that have their snapshots updated but not removed (because they are still referenced by other
128+
* snapshots) after removing the given snapshot from the repository.
129+
*
130+
* @param snapshotId SnapshotId to remove
131+
* @return List of indices that are changed but not removed
132+
*/
133+
public List<IndexId> indicesToUpdateAfterRemovingSnapshot(SnapshotId snapshotId) {
134+
return indexSnapshots.entrySet().stream()
135+
.filter(entry -> entry.getValue().size() > 1 && entry.getValue().contains(snapshotId))
136+
.map(Map.Entry::getKey)
137+
.collect(Collectors.toList());
138+
}
139+
126140
/**
127141
* Add a snapshot and its indices to the repository; returns a new instance. If the snapshot
128142
* already exists in the repository data, this method throws an IllegalArgumentException.

server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java

+4-31
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.apache.lucene.store.IndexInput;
2828
import org.apache.lucene.store.RateLimiter;
2929
import org.apache.lucene.util.SetOnce;
30-
import org.elasticsearch.ElasticsearchParseException;
3130
import org.elasticsearch.ExceptionsHelper;
3231
import org.elasticsearch.Version;
3332
import org.elasticsearch.action.ActionListener;
@@ -62,7 +61,6 @@
6261
import org.elasticsearch.common.unit.ByteSizeUnit;
6362
import org.elasticsearch.common.unit.ByteSizeValue;
6463
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
65-
import org.elasticsearch.common.util.set.Sets;
6664
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
6765
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
6866
import org.elasticsearch.common.xcontent.XContentFactory;
@@ -101,17 +99,13 @@
10199
import java.io.InputStream;
102100
import java.nio.file.NoSuchFileException;
103101
import java.util.ArrayList;
104-
import java.util.Arrays;
105102
import java.util.Collection;
106103
import java.util.Collections;
107-
import java.util.HashSet;
108104
import java.util.List;
109105
import java.util.Map;
110-
import java.util.Optional;
111106
import java.util.Set;
112107
import java.util.concurrent.Executor;
113108
import java.util.concurrent.atomic.AtomicBoolean;
114-
import java.util.function.Function;
115109
import java.util.stream.Collectors;
116110

117111
import static org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot.FileInfo.canonicalName;
@@ -365,6 +359,8 @@ public void deleteSnapshot(SnapshotId snapshotId, long repositoryStateId, Action
365359
try {
366360
final Map<String, BlobMetaData> rootBlobs = blobContainer().listBlobs();
367361
final RepositoryData repositoryData = getRepositoryData(latestGeneration(rootBlobs.keySet()));
362+
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
363+
// delete an index that was created by another master node after writing this index-N blob.
368364
final Map<String, BlobContainer> foundIndices = blobStore().blobContainer(indicesPath()).children();
369365
doDeleteShardSnapshots(snapshotId, repositoryStateId, foundIndices, rootBlobs, repositoryData, listener);
370366
} catch (Exception ex) {
@@ -390,36 +386,13 @@ private void doDeleteShardSnapshots(SnapshotId snapshotId, long repositoryStateI
390386
Map<String, BlobMetaData> rootBlobs, RepositoryData repositoryData,
391387
ActionListener<Void> listener) throws IOException {
392388
final RepositoryData updatedRepositoryData = repositoryData.removeSnapshot(snapshotId);
393-
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
394-
// delete an index that was created by another master node after writing this index-N blob.
395389
writeIndexGen(updatedRepositoryData, repositoryStateId);
396-
SnapshotInfo snapshot = null;
397-
try {
398-
snapshot = getSnapshotInfo(snapshotId);
399-
} catch (SnapshotMissingException ex) {
400-
listener.onFailure(ex);
401-
return;
402-
} catch (IllegalStateException | SnapshotException | ElasticsearchParseException ex) {
403-
logger.warn(() -> new ParameterizedMessage("cannot read snapshot file [{}]", snapshotId), ex);
404-
}
405-
final List<String> snapMetaFilesToDelete =
406-
Arrays.asList(snapshotFormat.blobName(snapshotId.getUUID()), globalMetaDataFormat.blobName(snapshotId.getUUID()));
407-
try {
408-
blobContainer().deleteBlobsIgnoringIfNotExists(snapMetaFilesToDelete);
409-
} catch (IOException e) {
410-
logger.warn(() -> new ParameterizedMessage("[{}] Unable to delete global metadata files", snapshotId), e);
411-
}
412-
final var survivingIndices = updatedRepositoryData.getIndices();
413390
deleteIndices(
414391
updatedRepositoryData,
415-
Optional.ofNullable(snapshot).map(info -> info.indices().stream().filter(survivingIndices::containsKey)
416-
.map(updatedRepositoryData::resolveIndexId).collect(Collectors.toList())).orElse(Collections.emptyList()),
392+
repositoryData.indicesToUpdateAfterRemovingSnapshot(snapshotId),
417393
snapshotId,
418394
ActionListener.delegateFailure(listener,
419-
(l, v) -> cleanupStaleBlobs(foundIndices,
420-
Sets.difference(rootBlobs.keySet(), new HashSet<>(snapMetaFilesToDelete)).stream().collect(
421-
Collectors.toMap(Function.identity(), rootBlobs::get)),
422-
updatedRepositoryData, ActionListener.map(l, ignored -> null))));
395+
(l, v) -> cleanupStaleBlobs(foundIndices, rootBlobs, updatedRepositoryData, ActionListener.map(l, ignored -> null))));
423396
}
424397

425398
/**

server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java

+12
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.Set;
4343

4444
import static org.elasticsearch.repositories.RepositoryData.EMPTY_REPO_GEN;
45+
import static org.hamcrest.Matchers.containsInAnyOrder;
4546
import static org.hamcrest.Matchers.equalTo;
4647
import static org.hamcrest.Matchers.greaterThan;
4748

@@ -57,6 +58,17 @@ public void testEqualsAndHashCode() {
5758
assertEquals(repositoryData1.hashCode(), repositoryData2.hashCode());
5859
}
5960

61+
public void testIndicesToUpdateAfterRemovingSnapshot() {
62+
final RepositoryData repositoryData = generateRandomRepoData();
63+
final List<IndexId> indicesBefore = List.copyOf(repositoryData.getIndices().values());
64+
final SnapshotId randomSnapshot = randomFrom(repositoryData.getSnapshotIds());
65+
final IndexId[] indicesToUpdate = indicesBefore.stream().filter(index -> {
66+
final Set<SnapshotId> snapshotIds = repositoryData.getSnapshots(index);
67+
return snapshotIds.contains(randomSnapshot) && snapshotIds.size() > 1;
68+
}).toArray(IndexId[]::new);
69+
assertThat(repositoryData.indicesToUpdateAfterRemovingSnapshot(randomSnapshot), containsInAnyOrder(indicesToUpdate));
70+
}
71+
6072
public void testXContent() throws IOException {
6173
RepositoryData repositoryData = generateRandomRepoData();
6274
XContentBuilder builder = JsonXContent.contentBuilder();

0 commit comments

Comments
 (0)