Skip to content

Commit 4849c3e

Browse files
Reduce Number of List Calls During Snapshot Create and Delete (#44088)
* Reduce Numebr 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 6ec2647 commit 4849c3e

File tree

3 files changed

+54
-37
lines changed

3 files changed

+54
-37
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ public RepositoryData addSnapshot(final SnapshotId snapshotId,
174174
return new RepositoryData(genId, snapshots, newSnapshotStates, allIndexSnapshots, incompatibleSnapshotIds);
175175
}
176176

177+
/**
178+
* Create a new instance with the given generation and all other fields equal to this instance.
179+
*
180+
* @param newGeneration New Generation
181+
* @return New instance
182+
*/
183+
public RepositoryData withGenId(long newGeneration) {
184+
if (newGeneration == genId) {
185+
return this;
186+
}
187+
return new RepositoryData(newGeneration, this.snapshotIds, this.snapshotStates, this.indexSnapshots, incompatibleSnapshotIds);
188+
}
189+
177190
/**
178191
* Remove a snapshot and remove any indices that no longer exist in the repository due to the deletion of the snapshot.
179192
*/

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

+37-35
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,12 @@ public void deleteSnapshot(SnapshotId snapshotId, long repositoryStateId, Action
399399
final Map<String, BlobContainer> foundIndices;
400400
final Set<String> rootBlobs;
401401
try {
402-
final RepositoryData repositoryData = getRepositoryData();
402+
rootBlobs = blobContainer().listBlobs().keySet();
403+
final RepositoryData repositoryData = getRepositoryData(latestGeneration(rootBlobs));
403404
updatedRepositoryData = repositoryData.removeSnapshot(snapshotId);
404405
// Cache the indices that were found before writing out the new index-N blob so that a stuck master will never
405406
// delete an index that was created by another master node after writing this index-N blob.
406407
foundIndices = blobStore().blobContainer(basePath().add("indices")).children();
407-
rootBlobs = blobContainer().listBlobs().keySet();
408408
writeIndexGen(updatedRepositoryData, repositoryStateId);
409409
} catch (Exception ex) {
410410
listener.onFailure(new RepositoryException(metadata.name(), "failed to delete snapshot [" + snapshotId + "]", ex));
@@ -691,7 +691,20 @@ public void endVerification(String seed) {
691691
@Override
692692
public RepositoryData getRepositoryData() {
693693
try {
694-
final long indexGen = latestIndexBlobId();
694+
return getRepositoryData(latestIndexBlobId());
695+
} catch (NoSuchFileException ex) {
696+
// repository doesn't have an index blob, its a new blank repo
697+
return RepositoryData.EMPTY;
698+
} catch (IOException ioe) {
699+
throw new RepositoryException(metadata.name(), "Could not determine repository generation from root blobs", ioe);
700+
}
701+
}
702+
703+
private RepositoryData getRepositoryData(long indexGen) {
704+
if (indexGen == RepositoryData.EMPTY_REPO_GEN) {
705+
return RepositoryData.EMPTY;
706+
}
707+
try {
695708
final String snapshotsIndexBlobName = INDEX_FILE_PREFIX + Long.toString(indexGen);
696709

697710
RepositoryData repositoryData;
@@ -738,14 +751,14 @@ public boolean isReadOnly() {
738751
return readOnly;
739752
}
740753

741-
protected void writeIndexGen(final RepositoryData repositoryData, final long repositoryStateId) throws IOException {
754+
protected void writeIndexGen(final RepositoryData repositoryData, final long expectedGen) throws IOException {
742755
assert isReadOnly() == false; // can not write to a read only repository
743-
final long currentGen = latestIndexBlobId();
744-
if (currentGen != repositoryStateId) {
756+
final long currentGen = repositoryData.getGenId();
757+
if (currentGen != expectedGen) {
745758
// the index file was updated by a concurrent operation, so we were operating on stale
746759
// repository data
747760
throw new RepositoryException(metadata.name(), "concurrent modification of the index-N file, expected current generation [" +
748-
repositoryStateId + "], actual current generation [" + currentGen +
761+
expectedGen + "], actual current generation [" + currentGen +
749762
"] - possibly due to simultaneous snapshot deletion requests");
750763
}
751764
final long newGen = currentGen + 1;
@@ -823,14 +836,15 @@ long readSnapshotIndexLatestBlob() throws IOException {
823836
}
824837

825838
private long listBlobsToGetLatestIndexId() throws IOException {
826-
Map<String, BlobMetaData> blobs = blobContainer().listBlobsByPrefix(INDEX_FILE_PREFIX);
839+
return latestGeneration(blobContainer().listBlobsByPrefix(INDEX_FILE_PREFIX).keySet());
840+
}
841+
842+
private long latestGeneration(Collection<String> rootBlobs) {
827843
long latest = RepositoryData.EMPTY_REPO_GEN;
828-
if (blobs.isEmpty()) {
829-
// no snapshot index blobs have been written yet
830-
return latest;
831-
}
832-
for (final BlobMetaData blobMetaData : blobs.values()) {
833-
final String blobName = blobMetaData.name();
844+
for (String blobName : rootBlobs) {
845+
if (blobName.startsWith(INDEX_FILE_PREFIX) == false) {
846+
continue;
847+
}
834848
try {
835849
final long curr = Long.parseLong(blobName.substring(INDEX_FILE_PREFIX.length()));
836850
latest = Math.max(latest, curr);
@@ -865,9 +879,9 @@ public void snapshotShard(Store store, MapperService mapperService, SnapshotId s
865879
throw new IndexShardSnapshotFailedException(shardId, "failed to list blobs", e);
866880
}
867881

868-
Tuple<BlobStoreIndexShardSnapshots, Integer> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
882+
Tuple<BlobStoreIndexShardSnapshots, Long> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
869883
BlobStoreIndexShardSnapshots snapshots = tuple.v1();
870-
int fileListGeneration = tuple.v2();
884+
long fileListGeneration = tuple.v2();
871885

872886
if (snapshots.snapshots().stream().anyMatch(sf -> sf.snapshot().equals(snapshotId.getName()))) {
873887
throw new IndexShardSnapshotFailedException(shardId,
@@ -1069,9 +1083,9 @@ private void deleteShardSnapshot(IndexId indexId, ShardId snapshotShardId, Snaps
10691083
throw new IndexShardSnapshotException(snapshotShardId, "Failed to list content of shard directory", e);
10701084
}
10711085

1072-
Tuple<BlobStoreIndexShardSnapshots, Integer> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
1086+
Tuple<BlobStoreIndexShardSnapshots, Long> tuple = buildBlobStoreIndexShardSnapshots(blobs, shardContainer);
10731087
BlobStoreIndexShardSnapshots snapshots = tuple.v1();
1074-
int fileListGeneration = tuple.v2();
1088+
long fileListGeneration = tuple.v2();
10751089

10761090
try {
10771091
indexShardSnapshotFormat.delete(shardContainer, snapshotId.getUUID());
@@ -1114,9 +1128,9 @@ private BlobStoreIndexShardSnapshot loadShardSnapshot(BlobContainer shardContain
11141128
* @param blobs list of blobs in the container
11151129
* @param reason a reason explaining why the shard index file is written
11161130
*/
1117-
private void finalizeShard(List<SnapshotFiles> snapshots, int fileListGeneration, Map<String, BlobMetaData> blobs,
1131+
private void finalizeShard(List<SnapshotFiles> snapshots, long fileListGeneration, Map<String, BlobMetaData> blobs,
11181132
String reason, BlobContainer shardContainer, ShardId shardId, SnapshotId snapshotId) {
1119-
final String indexGeneration = Integer.toString(fileListGeneration + 1);
1133+
final String indexGeneration = Long.toString(fileListGeneration + 1);
11201134
try {
11211135
final List<String> blobsToDelete;
11221136
if (snapshots.isEmpty()) {
@@ -1150,26 +1164,14 @@ private void finalizeShard(List<SnapshotFiles> snapshots, int fileListGeneration
11501164
* @param blobs list of blobs in repository
11511165
* @return tuple of BlobStoreIndexShardSnapshots and the last snapshot index generation
11521166
*/
1153-
private Tuple<BlobStoreIndexShardSnapshots, Integer> buildBlobStoreIndexShardSnapshots(Map<String, BlobMetaData> blobs,
1167+
private Tuple<BlobStoreIndexShardSnapshots, Long> buildBlobStoreIndexShardSnapshots(Map<String, BlobMetaData> blobs,
11541168
BlobContainer shardContainer) {
1155-
int latest = -1;
11561169
Set<String> blobKeys = blobs.keySet();
1157-
for (String name : blobKeys) {
1158-
if (name.startsWith(SNAPSHOT_INDEX_PREFIX)) {
1159-
try {
1160-
int gen = Integer.parseInt(name.substring(SNAPSHOT_INDEX_PREFIX.length()));
1161-
if (gen > latest) {
1162-
latest = gen;
1163-
}
1164-
} catch (NumberFormatException ex) {
1165-
logger.warn("failed to parse index file name [{}]", name);
1166-
}
1167-
}
1168-
}
1170+
long latest = latestGeneration(blobKeys);
11691171
if (latest >= 0) {
11701172
try {
11711173
final BlobStoreIndexShardSnapshots shardSnapshots =
1172-
indexShardSnapshotsFormat.read(shardContainer, Integer.toString(latest));
1174+
indexShardSnapshotsFormat.read(shardContainer, Long.toString(latest));
11731175
return new Tuple<>(shardSnapshots, latest);
11741176
} catch (IOException e) {
11751177
final String file = SNAPSHOT_INDEX_PREFIX + latest;

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,13 @@ public void testRepositoryDataConcurrentModificationNotAllowed() throws IOExcept
189189

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

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

199201
public void testReadAndWriteIncompatibleSnapshots() throws Exception {

0 commit comments

Comments
 (0)