Skip to content

Commit

Permalink
Run TransportGetComposableIndexTemplate on local node (#119830)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nielsbauman authored Jan 9, 2025
1 parent 48b9fae commit 65e4ec1
Show file tree
Hide file tree
Showing 16 changed files with 129 additions and 160 deletions.
5 changes: 5 additions & 0 deletions docs/changelog/119830.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 119830
summary: Run `TransportGetComposableIndexTemplate` on local node
area: Indices APIs
type: enhancement
issues: []
2 changes: 1 addition & 1 deletion docs/reference/indices/get-index-template.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ expressions. If omitted, all templates are returned.

include::{docdir}/rest-api/common-parms.asciidoc[tag=flat-settings]

include::{docdir}/rest-api/common-parms.asciidoc[tag=local]
include::{docdir}/rest-api/common-parms.asciidoc[tag=local-deprecated-9.0.0]

include::{docdir}/rest-api/common-parms.asciidoc[tag=master-timeout]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ public void testCannotDeleteComposableTemplateUsedByDataStream() throws Exceptio
TransportDeleteComposableIndexTemplateAction.Request deleteRequest = new TransportDeleteComposableIndexTemplateAction.Request("id");
client().execute(TransportDeleteComposableIndexTemplateAction.TYPE, deleteRequest).get();

GetComposableIndexTemplateAction.Request getReq = new GetComposableIndexTemplateAction.Request("id");
GetComposableIndexTemplateAction.Request getReq = new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "id");
Exception e3 = expectThrows(Exception.class, client().execute(GetComposableIndexTemplateAction.INSTANCE, getReq));
maybeE = ExceptionsHelper.unwrapCausesAndSuppressed(e3, err -> err.getMessage().contains("index template matching [id] not found"));
assertTrue(maybeE.isPresent());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
import org.elasticsearch.action.admin.indices.recovery.RecoveryAction;
import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction;
import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction;
import org.elasticsearch.action.support.CancellableActionTestPlugin;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.action.support.RefCountingListener;
Expand Down Expand Up @@ -71,6 +72,10 @@ public void testGetComponentTemplateCancellation() {
runRestActionCancellationTest(new Request(HttpGet.METHOD_NAME, "/_component_template"), GetComponentTemplateAction.NAME);
}

public void testGetComposableTemplateCancellation() {
runRestActionCancellationTest(new Request(HttpGet.METHOD_NAME, "/_index_template"), GetComposableIndexTemplateAction.NAME);
}

