Skip to content

Commit c1be7a8

Browse files
Simplify Snapshot Delete Process (#47439) (#47533)
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 ec9b77d commit c1be7a8

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

Lines changed: 14 additions & 0 deletions
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

Lines changed: 4 additions & 31 deletions
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;
@@ -61,7 +60,6 @@
6160
import org.elasticsearch.common.unit.ByteSizeUnit;
6261
import org.elasticsearch.common.unit.ByteSizeValue;
6362
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
64-
import org.elasticsearch.common.util.set.Sets;
6563
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
6664
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
6765
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;
@@ -407,6 +401,8 @@ public void deleteSnapshot(SnapshotId snapshotId, long repositoryStateId, Action
407401
try {
408402
final Map<String, BlobMetaData> rootBlobs = blobContainer().listBlobs();
409403
final RepositoryData repositoryData = getRepositoryData(latestGeneration(rootBlobs.keySet()));
404+
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
405+
// delete an index that was created by another master node after writing this index-N blob.
410406
final Map<String, BlobContainer> foundIndices = blobStore().blobContainer(indicesPath()).children();
411407
doDeleteShardSnapshots(snapshotId, repositoryStateId, foundIndices, rootBlobs, repositoryData, listener);
412408
} catch (Exception ex) {
@@ -432,36 +428,13 @@ private void doDeleteShardSnapshots(SnapshotId snapshotId, long repositoryStateI
432428
Map<String, BlobMetaData> rootBlobs, RepositoryData repositoryData,
433429
ActionListener<Void> listener) throws IOException {
434430
final RepositoryData updatedRepositoryData = repositoryData.removeSnapshot(snapshotId);
435-
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
436-
// delete an index that was created by another master node after writing this index-N blob.
437431
writeIndexGen(updatedRepositoryData, repositoryStateId);
438-
SnapshotInfo snapshot = null;
439-
try {
440-
snapshot = getSnapshotInfo(snapshotId);
441-
} catch (SnapshotMissingException ex) {
442-
listener.onFailure(ex);
443-
return;
444-
} catch (IllegalStateException | SnapshotException | ElasticsearchParseException ex) {
445-
logger.warn(() -> new ParameterizedMessage("cannot read snapshot file [{}]", snapshotId), ex);
446-
}
447-
final List<String> snapMetaFilesToDelete =
448-
Arrays.asList(snapshotFormat.blobName(snapshotId.getUUID()), globalMetaDataFormat.blobName(snapshotId.getUUID()));
449-
try {
450-
blobContainer().deleteBlobsIgnoringIfNotExists(snapMetaFilesToDelete);
451-
} catch (IOException e) {
452-
logger.warn(() -> new ParameterizedMessage("[{}] Unable to delete global metadata files", snapshotId), e);
453-
}
454-
final Map<String, IndexId> survivingIndices = updatedRepositoryData.getIndices();
455432
deleteIndices(
456433
updatedRepositoryData,
457-
Optional.ofNullable(snapshot).map(info -> info.indices().stream().filter(survivingIndices::containsKey)
458-
.map(updatedRepositoryData::resolveIndexId).collect(Collectors.toList())).orElse(Collections.emptyList()),
434+
repositoryData.indicesToUpdateAfterRemovingSnapshot(snapshotId),
459435
snapshotId,
460436
ActionListener.delegateFailure(listener,
461-
(l, v) -> cleanupStaleBlobs(foundIndices,
462-
Sets.difference(rootBlobs.keySet(), new HashSet<>(snapMetaFilesToDelete)).stream().collect(
463-
Collectors.toMap(Function.identity(), rootBlobs::get)),
464-
updatedRepositoryData, ActionListener.map(l, ignored -> null))));
437+
(l, v) -> cleanupStaleBlobs(foundIndices, rootBlobs, updatedRepositoryData, ActionListener.map(l, ignored -> null))));
465438
}
466439

467440
/**

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

Lines changed: 12 additions & 0 deletions
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 = new ArrayList<>(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)