|
@@ -59,6 +59,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
import static com.google.common.collect.Lists.newArrayList;
|
|
|
import static com.google.common.collect.Maps.newHashMap;
|
|
|
import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
|
|
|
+import static com.google.common.collect.Sets.newHashSet;
|
|
|
|
|
|
/**
|
|
|
* Service responsible for creating snapshots
|
|
@@ -67,7 +68,7 @@ import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
|
|
|
* <ul>
|
|
|
* <li>On the master node the {@link #createSnapshot(SnapshotRequest, CreateSnapshotListener)} is called and makes sure that no snapshots is currently running
|
|
|
* and registers the new snapshot in cluster state</li>
|
|
|
- * <li>When cluster state is updated the {@link #beginSnapshot(ClusterState, SnapshotMetaData.Entry, CreateSnapshotListener)} method
|
|
|
+ * <li>When cluster state is updated the {@link #beginSnapshot(ClusterState, SnapshotMetaData.Entry, boolean, CreateSnapshotListener)} method
|
|
|
* kicks in and initializes the snapshot in the repository and then populates list of shards that needs to be snapshotted in cluster state</li>
|
|
|
* <li>Each data node is watching for these shards and when new shards scheduled for snapshotting appear in the cluster state, data nodes
|
|
|
* start processing them through {@link #processIndexShardSnapshots(SnapshotMetaData)} method</li>
|
|
@@ -187,7 +188,7 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
threadPool.executor(ThreadPool.Names.SNAPSHOT).execute(new Runnable() {
|
|
|
@Override
|
|
|
public void run() {
|
|
|
- beginSnapshot(newState, newSnapshot, listener);
|
|
|
+ beginSnapshot(newState, newSnapshot, request.partial, listener);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -243,9 +244,10 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
*
|
|
|
* @param clusterState cluster state
|
|
|
* @param snapshot snapshot meta data
|
|
|
+ * @param partial allow partial snapshots
|
|
|
* @param userCreateSnapshotListener listener
|
|
|
*/
|
|
|
- private void beginSnapshot(ClusterState clusterState, final SnapshotMetaData.Entry snapshot, final CreateSnapshotListener userCreateSnapshotListener) {
|
|
|
+ private void beginSnapshot(ClusterState clusterState, final SnapshotMetaData.Entry snapshot, final boolean partial, final CreateSnapshotListener userCreateSnapshotListener) {
|
|
|
boolean snapshotCreated = false;
|
|
|
try {
|
|
|
Repository repository = repositoriesService.repository(snapshot.snapshotId().getRepository());
|
|
@@ -271,6 +273,7 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
clusterService.submitStateUpdateTask("update_snapshot [" + snapshot + "]", new ProcessedClusterStateUpdateTask() {
|
|
|
boolean accepted = false;
|
|
|
SnapshotMetaData.Entry updatedSnapshot;
|
|
|
+ String failure = null;
|
|
|
|
|
|
@Override
|
|
|
public ClusterState execute(ClusterState currentState) {
|
|
@@ -282,6 +285,15 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
if (entry.snapshotId().equals(snapshot.snapshotId())) {
|
|
|
// Replace the snapshot that was just created
|
|
|
ImmutableMap<ShardId, SnapshotMetaData.ShardSnapshotStatus> shards = shards(snapshot.snapshotId(), currentState, snapshot.indices());
|
|
|
+ if (!partial) {
|
|
|
+ Set<String> indicesWithMissingShards = indicesWithMissingShards(shards);
|
|
|
+ if (indicesWithMissingShards != null) {
|
|
|
+ updatedSnapshot = new SnapshotMetaData.Entry(snapshot.snapshotId(), snapshot.includeGlobalState(), State.FAILED, snapshot.indices(), shards);
|
|
|
+ entries.add(updatedSnapshot);
|
|
|
+ failure = "Indices don't have primary shards +[" + indicesWithMissingShards + "]";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
updatedSnapshot = new SnapshotMetaData.Entry(snapshot.snapshotId(), snapshot.includeGlobalState(), State.STARTED, snapshot.indices(), shards);
|
|
|
entries.add(updatedSnapshot);
|
|
|
if (!completed(shards.values())) {
|
|
@@ -310,8 +322,11 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
userCreateSnapshotListener.onResponse();
|
|
|
|
|
|
// Now that snapshot completion listener is registered we can end the snapshot if needed
|
|
|
+ // We should end snapshot only if 1) we didn't accept it for processing (which happens when there
|
|
|
+ // is nothing to do) and 2) there was a snapshot in metadata that we should end. Otherwise we should
|
|
|
+ // go ahead and continue working on this snapshot rather then end here.
|
|
|
if (!accepted && updatedSnapshot != null) {
|
|
|
- endSnapshot(updatedSnapshot);
|
|
|
+ endSnapshot(updatedSnapshot, failure);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -602,6 +617,25 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns list of indices with missing shards
|
|
|
+ *
|
|
|
+ * @param shards list of shard statuses
|
|
|
+ * @return list of failed indices
|
|
|
+ */
|
|
|
+ private Set<String> indicesWithMissingShards(ImmutableMap<ShardId, SnapshotMetaData.ShardSnapshotStatus> shards) {
|
|
|
+ Set<String> indices = null;
|
|
|
+ for (ImmutableMap.Entry<ShardId, SnapshotMetaData.ShardSnapshotStatus> entry : shards.entrySet()) {
|
|
|
+ if (entry.getValue().state() == State.MISSING) {
|
|
|
+ if (indices == null) {
|
|
|
+ indices = newHashSet();
|
|
|
+ }
|
|
|
+ indices.add(entry.getKey().getIndex());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return indices;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Updates the shard status on master node
|
|
|
*
|
|
@@ -661,25 +695,38 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
*
|
|
|
* @param entry snapshot
|
|
|
*/
|
|
|
- private void endSnapshot(final SnapshotMetaData.Entry entry) {
|
|
|
+ private void endSnapshot(SnapshotMetaData.Entry entry) {
|
|
|
+ endSnapshot(entry, null);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Finalizes the shard in repository and then removes it from cluster state
|
|
|
+ * <p/>
|
|
|
+ * This is non-blocking method that runs on a thread from SNAPSHOT thread pool
|
|
|
+ *
|
|
|
+ * @param entry snapshot
|
|
|
+ * @param failure failure reason or null if snapshot was successful
|
|
|
+ */
|
|
|
+ private void endSnapshot(final SnapshotMetaData.Entry entry, final String failure) {
|
|
|
threadPool.executor(ThreadPool.Names.SNAPSHOT).execute(new Runnable() {
|
|
|
@Override
|
|
|
public void run() {
|
|
|
SnapshotId snapshotId = entry.snapshotId();
|
|
|
try {
|
|
|
final Repository repository = repositoriesService.repository(snapshotId.getRepository());
|
|
|
- logger.trace("[{}] finalizing snapshot in repository", snapshotId);
|
|
|
+ logger.trace("[{}] finalizing snapshot in repository, state: [{}], failure[{}]", snapshotId, entry.state(), failure);
|
|
|
ArrayList<ShardSearchFailure> failures = newArrayList();
|
|
|
ArrayList<SnapshotShardFailure> shardFailures = newArrayList();
|
|
|
for (Map.Entry<ShardId, ShardSnapshotStatus> shardStatus : entry.shards().entrySet()) {
|
|
|
ShardId shardId = shardStatus.getKey();
|
|
|
ShardSnapshotStatus status = shardStatus.getValue();
|
|
|
- if (status.state() == State.FAILED) {
|
|
|
+ if (status.state().failed()) {
|
|
|
failures.add(new ShardSearchFailure(status.reason(), new SearchShardTarget(status.nodeId(), shardId.getIndex(), shardId.id())));
|
|
|
shardFailures.add(new SnapshotShardFailure(status.nodeId(), shardId.getIndex(), shardId.id(), status.reason()));
|
|
|
}
|
|
|
}
|
|
|
- Snapshot snapshot = repository.finalizeSnapshot(snapshotId, null, entry.shards().size(), ImmutableList.copyOf(shardFailures));
|
|
|
+ Snapshot snapshot = repository.finalizeSnapshot(snapshotId, failure, entry.shards().size(), ImmutableList.copyOf(shardFailures));
|
|
|
removeSnapshotFromClusterState(snapshotId, new SnapshotInfo(snapshot), null);
|
|
|
} catch (Throwable t) {
|
|
|
logger.warn("[{}] failed to finalize snapshot", t, snapshotId);
|
|
@@ -841,16 +888,17 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
|
|
|
/**
|
|
|
* Checks if a repository is currently in use by one of the snapshots
|
|
|
+ *
|
|
|
* @param clusterState cluster state
|
|
|
- * @param repository repository id
|
|
|
+ * @param repository repository id
|
|
|
* @return true if repository is currently in use by one of the running snapshots
|
|
|
*/
|
|
|
public static boolean isRepositoryInUse(ClusterState clusterState, String repository) {
|
|
|
MetaData metaData = clusterState.metaData();
|
|
|
SnapshotMetaData snapshots = metaData.custom(SnapshotMetaData.TYPE);
|
|
|
if (snapshots != null) {
|
|
|
- for(SnapshotMetaData.Entry snapshot : snapshots.entries()) {
|
|
|
- if(repository.equals(snapshot.snapshotId().getRepository())) {
|
|
|
+ for (SnapshotMetaData.Entry snapshot : snapshots.entries()) {
|
|
|
+ if (repository.equals(snapshot.snapshotId().getRepository())) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -900,10 +948,9 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
ShardId shardId = new ShardId(index, i);
|
|
|
ShardRouting primary = indexRoutingTable.shard(i).primaryShard();
|
|
|
if (primary == null || !primary.assignedToNode()) {
|
|
|
- //TODO: Should we bailout completely or just mark this shard as failed?
|
|
|
- builder.put(shardId, new SnapshotMetaData.ShardSnapshotStatus(null, State.FAILED, "primary shard is not allocated"));
|
|
|
+ builder.put(shardId, new SnapshotMetaData.ShardSnapshotStatus(null, State.MISSING, "primary shard is not allocated"));
|
|
|
} else if (!primary.started()) {
|
|
|
- builder.put(shardId, new SnapshotMetaData.ShardSnapshotStatus(primary.currentNodeId(), State.FAILED, "primary shard hasn't been started yet"));
|
|
|
+ builder.put(shardId, new SnapshotMetaData.ShardSnapshotStatus(primary.currentNodeId(), State.MISSING, "primary shard hasn't been started yet"));
|
|
|
} else {
|
|
|
builder.put(shardId, new SnapshotMetaData.ShardSnapshotStatus(primary.currentNodeId()));
|
|
|
}
|
|
@@ -985,6 +1032,8 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
|
|
|
private IndicesOptions indicesOptions = IndicesOptions.strict();
|
|
|
|
|
|
+ private boolean partial;
|
|
|
+
|
|
|
private Settings settings;
|
|
|
|
|
|
private boolean includeGlobalState;
|
|
@@ -1059,6 +1108,17 @@ public class SnapshotsService extends AbstractComponent implements ClusterStateL
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Set to true if partial snapshot should be allowed
|
|
|
+ *
|
|
|
+ * @param partial true if partial snapshots should be allowed
|
|
|
+ * @return this request
|
|
|
+ */
|
|
|
+ public SnapshotRequest partial(boolean partial) {
|
|
|
+ this.partial = partial;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Returns cause for snapshot operation
|
|
|
*
|