-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make BlobStoreRepository#writeIndexGen API Async #49584
Merged
original-brownbear
merged 1 commit into
elastic:master
from
original-brownbear:async-index-gen-write
Nov 26, 2019
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -427,7 +427,7 @@ private RepositoryData safeRepositoryData(long repositoryStateId, Map<String, Bl | |
*/ | ||
private void doDeleteShardSnapshots(SnapshotId snapshotId, long repositoryStateId, Map<String, BlobContainer> foundIndices, | ||
Map<String, BlobMetaData> rootBlobs, RepositoryData repositoryData, boolean writeShardGens, | ||
ActionListener<Void> listener) throws IOException { | ||
ActionListener<Void> listener) { | ||
|
||
if (writeShardGens) { | ||
// First write the new shard state metadata (with the removed snapshot) and compute deletion targets | ||
|
@@ -442,14 +442,14 @@ private void doDeleteShardSnapshots(SnapshotId snapshotId, long repositoryStateI | |
// written if all shard paths have been successfully updated. | ||
final StepListener<RepositoryData> writeUpdatedRepoDataStep = new StepListener<>(); | ||
writeShardMetaDataAndComputeDeletesStep.whenComplete(deleteResults -> { | ||
final ShardGenerations.Builder builder = ShardGenerations.builder(); | ||
for (ShardSnapshotMetaDeleteResult newGen : deleteResults) { | ||
builder.put(newGen.indexId, newGen.shardId, newGen.newGeneration); | ||
} | ||
final RepositoryData updatedRepoData = repositoryData.removeSnapshot(snapshotId, builder.build()); | ||
writeIndexGen(updatedRepoData, repositoryStateId, true); | ||
writeUpdatedRepoDataStep.onResponse(updatedRepoData); | ||
}, listener::onFailure); | ||
final ShardGenerations.Builder builder = ShardGenerations.builder(); | ||
for (ShardSnapshotMetaDeleteResult newGen : deleteResults) { | ||
builder.put(newGen.indexId, newGen.shardId, newGen.newGeneration); | ||
} | ||
final RepositoryData updatedRepoData = repositoryData.removeSnapshot(snapshotId, builder.build()); | ||
writeIndexGen(updatedRepoData, repositoryStateId, true, | ||
ActionListener.wrap(v -> writeUpdatedRepoDataStep.onResponse(updatedRepoData), listener::onFailure)); | ||
}, listener::onFailure); | ||
// Once we have updated the repository, run the clean-ups | ||
writeUpdatedRepoDataStep.whenComplete(updatedRepoData -> { | ||
// Run unreferenced blobs cleanup in parallel to shard-level snapshot deletion | ||
|
@@ -461,15 +461,17 @@ private void doDeleteShardSnapshots(SnapshotId snapshotId, long repositoryStateI | |
} else { | ||
// Write the new repository data first (with the removed snapshot), using no shard generations | ||
final RepositoryData updatedRepoData = repositoryData.removeSnapshot(snapshotId, ShardGenerations.EMPTY); | ||
writeIndexGen(updatedRepoData, repositoryStateId, false); | ||
// Run unreferenced blobs cleanup in parallel to shard-level snapshot deletion | ||
final ActionListener<Void> afterCleanupsListener = | ||
new GroupedActionListener<>(ActionListener.wrap(() -> listener.onResponse(null)), 2); | ||
asyncCleanupUnlinkedRootAndIndicesBlobs(foundIndices, rootBlobs, updatedRepoData, afterCleanupsListener); | ||
final StepListener<Collection<ShardSnapshotMetaDeleteResult>> writeMetaAndComputeDeletesStep = new StepListener<>(); | ||
writeUpdatedShardMetaDataAndComputeDeletes(snapshotId, repositoryData, false, writeMetaAndComputeDeletesStep); | ||
writeMetaAndComputeDeletesStep.whenComplete(deleteResults -> | ||
asyncCleanupUnlinkedShardLevelBlobs(snapshotId, deleteResults, afterCleanupsListener), afterCleanupsListener::onFailure); | ||
writeIndexGen(updatedRepoData, repositoryStateId, false, ActionListener.wrap(v -> { | ||
// Run unreferenced blobs cleanup in parallel to shard-level snapshot deletion | ||
final ActionListener<Void> afterCleanupsListener = | ||
new GroupedActionListener<>(ActionListener.wrap(() -> listener.onResponse(null)), 2); | ||
asyncCleanupUnlinkedRootAndIndicesBlobs(foundIndices, rootBlobs, updatedRepoData, afterCleanupsListener); | ||
final StepListener<Collection<ShardSnapshotMetaDeleteResult>> writeMetaAndComputeDeletesStep = new StepListener<>(); | ||
writeUpdatedShardMetaDataAndComputeDeletes(snapshotId, repositoryData, false, writeMetaAndComputeDeletesStep); | ||
writeMetaAndComputeDeletesStep.whenComplete(deleteResults -> | ||
asyncCleanupUnlinkedShardLevelBlobs(snapshotId, deleteResults, afterCleanupsListener), | ||
afterCleanupsListener::onFailure); | ||
}, listener::onFailure)); | ||
} | ||
} | ||
|
||
|
@@ -650,8 +652,9 @@ public void cleanup(long repositoryStateId, boolean writeShardGens, ActionListen | |
listener.onResponse(new RepositoryCleanupResult(DeleteResult.ZERO)); | ||
} else { | ||
// write new index-N blob to ensure concurrent operations will fail | ||
writeIndexGen(repositoryData, repositoryStateId, writeShardGens); | ||
cleanupStaleBlobs(foundIndices, rootBlobs, repositoryData, ActionListener.map(listener, RepositoryCleanupResult::new)); | ||
writeIndexGen(repositoryData, repositoryStateId, writeShardGens, | ||
ActionListener.wrap(v -> cleanupStaleBlobs(foundIndices, rootBlobs, repositoryData, | ||
ActionListener.map(listener, RepositoryCleanupResult::new)), listener::onFailure)); | ||
} | ||
} catch (Exception e) { | ||
listener.onFailure(e); | ||
|
@@ -762,11 +765,12 @@ public void finalizeSnapshot(final SnapshotId snapshotId, | |
getRepositoryData(ActionListener.wrap(existingRepositoryData -> { | ||
final RepositoryData updatedRepositoryData = | ||
existingRepositoryData.addSnapshot(snapshotId, snapshotInfo.state(), shardGenerations); | ||
writeIndexGen(updatedRepositoryData, repositoryStateId, writeShardGens); | ||
if (writeShardGens) { | ||
cleanupOldShardGens(existingRepositoryData, updatedRepositoryData); | ||
} | ||
listener.onResponse(snapshotInfo); | ||
writeIndexGen(updatedRepositoryData, repositoryStateId, writeShardGens, ActionListener.wrap(v -> { | ||
if (writeShardGens) { | ||
cleanupOldShardGens(existingRepositoryData, updatedRepositoryData); | ||
} | ||
listener.onResponse(snapshotInfo); | ||
}, onUpdateFailure)); | ||
}, onUpdateFailure)); | ||
}, onUpdateFailure), 2 + indices.size()); | ||
final Executor executor = threadPool.executor(ThreadPool.Names.SNAPSHOT); | ||
|
@@ -995,50 +999,58 @@ public boolean isReadOnly() { | |
return readOnly; | ||
} | ||
|
||
protected void writeIndexGen(final RepositoryData repositoryData, final long expectedGen, | ||
final boolean writeShardGens) throws IOException { | ||
assert isReadOnly() == false; // can not write to a read only repository | ||
final long currentGen = repositoryData.getGenId(); | ||
if (currentGen != expectedGen) { | ||
// the index file was updated by a concurrent operation, so we were operating on stale | ||
// repository data | ||
throw new RepositoryException(metadata.name(), "concurrent modification of the index-N file, expected current generation [" + | ||
expectedGen + "], actual current generation [" + currentGen + | ||
"] - possibly due to simultaneous snapshot deletion requests"); | ||
} | ||
final long newGen = currentGen + 1; | ||
if (latestKnownRepoGen.get() >= newGen) { | ||
throw new IllegalArgumentException( | ||
"Tried writing generation [" + newGen + "] but repository is at least at generation [" + newGen + "] already"); | ||
} | ||
// write the index file | ||
final String indexBlob = INDEX_FILE_PREFIX + Long.toString(newGen); | ||
logger.debug("Repository [{}] writing new index generational blob [{}]", metadata.name(), indexBlob); | ||
writeAtomic(indexBlob, | ||
BytesReference.bytes(repositoryData.snapshotsToXContent(XContentFactory.jsonBuilder(), writeShardGens)), true); | ||
final long latestKnownGen = latestKnownRepoGen.updateAndGet(known -> Math.max(known, newGen)); | ||
if (newGen < latestKnownGen) { | ||
// Don't mess up the index.latest blob | ||
throw new IllegalStateException( | ||
"Wrote generation [" + newGen + "] but latest known repo gen concurrently changed to [" + latestKnownGen + "]"); | ||
} | ||
// write the current generation to the index-latest file | ||
final BytesReference genBytes; | ||
try (BytesStreamOutput bStream = new BytesStreamOutput()) { | ||
bStream.writeLong(newGen); | ||
genBytes = bStream.bytes(); | ||
} | ||
logger.debug("Repository [{}] updating index.latest with generation [{}]", metadata.name(), newGen); | ||
writeAtomic(INDEX_LATEST_BLOB, genBytes, false); | ||
// delete the N-2 index file if it exists, keep the previous one around as a backup | ||
if (newGen - 2 >= 0) { | ||
final String oldSnapshotIndexFile = INDEX_FILE_PREFIX + Long.toString(newGen - 2); | ||
try { | ||
blobContainer().deleteBlobIgnoringIfNotExists(oldSnapshotIndexFile); | ||
} catch (IOException e) { | ||
logger.warn("Failed to clean up old index blob [{}]", oldSnapshotIndexFile); | ||
/** | ||
* @param repositoryData RepositoryData to write | ||
* @param expectedGen expected repository generation at the start of the operation | ||
* @param writeShardGens whether to write {@link ShardGenerations} to the new {@link RepositoryData} blob | ||
* @param listener completion listener | ||
*/ | ||
protected void writeIndexGen(RepositoryData repositoryData, long expectedGen, boolean writeShardGens, ActionListener<Void> listener) { | ||
ActionListener.completeWith(listener, () -> { | ||
assert isReadOnly() == false; // can not write to a read only repository | ||
final long currentGen = repositoryData.getGenId(); | ||
if (currentGen != expectedGen) { | ||
// the index file was updated by a concurrent operation, so we were operating on stale | ||
// repository data | ||
throw new RepositoryException(metadata.name(), | ||
"concurrent modification of the index-N file, expected current generation [" + expectedGen + | ||
"], actual current generation [" + currentGen + "] - possibly due to simultaneous snapshot deletion requests"); | ||
} | ||
} | ||
final long newGen = currentGen + 1; | ||
if (latestKnownRepoGen.get() >= newGen) { | ||
throw new IllegalArgumentException( | ||
"Tried writing generation [" + newGen + "] but repository is at least at generation [" + newGen + "] already"); | ||
} | ||
// write the index file | ||
final String indexBlob = INDEX_FILE_PREFIX + Long.toString(newGen); | ||
logger.debug("Repository [{}] writing new index generational blob [{}]", metadata.name(), indexBlob); | ||
writeAtomic(indexBlob, | ||
BytesReference.bytes(repositoryData.snapshotsToXContent(XContentFactory.jsonBuilder(), writeShardGens)), true); | ||
final long latestKnownGen = latestKnownRepoGen.updateAndGet(known -> Math.max(known, newGen)); | ||
if (newGen < latestKnownGen) { | ||
// Don't mess up the index.latest blob | ||
throw new IllegalStateException( | ||
"Wrote generation [" + newGen + "] but latest known repo gen concurrently changed to [" + latestKnownGen + "]"); | ||
} | ||
// write the current generation to the index-latest file | ||
final BytesReference genBytes; | ||
try (BytesStreamOutput bStream = new BytesStreamOutput()) { | ||
bStream.writeLong(newGen); | ||
genBytes = bStream.bytes(); | ||
} | ||
logger.debug("Repository [{}] updating index.latest with generation [{}]", metadata.name(), newGen); | ||
writeAtomic(INDEX_LATEST_BLOB, genBytes, false); | ||
// delete the N-2 index file if it exists, keep the previous one around as a backup | ||
if (newGen - 2 >= 0) { | ||
final String oldSnapshotIndexFile = INDEX_FILE_PREFIX + Long.toString(newGen - 2); | ||
try { | ||
blobContainer().deleteBlobIgnoringIfNotExists(oldSnapshotIndexFile); | ||
} catch (IOException e) { | ||
logger.warn("Failed to clean up old index blob [{}]", oldSnapshotIndexFile); | ||
} | ||
} | ||
return null; | ||
}); | ||
} | ||
|
||
/** | ||
|
@@ -1432,7 +1444,7 @@ public void verify(String seed, DiscoveryNode localNode) { | |
public String toString() { | ||
return "BlobStoreRepository[" + | ||
"[" + metadata.name() + | ||
"], [" + blobStore() + ']' + | ||
"], [" + blobStore.get() + ']' + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not related to the other changes, but this one was weird as it created a side effect (lazy initializing the |
||
']'; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting was just off here (double indent) before and I fixed it in this one.