Skip to content

Commit 9e920f9

Browse files
Make Timestamps Returned by Snapshot APIs Consistent (#43148) (#44261)
* We don't have to calculate the start and end times form the shards for the status API, we have the start time available from the CS or the `SnapshotInfo` in the repo and can either take the end time form the `SnapshotInfo` or take the most recent time from the shard stats for in progress snapshots * Closes #43074
1 parent e490ecb commit 9e920f9

File tree

6 files changed

+111
-15
lines changed

6 files changed

+111
-15
lines changed

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class SnapshotIndexStatus implements Iterable<SnapshotIndexShardStatus>,
5858
stats = new SnapshotStats();
5959
for (SnapshotIndexShardStatus shard : shards) {
6060
indexShards.put(shard.getShardId().getId(), shard);
61-
stats.add(shard.getStats());
61+
stats.add(shard.getStats(), true);
6262
}
6363
shardsStats = new SnapshotShardsStats(shards);
6464
this.indexShards = unmodifiableMap(indexShards);

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStats.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,12 @@ public static SnapshotStats fromXContent(XContentParser parser) throws IOExcepti
304304
processedSize);
305305
}
306306

307-
void add(SnapshotStats stats) {
307+
/**
308+
* Add stats instance to the total
309+
* @param stats Stats instance to add
310+
* @param updateTimestamps Whether or not start time and duration should be updated
311+
*/
312+
void add(SnapshotStats stats, boolean updateTimestamps) {
308313
incrementalFileCount += stats.incrementalFileCount;
309314
totalFileCount += stats.totalFileCount;
310315
processedFileCount += stats.processedFileCount;
@@ -317,7 +322,7 @@ void add(SnapshotStats stats) {
317322
// First time here
318323
startTime = stats.startTime;
319324
time = stats.time;
320-
} else {
325+
} else if (updateTimestamps) {
321326
// The time the last snapshot ends
322327
long endTime = Math.max(startTime + time, stats.startTime + stats.time);
323328

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatus.java

+22-8
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import org.elasticsearch.Version;
2323
import org.elasticsearch.cluster.SnapshotsInProgress;
2424
import org.elasticsearch.cluster.SnapshotsInProgress.State;
25+
import org.elasticsearch.common.Nullable;
2526
import org.elasticsearch.common.ParseField;
2627
import org.elasticsearch.common.Strings;
27-
import org.elasticsearch.common.Nullable;
2828
import org.elasticsearch.common.io.stream.StreamInput;
2929
import org.elasticsearch.common.io.stream.StreamOutput;
3030
import org.elasticsearch.common.io.stream.Streamable;
@@ -72,14 +72,14 @@ public class SnapshotStatus implements ToXContentObject, Streamable {
7272
@Nullable
7373
private Boolean includeGlobalState;
7474

75-
SnapshotStatus(final Snapshot snapshot, final State state, final List<SnapshotIndexShardStatus> shards,
76-
final Boolean includeGlobalState) {
75+
SnapshotStatus(Snapshot snapshot, State state, List<SnapshotIndexShardStatus> shards, Boolean includeGlobalState,
76+
long startTime, long time) {
7777
this.snapshot = Objects.requireNonNull(snapshot);
7878
this.state = Objects.requireNonNull(state);
7979
this.shards = Objects.requireNonNull(shards);
8080
this.includeGlobalState = includeGlobalState;
8181
shardsStats = new SnapshotShardsStats(shards);
82-
updateShardStats();
82+
updateShardStats(startTime, time);
8383
}
8484

8585
private SnapshotStatus(Snapshot snapshot, State state, List<SnapshotIndexShardStatus> shards,
@@ -172,7 +172,16 @@ public void readFrom(StreamInput in) throws IOException {
172172
if (in.getVersion().onOrAfter(Version.V_6_2_0)) {
173173
includeGlobalState = in.readOptionalBoolean();
174174
}
175-
updateShardStats();
175+
final long startTime;
176+
final long time;
177+
if (in.getVersion().onOrAfter(Version.V_7_4_0)) {
178+
startTime = in.readLong();
179+
time = in.readLong();
180+
} else {
181+
startTime = 0L;
182+
time = 0L;
183+
}
184+
updateShardStats(startTime, time);
176185
}
177186

178187
@Override
@@ -186,6 +195,10 @@ public void writeTo(StreamOutput out) throws IOException {
186195
if (out.getVersion().onOrAfter(Version.V_6_2_0)) {
187196
out.writeOptionalBoolean(includeGlobalState);
188197
}
198+
if (out.getVersion().onOrAfter(Version.V_7_4_0)) {
199+
out.writeLong(stats.getStartTime());
200+
out.writeLong(stats.getTime());
201+
}
189202
}
190203

191204
/**
@@ -286,11 +299,12 @@ public static SnapshotStatus fromXContent(XContentParser parser) throws IOExcept
286299
return PARSER.parse(parser, null);
287300
}
288301

289-
private void updateShardStats() {
290-
stats = new SnapshotStats();
302+
private void updateShardStats(long startTime, long time) {
303+
stats = new SnapshotStats(startTime, time, 0, 0, 0, 0, 0, 0);
291304
shardsStats = new SnapshotShardsStats(shards);
292305
for (SnapshotIndexShardStatus shard : shards) {
293-
stats.add(shard.getStats());
306+
// BWC: only update timestamps when we did not get a start time from an old node
307+
stats.add(shard.getStats(), startTime == 0L);
294308
}
295309
}
296310

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ private SnapshotsStatusResponse buildResponse(SnapshotsStatusRequest request, Li
185185
shardStatusBuilder.add(shardStatus);
186186
}
187187
builder.add(new SnapshotStatus(entry.snapshot(), entry.state(),
188-
Collections.unmodifiableList(shardStatusBuilder), entry.includeGlobalState()));
188+
Collections.unmodifiableList(shardStatusBuilder), entry.includeGlobalState(), entry.startTime(),
189+
Math.max(threadPool.absoluteTimeInMillis() - entry.startTime(), 0L)));
189190
}
190191
}
191192
// Now add snapshots on disk that are not currently running
@@ -236,8 +237,10 @@ private SnapshotsStatusResponse buildResponse(SnapshotsStatusRequest request, Li
236237
default:
237238
throw new IllegalArgumentException("Unknown snapshot state " + snapshotInfo.state());
238239
}
240+
final long startTime = snapshotInfo.startTime();
239241
builder.add(new SnapshotStatus(new Snapshot(repositoryName, snapshotId), state,
240-
Collections.unmodifiableList(shardStatusBuilder), snapshotInfo.includeGlobalState()));
242+
Collections.unmodifiableList(shardStatusBuilder), snapshotInfo.includeGlobalState(),
243+
startTime, snapshotInfo.endTime() - startTime));
241244
}
242245
}
243246
}

server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotStatusTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void testToString() throws Exception {
5050
List<SnapshotIndexShardStatus> snapshotIndexShardStatuses = new ArrayList<>();
5151
snapshotIndexShardStatuses.add(snapshotIndexShardStatus);
5252
boolean includeGlobalState = randomBoolean();
53-
SnapshotStatus status = new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState);
53+
SnapshotStatus status = new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState, 0L, 0L);
5454

5555
int initializingShards = 0;
5656
int startedShards = 0;
@@ -166,7 +166,7 @@ protected SnapshotStatus createTestInstance() {
166166
snapshotIndexShardStatuses.add(snapshotIndexShardStatus);
167167
}
168168
boolean includeGlobalState = randomBoolean();
169-
return new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState);
169+
return new SnapshotStatus(snapshot, state, snapshotIndexShardStatuses, includeGlobalState, 0L, 0L);
170170
}
171171

172172
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.snapshots;
20+
21+
import org.elasticsearch.Version;
22+
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
23+
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
24+
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
25+
import org.elasticsearch.client.Client;
26+
import org.elasticsearch.common.settings.Settings;
27+
28+
import java.util.List;
29+
30+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
31+
import static org.hamcrest.Matchers.equalTo;
32+
import static org.hamcrest.Matchers.greaterThan;
33+
34+
public class SnapshotStatusApisIT extends AbstractSnapshotIntegTestCase {
35+
36+
public void testStatusApiConsistency() {
37+
Client client = client();
38+
39+
logger.info("--> creating repository");
40+
assertAcked(client.admin().cluster().preparePutRepository("test-repo").setType("fs").setSettings(
41+
Settings.builder().put("location", randomRepoPath()).build()));
42+
43+
createIndex("test-idx-1", "test-idx-2", "test-idx-3");
44+
ensureGreen();
45+
46+
logger.info("--> indexing some data");
47+
for (int i = 0; i < 100; i++) {
48+
index("test-idx-1", "_doc", Integer.toString(i), "foo", "bar" + i);
49+
index("test-idx-2", "_doc", Integer.toString(i), "foo", "baz" + i);
50+
index("test-idx-3", "_doc", Integer.toString(i), "foo", "baz" + i);
51+
}
52+
refresh();
53+
54+
logger.info("--> snapshot");
55+
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
56+
.setWaitForCompletion(true).get();
57+
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
58+
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(),
59+
equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
60+
61+
List<SnapshotInfo> snapshotInfos = client.admin().cluster().prepareGetSnapshots("test-repo").get().getSnapshots();
62+
assertThat(snapshotInfos.size(), equalTo(1));
63+
SnapshotInfo snapshotInfo = snapshotInfos.get(0);
64+
assertThat(snapshotInfo.state(), equalTo(SnapshotState.SUCCESS));
65+
assertThat(snapshotInfo.version(), equalTo(Version.CURRENT));
66+
67+
final List<SnapshotStatus> snapshotStatus = client.admin().cluster().snapshotsStatus(
68+
new SnapshotsStatusRequest("test-repo", new String[]{"test-snap"})).actionGet().getSnapshots();
69+
assertThat(snapshotStatus.size(), equalTo(1));
70+
final SnapshotStatus snStatus = snapshotStatus.get(0);
71+
assertEquals(snStatus.getStats().getStartTime(), snapshotInfo.startTime());
72+
assertEquals(snStatus.getStats().getTime(), snapshotInfo.endTime() - snapshotInfo.startTime());
73+
}
74+
}

0 commit comments

Comments
 (0)