-
Notifications
You must be signed in to change notification settings - Fork 25.3k
Add recovery infrastructure hook to work with older Lucene indices #81056
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
Changes from all commits
8e1e74e
16787b0
5eb1652
0573748
34e3249
2b81012
8bcfd6c
74209b5
0a2d9c7
01fb461
72fd881
e9b8039
63f6fec
38537c0
737421a
67eb0d1
f9ef94b
b3e91e6
43ab5e3
01debd3
1680afa
f6b4450
3357ec3
a134574
833e2e0
697b0f0
1878a0d
13dcf38
bab959e
8132cee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ | |
import org.apache.logging.log4j.Logger; | ||
import org.apache.logging.log4j.message.ParameterizedMessage; | ||
import org.elasticsearch.action.ActionListener; | ||
import org.elasticsearch.common.lucene.Lucene; | ||
import org.elasticsearch.common.util.iterable.Iterables; | ||
import org.elasticsearch.index.shard.ShardId; | ||
import org.elasticsearch.index.snapshots.IndexShardRestoreFailedException; | ||
|
@@ -69,64 +68,71 @@ protected FileRestoreContext(String repositoryName, ShardId shardId, SnapshotId | |
public void restore(SnapshotFiles snapshotFiles, Store store, ActionListener<Void> listener) { | ||
store.incRef(); | ||
try { | ||
logger.debug("[{}] [{}] restoring to [{}] ...", snapshotId, repositoryName, shardId); | ||
Store.MetadataSnapshot recoveryTargetMetadata; | ||
try { | ||
// this will throw an IOException if the store has no segments infos file. The | ||
// store can still have existing files but they will be deleted just before being | ||
// restored. | ||
recoveryTargetMetadata = store.getMetadata(null, true); | ||
} catch (org.apache.lucene.index.IndexNotFoundException e) { | ||
// happens when restore to an empty shard, not a big deal | ||
logger.trace("[{}] [{}] restoring from to an empty shard", shardId, snapshotId); | ||
recoveryTargetMetadata = Store.MetadataSnapshot.EMPTY; | ||
} catch (IOException e) { | ||
logger.warn( | ||
new ParameterizedMessage( | ||
"[{}] [{}] Can't read metadata from store, will not reuse local files during restore", | ||
shardId, | ||
snapshotId | ||
), | ||
e | ||
); | ||
recoveryTargetMetadata = Store.MetadataSnapshot.EMPTY; | ||
} | ||
final List<BlobStoreIndexShardSnapshot.FileInfo> filesToRecover = new ArrayList<>(); | ||
final Map<String, StoreFileMetadata> snapshotMetadata = new HashMap<>(); | ||
final Map<String, BlobStoreIndexShardSnapshot.FileInfo> fileInfos = new HashMap<>(); | ||
for (final BlobStoreIndexShardSnapshot.FileInfo fileInfo : snapshotFiles.indexFiles()) { | ||
snapshotMetadata.put(fileInfo.metadata().name(), fileInfo.metadata()); | ||
fileInfos.put(fileInfo.metadata().name(), fileInfo); | ||
} | ||
if (isSearchableSnapshotStore(store.indexSettings().getSettings())) { | ||
for (BlobStoreIndexShardSnapshot.FileInfo fileInfo : snapshotFiles.indexFiles()) { | ||
assert store.directory().fileLength(fileInfo.physicalName()) == fileInfo.length(); | ||
recoveryState.getIndex().addFileDetail(fileInfo.physicalName(), fileInfo.length(), true); | ||
ywelsch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} else { | ||
logger.debug("[{}] [{}] restoring to [{}] ...", snapshotId, repositoryName, shardId); | ||
Store.MetadataSnapshot recoveryTargetMetadata; | ||
try { | ||
// this will throw an IOException if the store has no segments infos file. The | ||
// store can still have existing files but they will be deleted just before being | ||
// restored. | ||
recoveryTargetMetadata = store.getMetadata(null, true); | ||
ywelsch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} catch (org.apache.lucene.index.IndexNotFoundException e) { | ||
// happens when restore to an empty shard, not a big deal | ||
logger.trace("[{}] [{}] restoring from to an empty shard", shardId, snapshotId); | ||
recoveryTargetMetadata = Store.MetadataSnapshot.EMPTY; | ||
} catch (IOException e) { | ||
logger.warn( | ||
new ParameterizedMessage( | ||
"[{}] [{}] Can't read metadata from store, will not reuse local files during restore", | ||
shardId, | ||
snapshotId | ||
), | ||
e | ||
); | ||
recoveryTargetMetadata = Store.MetadataSnapshot.EMPTY; | ||
} | ||
final Map<String, StoreFileMetadata> snapshotMetadata = new HashMap<>(); | ||
final Map<String, BlobStoreIndexShardSnapshot.FileInfo> fileInfos = new HashMap<>(); | ||
for (final BlobStoreIndexShardSnapshot.FileInfo fileInfo : snapshotFiles.indexFiles()) { | ||
snapshotMetadata.put(fileInfo.metadata().name(), fileInfo.metadata()); | ||
fileInfos.put(fileInfo.metadata().name(), fileInfo); | ||
} | ||
|
||
final Store.MetadataSnapshot sourceMetadata = new Store.MetadataSnapshot(unmodifiableMap(snapshotMetadata), emptyMap(), 0); | ||
final Store.MetadataSnapshot sourceMetadata = new Store.MetadataSnapshot(unmodifiableMap(snapshotMetadata), emptyMap(), 0); | ||
|
||
final StoreFileMetadata restoredSegmentsFile = sourceMetadata.getSegmentsFile(); | ||
if (restoredSegmentsFile == null) { | ||
throw new IndexShardRestoreFailedException(shardId, "Snapshot has no segments file"); | ||
} | ||
final StoreFileMetadata restoredSegmentsFile = sourceMetadata.getSegmentsFile(); | ||
if (restoredSegmentsFile == null) { | ||
throw new IndexShardRestoreFailedException(shardId, "Snapshot has no segments file"); | ||
} | ||
|
||
final Store.RecoveryDiff diff = sourceMetadata.recoveryDiff(recoveryTargetMetadata); | ||
for (StoreFileMetadata md : diff.identical) { | ||
BlobStoreIndexShardSnapshot.FileInfo fileInfo = fileInfos.get(md.name()); | ||
recoveryState.getIndex().addFileDetail(fileInfo.physicalName(), fileInfo.length(), true); | ||
if (logger.isTraceEnabled()) { | ||
logger.trace( | ||
"[{}] [{}] not_recovering file [{}] from [{}], exists in local store and is same", | ||
shardId, | ||
snapshotId, | ||
fileInfo.physicalName(), | ||
fileInfo.name() | ||
); | ||
final Store.RecoveryDiff diff = sourceMetadata.recoveryDiff(recoveryTargetMetadata); | ||
for (StoreFileMetadata md : diff.identical) { | ||
BlobStoreIndexShardSnapshot.FileInfo fileInfo = fileInfos.get(md.name()); | ||
recoveryState.getIndex().addFileDetail(fileInfo.physicalName(), fileInfo.length(), true); | ||
if (logger.isTraceEnabled()) { | ||
logger.trace( | ||
"[{}] [{}] not_recovering file [{}] from [{}], exists in local store and is same", | ||
shardId, | ||
snapshotId, | ||
fileInfo.physicalName(), | ||
fileInfo.name() | ||
); | ||
} | ||
} | ||
} | ||
|
||
for (StoreFileMetadata md : concat(diff)) { | ||
BlobStoreIndexShardSnapshot.FileInfo fileInfo = fileInfos.get(md.name()); | ||
filesToRecover.add(fileInfo); | ||
recoveryState.getIndex().addFileDetail(fileInfo.physicalName(), fileInfo.length(), false); | ||
if (logger.isTraceEnabled()) { | ||
logger.trace("[{}] [{}] recovering [{}] from [{}]", shardId, snapshotId, fileInfo.physicalName(), fileInfo.name()); | ||
for (StoreFileMetadata md : concat(diff)) { | ||
BlobStoreIndexShardSnapshot.FileInfo fileInfo = fileInfos.get(md.name()); | ||
filesToRecover.add(fileInfo); | ||
recoveryState.getIndex().addFileDetail(fileInfo.physicalName(), fileInfo.length(), false); | ||
if (logger.isTraceEnabled()) { | ||
logger.trace("[{}] [{}] recovering [{}] from [{}]", shardId, snapshotId, fileInfo.physicalName(), fileInfo.name()); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -157,7 +163,7 @@ public void restore(SnapshotFiles snapshotFiles, Store store, ActionListener<Voi | |
restoreFiles(filesToRecover, store, ActionListener.wrap(v -> { | ||
store.incRef(); | ||
try { | ||
afterRestore(snapshotFiles, store, restoredSegmentsFile); | ||
afterRestore(snapshotFiles, store); | ||
listener.onResponse(null); | ||
} finally { | ||
store.decRef(); | ||
|
@@ -173,20 +179,8 @@ public void restore(SnapshotFiles snapshotFiles, Store store, ActionListener<Voi | |
} | ||
} | ||
|
||
private void afterRestore(SnapshotFiles snapshotFiles, Store store, StoreFileMetadata restoredSegmentsFile) { | ||
try { | ||
if (isSearchableSnapshotStore(store.indexSettings().getSettings()) == false) { | ||
Lucene.pruneUnreferencedFiles(restoredSegmentsFile.name(), store.directory()); | ||
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. This code does not look to do anything, and is actually in the way as it opens the Lucene index before it's converted. No tests are breaking with this code, and it's unclear whether it still serves a purpose (even after some historic digging) 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. Yes, it looks like we clean up according to the snapshot contents anyway - this second cleanup was added later on in #8969 but there's no indication why it was needed. I think you're right anyway, no need to open the index here. |
||
} | ||
} catch (IOException e) { | ||
throw new IndexShardRestoreFailedException( | ||
shardId, | ||
"Failed to remove files not referenced in segment file [" + restoredSegmentsFile.name() + "] after restore", | ||
e | ||
); | ||
} | ||
|
||
/// now, go over and clean files that are in the store, but were not in the snapshot | ||
private void afterRestore(SnapshotFiles snapshotFiles, Store store) { | ||
// clean files that are in the store, but were not in the snapshot | ||
try { | ||
for (String storeFile : store.directory().listAll()) { | ||
if (Store.isAutogenerated(storeFile) || snapshotFiles.containPhysicalIndexFile(storeFile)) { | ||
|
@@ -195,9 +189,10 @@ private void afterRestore(SnapshotFiles snapshotFiles, Store store, StoreFileMet | |
try { | ||
store.directory().deleteFile(storeFile); | ||
} catch (ImmutableDirectoryException e) { | ||
// snapshots of immutable directories only contain an empty `segments_N` file since the data lives elsewhere, and if we | ||
// restore such a snapshot then the real data is already present in the directory and cannot be removed. | ||
assert snapshotFiles.indexFiles().size() == 1 : snapshotFiles; | ||
// snapshots of immutable directories either only contain an empty `segments_N` file or no data at all, | ||
// since the data lives elsewhere, and if we restore such a snapshot then the real data is already present | ||
// in the directory and cannot be removed. | ||
assert snapshotFiles.indexFiles().size() <= 1 : snapshotFiles; | ||
} catch (IOException e) { | ||
logger.warn("[{}] [{}] failed to delete file [{}] during snapshot cleanup", shardId, snapshotId, storeFile); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
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.
This is just copy paste from the regular IndexMetadata parsing code.
in_sync_allocations
are required to exist in IndexMetadata for a restore to go through.