Skip to content

Commit 4a19c12

Browse files
committed
Run TransportGetComposableIndexTemplate on local node
This action solely needs the cluster state, it can run on any node. Additionally, it needs to be cancellable to avoid doing unnecessary work after a client failure or timeout. As a drive-by, this removes another usage of the trappy default master node timeout.
1 parent 69dcc6c commit 4a19c12

File tree

14 files changed

+123
-159
lines changed

14 files changed

+123
-159
lines changed

modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ public void testCannotDeleteComposableTemplateUsedByDataStream() throws Exceptio
718718
TransportDeleteComposableIndexTemplateAction.Request deleteRequest = new TransportDeleteComposableIndexTemplateAction.Request("id");
719719
client().execute(TransportDeleteComposableIndexTemplateAction.TYPE, deleteRequest).get();
720720

721-
GetComposableIndexTemplateAction.Request getReq = new GetComposableIndexTemplateAction.Request("id");
721+
GetComposableIndexTemplateAction.Request getReq = new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "id");
722722
Exception e3 = expectThrows(Exception.class, client().execute(GetComposableIndexTemplateAction.INSTANCE, getReq));
723723
maybeE = ExceptionsHelper.unwrapCausesAndSuppressed(e3, err -> err.getMessage().contains("index template matching [id] not found"));
724724
assertTrue(maybeE.isPresent());

qa/smoke-test-http/src/javaRestTest/java/org/elasticsearch/http/RestActionCancellationIT.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
1717
import org.elasticsearch.action.admin.indices.recovery.RecoveryAction;
1818
import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction;
19+
import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction;
1920
import org.elasticsearch.action.support.CancellableActionTestPlugin;
2021
import org.elasticsearch.action.support.PlainActionFuture;
2122
import org.elasticsearch.action.support.RefCountingListener;
@@ -71,6 +72,10 @@ public void testGetComponentTemplateCancellation() {
7172
runRestActionCancellationTest(new Request(HttpGet.METHOD_NAME, "/_component_template"), GetComponentTemplateAction.NAME);
7273
}
7374

75+
public void testGetComposableTemplateCancellation() {
76+
runRestActionCancellationTest(new Request(HttpGet.METHOD_NAME, "/_index_template"), GetComposableIndexTemplateAction.NAME);
77+
}
78+
7479
private void runRestActionCancellationTest(Request request, String actionName) {
7580
final var node = usually() ? internalCluster().getRandomNodeName() : internalCluster().startCoordinatingOnlyNode(Settings.EMPTY);
7681

rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_template.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232
},
3333
"master_timeout":{
3434
"type":"time",
35-
"description":"Explicit operation timeout for connection to master node"
35+
"description":"Timeout for waiting for new cluster state in case it is blocked"
3636
},
3737
"local":{
38+
"deprecated":true,
3839
"type":"boolean",
3940
"description":"Return local information, do not retrieve the state from master node (default: false)"
4041
}

rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_index_template.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@
3838
},
3939
"master_timeout":{
4040
"type":"time",
41-
"description":"Explicit operation timeout for connection to master node"
41+
"description":"Timeout for waiting for new cluster state in case it is blocked"
4242
},
4343
"local":{
44+
"deprecated":true,
4445
"type":"boolean",
4546
"description":"Return local information, do not retrieve the state from master node (default: false)"
4647
},

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get_index_template/10_basic.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,48 @@ setup:
8282

8383
---
8484
"Get index template with local flag":
85+
- requires:
86+
test_runner_features: ["allowed_warnings"]
8587

8688
- do:
8789
indices.get_index_template:
8890
name: test
8991
local: true
92+
allowed_warnings:
93+
- "the [?local] query parameter to this API has no effect, is now deprecated, and will be removed in a future version"
9094

9195
- match: {index_templates.0.name: test}
9296

