Skip to content

Commit 1425e28

Browse files
committed
Add ability to restore partial snapshots
Closes #5742
1 parent 46f1e30 commit 1425e28

File tree

8 files changed

+291
-198
lines changed

8 files changed

+291
-198
lines changed

docs/reference/modules/snapshots.asciidoc

+10
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,16 @@ didn't exist in the cluster. If cluster state is restored, the restored template
207207
cluster are added and existing templates with the same name are replaced by the restored templates. The restored
208208
persistent settings are added to the existing persistent settings.
209209

210+
[float]
211+
=== Partial restore
212+
213+
added[1.3.0]
214+
215+
By default, entire restore operation will fail if one or more indices participating in the operation don't have
216+
snapshots of all shards available. It can occur if some shards failed to snapshot for example. It is still possible to
217+
restore such indices by setting `partial` to `true`. Please note, that only successfully snapshotted shards will be
218+
restored in this case and all missing shards will be recreated empty.
219+
210220

211221
[float]
212222
=== Snapshot status

src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java

+31
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.ElasticsearchGenerationException;
2323
import org.elasticsearch.ElasticsearchIllegalArgumentException;
24+
import org.elasticsearch.Version;
2425
import org.elasticsearch.action.ActionRequestValidationException;
2526
import org.elasticsearch.action.support.IndicesOptions;
2627
import org.elasticsearch.action.support.master.MasterNodeOperationRequest;
@@ -67,6 +68,8 @@ public class RestoreSnapshotRequest extends MasterNodeOperationRequest<RestoreSn
6768

6869
private boolean includeGlobalState = true;
6970

71+
private boolean partial = false;
72+
7073
private Settings settings = EMPTY_SETTINGS;
7174

