Skip to content

Commit 36f8531

Browse files
authored
Don't load global state when only restoring indices (#29239)
Restoring a snapshot, or getting the status of finished snapshots, currently always load the global state metadata file from the repository even if it not required. This slows down the restore process (or listing statuses process) and can also be an issue if the global state cannot be deserialized (because it has unknown customs for example). This commit splits the Repository.getSnapshotMetadata() method into two distincts methods: getGlobalMetadata() and getIndexMetadata() that are now called only when needed.
1 parent 1f6a3c1 commit 36f8531

File tree

7 files changed

+480
-85
lines changed

7 files changed

+480
-85
lines changed

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.lucene.index.IndexCommit;
2222
import org.elasticsearch.Version;
23+
import org.elasticsearch.cluster.metadata.IndexMetaData;
2324
import org.elasticsearch.cluster.metadata.MetaData;
2425
import org.elasticsearch.cluster.metadata.RepositoryMetaData;
2526
import org.elasticsearch.cluster.node.DiscoveryNode;
@@ -78,15 +79,21 @@ interface Factory {
7879
SnapshotInfo getSnapshotInfo(SnapshotId snapshotId);
7980

8081
/**
81-
* Returns global metadata associate with the snapshot.
82-
* <p>
83-
* The returned meta data contains global metadata as well as metadata for all indices listed in the indices parameter.
82+
* Returns global metadata associated with the snapshot.
8483
*
85-
* @param snapshot snapshot
86-
* @param indices list of indices
87-
* @return information about snapshot
84+
* @param snapshotId the snapshot id to load the global metadata from
85+
* @return the global metadata about the snapshot
86+
*/
87+
MetaData getSnapshotGlobalMetaData(SnapshotId snapshotId);
88+
89+
/**
90+
* Returns the index metadata associated with the snapshot.
91+
*
92+
* @param snapshotId the snapshot id to load the index metadata from
93+
* @param index the {@link IndexId} to load the metadata from
94+
* @return the index metadata about the given index for the given snapshot
8895
*/
89-
MetaData getSnapshotMetaData(SnapshotInfo snapshot, List<IndexId> indices) throws IOException;
96+
IndexMetaData getSnapshotIndexMetaData(SnapshotId snapshotId, IndexId index) throws IOException;
9097

9198
/**
9299
* Returns a {@link RepositoryData} to describe the data in the repository, including the snapshots

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

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -480,11 +480,6 @@ public SnapshotInfo finalizeSnapshot(final SnapshotId snapshotId,
480480
return blobStoreSnapshot;
481481
}
482482

483-
@Override
484-
public MetaData getSnapshotMetaData(SnapshotInfo snapshot, List<IndexId> indices) throws IOException {
485-
return readSnapshotMetaData(snapshot.snapshotId(), snapshot.version(), indices, false);
486-
}
487-
488483
@Override
489484
public SnapshotInfo getSnapshotInfo(final SnapshotId snapshotId) {
490485
try {
@@ -496,38 +491,59 @@ public SnapshotInfo getSnapshotInfo(final SnapshotId snapshotId) {
496491
}
497492
}
498493

499-
private MetaData readSnapshotMetaData(SnapshotId snapshotId, Version snapshotVersion, List<IndexId> indices, boolean ignoreIndexErrors) throws IOException {
500-
MetaData metaData;
494+
@Override
495+
public MetaData getSnapshotGlobalMetaData(final SnapshotId snapshotId) {
496+
try {
497+
return globalMetaDataFormat.read(snapshotsBlobContainer, snapshotId.getUUID());
498+
} catch (NoSuchFileException ex) {
499+
throw new SnapshotMissingException(metadata.name(), snapshotId, ex);
500+
} catch (IOException ex) {
501+
throw new SnapshotException(metadata.name(), snapshotId, "failed to read global metadata", ex);
502+
}
503+
}
504+
505+
@Override
506+
public IndexMetaData getSnapshotIndexMetaData(final SnapshotId snapshotId, final IndexId index) throws IOException {
507+
final BlobPath indexPath = basePath().add("indices").add(index.getId());
508+
return indexMetaDataFormat.read(blobStore().blobContainer(indexPath), snapshotId.getUUID());
509+
}
510+
511+
/**
512+
* Returns the global metadata associated with the snapshot.
513+
* <p>
514+
* The returned meta data contains global metadata as well as metadata
515+
* for all indices listed in the indices parameter.
516+
*/
517+
private MetaData readSnapshotMetaData(final SnapshotId snapshotId,
518+
final Version snapshotVersion,
519+
final List<IndexId> indices,
520+
final boolean ignoreErrors) throws IOException {
501521
if (snapshotVersion == null) {
502522
// When we delete corrupted snapshots we might not know which version we are dealing with
503523
// We can try detecting the version based on the metadata file format
504-
assert ignoreIndexErrors;
524+
assert ignoreErrors;
505525
if (globalMetaDataFormat.exists(snapshotsBlobContainer, snapshotId.getUUID()) == false) {
506526
throw new SnapshotMissingException(metadata.name(), snapshotId);
507527
}
508528
}
509-
try {
510-
metaData = globalMetaDataFormat.read(snapshotsBlobContainer, snapshotId.getUUID());
511-
} catch (NoSuchFileException ex) {
512-
throw new SnapshotMissingException(metadata.name(), snapshotId, ex);
513-
} catch (IOException ex) {
514-
throw new SnapshotException(metadata.name(), snapshotId, "failed to get snapshots", ex);
515-
}
516-
MetaData.Builder metaDataBuilder = MetaData.builder(metaData);
517-
for (IndexId index : indices) {
518-
BlobPath indexPath = basePath().add("indices").add(index.getId());
519-
BlobContainer indexMetaDataBlobContainer = blobStore().blobContainer(indexPath);
520-
try {
521-
metaDataBuilder.put(indexMetaDataFormat.read(indexMetaDataBlobContainer, snapshotId.getUUID()), false);
522-
} catch (ElasticsearchParseException | IOException ex) {
523-
if (ignoreIndexErrors) {
524-
logger.warn(() -> new ParameterizedMessage("[{}] [{}] failed to read metadata for index", snapshotId, index.getName()), ex);
525-
} else {
526-
throw ex;
529+
530+
final MetaData.Builder metaData = MetaData.builder(getSnapshotGlobalMetaData(snapshotId));
531+
if (indices != null) {
532+
for (IndexId index : indices) {
533+
try {
534+
metaData.put(getSnapshotIndexMetaData(snapshotId, index), false);
535+
} catch (ElasticsearchParseException | IOException ex) {
536+
if (ignoreErrors == false) {
537+
throw new SnapshotException(metadata.name(), snapshotId,
538+
"[" + index.getName() + "] failed to read metadata for index", ex);
539+
} else {
540+
logger.warn(() ->
541+
new ParameterizedMessage("[{}] [{}] failed to read metadata for index", snapshotId, index.getName()), ex);
542+
}
527543
}
528544
}
529545
}
530-
return metaDataBuilder.build();
546+
return metaData.build();
531547
}
532548

533549
/**

server/src/main/java/org/elasticsearch/snapshots/RestoreService.java

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.elasticsearch.index.Index;
6767
import org.elasticsearch.index.shard.IndexShard;
6868
import org.elasticsearch.index.shard.ShardId;
69+
import org.elasticsearch.repositories.IndexId;
6970
import org.elasticsearch.repositories.RepositoriesService;
7071
import org.elasticsearch.repositories.Repository;
7172
import org.elasticsearch.repositories.RepositoryData;
@@ -91,6 +92,7 @@
9192
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_CREATED;
9293
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_UPGRADED;
9394
import static org.elasticsearch.common.util.set.Sets.newHashSet;
95+
import static org.elasticsearch.snapshots.SnapshotUtils.filterIndices;
9496

9597
/**
9698
* Service responsible for restoring snapshots
@@ -182,17 +184,34 @@ public void restoreSnapshot(final RestoreRequest request, final ActionListener<R
182184
if (matchingSnapshotId.isPresent() == false) {
183185
throw new SnapshotRestoreException(request.repositoryName, request.snapshotName, "snapshot does not exist");
184186
}
187+
185188
final SnapshotId snapshotId = matchingSnapshotId.get();
186189
final SnapshotInfo snapshotInfo = repository.getSnapshotInfo(snapshotId);
187190
final Snapshot snapshot = new Snapshot(request.repositoryName, snapshotId);
188-
List<String> filteredIndices = SnapshotUtils.filterIndices(snapshotInfo.indices(), request.indices(), request.indicesOptions());
189-
final MetaData metaData = repository.getSnapshotMetaData(snapshotInfo, repositoryData.resolveIndices(filteredIndices));
190191

191192
// Make sure that we can restore from this snapshot
192193
validateSnapshotRestorable(request.repositoryName, snapshotInfo);
193194

194-
// Find list of indices that we need to restore
195-
final Map<String, String> renamedIndices = renamedIndices(request, filteredIndices);
195+
// Resolve the indices from the snapshot that need to be restored
196+
final List<String> indicesInSnapshot = filterIndices(snapshotInfo.indices(), request.indices(), request.indicesOptions());
197+
198+
final MetaData.Builder metaDataBuilder;
199+
if (request.includeGlobalState()) {
200+
metaDataBuilder = MetaData.builder(repository.getSnapshotGlobalMetaData(snapshotId));
201+
} else {
202+
metaDataBuilder = MetaData.builder();
203+
}
204+
205+
final List<IndexId> indexIdsInSnapshot = repositoryData.resolveIndices(indicesInSnapshot);
206+
for (IndexId indexId : indexIdsInSnapshot) {
207+
metaDataBuilder.put(repository.getSnapshotIndexMetaData(snapshotId, indexId), false);
208+
}
209+
210+
final MetaData metaData = metaDataBuilder.build();
211+
212+
// Apply renaming on index names, returning a map of names where
213+
// the key is the renamed index and the value is the original name
214+
final Map<String, String> indices = renamedIndices(request, indicesInSnapshot);
196215

197216
// Now we can start the actual restore process by adding shards to be recovered in the cluster state
198217
// and updating cluster metadata (global and index) as needed
@@ -222,12 +241,13 @@ public ClusterState execute(ClusterState currentState) {
222241
RoutingTable.Builder rtBuilder = RoutingTable.builder(currentState.routingTable());
223242
ImmutableOpenMap<ShardId, RestoreInProgress.ShardRestoreStatus> shards;
224243
Set<String> aliases = new HashSet<>();
225-
if (!renamedIndices.isEmpty()) {
244+
245+
if (indices.isEmpty() == false) {
226246
// We have some indices to restore
227247
ImmutableOpenMap.Builder<ShardId, RestoreInProgress.ShardRestoreStatus> shardsBuilder = ImmutableOpenMap.builder();
228248
final Version minIndexCompatibilityVersion = currentState.getNodes().getMaxNodeVersion()
229249
.minimumIndexCompatibilityVersion();
230-
for (Map.Entry<String, String> indexEntry : renamedIndices.entrySet()) {
250+
for (Map.Entry<String, String> indexEntry : indices.entrySet()) {
231251
String index = indexEntry.getValue();
232252
boolean partial = checkPartial(index);
233253
SnapshotRecoverySource recoverySource = new SnapshotRecoverySource(snapshot, snapshotInfo.version(), index);
@@ -304,21 +324,42 @@ public ClusterState execute(ClusterState currentState) {
304324
}
305325

306326
shards = shardsBuilder.build();
307-
RestoreInProgress.Entry restoreEntry = new RestoreInProgress.Entry(snapshot, overallState(RestoreInProgress.State.INIT, shards), Collections.unmodifiableList(new ArrayList<>(renamedIndices.keySet())), shards);
327+
RestoreInProgress.Entry restoreEntry = new RestoreInProgress.Entry(snapshot, overallState(RestoreInProgress.State.INIT, shards), Collections.unmodifiableList(new ArrayList<>(indices.keySet())), shards);
308328
builder.putCustom(RestoreInProgress.TYPE, new RestoreInProgress(restoreEntry));
309329
} else {
310330
shards = ImmutableOpenMap.of();
311331
}
312332

313-
checkAliasNameConflicts(renamedIndices, aliases);
333+
checkAliasNameConflicts(indices, aliases);
314334

315335
// Restore global state if needed
316-
restoreGlobalStateIfRequested(mdBuilder);
336+
if (request.includeGlobalState()) {
337+
if (metaData.persistentSettings() != null) {
338+
Settings settings = metaData.persistentSettings();
339+
clusterSettings.validateUpdate(settings);
340+
mdBuilder.persistentSettings(settings);
341+
}
342+
if (metaData.templates() != null) {
343+
// TODO: Should all existing templates be deleted first?
344+
for (ObjectCursor<IndexTemplateMetaData> cursor : metaData.templates().values()) {
345+
mdBuilder.put(cursor.value);
346+
}
347+
}
348+
if (metaData.customs() != null) {
349+
for (ObjectObjectCursor<String, MetaData.Custom> cursor : metaData.customs()) {
350+
if (!RepositoriesMetaData.TYPE.equals(cursor.key)) {
351+
// Don't restore repositories while we are working with them
352+
// TODO: Should we restore them at the end?
353+
mdBuilder.putCustom(cursor.key, cursor.value);
354+
}
355+
}
356+
}
357+
}
317358

318359
if (completed(shards)) {
319360
// We don't have any indices to restore - we are done
320361
restoreInfo = new RestoreInfo(snapshotId.getName(),
321-
Collections.unmodifiableList(new ArrayList<>(renamedIndices.keySet())),
362+
Collections.unmodifiableList(new ArrayList<>(indices.keySet())),
322363
shards.size(),
323364
shards.size() - failedShards(shards));
324365
}
@@ -426,32 +467,6 @@ private IndexMetaData updateIndexSettings(IndexMetaData indexMetaData, Settings
426467
return builder.settings(settingsBuilder).build();
427468
}
428469

429-
private void restoreGlobalStateIfRequested(MetaData.Builder mdBuilder) {
430-
if (request.includeGlobalState()) {
431-
if (metaData.persistentSettings() != null) {
432-
Settings settings = metaData.persistentSettings();
433-
clusterSettings.validateUpdate(settings);
434-
mdBuilder.persistentSettings(settings);
435-
}
436-
if (metaData.templates() != null) {
437-
// TODO: Should all existing templates be deleted first?
438-
for (ObjectCursor<IndexTemplateMetaData> cursor : metaData.templates().values()) {
439-
mdBuilder.put(cursor.value);
440-
}
441-
}
442-
if (metaData.customs() != null) {
443-
for (ObjectObjectCursor<String, MetaData.Custom> cursor : metaData.customs()) {
444-
if (!RepositoriesMetaData.TYPE.equals(cursor.key)) {
445-
// Don't restore repositories while we are working with them
446-
// TODO: Should we restore them at the end?
447-
mdBuilder.putCustom(cursor.key, cursor.value);
448-
}
449-
}
450-
}
451-
}
452-
}
453-
454-
455470
@Override
456471
public void onFailure(String source, Exception e) {
457472
logger.warn(() -> new ParameterizedMessage("[{}] failed to restore snapshot", snapshotId), e);
@@ -757,7 +772,7 @@ private Map<String, String> renamedIndices(RestoreRequest request, List<String>
757772
"indices [" + index + "] and [" + previousIndex + "] are renamed into the same index [" + renamedIndex + "]");
758773
}
759774
}
760-
return renamedIndices;
775+
return Collections.unmodifiableMap(renamedIndices);
761776
}
762777

763778
/**

server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public RepositoryData getRepositoryData(final String repositoryName) {
148148
* @throws SnapshotMissingException if snapshot is not found
149149
*/
150150
public SnapshotInfo snapshot(final String repositoryName, final SnapshotId snapshotId) {
151-
List<SnapshotsInProgress.Entry> entries = currentSnapshots(repositoryName, Arrays.asList(snapshotId.getName()));
151+
List<SnapshotsInProgress.Entry> entries = currentSnapshots(repositoryName, Collections.singletonList(snapshotId.getName()));
152152
if (!entries.isEmpty()) {
153153
return inProgressSnapshot(entries.iterator().next());
154154
}
@@ -593,13 +593,13 @@ public List<SnapshotsInProgress.Entry> currentSnapshots(final String repository,
593593
*/
594594
public Map<ShardId, IndexShardSnapshotStatus> snapshotShards(final String repositoryName,
595595
final SnapshotInfo snapshotInfo) throws IOException {
596-
Map<ShardId, IndexShardSnapshotStatus> shardStatus = new HashMap<>();
597-
Repository repository = repositoriesService.repository(repositoryName);
598-
RepositoryData repositoryData = repository.getRepositoryData();
599-
MetaData metaData = repository.getSnapshotMetaData(snapshotInfo, repositoryData.resolveIndices(snapshotInfo.indices()));
596+
final Repository repository = repositoriesService.repository(repositoryName);
597+
final RepositoryData repositoryData = repository.getRepositoryData();
598+
599+
final Map<ShardId, IndexShardSnapshotStatus> shardStatus = new HashMap<>();
600600
for (String index : snapshotInfo.indices()) {
601601
IndexId indexId = repositoryData.resolveIndexId(index);
602-
IndexMetaData indexMetaData = metaData.indices().get(index);
602+
IndexMetaData indexMetaData = repository.getSnapshotIndexMetaData(snapshotInfo.snapshotId(), indexId);
603603
if (indexMetaData != null) {
604604
int numberOfShards = indexMetaData.getNumberOfShards();
605605
for (int i = 0; i < numberOfShards; i++) {
@@ -633,7 +633,6 @@ public Map<ShardId, IndexShardSnapshotStatus> snapshotShards(final String reposi
633633
return unmodifiableMap(shardStatus);
634634
}
635635

636-
637636
private SnapshotShardFailure findShardFailure(List<SnapshotShardFailure> shardFailures, ShardId shardId) {
638637
for (SnapshotShardFailure shardFailure : shardFailures) {
639638
if (shardId.getIndexName().equals(shardFailure.index()) && shardId.getId() == shardFailure.shardId()) {

server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2605,7 +2605,12 @@ public SnapshotInfo getSnapshotInfo(SnapshotId snapshotId) {
26052605
}
26062606

26072607
@Override
2608-
public MetaData getSnapshotMetaData(SnapshotInfo snapshot, List<IndexId> indices) throws IOException {
2608+
public MetaData getSnapshotGlobalMetaData(SnapshotId snapshotId) {
2609+
return null;
2610+
}
2611+
2612+
@Override
2613+
public IndexMetaData getSnapshotIndexMetaData(SnapshotId snapshotId, IndexId index) throws IOException {
26092614
return null;
26102615
}
26112616

0 commit comments

Comments
 (0)