diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java index e8c797e45a0..a4613af601a 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java @@ -19,8 +19,10 @@ package org.elasticsearch.action.admin.cluster.snapshots.status; +import org.elasticsearch.Version; import org.elasticsearch.cluster.SnapshotsInProgress.State; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; @@ -57,10 +59,15 @@ public class SnapshotStatus implements ToXContentObject, Streamable { private SnapshotStats stats; - SnapshotStatus(final Snapshot snapshot, final State state, final List shards) { + @Nullable + private Boolean includeGlobalState; + + SnapshotStatus(final Snapshot snapshot, final State state, final List shards, + final Boolean includeGlobalState) { this.snapshot = Objects.requireNonNull(snapshot); this.state = Objects.requireNonNull(state); this.shards = Objects.requireNonNull(shards); + this.includeGlobalState = includeGlobalState; shardsStats = new SnapshotShardsStats(shards); updateShardStats(); } @@ -82,6 +89,14 @@ public class SnapshotStatus implements ToXContentObject, Streamable { return state; } + /** + * Returns true if global state is included in the snapshot, false otherwise. + * Can be null if this information is unknown. + */ + public Boolean includeGlobalState() { + return includeGlobalState; + } + /** * Returns list of snapshot shards */ @@ -132,6 +147,9 @@ public class SnapshotStatus implements ToXContentObject, Streamable { builder.add(SnapshotIndexShardStatus.readShardSnapshotStatus(in)); } shards = Collections.unmodifiableList(builder); + if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { + includeGlobalState = in.readOptionalBoolean(); + } updateShardStats(); } @@ -143,6 +161,9 @@ public class SnapshotStatus implements ToXContentObject, Streamable { for (SnapshotIndexShardStatus shard : shards) { shard.writeTo(out); } + if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) { + out.writeOptionalBoolean(includeGlobalState); + } } /** @@ -174,6 +195,7 @@ public class SnapshotStatus implements ToXContentObject, Streamable { private static final String UUID = "uuid"; private static final String STATE = "state"; private static final String INDICES = "indices"; + private static final String INCLUDE_GLOBAL_STATE = "include_global_state"; @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { @@ -182,6 +204,9 @@ public class SnapshotStatus implements ToXContentObject, Streamable { builder.field(REPOSITORY, snapshot.getRepository()); builder.field(UUID, snapshot.getSnapshotId().getUUID()); builder.field(STATE, state.name()); + if (includeGlobalState != null) { + builder.field(INCLUDE_GLOBAL_STATE, includeGlobalState); + } shardsStats.toXContent(builder, params); stats.toXContent(builder, params); builder.startObject(INDICES); diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java index 88884fd4395..71bb1995dd5 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java @@ -196,7 +196,8 @@ public class TransportSnapshotsStatusAction extends TransportMasterNodeAction indices, long startTime, String failure, int totalShards, - List shardFailures, long repositoryStateId); + List shardFailures, long repositoryStateId, boolean includeGlobalState); /** * Deletes snapshot diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 440867577ea..a66a5a51d10 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -468,11 +468,12 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp final String failure, final int totalShards, final List shardFailures, - final long repositoryStateId) { - + final long repositoryStateId, + final boolean includeGlobalState) { SnapshotInfo blobStoreSnapshot = new SnapshotInfo(snapshotId, indices.stream().map(IndexId::getName).collect(Collectors.toList()), - startTime, failure, System.currentTimeMillis(), totalShards, shardFailures); + startTime, failure, System.currentTimeMillis(), totalShards, shardFailures, + includeGlobalState); try { snapshotFormat.write(blobStoreSnapshot, snapshotsBlobContainer, snapshotId.getUUID()); final RepositoryData repositoryData = getRepositoryData(); diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index de0a52ed0e4..ea2a9ac78dd 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -69,8 +69,10 @@ public final class SnapshotInfo implements Comparable, ToXContent, private static final String NAME = "name"; private static final String TOTAL_SHARDS = "total_shards"; private static final String SUCCESSFUL_SHARDS = "successful_shards"; + private static final String INCLUDE_GLOBAL_STATE = "include_global_state"; private static final Version VERSION_INCOMPATIBLE_INTRODUCED = Version.V_5_2_0; + private static final Version INCLUDE_GLOBAL_STATE_INTRODUCED = Version.V_7_0_0_alpha1; public static final Version VERBOSE_INTRODUCED = Version.V_5_5_0; private static final Comparator COMPARATOR = @@ -94,27 +96,32 @@ public final class SnapshotInfo implements Comparable, ToXContent, private final int successfulShards; + @Nullable + private Boolean includeGlobalState; + @Nullable private final Version version; private final List shardFailures; public SnapshotInfo(SnapshotId snapshotId, List indices, SnapshotState state) { - this(snapshotId, indices, state, null, null, 0L, 0L, 0, 0, Collections.emptyList()); + this(snapshotId, indices, state, null, null, 0L, 0L, 0, 0, Collections.emptyList(), null); } - public SnapshotInfo(SnapshotId snapshotId, List indices, long startTime) { - this(snapshotId, indices, SnapshotState.IN_PROGRESS, null, Version.CURRENT, startTime, 0L, 0, 0, Collections.emptyList()); + public SnapshotInfo(SnapshotId snapshotId, List indices, long startTime, Boolean includeGlobalState) { + this(snapshotId, indices, SnapshotState.IN_PROGRESS, null, Version.CURRENT, startTime, 0L, 0, 0, + Collections.emptyList(), includeGlobalState); } public SnapshotInfo(SnapshotId snapshotId, List indices, long startTime, String reason, long endTime, - int totalShards, List shardFailures) { + int totalShards, List shardFailures, Boolean includeGlobalState) { this(snapshotId, indices, snapshotState(reason, shardFailures), reason, Version.CURRENT, - startTime, endTime, totalShards, totalShards - shardFailures.size(), shardFailures); + startTime, endTime, totalShards, totalShards - shardFailures.size(), shardFailures, includeGlobalState); } private SnapshotInfo(SnapshotId snapshotId, List indices, SnapshotState state, String reason, Version version, - long startTime, long endTime, int totalShards, int successfulShards, List shardFailures) { + long startTime, long endTime, int totalShards, int successfulShards, List shardFailures, + Boolean includeGlobalState) { this.snapshotId = Objects.requireNonNull(snapshotId); this.indices = Collections.unmodifiableList(Objects.requireNonNull(indices)); this.state = state; @@ -125,6 +132,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, this.totalShards = totalShards; this.successfulShards = successfulShards; this.shardFailures = Objects.requireNonNull(shardFailures); + this.includeGlobalState = includeGlobalState; } /** @@ -163,6 +171,9 @@ public final class SnapshotInfo implements Comparable, ToXContent, } else { version = in.readBoolean() ? Version.readVersion(in) : null; } + if (in.getVersion().onOrAfter(INCLUDE_GLOBAL_STATE_INTRODUCED)) { + includeGlobalState = in.readOptionalBoolean(); + } } /** @@ -172,7 +183,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, public static SnapshotInfo incompatible(SnapshotId snapshotId) { return new SnapshotInfo(snapshotId, Collections.emptyList(), SnapshotState.INCOMPATIBLE, "the snapshot is incompatible with the current version of Elasticsearch and its exact version is unknown", - null, 0L, 0L, 0, 0, Collections.emptyList()); + null, 0L, 0L, 0, 0, Collections.emptyList(), null); } /** @@ -271,6 +282,10 @@ public final class SnapshotInfo implements Comparable, ToXContent, return successfulShards; } + public Boolean includeGlobalState() { + return includeGlobalState; + } + /** * Returns shard failures; an empty list will be returned if there were no shard * failures, or if {@link #state()} returns {@code null}. @@ -361,6 +376,9 @@ public final class SnapshotInfo implements Comparable, ToXContent, builder.value(index); } builder.endArray(); + if (includeGlobalState != null) { + builder.field(INCLUDE_GLOBAL_STATE, includeGlobalState); + } if (verbose || state != null) { builder.field(STATE, state); } @@ -411,6 +429,9 @@ public final class SnapshotInfo implements Comparable, ToXContent, if (reason != null) { builder.field(REASON, reason); } + if (includeGlobalState != null) { + builder.field(INCLUDE_GLOBAL_STATE, includeGlobalState); + } builder.field(START_TIME, startTime); builder.field(END_TIME, endTime); builder.field(TOTAL_SHARDS, totalShards); @@ -442,6 +463,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, long endTime = 0; int totalShards = 0; int successfulShards = 0; + Boolean includeGlobalState = null; List shardFailures = Collections.emptyList(); if (parser.currentToken() == null) { // fresh parser? move to the first token parser.nextToken(); @@ -476,6 +498,8 @@ public final class SnapshotInfo implements Comparable, ToXContent, successfulShards = parser.intValue(); } else if (VERSION_ID.equals(currentFieldName)) { version = Version.fromId(parser.intValue()); + } else if (INCLUDE_GLOBAL_STATE.equals(currentFieldName)) { + includeGlobalState = parser.booleanValue(); } } else if (token == XContentParser.Token.START_ARRAY) { if (INDICES.equals(currentFieldName)) { @@ -517,7 +541,8 @@ public final class SnapshotInfo implements Comparable, ToXContent, endTime, totalShards, successfulShards, - shardFailures); + shardFailures, + includeGlobalState); } @Override @@ -564,6 +589,9 @@ public final class SnapshotInfo implements Comparable, ToXContent, out.writeBoolean(false); } } + if (out.getVersion().onOrAfter(INCLUDE_GLOBAL_STATE_INTRODUCED)) { + out.writeOptionalBoolean(includeGlobalState); + } } private static SnapshotState snapshotState(final String reason, final List shardFailures) { diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 0804e69e46e..5092a58adaa 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -496,7 +496,8 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus ExceptionsHelper.detailedMessage(exception), 0, Collections.emptyList(), - snapshot.getRepositoryStateId()); + snapshot.getRepositoryStateId(), + snapshot.includeGlobalState()); } catch (Exception inner) { inner.addSuppressed(exception); logger.warn((Supplier) () -> new ParameterizedMessage("[{}] failed to close snapshot in repository", snapshot.snapshot()), inner); @@ -510,7 +511,7 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus private SnapshotInfo inProgressSnapshot(SnapshotsInProgress.Entry entry) { return new SnapshotInfo(entry.snapshot().getSnapshotId(), entry.indices().stream().map(IndexId::getName).collect(Collectors.toList()), - entry.startTime()); + entry.startTime(), entry.includeGlobalState()); } /** @@ -968,7 +969,8 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus failure, entry.shards().size(), Collections.unmodifiableList(shardFailures), - entry.getRepositoryStateId()); + entry.getRepositoryStateId(), + entry.includeGlobalState()); removeSnapshotFromClusterState(snapshot, snapshotInfo, null); } catch (Exception e) { logger.warn((Supplier) () -> new ParameterizedMessage("[{}] failed to finalize snapshot", snapshot), e); diff --git a/core/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java b/core/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java index 481bf5579e2..5c386174610 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java @@ -46,7 +46,8 @@ public class SnapshotStatusTests extends ESTestCase { SnapshotIndexShardStatus snapshotIndexShardStatus = new SnapshotIndexShardStatus(testShardId, shardStage); List snapshotIndexShardStatuses = new ArrayList<>(); snapshotIndexShardStatuses.add(snapshotIndexShardStatus); - SnapshotStatus status = new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses); + boolean includeGlobalState = randomBoolean(); + SnapshotStatus status = new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState); int initializingShards = 0; int startedShards = 0; @@ -80,6 +81,7 @@ public class SnapshotStatusTests extends ESTestCase { " \"repository\" : \"test-repo\",\n" + " \"uuid\" : \"" + uuid + "\",\n" + " \"state\" : \"" + state.toString() + "\",\n" + + " \"include_global_state\" : " + includeGlobalState + ",\n" + " \"shards_stats\" : {\n" + " \"initializing\" : " + initializingShards + ",\n" + " \"started\" : " + startedShards + ",\n" + diff --git a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index fe6d4ccdea0..00f29dc252d 100644 --- a/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/core/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -2542,8 +2542,9 @@ public class IndexShardTests extends IndexShardTestCase { } @Override - public SnapshotInfo finalizeSnapshot(SnapshotId snapshotId, List indices, long startTime, String failure, int totalShards, - List shardFailures, long repositoryStateId) { + public SnapshotInfo finalizeSnapshot(SnapshotId snapshotId, List indices, long startTime, String failure, + int totalShards, List shardFailures, long repositoryStateId, + boolean includeGlobalState) { return null; } diff --git a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index 6a6825a8ada..fa3920c1e8d 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -590,12 +590,20 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), equalTo(0)); assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(0)); assertThat(client.admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap-no-global-state").get().getSnapshots().get(0).state(), equalTo(SnapshotState.SUCCESS)); + SnapshotsStatusResponse snapshotsStatusResponse = client.admin().cluster().prepareSnapshotStatus("test-repo").addSnapshots("test-snap-no-global-state").get(); + assertThat(snapshotsStatusResponse.getSnapshots().size(), equalTo(1)); + SnapshotStatus snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0); + assertThat(snapshotStatus.includeGlobalState(), equalTo(false)); logger.info("--> snapshot with global state"); createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap-with-global-state").setIndices().setIncludeGlobalState(true).setWaitForCompletion(true).get(); assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), equalTo(0)); assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(0)); assertThat(client.admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap-with-global-state").get().getSnapshots().get(0).state(), equalTo(SnapshotState.SUCCESS)); + snapshotsStatusResponse = client.admin().cluster().prepareSnapshotStatus("test-repo").addSnapshots("test-snap-with-global-state").get(); + assertThat(snapshotsStatusResponse.getSnapshots().size(), equalTo(1)); + snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0); + assertThat(snapshotStatus.includeGlobalState(), equalTo(true)); if (testTemplate) { logger.info("--> delete test template"); @@ -1635,7 +1643,8 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas String blockedNode = blockNodeWithIndex("test-repo", "test-idx"); logger.info("--> snapshot"); - client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setWaitForCompletion(false).setIndices("test-idx").get(); + client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap") + .setWaitForCompletion(false).setIncludeGlobalState(false).setIndices("test-idx").get(); logger.info("--> waiting for block to kick in"); waitForBlock(blockedNode, "test-repo", TimeValue.timeValueSeconds(60)); @@ -1645,6 +1654,8 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas assertThat(response.getSnapshots().size(), equalTo(1)); SnapshotStatus snapshotStatus = response.getSnapshots().get(0); assertThat(snapshotStatus.getState(), equalTo(SnapshotsInProgress.State.STARTED)); + assertThat(snapshotStatus.includeGlobalState(), equalTo(false)); + // We blocked the node during data write operation, so at least one shard snapshot should be in STARTED stage assertThat(snapshotStatus.getShardsStats().getStartedShards(), greaterThan(0)); for (SnapshotIndexShardStatus shardStatus : snapshotStatus.getIndices().get("test-idx")) { @@ -1658,6 +1669,8 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas assertThat(response.getSnapshots().size(), equalTo(1)); snapshotStatus = response.getSnapshots().get(0); assertThat(snapshotStatus.getState(), equalTo(SnapshotsInProgress.State.STARTED)); + assertThat(snapshotStatus.includeGlobalState(), equalTo(false)); + // We blocked the node during data write operation, so at least one shard snapshot should be in STARTED stage assertThat(snapshotStatus.getShardsStats().getStartedShards(), greaterThan(0)); for (SnapshotIndexShardStatus shardStatus : snapshotStatus.getIndices().get("test-idx")) { @@ -1684,6 +1697,8 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas response = client.admin().cluster().prepareSnapshotStatus("test-repo").addSnapshots("test-snap").execute().actionGet(); snapshotStatus = response.getSnapshots().get(0); assertThat(snapshotStatus.getIndices().size(), equalTo(1)); + assertThat(snapshotStatus.includeGlobalState(), equalTo(false)); + SnapshotIndexStatus indexStatus = snapshotStatus.getIndices().get("test-idx"); assertThat(indexStatus, notNullValue()); assertThat(indexStatus.getShardsStats().getInitializingShards(), equalTo(0));