Skip to content

Commit 0fb31d9

Browse files
committed
Allow static cluster.max_voting_config_exclusions (#53717)
Today we only read `cluster.max_voting_config_exclusions` from the dynamic settings in the cluster metadata, ignoring any value set in `elasticsearch.yml`. This commit addresses this. Closes #53455
1 parent efd1838 commit 0fb31d9

File tree

2 files changed

+58
-19
lines changed

2 files changed

+58
-19
lines changed

server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java

+23-9
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@
3939
import org.elasticsearch.common.Priority;
4040
import org.elasticsearch.common.inject.Inject;
4141
import org.elasticsearch.common.io.stream.StreamInput;
42+
import org.elasticsearch.common.settings.ClusterSettings;
4243
import org.elasticsearch.common.settings.Setting;
4344
import org.elasticsearch.common.settings.Setting.Property;
45+
import org.elasticsearch.common.settings.Settings;
4446
import org.elasticsearch.common.unit.TimeValue;
4547
import org.elasticsearch.threadpool.ThreadPool;
4648
import org.elasticsearch.threadpool.ThreadPool.Names;
@@ -59,11 +61,21 @@ public class TransportAddVotingConfigExclusionsAction extends TransportMasterNod
5961
public static final Setting<Integer> MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING
6062
= Setting.intSetting("cluster.max_voting_config_exclusions", 10, 1, Property.Dynamic, Property.NodeScope);
6163

64+
private volatile int maxVotingConfigExclusions;
65+
6266
@Inject
63-
public TransportAddVotingConfigExclusionsAction(TransportService transportService, ClusterService clusterService, ThreadPool threadPool,
64-
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
67+
public TransportAddVotingConfigExclusionsAction(Settings settings, ClusterSettings clusterSettings, TransportService transportService,
68+
ClusterService clusterService, ThreadPool threadPool, ActionFilters actionFilters,
69+
IndexNameExpressionResolver indexNameExpressionResolver) {
6570
super(AddVotingConfigExclusionsAction.NAME, transportService, clusterService, threadPool, actionFilters,
6671
AddVotingConfigExclusionsRequest::new, indexNameExpressionResolver);
72+
73+
maxVotingConfigExclusions = MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(settings);
74+
clusterSettings.addSettingsUpdateConsumer(MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING, this::setMaxVotingConfigExclusions);
75+
}
76+
77+
private void setMaxVotingConfigExclusions(int maxVotingConfigExclusions) {
78+
this.maxVotingConfigExclusions = maxVotingConfigExclusions;
6779
}
6880

6981
@Override
@@ -80,7 +92,8 @@ protected AddVotingConfigExclusionsResponse read(StreamInput in) throws IOExcept
8092
protected void masterOperation(AddVotingConfigExclusionsRequest request, ClusterState state,
8193
ActionListener<AddVotingConfigExclusionsResponse> listener) throws Exception {
8294

83-
resolveVotingConfigExclusionsAndCheckMaximum(request, state); // throws IAE if no nodes matched or maximum exceeded
95+
resolveVotingConfigExclusionsAndCheckMaximum(request, state, maxVotingConfigExclusions);
96+
// throws IAE if no nodes matched or maximum exceeded
8497

8598
clusterService.submitStateUpdateTask("add-voting-config-exclusions", new ClusterStateUpdateTask(Priority.URGENT) {
8699

@@ -89,14 +102,14 @@ protected void masterOperation(AddVotingConfigExclusionsRequest request, Cluster
89102
@Override
90103
public ClusterState execute(ClusterState currentState) {
91104
assert resolvedExclusions == null : resolvedExclusions;
92-
resolvedExclusions = resolveVotingConfigExclusionsAndCheckMaximum(request, currentState);
105+
final int finalMaxVotingConfigExclusions = TransportAddVotingConfigExclusionsAction.this.maxVotingConfigExclusions;
106+
resolvedExclusions = resolveVotingConfigExclusionsAndCheckMaximum(request, currentState, finalMaxVotingConfigExclusions);
93107

94108
final CoordinationMetaData.Builder builder = CoordinationMetaData.builder(currentState.coordinationMetaData());
95109
resolvedExclusions.forEach(builder::addVotingConfigExclusion);
96110
final MetaData newMetaData = MetaData.builder(currentState.metaData()).coordinationMetaData(builder.build()).build();
97111
final ClusterState newState = ClusterState.builder(currentState).metaData(newMetaData).build();
98-
assert newState.getVotingConfigExclusions().size() <= MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(
99-
currentState.metaData().settings());
112+
assert newState.getVotingConfigExclusions().size() <= finalMaxVotingConfigExclusions;
100113
return newState;
101114
}
102115

@@ -148,9 +161,10 @@ public void onTimeout(TimeValue timeout) {
148161
}
149162

150163
private static Set<VotingConfigExclusion> resolveVotingConfigExclusionsAndCheckMaximum(AddVotingConfigExclusionsRequest request,
151-
ClusterState state) {
152-
return request.resolveVotingConfigExclusionsAndCheckMaximum(state,
153-
MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(state.metaData().settings()), MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey());
164+
ClusterState state,
165+
int maxVotingConfigExclusions) {
166+
return request.resolveVotingConfigExclusionsAndCheckMaximum(state, maxVotingConfigExclusions,
167+
MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey());
154168
}
155169

156170
@Override

server/src/test/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsActionTests.java

+35-10
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.elasticsearch.cluster.node.DiscoveryNodes.Builder;
3838
import org.elasticsearch.cluster.service.ClusterService;
3939
import org.elasticsearch.common.io.stream.StreamInput;
40+
import org.elasticsearch.common.settings.ClusterSettings;
4041
import org.elasticsearch.common.settings.Settings;
4142
import org.elasticsearch.common.unit.TimeValue;
4243
import org.elasticsearch.test.ESTestCase;
@@ -81,6 +82,8 @@ public class TransportAddVotingConfigExclusionsActionTests extends ESTestCase {
8182

8283
private TransportService transportService;
8384
private ClusterStateObserver clusterStateObserver;
85+
private ClusterSettings clusterSettings;
86+
private int staticMaximum;
8487

8588
@BeforeClass
8689
public static void createThreadPoolAndClusterService() {
@@ -117,8 +120,18 @@ public void setupForTest() {
117120
transportService = transport.createTransportService(Settings.EMPTY, threadPool,
118121
TransportService.NOOP_TRANSPORT_INTERCEPTOR, boundTransportAddress -> localNode, null, emptySet());
119122

120-
new TransportAddVotingConfigExclusionsAction(transportService, clusterService, threadPool, new ActionFilters(emptySet()),
121-
new IndexNameExpressionResolver()); // registers action
123+
final Settings.Builder nodeSettingsBuilder = Settings.builder();
124+
if (randomBoolean()) {
125+
staticMaximum = between(5, 15);
126+
nodeSettingsBuilder.put(MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey(), staticMaximum);
127+
} else {
128+
staticMaximum = MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(Settings.EMPTY);
129+
}
130+
final Settings nodeSettings = nodeSettingsBuilder.build();
131+
clusterSettings = new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
132+
133+
new TransportAddVotingConfigExclusionsAction(nodeSettings, clusterSettings, transportService, clusterService, threadPool,
134+
new ActionFilters(emptySet()), new IndexNameExpressionResolver()); // registers action
122135

123136
transportService.start();
124137
transportService.acceptIncomingRequests();
@@ -308,20 +321,32 @@ public void testSucceedsEvenIfAllExclusionsAlreadyAdded() throws InterruptedExce
308321
}
309322

310323
public void testReturnsErrorIfMaximumExclusionCountExceeded() throws InterruptedException {
311-
final MetaData.Builder metaDataBuilder = MetaData.builder(clusterService.state().metaData()).persistentSettings(
312-
Settings.builder().put(clusterService.state().metaData().persistentSettings())
313-
.put(MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey(), 2).build());
324+
final MetaData.Builder metaDataBuilder = MetaData.builder(clusterService.state().metaData());
314325
CoordinationMetaData.Builder coordinationMetaDataBuilder =
315-
CoordinationMetaData.builder(clusterService.state().coordinationMetaData())
316-
.addVotingConfigExclusion(localNodeExclusion);
326+
CoordinationMetaData.builder(clusterService.state().coordinationMetaData())
327+
.addVotingConfigExclusion(localNodeExclusion);
328+
329+
final int actualMaximum;
330+
if (randomBoolean()) {
331+
actualMaximum = staticMaximum;
332+
} else {
333+
actualMaximum = between(2, 15);
334+
clusterSettings.applySettings(Settings.builder().put(clusterService.state().metaData().persistentSettings())
335+
.put(MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey(), actualMaximum).build());
336+
}
337+
338+
for (int i = 2; i < actualMaximum; i++) {
339+
coordinationMetaDataBuilder.addVotingConfigExclusion(
340+
new VotingConfigExclusion(randomAlphaOfLength(10), randomAlphaOfLength(10)));
341+
}
317342

318343
final int existingCount, newCount;
319344
if (randomBoolean()) {
320345
coordinationMetaDataBuilder.addVotingConfigExclusion(otherNode1Exclusion);
321-
existingCount = 2;
346+
existingCount = actualMaximum;
322347
newCount = 1;
323348
} else {
324-
existingCount = 1;
349+
existingCount = actualMaximum - 1;
325350
newCount = 2;
326351
}
327352

@@ -346,7 +371,7 @@ public void testReturnsErrorIfMaximumExclusionCountExceeded() throws Interrupted
346371
assertThat(rootCause, instanceOf(IllegalArgumentException.class));
347372
assertThat(rootCause.getMessage(), equalTo("add voting config exclusions request for [other*] would add [" + newCount +
348373
"] exclusions to the existing [" + existingCount +
349-
"] which would exceed the maximum of [2] set by [cluster.max_voting_config_exclusions]"));
374+
"] which would exceed the maximum of [" + actualMaximum + "] set by [cluster.max_voting_config_exclusions]"));
350375
}
351376

352377
public void testTimesOut() throws InterruptedException {

0 commit comments

Comments
 (0)