Skip to content

Commit 7255562

Browse files
committed
Add start and stop time to cat recovery API (#40378)
The cat recovery API is incredibly useful. Yet it is missing the start and stop time as an option from the output. This commit adds these as options to the cat recovery API. We elect to make these not visible by default to avoid breaking the output that users might rely on.
1 parent 6f26156 commit 7255562

File tree

3 files changed

+90
-58
lines changed

3 files changed

+90
-58
lines changed

server/src/main/java/org/elasticsearch/action/ActionModule.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@
289289
import org.elasticsearch.rest.action.cat.RestAliasAction;
290290
import org.elasticsearch.rest.action.cat.RestAllocationAction;
291291
import org.elasticsearch.rest.action.cat.RestCatAction;
292+
import org.elasticsearch.rest.action.cat.RestCatRecoveryAction;
292293
import org.elasticsearch.rest.action.cat.RestFielddataAction;
293294
import org.elasticsearch.rest.action.cat.RestHealthAction;
294295
import org.elasticsearch.rest.action.cat.RestIndicesAction;
@@ -665,7 +666,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {
665666
// Fully qualified to prevent interference with rest.action.count.RestCountAction
666667
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestCountAction(settings, restController));
667668
// Fully qualified to prevent interference with rest.action.indices.RestRecoveryAction
668-
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestRecoveryAction(settings, restController));
669+
registerHandler.accept(new RestCatRecoveryAction(settings, restController));
669670
registerHandler.accept(new RestHealthAction(settings, restController));
670671
registerHandler.accept(new org.elasticsearch.rest.action.cat.RestPendingClusterTasksAction(settings, restController));
671672
registerHandler.accept(new RestAliasAction(settings, restController));