7275
RestoreSnapshotRequest() {
@@ -272,6 +275,26 @@ public boolean waitForCompletion() {
272275
return waitForCompletion;
273276
}
274277

278+
/**
279+
* Returns true if indices with failed to snapshot shards should be partially restored.
280+
*
281+
* @return true if indices with failed to snapshot shards should be partially restored
282+
*/
283+
public boolean partial() {
284+
return partial;
285+
}
286+
287+
/**
288+
* Set to true to allow indices with failed to snapshot shards should be partially restored.
289+
*
290+
* @param partial true if indices with failed to snapshot shards should be partially restored.
291+
* @return this request
292+
*/
293+
public RestoreSnapshotRequest partial(boolean partial) {
294+
this.partial = partial;
295+
return this;
296+
}
297+
275298
/**
276299
* Sets repository-specific restore settings.
277300
* <p/>
@@ -405,6 +428,8 @@ public RestoreSnapshotRequest source(Map source) {
405428
expandWildcardsOpen = nodeBooleanValue(entry.getValue());
406429
} else if (name.equals("expand_wildcards_closed") || name.equals("expandWildcardsClosed")) {
407430
expandWildcardsClosed = nodeBooleanValue(entry.getValue());
431+
} else if (name.equals("partial")) {
432+
partial(nodeBooleanValue(entry.getValue()));
408433
} else if (name.equals("settings")) {
409434
if (!(entry.getValue() instanceof Map)) {
410435
throw new ElasticsearchIllegalArgumentException("malformed settings section, should indices an inner object");
@@ -511,6 +536,9 @@ public void readFrom(StreamInput in) throws IOException {
511536
renameReplacement = in.readOptionalString();
512537
waitForCompletion = in.readBoolean();
513538
includeGlobalState = in.readBoolean();
539+
if (in.getVersion().onOrAfter(Version.V_1_3_0)) {
540+
partial = in.readBoolean();
541+
}
514542
settings = readSettingsFromStream(in);
515543
}
516544

@@ -525,6 +553,9 @@ public void writeTo(StreamOutput out) throws IOException {
525553
out.writeOptionalString(renameReplacement);
526554
out.writeBoolean(waitForCompletion);
527555
out.writeBoolean(includeGlobalState);
556+
if (out.getVersion().onOrAfter(Version.V_1_3_0)) {
557+
out.writeBoolean(partial);
558+
}
528559
writeSettingsToStream(settings, out);
529560
}
530561
}

src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java

+11
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,17 @@ public RestoreSnapshotRequestBuilder setRestoreGlobalState(boolean restoreGlobal
208208
return this;
209209
}
210210

211+
/**
212+
* If set to true the restore procedure will restore partially snapshotted indices
213+
*
214+
* @param partial true if partially snapshotted indices should be restored
215+
* @return this request
216+
*/
217+
public RestoreSnapshotRequestBuilder setPartial(boolean partial) {
218+
request.partial(partial);
219+
return this;
220+
}
221+
211222
@Override
212223
protected void doExecute(ActionListener<RestoreSnapshotResponse> listener) {
213224
client.restoreSnapshot(request, listener);

src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java

+4-9
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,10 @@ protected ClusterBlockException checkBlock(RestoreSnapshotRequest request, Clust
7575

7676
@Override
7777
protected void masterOperation(final RestoreSnapshotRequest request, ClusterState state, final ActionListener<RestoreSnapshotResponse> listener) throws ElasticsearchException {
78-
RestoreService.RestoreRequest restoreRequest =
79-
new RestoreService.RestoreRequest("restore_snapshot[" + request.snapshot() + "]", request.repository(), request.snapshot())
80-
.indices(request.indices())
81-
.indicesOptions(request.indicesOptions())
82-
.renamePattern(request.renamePattern())
83-
.renameReplacement(request.renameReplacement())
84-
.includeGlobalState(request.includeGlobalState())
85-
.settings(request.settings())
86-
.masterNodeTimeout(request.masterNodeTimeout());
78+
RestoreService.RestoreRequest restoreRequest = new RestoreService.RestoreRequest(
79+
"restore_snapshot[" + request.snapshot() + "]", request.repository(), request.snapshot(),
80+
request.indices(), request.indicesOptions(), request.renamePattern(), request.renameReplacement(),
81+
request.settings(), request.masterNodeTimeout(), request.includeGlobalState(), request.partial());
8782
restoreService.restoreSnapshot(restoreRequest, new RestoreSnapshotListener() {
8883
@Override
8984
public void onResponse(RestoreInfo restoreInfo) {

src/main/java/org/elasticsearch/cluster/routing/IndexRoutingTable.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.cluster.routing;
2121

22+
import com.carrotsearch.hppc.IntSet;
2223
import com.carrotsearch.hppc.cursors.IntCursor;
2324
import com.carrotsearch.hppc.cursors.IntObjectCursor;
2425
import com.google.common.collect.ImmutableList;
@@ -381,28 +382,33 @@ public Builder initializeAsRecovery(IndexMetaData indexMetaData) {
381382
/**
382383
* Initializes a new empty index, to be restored from a snapshot
383384
*/
384-
public Builder initializeAsNewRestore(IndexMetaData indexMetaData, RestoreSource restoreSource) {
385-
return initializeAsRestore(indexMetaData, restoreSource, true);
385+
public Builder initializeAsNewRestore(IndexMetaData indexMetaData, RestoreSource restoreSource, IntSet ignoreShards) {
386+
return initializeAsRestore(indexMetaData, restoreSource, ignoreShards, true);
386387
}
387388

388389
/**
389390
* Initializes an existing index, to be restored from a snapshot
390391
*/
391392
public Builder initializeAsRestore(IndexMetaData indexMetaData, RestoreSource restoreSource) {
392-
return initializeAsRestore(indexMetaData, restoreSource, false);
393+
return initializeAsRestore(indexMetaData, restoreSource, null, false);
393394
}
394395

395396
/**
396397
* Initializes an index, to be restored from snapshot
397398
*/
398-
private Builder initializeAsRestore(IndexMetaData indexMetaData, RestoreSource restoreSource, boolean asNew) {
399+
private Builder initializeAsRestore(IndexMetaData indexMetaData, RestoreSource restoreSource, IntSet ignoreShards, boolean asNew) {
399400
if (!shards.isEmpty()) {
400401
throw new ElasticsearchIllegalStateException("trying to initialize an index with fresh shards, but already has shards created");
401402
}
402403
for (int shardId = 0; shardId < indexMetaData.numberOfShards(); shardId++) {
403404
IndexShardRoutingTable.Builder indexShardRoutingBuilder = new IndexShardRoutingTable.Builder(new ShardId(indexMetaData.index(), shardId), asNew ? false : true);
404405
for (int i = 0; i <= indexMetaData.numberOfReplicas(); i++) {
405-
indexShardRoutingBuilder.addShard(new ImmutableShardRouting(index, shardId, null, null, i == 0 ? restoreSource : null, i == 0, ShardRoutingState.UNASSIGNED, 0));
406+
if (asNew && ignoreShards.contains(shardId)) {
407+
// This shards wasn't completely snapshotted - restore it as new shard
408+
indexShardRoutingBuilder.addShard(new ImmutableShardRouting(index, shardId, null, i == 0, ShardRoutingState.UNASSIGNED, 0));
409+
} else {
410+
indexShardRoutingBuilder.addShard(new ImmutableShardRouting(index, shardId, null, null, i == 0 ? restoreSource : null, i == 0, ShardRoutingState.UNASSIGNED, 0));
411+
}
406412
}
407413
shards.put(shardId, indexShardRoutingBuilder.build());
408414
}

src/main/java/org/elasticsearch/cluster/routing/RoutingTable.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.cluster.routing;
2121

22+
import com.carrotsearch.hppc.IntSet;
2223
import com.google.common.collect.*;
2324
import org.elasticsearch.cluster.ClusterState;
2425
import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -417,9 +418,9 @@ public Builder addAsRestore(IndexMetaData indexMetaData, RestoreSource restoreSo
417418
return this;
418419
}
419420

420-
public Builder addAsNewRestore(IndexMetaData indexMetaData, RestoreSource restoreSource) {
421+
public Builder addAsNewRestore(IndexMetaData indexMetaData, RestoreSource restoreSource, IntSet ignoreShards) {
421422
IndexRoutingTable.Builder indexRoutingBuilder = new IndexRoutingTable.Builder(indexMetaData.index())
422-
.initializeAsNewRestore(indexMetaData, restoreSource);
423+
.initializeAsNewRestore(indexMetaData, restoreSource, ignoreShards);
423424
add(indexRoutingBuilder);
424425
return this;
425426
}

0 commit comments

Comments
 (0)