97+
---
98+
"Deprecated local parameter":
99+
- requires:
100+
capabilities:
101+
- method: GET
102+
path: /_get_index_template
103+
capabilities: ["local_param_deprecated"]
104+
test_runner_features: ["capabilities", "warnings"]
105+
reason: Deprecation was implemented with capability
106+
107+
- do:
108+
indices.get_index_template:
109+
local: true
110+
warnings:
111+
- "the [?local] query parameter to this API has no effect, is now deprecated, and will be removed in a future version"
112+
113+
---
114+
"Deprecated local parameter works in v8 compat mode":
115+
- requires:
116+
test_runner_features: ["headers"]
117+
118+
- do:
119+
headers:
120+
Content-Type: "application/vnd.elasticsearch+json;compatible-with=8"
121+
Accept: "application/vnd.elasticsearch+json;compatible-with=8"
122+
indices.get_index_template:
123+
local: true
124+
125+
- exists: index_templates
126+
93127
---
94128
"Add data stream lifecycle":
95129
- requires:

server/src/internalClusterTest/java/org/elasticsearch/reservedstate/service/ComponentTemplatesFileSettingsIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ private void assertComponentAndIndexTemplateDelete(CountDownLatch savedClusterSt
456456

457457
final var response = client().execute(
458458
GetComposableIndexTemplateAction.INSTANCE,
459-
new GetComposableIndexTemplateAction.Request("template*")
459+
new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "template*")
460460
).get();
461461

462462
assertThat(response.indexTemplates().keySet().stream().collect(Collectors.toSet()), containsInAnyOrder("template_1", "template_2"));
@@ -594,7 +594,7 @@ private void assertClusterStateNotSaved(CountDownLatch savedClusterState, Atomic
594594

595595
final var response = client().execute(
596596
GetComposableIndexTemplateAction.INSTANCE,
597-
new GetComposableIndexTemplateAction.Request("err*")
597+
new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "err*")
598598
).get();
599599

600600
assertTrue(response.indexTemplates().isEmpty());