server/src/main/java/org/elasticsearch/rest/action/cat/RestRecoveryAction.java renamed to server/src/main/java/org/elasticsearch/rest/action/cat/RestCatRecoveryAction.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.elasticsearch.common.Table;
3131
import org.elasticsearch.common.settings.Settings;
3232
import org.elasticsearch.common.unit.TimeValue;
33+
import org.elasticsearch.common.xcontent.XContentElasticsearchExtension;
3334
import org.elasticsearch.indices.recovery.RecoveryState;
3435
import org.elasticsearch.rest.RestController;
3536
import org.elasticsearch.rest.RestRequest;
@@ -47,8 +48,8 @@
4748
* in a string format, designed to be used at the command line. An Index can
4849
* be specified to limit output to a particular index or indices.
4950
*/
50-
public class RestRecoveryAction extends AbstractCatAction {
51-
public RestRecoveryAction(Settings settings, RestController restController) {
51+
public class RestCatRecoveryAction extends AbstractCatAction {
52+
public RestCatRecoveryAction(Settings settings, RestController restController) {
5253
super(settings);
5354
restController.registerHandler(GET, "/_cat/recovery", this);
5455
restController.registerHandler(GET, "/_cat/recovery/{index}", this);
@@ -86,6 +87,10 @@ protected Table getTableWithHeader(RestRequest request) {
8687
t.startHeaders()
8788
.addCell("index", "alias:i,idx;desc:index name")
8889
.addCell("shard", "alias:s,sh;desc:shard name")
90+
.addCell("start_time", "default:false;alias:start;desc:recovery start time")
91+
.addCell("start_time_millis", "default:false;alias:start_millis;desc:recovery start time in epoch milliseconds")
92+
.addCell("stop_time", "default:false;alias:stop;desc:recovery stop time")
93+
.addCell("stop_time_millis", "default:false;alias:stop_millis;desc:recovery stop time in epoch milliseconds")
8994
.addCell("time", "alias:t,ti;desc:recovery time")
9095
.addCell("type", "alias:ty;desc:recovery type")
9196
.addCell("stage", "alias:st;desc:recovery stage")
@@ -149,6 +154,10 @@ public int compare(RecoveryState o1, RecoveryState o2) {
149154
t.startRow();
150155
t.addCell(index);
151156
t.addCell(state.getShardId().id());
157+
t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime()));
158+
t.addCell(state.getTimer().startTime());
159+
t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime()));
160+
t.addCell(state.getTimer().stopTime());
152161
t.addCell(new TimeValue(state.getTimer().time()));
153162
t.addCell(state.getRecoverySource().getType().toString().toLowerCase(Locale.ROOT));
154163
t.addCell(state.getStage().toString().toLowerCase(Locale.ROOT));

server/src/test/java/org/elasticsearch/rest/action/cat/RestRecoveryActionTests.java

+77-55
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.common.Table;
3030
import org.elasticsearch.common.settings.Settings;
3131
import org.elasticsearch.common.unit.TimeValue;
32+
import org.elasticsearch.common.xcontent.XContentElasticsearchExtension;
3233
import org.elasticsearch.index.Index;
3334
import org.elasticsearch.index.shard.ShardId;
3435
import org.elasticsearch.indices.recovery.RecoveryState;
@@ -37,7 +38,9 @@
3738
import org.elasticsearch.usage.UsageService;
3839

3940
import java.util.ArrayList;
41+
import java.util.Arrays;
4042
import java.util.Collections;
43+
import java.util.Date;
4144
import java.util.HashMap;
4245
import java.util.List;
4346
import java.util.Locale;
@@ -53,7 +56,7 @@ public void testRestRecoveryAction() {
5356
final Settings settings = Settings.EMPTY;
5457
UsageService usageService = new UsageService();
5558
final RestController restController = new RestController(Collections.emptySet(), null, null, null, usageService);
56-
final RestRecoveryAction action = new RestRecoveryAction(settings, restController);
59+
final RestCatRecoveryAction action = new RestCatRecoveryAction(settings, restController);
5760
final int totalShards = randomIntBetween(1, 32);
5861
final int successfulShards = Math.max(0, totalShards - randomIntBetween(1, 2));
5962
final int failedShards = totalShards - successfulShards;
@@ -64,7 +67,11 @@ public void testRestRecoveryAction() {
6467
final RecoveryState state = mock(RecoveryState.class);
6568
when(state.getShardId()).thenReturn(new ShardId(new Index("index", "_na_"), i));
6669
final RecoveryState.Timer timer = mock(RecoveryState.Timer.class);
67-
when(timer.time()).thenReturn((long)randomIntBetween(1000000, 10 * 1000000));
70+
final long startTime = randomLongBetween(0, new Date().getTime());
71+
when(timer.startTime()).thenReturn(startTime);
72+
final long time = randomLongBetween(1000000, 10 * 1000000);
73+
when(timer.time()).thenReturn(time);
74+
when(timer.stopTime()).thenReturn(startTime + time);
6875
when(state.getTimer()).thenReturn(timer);
6976
when(state.getRecoverySource()).thenReturn(TestShardRouting.randomRecoverySource());
7077
when(state.getStage()).thenReturn(randomFrom(RecoveryState.Stage.values()));
@@ -122,63 +129,78 @@ public void testRestRecoveryAction() {
122129

123130
List<Table.Cell> headers = table.getHeaders();
124131

125-
assertThat(headers.get(0).value, equalTo("index"));
126-
assertThat(headers.get(1).value, equalTo("shard"));
127-
assertThat(headers.get(2).value, equalTo("time"));
128-
assertThat(headers.get(3).value, equalTo("type"));
129-
assertThat(headers.get(4).value, equalTo("stage"));
130-
assertThat(headers.get(5).value, equalTo("source_host"));
131-
assertThat(headers.get(6).value, equalTo("source_node"));
132-
assertThat(headers.get(7).value, equalTo("target_host"));
133-
assertThat(headers.get(8).value, equalTo("target_node"));
134-
assertThat(headers.get(9).value, equalTo("repository"));
135-
assertThat(headers.get(10).value, equalTo("snapshot"));
136-
assertThat(headers.get(11).value, equalTo("files"));
137-
assertThat(headers.get(12).value, equalTo("files_recovered"));
138-
assertThat(headers.get(13).value, equalTo("files_percent"));
139-
assertThat(headers.get(14).value, equalTo("files_total"));
140-
assertThat(headers.get(15).value, equalTo("bytes"));
141-
assertThat(headers.get(16).value, equalTo("bytes_recovered"));
142-
assertThat(headers.get(17).value, equalTo("bytes_percent"));
143-
assertThat(headers.get(18).value, equalTo("bytes_total"));
144-
assertThat(headers.get(19).value, equalTo("translog_ops"));
145-
assertThat(headers.get(20).value, equalTo("translog_ops_recovered"));
146-
assertThat(headers.get(21).value, equalTo("translog_ops_percent"));
132+
final List<String> expectedHeaders = Arrays.asList(
133+
"index",
134+
"shard",
135+
"start_time",
136+
"start_time_millis",
137+
"stop_time",
138+
"stop_time_millis",
139+
"time",
140+
"type",
141+
"stage",
142+
"source_host",
143+
"source_node",
144+
"target_host",
145+
"target_node",
146+
"repository",
147+
"snapshot",
148+
"files",
149+
"files_recovered",
150+
"files_percent",
151+
"files_total",
152+
"bytes",
153+
"bytes_recovered",
154+
"bytes_percent",
155+
"bytes_total",
156+
"translog_ops",
157+
"translog_ops_recovered",
158+
"translog_ops_percent");
159+
160+
for (int i = 0; i < expectedHeaders.size(); i++) {
161+
assertThat(headers.get(i).value, equalTo(expectedHeaders.get(i)));
162+
}
147163

148164
assertThat(table.getRows().size(), equalTo(successfulShards));
165+
149166
for (int i = 0; i < successfulShards; i++) {
150167
final RecoveryState state = recoveryStates.get(i);
151-
List<Table.Cell> cells = table.getRows().get(i);
152-
assertThat(cells.get(0).value, equalTo("index"));
153-
assertThat(cells.get(1).value, equalTo(i));
154-
assertThat(cells.get(2).value, equalTo(new TimeValue(state.getTimer().time())));
155-
assertThat(cells.get(3).value, equalTo(state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT)));
156-
assertThat(cells.get(4).value, equalTo(state.getStage().name().toLowerCase(Locale.ROOT)));
157-
assertThat(cells.get(5).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName()));
158-
assertThat(cells.get(6).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName()));
159-
assertThat(cells.get(7).value, equalTo(state.getTargetNode().getHostName()));
160-
assertThat(cells.get(8).value, equalTo(state.getTargetNode().getName()));
161-
assertThat(
162-
cells.get(9).value,
163-
equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
164-
"n/a" :
165-
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository()));
166-
assertThat(
167-
cells.get(10).value,
168-
equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
169-
"n/a" :
170-
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName()));
171-
assertThat(cells.get(11).value, equalTo(state.getIndex().totalRecoverFiles()));
172-
assertThat(cells.get(12).value, equalTo(state.getIndex().recoveredFileCount()));
173-
assertThat(cells.get(13).value, equalTo(percent(state.getIndex().recoveredFilesPercent())));
174-
assertThat(cells.get(14).value, equalTo(state.getIndex().totalFileCount()));
175-
assertThat(cells.get(15).value, equalTo(state.getIndex().totalRecoverBytes()));
176-
assertThat(cells.get(16).value, equalTo(state.getIndex().recoveredBytes()));
177-
assertThat(cells.get(17).value, equalTo(percent(state.getIndex().recoveredBytesPercent())));
178-
assertThat(cells.get(18).value, equalTo(state.getIndex().totalBytes()));
179-
assertThat(cells.get(19).value, equalTo(state.getTranslog().totalOperations()));
180-
assertThat(cells.get(20).value, equalTo(state.getTranslog().recoveredOperations()));
181-
assertThat(cells.get(21).value, equalTo(percent(state.getTranslog().recoveredPercent())));
168+
final List<Object> expectedValues = Arrays.asList(
169+
"index",
170+
i,
171+
XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime()),
172+
state.getTimer().startTime(),
173+
XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime()),
174+
state.getTimer().stopTime(),
175+
new TimeValue(state.getTimer().time()),
176+
state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT),
177+
state.getStage().name().toLowerCase(Locale.ROOT),
178+
state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName(),
179+
state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName(),
180+
state.getTargetNode().getHostName(),
181+
state.getTargetNode().getName(),
182+
state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
183+
"n/a" :
184+
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository(),
185+
state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ?
186+
"n/a" :
187+
((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName(),
188+
state.getIndex().totalRecoverFiles(),
189+
state.getIndex().recoveredFileCount(),
190+
percent(state.getIndex().recoveredFilesPercent()),
191+
state.getIndex().totalFileCount(),
192+
state.getIndex().totalRecoverBytes(),
193+
state.getIndex().recoveredBytes(),
194+
percent(state.getIndex().recoveredBytesPercent()),
195+
state.getIndex().totalBytes(),
196+
state.getTranslog().totalOperations(),
197+
state.getTranslog().recoveredOperations(),
198+
percent(state.getTranslog().recoveredPercent()));
199+
200+
final List<Table.Cell> cells = table.getRows().get(i);
201+
for (int j = 0; j < expectedValues.size(); j++) {
202+
assertThat(cells.get(j).value, equalTo(expectedValues.get(j)));
203+
}
182204
}
183205
}
184206

0 commit comments

Comments
 (0)