-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Faster GET _cluster/settings API #86405
Conversation
The existing API to retrieve the cluster settings relies on pulling in the full cluster state, which can be very expensive. This change adds a dedicated cluster settings API, avoiding serializing the full cluster state. Closes elastic#82342
Pinging @elastic/es-core-infra (Team:Core/Infra) |
Hi @grcevski, I've created a changelog YAML for you. |
} | ||
|
||
public Settings settings() { | ||
return Settings.builder().put(persistentSettings).put(transientSettings).build(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks right:
elasticsearch/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java
Line 1875 in 5791567
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.
…evski/elasticsearch into enhancement/cheaper_settings_request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I left some small comments.
In terms of tests, yes this seems mostly pretty well covered. I think we should have some tests that verify that we haven't mixed up the persistent and transient settings anywhere along the way, and that the new transport action applies settings filters correctly. Also I think we should have an AbstractWireSerializingTestCase<ClusterGetSettingsAction.Response>
(including a mutateInstance
implementation) to ensure it round-trips properly.
} | ||
|
||
public Settings settings() { | ||
return Settings.builder().put(persistentSettings).put(transientSettings).build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks right:
elasticsearch/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java
Line 1875 in 5791567
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.
ActionListener<ClusterGetSettingsAction.Response> listener | ||
) throws Exception { | ||
Metadata metadata = state.metadata(); | ||
listener.onResponse(new ClusterGetSettingsAction.Response(metadata.persistentSettings(), metadata.transientSettings())); |
There was a problem hiding this comment.
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.
public class ClusterGetSettingsAction extends ActionType<ClusterGetSettingsAction.Response> { | ||
|
||
public static final ClusterGetSettingsAction INSTANCE = new ClusterGetSettingsAction(); | ||
public static final String NAME = "cluster:admin/settings/get"; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
/** | ||
* Response for cluster settings | ||
*/ | ||
public static class Response extends ActionResponse implements ToXContentObject { |
There was a problem hiding this comment.
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.
.../src/main/java/org/elasticsearch/action/admin/cluster/settings/ClusterGetSettingsAction.java
Show resolved
Hide resolved
/** | ||
* Cluster get settings request builder | ||
*/ | ||
public static class RequestBuilder extends MasterNodeReadOperationRequestBuilder<Request, Response, RequestBuilder> { |
There was a problem hiding this comment.
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.
.../java/org/elasticsearch/action/admin/cluster/settings/TransportClusterGetSettingsAction.java
Outdated
Show resolved
Hide resolved
* @param request The cluster settings request | ||
* @return The result future | ||
*/ | ||
ActionFuture<ClusterGetSettingsAction.Response> clusterSettings(ClusterGetSettingsAction.Request request); |
There was a problem hiding this comment.
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.
.../src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterGetSettingsAction.java
Outdated
Show resolved
Hide resolved
clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout())); | ||
return channel -> client.admin() | ||
.cluster() | ||
.state(clusterStateRequest, new RestToXContentListener<RestClusterGetSettingsResponse>(channel).map(response -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be slightly nicer to use the same ActionListener<ClusterGetSettingsAction.Response>
for both the legacy and the regular case, with a .map(clusterState -> new ClusterGetSettingsAction.Response(...))
to do the extra conversion step in the legacy case.
…ttings/TransportClusterGetSettingsAction.java disable circuit breakers Co-authored-by: David Turner <[email protected]>
…er/RestClusterGetSettingsAction.java Change onOrAfter to before. Co-authored-by: David Turner <[email protected]>
…evski/elasticsearch into enhancement/cheaper_settings_request
We use the minimum node version in the cluster state to make decisions about backwards compatibility (e.g. to choose newer actions in the REST layer only if all nodes will support it). Once the cluster is fully formed we reject attempts by older nodes to join the cluster so that the minimum node version only ever increases, which makes backwards-compatibility decisions safe. However, it's possible that the REST layer will make decisions about backwards compatibility before the cluster is fully formed. In this state, older nodes may still join the cluster and may therefore see actions that they do not understand. With this commit we report no nodes to the REST layer until the cluster is fully-formed, and change the minimum node version in an empty cluster to be the minimum compatible version. This means the REST layer will operate in a maximally-compatible mode until the cluster is formed. Relates elastic#86405
We use the minimum node version in the cluster state to make decisions about backwards compatibility (e.g. to choose newer actions in the REST layer only if all nodes will support it). Once the cluster is fully formed we reject attempts by older nodes to join the cluster so that the minimum node version only ever increases, which makes backwards-compatibility decisions safe. However, it's possible that the REST layer will make decisions about backwards compatibility before the cluster is fully formed. In this state, older nodes may still join the cluster and may therefore see actions that they do not understand. With this commit we report no nodes to the REST layer until the cluster is fully-formed, and change the minimum node version in an empty cluster to be the minimum compatible version. This means the REST layer will operate in a maximally-compatible mode until the cluster is formed. Relates #86405
...r/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterGetSettingsTests.java
Outdated
Show resolved
Hide resolved
…ttings/ClusterGetSettingsTests.java Co-authored-by: David Turner <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -756,4 +756,5 @@ public interface ClusterAdminClient extends ElasticsearchClient { | |||
* Delete specified dangling indices. | |||
*/ | |||
ActionFuture<AcknowledgedResponse> deleteDanglingIndex(DeleteDanglingIndexRequest request); | |||
|
There was a problem hiding this comment.
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
ClusterGetSettingsAction.INSTANCE, | ||
clusterSettingsRequest, | ||
new RestToXContentListener<RestClusterGetSettingsResponse>(channel).map( | ||
r -> response(r, renderDefaults, settingsFilter, clusterSettings, settings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still sort of like to reduce the duplication here between the two branches but I see it's not so simple because we have to consume the request params before we get hold of the channel. Not a blocking suggestion.
Related to #77466 |
The existing API to retrieve the cluster settings relies on
pulling in the full cluster state, which can be very expensive.
This change adds a dedicated cluster settings API, avoiding
serializing the full cluster state.
Closes #82342