private void runRestActionCancellationTest(Request request, String actionName) {
final var node = usually() ? internalCluster().getRandomNodeName() : internalCluster().startCoordinatingOnlyNode(Settings.EMPTY);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
},
"master_timeout":{
"type":"time",
"description":"Explicit operation timeout for connection to master node"
"description":"Timeout for waiting for new cluster state in case it is blocked"
},
"local":{
"deprecated":true,
"type":"boolean",
"description":"Return local information, do not retrieve the state from master node (default: false)"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@
},
"master_timeout":{
"type":"time",
"description":"Explicit operation timeout for connection to master node"
"description":"Timeout for waiting for new cluster state in case it is blocked"
},
"local":{
"deprecated":true,
"type":"boolean",
"description":"Return local information, do not retrieve the state from master node (default: false)"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,48 @@ setup:

---
"Get index template with local flag":
- requires:
test_runner_features: ["allowed_warnings"]

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

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

---
"Deprecated local parameter":
- requires:
capabilities:
- method: GET
path: /_get_index_template
capabilities: ["local_param_deprecated"]
test_runner_features: ["capabilities", "warnings"]
reason: Deprecation was implemented with capability

- do:
indices.get_index_template:
local: true
warnings:
- "the [?local] query parameter to this API has no effect, is now deprecated, and will be removed in a future version"

---
"Deprecated local parameter works in v8 compat mode":
- requires:
test_runner_features: ["headers"]

- do:
headers:
Content-Type: "application/vnd.elasticsearch+json;compatible-with=8"
Accept: "application/vnd.elasticsearch+json;compatible-with=8"
indices.get_index_template:
local: true

- exists: index_templates

---
"Add data stream lifecycle":
- requires:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ private void assertComponentAndIndexTemplateDelete(CountDownLatch savedClusterSt

final var response = client().execute(
GetComposableIndexTemplateAction.INSTANCE,
new GetComposableIndexTemplateAction.Request("template*")
new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "template*")
).get();

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

final var response = client().execute(
GetComposableIndexTemplateAction.INSTANCE,
new GetComposableIndexTemplateAction.Request("err*")
new GetComposableIndexTemplateAction.Request(TEST_REQUEST_TIMEOUT, "err*")
).get();

assertTrue(response.indexTemplates().isEmpty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.admin.indices.rollover.RolloverConfiguration;
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
import org.elasticsearch.action.support.local.LocalClusterStateRequest;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.metadata.DataStreamGlobalRetention;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.UpdateForV10;
import org.elasticsearch.tasks.CancellableTask;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;
Expand All @@ -40,24 +45,30 @@ private GetComposableIndexTemplateAction() {
/**
* Request that to retrieve one or more index templates
*/
public static class Request extends MasterNodeReadRequest<Request> {
public static class Request extends LocalClusterStateRequest {

@Nullable
private final String name;
private boolean includeDefaults;

/**
* @param masterTimeout Timeout for waiting for new cluster state in case it is blocked.
* @param name A template name or pattern, or {@code null} to retrieve all templates.
*/
public Request(@Nullable String name) {
super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
public Request(TimeValue masterTimeout, @Nullable String name) {
super(masterTimeout);
if (name != null && name.contains(",")) {
throw new IllegalArgumentException("template name may not contain ','");
}
this.name = name;
this.includeDefaults = false;
}

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

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalString(name);
if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) {
out.writeBoolean(includeDefaults);
}
}

public void includeDefaults(boolean includeDefaults) {
this.includeDefaults = includeDefaults;
}
Expand All @@ -90,6 +92,11 @@ public ActionRequestValidationException validate() {
return null;
}

@Override
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) {
return new CancellableTask(id, type, action, "", parentTaskId, headers);
}

/**
* The name of the index templates.
*/
Expand Down Expand Up @@ -124,19 +131,6 @@ public static class Response extends ActionResponse implements ToXContentObject
@Nullable
private final RolloverConfiguration rolloverConfiguration;

public Response(StreamInput in) throws IOException {
super(in);
indexTemplates = in.readMap(ComposableIndexTemplate::new);
if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) {
rolloverConfiguration = in.readOptionalWriteable(RolloverConfiguration::new);
} else {
rolloverConfiguration = null;
}
if (in.getTransportVersion().between(TransportVersions.V_8_14_0, TransportVersions.V_8_16_0)) {
in.readOptionalWriteable(DataStreamGlobalRetention::read);
}
}

/**
* Please use {@link GetComposableIndexTemplateAction.Response#Response(Map)}
*/
Expand Down Expand Up @@ -184,6 +178,11 @@ public RolloverConfiguration getRolloverConfiguration() {
return rolloverConfiguration;
}

/**
* NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC we must remain able to write these responses until
* we no longer need to support calling this action remotely.
*/
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeMap(indexTemplates, StreamOutput::writeWriteable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,61 @@
import org.elasticsearch.ResourceNotFoundException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
import org.elasticsearch.action.support.ChannelActionListener;
import org.elasticsearch.action.support.local.TransportLocalClusterStateAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.metadata.DataStreamLifecycle;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.core.UpdateForV10;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.CancellableTask;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

import java.util.HashMap;
import java.util.Map;

public class TransportGetComposableIndexTemplateAction extends TransportMasterNodeReadAction<
public class TransportGetComposableIndexTemplateAction extends TransportLocalClusterStateAction<
GetComposableIndexTemplateAction.Request,
GetComposableIndexTemplateAction.Response> {

private final ClusterSettings clusterSettings;

/**
* NB prior to 9.0 this was a TransportMasterNodeReadAction so for BwC it must be registered with the TransportService until
* we no longer need to support calling this action remotely.
*/
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
@SuppressWarnings("this-escape")
@Inject
public TransportGetComposableIndexTemplateAction(
TransportService transportService,
ClusterService clusterService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
ActionFilters actionFilters
) {
super(
GetComposableIndexTemplateAction.NAME,
transportService,
clusterService,
threadPool,
actionFilters,
GetComposableIndexTemplateAction.Request::new,
indexNameExpressionResolver,
GetComposableIndexTemplateAction.Response::new,
transportService.getTaskManager(),
clusterService,
EsExecutors.DIRECT_EXECUTOR_SERVICE
);
clusterSettings = clusterService.getClusterSettings();

transportService.registerRequestHandler(
actionName,
executor,
false,
true,
GetComposableIndexTemplateAction.Request::new,
(request, channel, task) -> executeDirect(task, request, new ChannelActionListener<>(channel))
);
}

@Override
Expand All @@ -65,12 +75,13 @@ protected ClusterBlockException checkBlock(GetComposableIndexTemplateAction.Requ
}

@Override
protected void masterOperation(
protected void localClusterStateOperation(
Task task,
GetComposableIndexTemplateAction.Request request,
ClusterState state,
ActionListener<GetComposableIndexTemplateAction.Response> listener
) {
final var cancellableTask = (CancellableTask) task;
Map<String, ComposableIndexTemplate> allTemplates = state.metadata().templatesV2();
Map<String, ComposableIndexTemplate> results;
// If we did not ask for a specific name, then we return all templates
Expand All @@ -91,6 +102,7 @@ protected void masterOperation(
throw new ResourceNotFoundException("index template matching [" + request.name() + "] not found");
}
}
cancellableTask.ensureNotCancelled();
if (request.includeDefaults()) {
listener.onResponse(
new GetComposableIndexTemplateAction.Response(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestUtils;
import org.elasticsearch.rest.Scope;
import org.elasticsearch.rest.ServerlessScope;
import org.elasticsearch.rest.action.RestCancellableNodeClient;
import org.elasticsearch.rest.action.RestToXContentListener;

import java.io.IOException;
Expand All @@ -26,7 +28,6 @@
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.RestUtils.getMasterNodeTimeout;

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

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
final GetComposableIndexTemplateAction.Request getRequest = new GetComposableIndexTemplateAction.Request(request.param("name"));

getRequest.local(request.paramAsBoolean("local", getRequest.local()));
getRequest.masterNodeTimeout(getMasterNodeTimeout(request));
final GetComposableIndexTemplateAction.Request getRequest = new GetComposableIndexTemplateAction.Request(
RestUtils.getMasterNodeTimeout(request),
request.param("name")
);
getRequest.includeDefaults(request.paramAsBoolean("include_defaults", false));
RestUtils.consumeDeprecatedLocalParameter(request);

final boolean implicitAll = getRequest.name() == null;

return channel -> client.execute(GetComposableIndexTemplateAction.INSTANCE, getRequest, new RestToXContentListener<>(channel, r -> {
final boolean templateExists = r.indexTemplates().isEmpty() == false;
return (templateExists || implicitAll) ? OK : NOT_FOUND;
}));
return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).execute(
GetComposableIndexTemplateAction.INSTANCE,
getRequest,
new RestToXContentListener<>(channel, r -> {
final boolean templateExists = r.indexTemplates().isEmpty() == false;
return (templateExists || implicitAll) ? OK : NOT_FOUND;
})
);
}

@Override
Expand Down
Loading

0 comments on commit 65e4ec1

Please sign in to comment.