Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster GET _cluster/settings API #86405

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog/86405.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 86405
summary: Faster GET _cluster/settings API
area: Infra/Core
type: enhancement
issues:
- 82342
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.reroute.TransportClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.TransportClusterGetSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.TransportClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsAction;
import org.elasticsearch.action.admin.cluster.shards.TransportClusterSearchShardsAction;
Expand Down Expand Up @@ -546,6 +548,7 @@ public <Request extends ActionRequest, Response extends ActionResponse> void reg
actions.register(ClusterStateAction.INSTANCE, TransportClusterStateAction.class);
actions.register(ClusterHealthAction.INSTANCE, TransportClusterHealthAction.class);
actions.register(ClusterUpdateSettingsAction.INSTANCE, TransportClusterUpdateSettingsAction.class);
actions.register(ClusterGetSettingsAction.INSTANCE, TransportClusterGetSettingsAction.class);
actions.register(ClusterRerouteAction.INSTANCE, TransportClusterRerouteAction.class);
actions.register(ClusterSearchShardsAction.INSTANCE, TransportClusterSearchShardsAction.class);
actions.register(PendingClusterTasksAction.INSTANCE, TransportPendingClusterTasksAction.class);
Expand Down Expand Up @@ -707,7 +710,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {
registerHandler.accept(new RestClusterStateAction(settingsFilter, threadPool));
registerHandler.accept(new RestClusterHealthAction());
registerHandler.accept(new RestClusterUpdateSettingsAction());
registerHandler.accept(new RestClusterGetSettingsAction(settings, clusterSettings, settingsFilter));
registerHandler.accept(new RestClusterGetSettingsAction(settings, clusterSettings, settingsFilter, nodesInCluster));
registerHandler.accept(new RestClusterRerouteAction(settingsFilter));
registerHandler.accept(new RestClusterSearchShardsAction());
registerHandler.accept(new RestPendingClusterTasksAction());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.action.admin.cluster.settings;

import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
import org.elasticsearch.client.internal.ElasticsearchClient;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Objects;

import static org.elasticsearch.action.admin.cluster.settings.RestClusterGetSettingsResponse.PERSISTENT_FIELD;
import static org.elasticsearch.action.admin.cluster.settings.RestClusterGetSettingsResponse.TRANSIENT_FIELD;

public class ClusterGetSettingsAction extends ActionType<ClusterGetSettingsAction.Response> {

public static final ClusterGetSettingsAction INSTANCE = new ClusterGetSettingsAction();
public static final String NAME = "cluster:admin/settings/get";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be under cluster:monitor/* (maybe cluster:monitor/settings?) so that it's still permitted by clients that only have the monitor privilege.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, this way I can undo some of the changes I needed to do in xpack.


public ClusterGetSettingsAction() {
super(NAME, Response::new);
}

/**
* Request to retrieve the cluster settings
*/
public static class Request extends MasterNodeReadRequest<Request> {
public Request() {}

public Request(StreamInput in) throws IOException {
super(in);
}
DaveCTurner marked this conversation as resolved.
Show resolved Hide resolved

@Override
public ActionRequestValidationException validate() {
return null;
}
}

/**
* Cluster get settings request builder
*/
public static class RequestBuilder extends MasterNodeReadOperationRequestBuilder<Request, Response, RequestBuilder> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder, do we really need all this client-facing machinery? AFAIK this pattern was good for the transport client, but we have no transport client any more so I think we can just call the action directly in the one place it's currently used.


public RequestBuilder(ElasticsearchClient client, ClusterGetSettingsAction action) {
super(client, action, new Request());
}
}

/**
* Response for cluster settings
*/
public static class Response extends ActionResponse implements ToXContentObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be implements ToXContentObject? I think we don't expose it as XContent directly, it's always converted to a RestClusterGetSettingsResponse first.

private final Settings persistentSettings;
private final Settings transientSettings;

public Response(StreamInput in) throws IOException {
super(in);
persistentSettings = Settings.readSettingsFromStream(in);
transientSettings = Settings.readSettingsFromStream(in);
}

public Response(Settings persistentSettings, Settings transientSettings) {
this.persistentSettings = Objects.requireNonNullElse(persistentSettings, Settings.EMPTY);
this.transientSettings = Objects.requireNonNullElse(transientSettings, Settings.EMPTY);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();

builder.startObject(PERSISTENT_FIELD);
persistentSettings.toXContent(builder, params);
builder.endObject();

builder.startObject(TRANSIENT_FIELD);
transientSettings.toXContent(builder, params);
builder.endObject();

builder.endObject();
return builder;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
Settings.writeSettingsToStream(persistentSettings, out);
Settings.writeSettingsToStream(transientSettings, out);
}

public Settings persistentSettings() {
return persistentSettings;
}

public Settings transientSettings() {
return transientSettings;
}

public Settings settings() {
return Settings.builder().put(persistentSettings).put(transientSettings).build();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the right way to get the combined settings, it didn't make sense to double serialize across the transport protocol.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks right:

Settings.builder().put(persistentSettings).put(transientSettings).build(),

However I think it would be fine (preferable even) to send the combined settings separately from the transient and persistent ones, rather than duplicating this logic and risking getting it wrong.

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.action.admin.cluster.settings;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

public class TransportClusterGetSettingsAction extends TransportMasterNodeReadAction<
ClusterGetSettingsAction.Request,
ClusterGetSettingsAction.Response> {

@Inject
public TransportClusterGetSettingsAction(
TransportService transportService,
ClusterService clusterService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
) {
super(
ClusterGetSettingsAction.NAME,
transportService,
clusterService,
threadPool,
actionFilters,
ClusterGetSettingsAction.Request::new,
indexNameExpressionResolver,
ClusterGetSettingsAction.Response::new,
ThreadPool.Names.SAME
);
grcevski marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
protected void masterOperation(
Task task,
ClusterGetSettingsAction.Request request,
ClusterState state,
ActionListener<ClusterGetSettingsAction.Response> listener
) throws Exception {
Metadata metadata = state.metadata();
listener.onResponse(new ClusterGetSettingsAction.Response(metadata.persistentSettings(), metadata.transientSettings()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should apply the node's SettingsFilter here. That's sort of a change in behaviour since we don't filter the settings in the cluster state today, but then again this is a security thing and in most of the other transport actions that send settings around we do filter them.

}

@Override
protected ClusterBlockException checkBlock(ClusterGetSettingsAction.Request request, ClusterState state) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteRequest;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteRequestBuilder;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteResponse;
import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequestBuilder;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse;
Expand Down Expand Up @@ -756,4 +757,26 @@ public interface ClusterAdminClient extends ElasticsearchClient {
* Delete specified dangling indices.
*/
ActionFuture<AcknowledgedResponse> deleteDanglingIndex(DeleteDanglingIndexRequest request);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this file only contains whitespace changes now

/**
* The cluster settings.
*
* @param request The cluster settings request
* @return The result future
*/
ActionFuture<ClusterGetSettingsAction.Response> clusterSettings(ClusterGetSettingsAction.Request request);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, I think this stuff is all unused in practice and best dropped.


/**
* The health of the cluster.
*
* @param request The cluster state request
* @param listener A listener to be notified with a result
*/
void clusterSettings(ClusterGetSettingsAction.Request request, ActionListener<ClusterGetSettingsAction.Response> listener);

/**
* The health of the cluster.
*/
ClusterGetSettingsAction.RequestBuilder prepareClusterSettings();

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteRequest;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteRequestBuilder;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteResponse;
import org.elasticsearch.action.admin.cluster.settings.ClusterGetSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequestBuilder;
Expand Down Expand Up @@ -1194,6 +1195,21 @@ public ActionFuture<AcknowledgedResponse> deleteDanglingIndex(DeleteDanglingInde
return execute(DeleteDanglingIndexAction.INSTANCE, request);
}

@Override
public ActionFuture<ClusterGetSettingsAction.Response> clusterSettings(ClusterGetSettingsAction.Request request) {
return execute(ClusterGetSettingsAction.INSTANCE, request);
}

@Override
public void clusterSettings(ClusterGetSettingsAction.Request request, ActionListener<ClusterGetSettingsAction.Response> listener) {
execute(ClusterGetSettingsAction.INSTANCE, request, listener);
}

@Override
public ClusterGetSettingsAction.RequestBuilder prepareClusterSettings() {
return new ClusterGetSettingsAction.RequestBuilder(this, ClusterGetSettingsAction.INSTANCE);
}

@Override
public void deleteDanglingIndex(DeleteDanglingIndexRequest request, ActionListener<AcknowledgedResponse> listener) {
execute(DeleteDanglingIndexAction.INSTANCE, request, listener);
Expand Down
Loading