Skip to content

Commit 51f0e94

Browse files
Reduce Number of List Calls During Snapshot Create and Delete (#44088) (#44209)
* Reduce Number of List Calls During Snapshot Create and Delete Some obvious cleanups I found when investigation the API call count metering: * No need to get the latest generation id after loading latest repository data * Loading RepositoryData already requires fetching the latest generation so we can reuse it * Also, reuse list of all root blobs when fetching latest repo generation during snapshot delete like we do for shard folders * Lastly, don't try and load `index--1` (N = -1) repository data, it doesn't exist -> just return the empty repo data initially
1 parent 8ce8c62 commit 51f0e94

File tree

3 files changed

+55
-36
lines changed

3 files changed

+55
-36
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@ public RepositoryData addSnapshot(final SnapshotId snapshotId,
165165
return new RepositoryData(genId, snapshots, newSnapshotStates, allIndexSnapshots);
166166
}
167167

168+
/**
169+
* Create a new instance with the given generation and all other fields equal to this instance.
170+
*
171+
* @param newGeneration New Generation
172+
* @return New instance
173+
*/
174+
public RepositoryData withGenId(long newGeneration) {
175+
if (newGeneration == genId) {
176+
return this;
177+
}
178+
return new RepositoryData(newGeneration, this.snapshotIds, this.snapshotStates, this.indexSnapshots);
179+
}
180+
168181
/**
169182
* Remove a snapshot and remove any indices that no longer exist in the repository due to the deletion of the snapshot.
170183
*/

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

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,10 @@ public void deleteSnapshot(SnapshotId snapshotId, long repositoryStateId, Action
419419
// Delete snapshot from the index file, since it is the maintainer of truth of active snapshots
420420
final RepositoryData updatedRepositoryData;
421421
final Map<String, BlobContainer> foundIndices;
422+
final Set<String> rootBlobs;
422423
try {
423-
final RepositoryData repositoryData = getRepositoryData();
424+
rootBlobs = blobContainer().listBlobs().keySet();
425+
final RepositoryData repositoryData = getRepositoryData(latestGeneration(rootBlobs));
424426
updatedRepositoryData = repositoryData.removeSnapshot(snapshotId);
425427
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
426428
// delete an index that was created by another master node after writing this index-N blob.
@@ -666,7 +668,20 @@ public void endVerification(String seed) {
666668
@Override
667669
public RepositoryData getRepositoryData() {
668670
try {
669-
final long indexGen = latestIndexBlobId();
671+
return getRepositoryData(latestIndexBlobId());
672+
} catch (NoSuchFileException ex) {
673+
// repository doesn't have an index blob, its a new blank repo
674+
return RepositoryData.EMPTY;
675+
} catch (IOException ioe) {
676+
throw new RepositoryException(metadata.name(), "Could not determine repository generation from root blobs", ioe);
677+
}
678+
}
679+
680+
private RepositoryData getRepositoryData(long indexGen) {
681+
if (indexGen == RepositoryData.EMPTY_REPO_GEN) {
682+
return RepositoryData.EMPTY;
683+
}
684+
try {
670685
final String snapshotsIndexBlobName = INDEX_FILE_PREFIX + Long.toString(indexGen);
671686

672687
RepositoryData repositoryData;
@@ -694,14 +709,14 @@ public boolean isReadOnly() {
694709
return readOnly;
695710
}
696711

697-
protected void writeIndexGen(final RepositoryData repositoryData, final long repositoryStateId) throws IOException {
712+
protected void writeIndexGen(final RepositoryData repositoryData, final long expectedGen) throws IOException {
698713
assert isReadOnly() == false; // can not write to a read only repository
699-
final long currentGen = latestIndexBlobId();
700-
if (currentGen != repositoryStateId) {
714+
final long currentGen = repositoryData.getGenId();
715+
if (currentGen != expectedGen) {
701716
// the index file was updated by a concurrent operation, so we were operating on stale
702717
// repository data
703718
throw new RepositoryException(metadata.name(), "concurrent modification of the index-N file, expected current generation [" +
704-
repositoryStateId + "], actual current generation [" + currentGen +
719+
expectedGen + "], actual current generation [" + currentGen +
705720
"] - possibly due to simultaneous snapshot deletion requests");
706721
}
707722
final long newGen = currentGen + 1;
@@ -767,14 +782,15 @@ long readSnapshotIndexLatestBlob() throws IOException {
767782
}
768783

769784
private long listBlobsToGetLatestIndexId() throws IOException {
770-
Map<String, BlobMetaData> blobs = blobContainer().listBlobsByPrefix(INDEX_FILE_PREFIX);
785+
return latestGeneration(blobContainer().listBlobsByPrefix(INDEX_FILE_PREFIX).keySet());
786+
}
787+
788+
private long latestGeneration(Collection<String> rootBlobs) {
771789
long latest = RepositoryData.EMPTY_REPO_GEN;
772-
if (blobs.isEmpty()) {
773-
// no snapshot index blobs have been written yet
774-
return latest;
775-
}
776-
for (final BlobMetaData blobMetaData : blobs.values()) {
777-
final String blobName = blobMetaData.name();
790+
for (String blobName : rootBlobs) {
791+
if (blobName.startsWith(INDEX_FILE_PREFIX) == false) {
792+
continue;
793+
}
778794
try {
779795
final long curr = Long.parseLong(blobName.substring(INDEX_FILE_PREFIX.length()));
780796
latest = Math.max(latest, curr);
@@ -809,9 +825,9 @@ public void snapshotShard(Store store, MapperService mapperService, SnapshotId s
809825
throw new IndexShardSnapshotFailedException(shardId, "failed to list blobs", e);
810826
}
811827

812-
Tuple<BlobStoreIndexShardSnapshots, Integer> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
828+
Tuple<BlobStoreIndexShardSnapshots, Long> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
813829
BlobStoreIndexShardSnapshots snapshots = tuple.v1();
814-
int fileListGeneration = tuple.v2();
830+
long fileListGeneration = tuple.v2();
815831

816832
if (snapshots.snapshots().stream().anyMatch(sf -> sf.snapshot().equals(snapshotId.getName()))) {
817833
throw new IndexShardSnapshotFailedException(shardId,
@@ -1013,9 +1029,9 @@ private void deleteShardSnapshot(IndexId indexId, ShardId snapshotShardId, Snaps
10131029
throw new IndexShardSnapshotException(snapshotShardId, "Failed to list content of shard directory", e);
10141030
}
10151031

1016-
Tuple<BlobStoreIndexShardSnapshots, Integer> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
1032+
Tuple<BlobStoreIndexShardSnapshots, Long> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
10171033
BlobStoreIndexShardSnapshots snapshots = tuple.v1();
1018-
int fileListGeneration = tuple.v2();
1034+
long fileListGeneration = tuple.v2();
10191035

10201036
try {
10211037
indexShardSnapshotFormat.delete(shardContainer, snapshotId.getUUID());
@@ -1058,9 +1074,9 @@ private BlobStoreIndexShardSnapshot loadShardSnapshot(BlobContainer shardContain
10581074
* @param blobs list of blobs in the container
10591075
* @param reason a reason explaining why the shard index file is written
10601076
*/
1061-
private void finalizeShard(List<SnapshotFiles> snapshots, int fileListGeneration, Map<String, BlobMetaData> blobs,
1077+
private void finalizeShard(List<SnapshotFiles> snapshots, long fileListGeneration, Map<String, BlobMetaData> blobs,
10621078
String reason, BlobContainer shardContainer, ShardId shardId, SnapshotId snapshotId) {
1063-
final String indexGeneration = Integer.toString(fileListGeneration + 1);
1079+
final String indexGeneration = Long.toString(fileListGeneration + 1);
10641080
try {
10651081
final List<String> blobsToDelete;
10661082
if (snapshots.isEmpty()) {
@@ -1094,26 +1110,14 @@ private void finalizeShard(List<SnapshotFiles> snapshots, int fileListGeneration
10941110
* @param blobs list of blobs in repository
10951111
* @return tuple of BlobStoreIndexShardSnapshots and the last snapshot index generation
10961112
*/
1097-
private Tuple<BlobStoreIndexShardSnapshots, Integer> buildBlobStoreIndexShardSnapshots(Map<String, BlobMetaData> blobs,
1113+
private Tuple<BlobStoreIndexShardSnapshots, Long> buildBlobStoreIndexShardSnapshots(Map<String, BlobMetaData> blobs,
10981114
BlobContainer shardContainer) {
1099-
int latest = -1;
11001115
Set<String> blobKeys = blobs.keySet();
1101-
for (String name : blobKeys) {
1102-
if (name.startsWith(SNAPSHOT_INDEX_PREFIX)) {
1103-
try {
1104-
int gen = Integer.parseInt(name.substring(SNAPSHOT_INDEX_PREFIX.length()));
1105-
if (gen > latest) {
1106-
latest = gen;
1107-
}
1108-
} catch (NumberFormatException ex) {
1109-
logger.warn("failed to parse index file name [{}]", name);
1110-
}
1111-
}
1112-
}
1116+
long latest = latestGeneration(blobKeys);
11131117
if (latest >= 0) {
11141118
try {
11151119
final BlobStoreIndexShardSnapshots shardSnapshots =
1116-
indexShardSnapshotsFormat.read(shardContainer, Integer.toString(latest));
1120+
indexShardSnapshotsFormat.read(shardContainer, Long.toString(latest));
11171121
return new Tuple<>(shardSnapshots, latest);
11181122
} catch (IOException e) {
11191123
final String file = SNAPSHOT_INDEX_PREFIX + latest;

server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,13 @@ public void testRepositoryDataConcurrentModificationNotAllowed() throws IOExcept
190190

191191
// write to index generational file
192192
RepositoryData repositoryData = generateRandomRepoData();
193-
repository.writeIndexGen(repositoryData, repositoryData.getGenId());
193+
final long startingGeneration = repositoryData.getGenId();
194+
repository.writeIndexGen(repositoryData, startingGeneration);
194195

195196
// write repo data again to index generational file, errors because we already wrote to the
196197
// N+1 generation from which this repository data instance was created
197-
expectThrows(RepositoryException.class, () -> repository.writeIndexGen(repositoryData, repositoryData.getGenId()));
198+
expectThrows(RepositoryException.class, () -> repository.writeIndexGen(
199+
repositoryData.withGenId(startingGeneration + 1), repositoryData.getGenId()));
198200
}
199201

200202
public void testBadChunksize() throws Exception {

0 commit comments

Comments
 (0)