server/src/main/java/org/elasticsearch/action/admin/indices/template/get/GetComposableIndexTemplateAction.java

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@
1414
import org.elasticsearch.action.ActionResponse;
1515
import org.elasticsearch.action.ActionType;
1616
import org.elasticsearch.action.admin.indices.rollover.RolloverConfiguration;
17-
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
17+
import org.elasticsearch.action.support.local.LocalClusterStateRequest;
1818
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
1919
import org.elasticsearch.cluster.metadata.DataStreamGlobalRetention;
2020
import org.elasticsearch.common.io.stream.StreamInput;
2121
import org.elasticsearch.common.io.stream.StreamOutput;
2222
import org.elasticsearch.core.Nullable;
23+
import org.elasticsearch.core.TimeValue;
24+
import org.elasticsearch.core.UpdateForV10;
25+
import org.elasticsearch.tasks.CancellableTask;
26+
import org.elasticsearch.tasks.Task;
27+
import org.elasticsearch.tasks.TaskId;
2328
import org.elasticsearch.xcontent.ParseField;
2429
import org.elasticsearch.xcontent.ToXContentObject;
2530
import org.elasticsearch.xcontent.XContentBuilder;
@@ -40,24 +45,30 @@ private GetComposableIndexTemplateAction() {
4045
/**
4146
* Request that to retrieve one or more index templates
4247
*/
43-
public static class Request extends MasterNodeReadRequest<Request> {
48+
public static class Request extends LocalClusterStateRequest {
4449

4550
@Nullable
4651
private final String name;
4752
private boolean includeDefaults;
4853

4954
/**
55+
* @param masterTimeout Timeout for waiting for new cluster state in case it is blocked.
5056
* @param name A template name or pattern, or {@code null} to retrieve all templates.
5157
*/
52-
public Request(@Nullable String name) {
53-
super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
58+
public Request(TimeValue masterTimeout, @Nullable String name) {
59+
super(masterTimeout);
5460
if (name != null && name.contains(",")) {
5561
throw new IllegalArgumentException("template name may not contain ','");
5662
}
5763
this.name = name;
5864
this.includeDefaults = false;
5965
}
6066

67+
/**
68+
* NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC we must remain able to read these requests until
69+
* we no longer need to support calling this action remotely.
70+
*/
71+
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
6172
public Request(StreamInput in) throws IOException {
6273
super(in);
6374
name = in.readOptionalString();
@@ -68,15 +79,6 @@ public Request(StreamInput in) throws IOException {
6879
}
6980
}
7081

71-
@Override
72-
public void writeTo(StreamOutput out) throws IOException {
73-
super.writeTo(out);
74-
out.writeOptionalString(name);
75-
if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) {
76-
out.writeBoolean(includeDefaults);
77-
}
78-
}
79-
8082
public void includeDefaults(boolean includeDefaults) {
8183
this.includeDefaults = includeDefaults;
8284
}
@@ -90,6 +92,11 @@ public ActionRequestValidationException validate() {
9092
return null;
9193
}
9294

95+
@Override
96+
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) {
97+
return new CancellableTask(id, type, action, "", parentTaskId, headers);
98+
}
99+
93100
/**
94101
* The name of the index templates.
95102
*/
@@ -124,19 +131,6 @@ public static class Response extends ActionResponse implements ToXContentObject
124131
@Nullable
125132
private final RolloverConfiguration rolloverConfiguration;
126133

127-
public Response(StreamInput in) throws IOException {
128-
super(in);
129-
indexTemplates = in.readMap(ComposableIndexTemplate::new);
130-
if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) {
131-
rolloverConfiguration = in.readOptionalWriteable(RolloverConfiguration::new);
132-
} else {
133-
rolloverConfiguration = null;
134-
}
135-
if (in.getTransportVersion().between(TransportVersions.V_8_14_0, TransportVersions.V_8_16_0)) {
136-
in.readOptionalWriteable(DataStreamGlobalRetention::read);
137-
}
138-
}
139-
140134
/**
141135
* Please use {@link GetComposableIndexTemplateAction.Response#Response(Map)}
142136
*/
@@ -184,6 +178,11 @@ public RolloverConfiguration getRolloverConfiguration() {
184178
return rolloverConfiguration;
185179
}
186180

181+
/**
182+
* NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC we must remain able to write these responses until
183+
* we no longer need to support calling this action remotely.
184+
*/
185+
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
187186
@Override
188187
public void writeTo(StreamOutput out) throws IOException {
189188
out.writeMap(indexTemplates, StreamOutput::writeWriteable);

server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,61 @@
1212
import org.elasticsearch.ResourceNotFoundException;
1313
import org.elasticsearch.action.ActionListener;
1414
import org.elasticsearch.action.support.ActionFilters;
15-
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
15+
import org.elasticsearch.action.support.ChannelActionListener;
16+
import org.elasticsearch.action.support.local.TransportLocalClusterStateAction;
1617
import org.elasticsearch.cluster.ClusterState;
1718
import org.elasticsearch.cluster.block.ClusterBlockException;
1819
import org.elasticsearch.cluster.block.ClusterBlockLevel;
1920
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
2021
import org.elasticsearch.cluster.metadata.DataStreamLifecycle;
21-
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
2222
import org.elasticsearch.cluster.service.ClusterService;
2323
import org.elasticsearch.common.regex.Regex;
2424
import org.elasticsearch.common.settings.ClusterSettings;
2525
import org.elasticsearch.common.util.concurrent.EsExecutors;
26+
import org.elasticsearch.core.UpdateForV10;
2627
import org.elasticsearch.injection.guice.Inject;
28+
import org.elasticsearch.tasks.CancellableTask;
2729
import org.elasticsearch.tasks.Task;
28-
import org.elasticsearch.threadpool.ThreadPool;
2930
import org.elasticsearch.transport.TransportService;
3031

3132
import java.util.HashMap;
3233
import java.util.Map;
3334

34-
public class TransportGetComposableIndexTemplateAction extends TransportMasterNodeReadAction<
35+
public class TransportGetComposableIndexTemplateAction extends TransportLocalClusterStateAction<
3536
GetComposableIndexTemplateAction.Request,
3637
GetComposableIndexTemplateAction.Response> {
3738

3839
private final ClusterSettings clusterSettings;
3940

41+
/**
42+
* NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC it must be registered with the TransportService until
43+
* we no longer need to support calling this action remotely.
44+
*/
45+
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
46+
@SuppressWarnings("this-escape")
4047
@Inject
4148
public TransportGetComposableIndexTemplateAction(
4249
TransportService transportService,
4350
ClusterService clusterService,
44-
ThreadPool threadPool,
45-
ActionFilters actionFilters,
46-
IndexNameExpressionResolver indexNameExpressionResolver
51+
ActionFilters actionFilters
4752
) {
4853
super(
4954
GetComposableIndexTemplateAction.NAME,
50-
transportService,
51-
clusterService,
52-
threadPool,
5355
actionFilters,
54-
GetComposableIndexTemplateAction.Request::new,
55-
indexNameExpressionResolver,
56-
GetComposableIndexTemplateAction.Response::new,
56+
transportService.getTaskManager(),
57+
clusterService,
5758
EsExecutors.DIRECT_EXECUTOR_SERVICE
5859
);
5960
clusterSettings = clusterService.getClusterSettings();
61+
62+
transportService.registerRequestHandler(
63+
actionName,
64+
executor,
65+
false,
66+
true,
67+
GetComposableIndexTemplateAction.Request::new,
68+
(request, channel, task) -> executeDirect(task, request, new ChannelActionListener<>(channel))
69+
);
6070
}
6171

6272
@Override
@@ -65,12 +75,13 @@ protected ClusterBlockException checkBlock(GetComposableIndexTemplateAction.Requ
6575
}
6676

6777
@Override
68-
protected void masterOperation(
78+
protected void localClusterStateOperation(
6979
Task task,
7080
GetComposableIndexTemplateAction.Request request,
7181
ClusterState state,
7282
ActionListener<GetComposableIndexTemplateAction.Response> listener
7383
) {
84+
final var cancellableTask = (CancellableTask) task;
7485
Map<String, ComposableIndexTemplate> allTemplates = state.metadata().templatesV2();
7586
Map<String, ComposableIndexTemplate> results;
7687
// If we did not ask for a specific name, then we return all templates
@@ -91,6 +102,7 @@ protected void masterOperation(
91102
throw new ResourceNotFoundException("index template matching [" + request.name() + "] not found");
92103
}
93104
}
105+
cancellableTask.ensureNotCancelled();
94106
if (request.includeDefaults()) {
95107
listener.onResponse(
96108
new GetComposableIndexTemplateAction.Response(

server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetComposableIndexTemplateAction.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
import org.elasticsearch.common.settings.Settings;
1515
import org.elasticsearch.rest.BaseRestHandler;
1616
import org.elasticsearch.rest.RestRequest;
17+
import org.elasticsearch.rest.RestUtils;
1718
import org.elasticsearch.rest.Scope;
1819
import org.elasticsearch.rest.ServerlessScope;
20+
import org.elasticsearch.rest.action.RestCancellableNodeClient;
1921
import org.elasticsearch.rest.action.RestToXContentListener;
2022

2123
import java.io.IOException;
@@ -26,7 +28,6 @@
2628
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
2729
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
2830
import static org.elasticsearch.rest.RestStatus.OK;
29-
import static org.elasticsearch.rest.RestUtils.getMasterNodeTimeout;
3031

3132
@ServerlessScope(Scope.PUBLIC)
3233
public class RestGetComposableIndexTemplateAction extends BaseRestHandler {
@@ -47,17 +48,23 @@ public String getName() {
4748

4849
@Override
4950
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
50-
final GetComposableIndexTemplateAction.Request getRequest = new GetComposableIndexTemplateAction.Request(request.param("name"));
51-
52-
getRequest.local(request.paramAsBoolean("local", getRequest.local()));
53-
getRequest.masterNodeTimeout(getMasterNodeTimeout(request));
51+
final GetComposableIndexTemplateAction.Request getRequest = new GetComposableIndexTemplateAction.Request(
52+
RestUtils.getMasterNodeTimeout(request),
53+
request.param("name")
54+
);
5455
getRequest.includeDefaults(request.paramAsBoolean("include_defaults", false));
56+
RestUtils.consumeDeprecatedLocalParameter(request);
57+
5558
final boolean implicitAll = getRequest.name() == null;
5659

57-
return channel -> client.execute(GetComposableIndexTemplateAction.INSTANCE, getRequest, new RestToXContentListener<>(channel, r -> {
58-
final boolean templateExists = r.indexTemplates().isEmpty() == false;
59-
return (templateExists || implicitAll) ? OK : NOT_FOUND;
60-
}));
60+
return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).execute(
61+
GetComposableIndexTemplateAction.INSTANCE,
62+
getRequest,
63+
new RestToXContentListener<>(channel, r -> {
64+
final boolean templateExists = r.indexTemplates().isEmpty() == false;
65+
return (templateExists || implicitAll) ? OK : NOT_FOUND;
66+
})
67+
);
6168
}
6269

6370
@Override

0 commit comments

Comments
 (0)