|
@@ -219,15 +219,18 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
// Now we can start the actual restore process by adding shards to be recovered in the cluster state
|
|
|
// and updating cluster metadata (global and index) as needed
|
|
|
clusterService.submitStateUpdateTask(request.cause(), new ClusterStateUpdateTask() {
|
|
|
+ String restoreUUID = UUIDs.randomBase64UUID();
|
|
|
RestoreInfo restoreInfo = null;
|
|
|
|
|
|
@Override
|
|
|
public ClusterState execute(ClusterState currentState) {
|
|
|
- // Check if another restore process is already running - cannot run two restore processes at the
|
|
|
- // same time
|
|
|
RestoreInProgress restoreInProgress = currentState.custom(RestoreInProgress.TYPE);
|
|
|
- if (restoreInProgress != null && !restoreInProgress.entries().isEmpty()) {
|
|
|
- throw new ConcurrentSnapshotExecutionException(snapshot, "Restore process is already running in this cluster");
|
|
|
+ if (currentState.getNodes().getMinNodeVersion().before(Version.V_7_0_0)) {
|
|
|
+ // Check if another restore process is already running - cannot run two restore processes at the
|
|
|
+ // same time in versions prior to 7.0
|
|
|
+ if (restoreInProgress != null && restoreInProgress.isEmpty() == false) {
|
|
|
+ throw new ConcurrentSnapshotExecutionException(snapshot, "Restore process is already running in this cluster");
|
|
|
+ }
|
|
|
}
|
|
|
// Check if the snapshot to restore is currently being deleted
|
|
|
SnapshotDeletionsInProgress deletionsInProgress = currentState.custom(SnapshotDeletionsInProgress.TYPE);
|
|
@@ -253,7 +256,7 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
for (Map.Entry<String, String> indexEntry : indices.entrySet()) {
|
|
|
String index = indexEntry.getValue();
|
|
|
boolean partial = checkPartial(index);
|
|
|
- SnapshotRecoverySource recoverySource = new SnapshotRecoverySource(snapshot, snapshotInfo.version(), index);
|
|
|
+ SnapshotRecoverySource recoverySource = new SnapshotRecoverySource(restoreUUID, snapshot, snapshotInfo.version(), index);
|
|
|
String renamedIndexName = indexEntry.getKey();
|
|
|
IndexMetaData snapshotIndexMetaData = metaData.index(index);
|
|
|
snapshotIndexMetaData = updateIndexSettings(snapshotIndexMetaData, request.indexSettings, request.ignoreIndexSettings);
|
|
@@ -329,8 +332,18 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
}
|
|
|
|
|
|
shards = shardsBuilder.build();
|
|
|
- RestoreInProgress.Entry restoreEntry = new RestoreInProgress.Entry(snapshot, overallState(RestoreInProgress.State.INIT, shards), Collections.unmodifiableList(new ArrayList<>(indices.keySet())), shards);
|
|
|
- builder.putCustom(RestoreInProgress.TYPE, new RestoreInProgress(restoreEntry));
|
|
|
+ RestoreInProgress.Entry restoreEntry = new RestoreInProgress.Entry(
|
|
|
+ restoreUUID, snapshot, overallState(RestoreInProgress.State.INIT, shards),
|
|
|
+ Collections.unmodifiableList(new ArrayList<>(indices.keySet())),
|
|
|
+ shards
|
|
|
+ );
|
|
|
+ RestoreInProgress.Builder restoreInProgressBuilder;
|
|
|
+ if (restoreInProgress != null) {
|
|
|
+ restoreInProgressBuilder = new RestoreInProgress.Builder(restoreInProgress);
|
|
|
+ } else {
|
|
|
+ restoreInProgressBuilder = new RestoreInProgress.Builder();
|
|
|
+ }
|
|
|
+ builder.putCustom(RestoreInProgress.TYPE, restoreInProgressBuilder.add(restoreEntry).build());
|
|
|
} else {
|
|
|
shards = ImmutableOpenMap.of();
|
|
|
}
|
|
@@ -485,7 +498,7 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
|
|
|
@Override
|
|
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
|
|
- listener.onResponse(new RestoreCompletionResponse(snapshot, restoreInfo));
|
|
|
+ listener.onResponse(new RestoreCompletionResponse(restoreUUID, snapshot, restoreInfo));
|
|
|
}
|
|
|
});
|
|
|
|
|
@@ -498,8 +511,8 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
|
|
|
public static RestoreInProgress updateRestoreStateWithDeletedIndices(RestoreInProgress oldRestore, Set<Index> deletedIndices) {
|
|
|
boolean changesMade = false;
|
|
|
- final List<RestoreInProgress.Entry> entries = new ArrayList<>();
|
|
|
- for (RestoreInProgress.Entry entry : oldRestore.entries()) {
|
|
|
+ RestoreInProgress.Builder builder = new RestoreInProgress.Builder();
|
|
|
+ for (RestoreInProgress.Entry entry : oldRestore) {
|
|
|
ImmutableOpenMap.Builder<ShardId, ShardRestoreStatus> shardsBuilder = null;
|
|
|
for (ObjectObjectCursor<ShardId, ShardRestoreStatus> cursor : entry.shards()) {
|
|
|
ShardId shardId = cursor.key;
|
|
@@ -513,27 +526,33 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
}
|
|
|
if (shardsBuilder != null) {
|
|
|
ImmutableOpenMap<ShardId, ShardRestoreStatus> shards = shardsBuilder.build();
|
|
|
- entries.add(new RestoreInProgress.Entry(entry.snapshot(), overallState(RestoreInProgress.State.STARTED, shards), entry.indices(), shards));
|
|
|
+ builder.add(new RestoreInProgress.Entry(entry.uuid(), entry.snapshot(), overallState(RestoreInProgress.State.STARTED, shards), entry.indices(), shards));
|
|
|
} else {
|
|
|
- entries.add(entry);
|
|
|
+ builder.add(entry);
|
|
|
}
|
|
|
}
|
|
|
if (changesMade) {
|
|
|
- return new RestoreInProgress(entries.toArray(new RestoreInProgress.Entry[entries.size()]));
|
|
|
+ return builder.build();
|
|
|
} else {
|
|
|
return oldRestore;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public static final class RestoreCompletionResponse {
|
|
|
+ private final String uuid;
|
|
|
private final Snapshot snapshot;
|
|
|
private final RestoreInfo restoreInfo;
|
|
|
|
|
|
- private RestoreCompletionResponse(final Snapshot snapshot, final RestoreInfo restoreInfo) {
|
|
|
+ private RestoreCompletionResponse(final String uuid, final Snapshot snapshot, final RestoreInfo restoreInfo) {
|
|
|
+ this.uuid = uuid;
|
|
|
this.snapshot = snapshot;
|
|
|
this.restoreInfo = restoreInfo;
|
|
|
}
|
|
|
|
|
|
+ public String getUuid() {
|
|
|
+ return uuid;
|
|
|
+ }
|
|
|
+
|
|
|
public Snapshot getSnapshot() {
|
|
|
return snapshot;
|
|
|
}
|
|
@@ -544,7 +563,7 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
}
|
|
|
|
|
|
public static class RestoreInProgressUpdater extends RoutingChangesObserver.AbstractRoutingChangesObserver {
|
|
|
- private final Map<Snapshot, Updates> shardChanges = new HashMap<>();
|
|
|
+ private final Map<String, Updates> shardChanges = new HashMap<>();
|
|
|
|
|
|
@Override
|
|
|
public void shardStarted(ShardRouting initializingShard, ShardRouting startedShard) {
|
|
@@ -552,8 +571,8 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
if (initializingShard.primary()) {
|
|
|
RecoverySource recoverySource = initializingShard.recoverySource();
|
|
|
if (recoverySource.getType() == RecoverySource.Type.SNAPSHOT) {
|
|
|
- Snapshot snapshot = ((SnapshotRecoverySource) recoverySource).snapshot();
|
|
|
- changes(snapshot).shards.put(initializingShard.shardId(),
|
|
|
+ changes(recoverySource).shards.put(
|
|
|
+ initializingShard.shardId(),
|
|
|
new ShardRestoreStatus(initializingShard.currentNodeId(), RestoreInProgress.State.SUCCESS));
|
|
|
}
|
|
|
}
|
|
@@ -564,13 +583,13 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
if (failedShard.primary() && failedShard.initializing()) {
|
|
|
RecoverySource recoverySource = failedShard.recoverySource();
|
|
|
if (recoverySource.getType() == RecoverySource.Type.SNAPSHOT) {
|
|
|
- Snapshot snapshot = ((SnapshotRecoverySource) recoverySource).snapshot();
|
|
|
// mark restore entry for this shard as failed when it's due to a file corruption. There is no need wait on retries
|
|
|
// to restore this shard on another node if the snapshot files are corrupt. In case where a node just left or crashed,
|
|
|
// however, we only want to acknowledge the restore operation once it has been successfully restored on another node.
|
|
|
if (unassignedInfo.getFailure() != null && Lucene.isCorruptionException(unassignedInfo.getFailure().getCause())) {
|
|
|
- changes(snapshot).shards.put(failedShard.shardId(), new ShardRestoreStatus(failedShard.currentNodeId(),
|
|
|
- RestoreInProgress.State.FAILURE, unassignedInfo.getFailure().getCause().getMessage()));
|
|
|
+ changes(recoverySource).shards.put(
|
|
|
+ failedShard.shardId(), new ShardRestoreStatus(failedShard.currentNodeId(),
|
|
|
+ RestoreInProgress.State.FAILURE, unassignedInfo.getFailure().getCause().getMessage()));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -581,9 +600,11 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
// if we force an empty primary, we should also fail the restore entry
|
|
|
if (unassignedShard.recoverySource().getType() == RecoverySource.Type.SNAPSHOT &&
|
|
|
initializedShard.recoverySource().getType() != RecoverySource.Type.SNAPSHOT) {
|
|
|
- Snapshot snapshot = ((SnapshotRecoverySource) unassignedShard.recoverySource()).snapshot();
|
|
|
- changes(snapshot).shards.put(unassignedShard.shardId(), new ShardRestoreStatus(null,
|
|
|
- RestoreInProgress.State.FAILURE, "recovery source type changed from snapshot to " + initializedShard.recoverySource()));
|
|
|
+ changes(unassignedShard.recoverySource()).shards.put(
|
|
|
+ unassignedShard.shardId(),
|
|
|
+ new ShardRestoreStatus(null,
|
|
|
+ RestoreInProgress.State.FAILURE, "recovery source type changed from snapshot to " + initializedShard.recoverySource())
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -592,19 +613,21 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
RecoverySource recoverySource = unassignedShard.recoverySource();
|
|
|
if (recoverySource.getType() == RecoverySource.Type.SNAPSHOT) {
|
|
|
if (newUnassignedInfo.getLastAllocationStatus() == UnassignedInfo.AllocationStatus.DECIDERS_NO) {
|
|
|
- Snapshot snapshot = ((SnapshotRecoverySource) recoverySource).snapshot();
|
|
|
String reason = "shard could not be allocated to any of the nodes";
|
|
|
- changes(snapshot).shards.put(unassignedShard.shardId(),
|
|
|
+ changes(recoverySource).shards.put(
|
|
|
+ unassignedShard.shardId(),
|
|
|
new ShardRestoreStatus(unassignedShard.currentNodeId(), RestoreInProgress.State.FAILURE, reason));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Helper method that creates update entry for the given shard id if such an entry does not exist yet.
|
|
|
+ * Helper method that creates update entry for the given recovery source's restore uuid
|
|
|
+ * if such an entry does not exist yet.
|
|
|
*/
|
|
|
- private Updates changes(Snapshot snapshot) {
|
|
|
- return shardChanges.computeIfAbsent(snapshot, k -> new Updates());
|
|
|
+ private Updates changes(RecoverySource recoverySource) {
|
|
|
+ assert recoverySource.getType() == RecoverySource.Type.SNAPSHOT;
|
|
|
+ return shardChanges.computeIfAbsent(((SnapshotRecoverySource) recoverySource).restoreUUID(), k -> new Updates());
|
|
|
}
|
|
|
|
|
|
private static class Updates {
|
|
@@ -613,38 +636,38 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
|
|
|
public RestoreInProgress applyChanges(final RestoreInProgress oldRestore) {
|
|
|
if (shardChanges.isEmpty() == false) {
|
|
|
- final List<RestoreInProgress.Entry> entries = new ArrayList<>();
|
|
|
- for (RestoreInProgress.Entry entry : oldRestore.entries()) {
|
|
|
- Snapshot snapshot = entry.snapshot();
|
|
|
- Updates updates = shardChanges.get(snapshot);
|
|
|
- if (updates.shards.isEmpty() == false) {
|
|
|
- ImmutableOpenMap.Builder<ShardId, ShardRestoreStatus> shardsBuilder = ImmutableOpenMap.builder(entry.shards());
|
|
|
+ RestoreInProgress.Builder builder = new RestoreInProgress.Builder();
|
|
|
+ for (RestoreInProgress.Entry entry : oldRestore) {
|
|
|
+ Updates updates = shardChanges.get(entry.uuid());
|
|
|
+ ImmutableOpenMap<ShardId, ShardRestoreStatus> shardStates = entry.shards();
|
|
|
+ if (updates != null && updates.shards.isEmpty() == false) {
|
|
|
+ ImmutableOpenMap.Builder<ShardId, ShardRestoreStatus> shardsBuilder = ImmutableOpenMap.builder(shardStates);
|
|
|
for (Map.Entry<ShardId, ShardRestoreStatus> shard : updates.shards.entrySet()) {
|
|
|
- shardsBuilder.put(shard.getKey(), shard.getValue());
|
|
|
+ ShardId shardId = shard.getKey();
|
|
|
+ ShardRestoreStatus status = shardStates.get(shardId);
|
|
|
+ if (status == null || status.state().completed() == false) {
|
|
|
+ shardsBuilder.put(shardId, shard.getValue());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
ImmutableOpenMap<ShardId, ShardRestoreStatus> shards = shardsBuilder.build();
|
|
|
RestoreInProgress.State newState = overallState(RestoreInProgress.State.STARTED, shards);
|
|
|
- entries.add(new RestoreInProgress.Entry(entry.snapshot(), newState, entry.indices(), shards));
|
|
|
+ builder.add(new RestoreInProgress.Entry(entry.uuid(), entry.snapshot(), newState, entry.indices(), shards));
|
|
|
} else {
|
|
|
- entries.add(entry);
|
|
|
+ builder.add(entry);
|
|
|
}
|
|
|
}
|
|
|
- return new RestoreInProgress(entries.toArray(new RestoreInProgress.Entry[entries.size()]));
|
|
|
+ return builder.build();
|
|
|
} else {
|
|
|
return oldRestore;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public static RestoreInProgress.Entry restoreInProgress(ClusterState state, Snapshot snapshot) {
|
|
|
+ public static RestoreInProgress.Entry restoreInProgress(ClusterState state, String restoreUUID) {
|
|
|
final RestoreInProgress restoreInProgress = state.custom(RestoreInProgress.TYPE);
|
|
|
if (restoreInProgress != null) {
|
|
|
- for (RestoreInProgress.Entry e : restoreInProgress.entries()) {
|
|
|
- if (e.snapshot().equals(snapshot)) {
|
|
|
- return e;
|
|
|
- }
|
|
|
- }
|
|
|
+ return restoreInProgress.get(restoreUUID);
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
@@ -652,15 +675,15 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
static class CleanRestoreStateTaskExecutor implements ClusterStateTaskExecutor<CleanRestoreStateTaskExecutor.Task>, ClusterStateTaskListener {
|
|
|
|
|
|
static class Task {
|
|
|
- final Snapshot snapshot;
|
|
|
+ final String uuid;
|
|
|
|
|
|
- Task(Snapshot snapshot) {
|
|
|
- this.snapshot = snapshot;
|
|
|
+ Task(String uuid) {
|
|
|
+ this.uuid = uuid;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
- return "clean restore state for restoring snapshot " + snapshot;
|
|
|
+ return "clean restore state for restore " + uuid;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -673,25 +696,24 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
@Override
|
|
|
public ClusterTasksResult<Task> execute(final ClusterState currentState, final List<Task> tasks) throws Exception {
|
|
|
final ClusterTasksResult.Builder<Task> resultBuilder = ClusterTasksResult.<Task>builder().successes(tasks);
|
|
|
- Set<Snapshot> completedSnapshots = tasks.stream().map(e -> e.snapshot).collect(Collectors.toSet());
|
|
|
- final List<RestoreInProgress.Entry> entries = new ArrayList<>();
|
|
|
+ Set<String> completedRestores = tasks.stream().map(e -> e.uuid).collect(Collectors.toSet());
|
|
|
+ RestoreInProgress.Builder restoreInProgressBuilder = new RestoreInProgress.Builder();
|
|
|
final RestoreInProgress restoreInProgress = currentState.custom(RestoreInProgress.TYPE);
|
|
|
boolean changed = false;
|
|
|
if (restoreInProgress != null) {
|
|
|
- for (RestoreInProgress.Entry entry : restoreInProgress.entries()) {
|
|
|
- if (completedSnapshots.contains(entry.snapshot()) == false) {
|
|
|
- entries.add(entry);
|
|
|
- } else {
|
|
|
+ for (RestoreInProgress.Entry entry : restoreInProgress) {
|
|
|
+ if (completedRestores.contains(entry.uuid())) {
|
|
|
changed = true;
|
|
|
+ } else {
|
|
|
+ restoreInProgressBuilder.add(entry);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (changed == false) {
|
|
|
return resultBuilder.build(currentState);
|
|
|
}
|
|
|
- RestoreInProgress updatedRestoreInProgress = new RestoreInProgress(entries.toArray(new RestoreInProgress.Entry[entries.size()]));
|
|
|
ImmutableOpenMap.Builder<String, ClusterState.Custom> builder = ImmutableOpenMap.builder(currentState.getCustoms());
|
|
|
- builder.put(RestoreInProgress.TYPE, updatedRestoreInProgress);
|
|
|
+ builder.put(RestoreInProgress.TYPE, restoreInProgressBuilder.build());
|
|
|
ImmutableOpenMap<String, ClusterState.Custom> customs = builder.build();
|
|
|
return resultBuilder.build(ClusterState.builder(currentState).customs(customs).build());
|
|
|
}
|
|
@@ -713,12 +735,12 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
|
|
|
RestoreInProgress restoreInProgress = state.custom(RestoreInProgress.TYPE);
|
|
|
if (restoreInProgress != null) {
|
|
|
- for (RestoreInProgress.Entry entry : restoreInProgress.entries()) {
|
|
|
+ for (RestoreInProgress.Entry entry : restoreInProgress) {
|
|
|
if (entry.state().completed()) {
|
|
|
assert completed(entry.shards()) : "state says completed but restore entries are not";
|
|
|
clusterService.submitStateUpdateTask(
|
|
|
"clean up snapshot restore state",
|
|
|
- new CleanRestoreStateTaskExecutor.Task(entry.snapshot()),
|
|
|
+ new CleanRestoreStateTaskExecutor.Task(entry.uuid()),
|
|
|
ClusterStateTaskConfig.build(Priority.URGENT),
|
|
|
cleanRestoreStateTaskExecutor,
|
|
|
cleanRestoreStateTaskExecutor);
|
|
@@ -815,7 +837,7 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
RestoreInProgress restore = currentState.custom(RestoreInProgress.TYPE);
|
|
|
if (restore != null) {
|
|
|
Set<Index> indicesToFail = null;
|
|
|
- for (RestoreInProgress.Entry entry : restore.entries()) {
|
|
|
+ for (RestoreInProgress.Entry entry : restore) {
|
|
|
for (ObjectObjectCursor<ShardId, RestoreInProgress.ShardRestoreStatus> shard : entry.shards()) {
|
|
|
if (!shard.value.state().completed()) {
|
|
|
IndexMetaData indexMetaData = currentState.metaData().index(shard.key.getIndex());
|
|
@@ -853,10 +875,10 @@ public class RestoreService implements ClusterStateApplier {
|
|
|
* @return true if repository is currently in use by one of the running snapshots
|
|
|
*/
|
|
|
public static boolean isRepositoryInUse(ClusterState clusterState, String repository) {
|
|
|
- RestoreInProgress snapshots = clusterState.custom(RestoreInProgress.TYPE);
|
|
|
- if (snapshots != null) {
|
|
|
- for (RestoreInProgress.Entry snapshot : snapshots.entries()) {
|
|
|
- if (repository.equals(snapshot.snapshot().getRepository())) {
|
|
|
+ RestoreInProgress restoreInProgress = clusterState.custom(RestoreInProgress.TYPE);
|
|
|
+ if (restoreInProgress != null) {
|
|
|
+ for (RestoreInProgress.Entry entry: restoreInProgress) {
|
|
|
+ if (repository.equals(entry.snapshot().getRepository())) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|