Skip to content

Commit 75cb784

Browse files
authored
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 856721c commit 75cb784

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

Lines changed: 23 additions & 9 deletions
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.tasks.Task;
4648
import org.elasticsearch.threadpool.ThreadPool;
@@ -60,11 +62,21 @@ public class TransportAddVotingConfigExclusionsAction extends TransportMasterNod
6062
public static final Setting<Integer> MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING
6163
= Setting.intSetting("cluster.max_voting_config_exclusions", 10, 1, Property.Dynamic, Property.NodeScope);
6264

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

7082
@Override
@@ -81,7 +93,8 @@ protected AddVotingConfigExclusionsResponse read(StreamInput in) throws IOExcept
8193
protected void masterOperation(Task task, AddVotingConfigExclusionsRequest request, ClusterState state,
8294
ActionListener<AddVotingConfigExclusionsResponse> listener) throws Exception {
8395

84-
resolveVotingConfigExclusionsAndCheckMaximum(request, state); // throws IAE if no nodes matched or maximum exceeded
96+
resolveVotingConfigExclusionsAndCheckMaximum(request, state, maxVotingConfigExclusions);
97+
// throws IAE if no nodes matched or maximum exceeded
8598

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

@@ -90,14 +103,14 @@ protected void masterOperation(Task task, AddVotingConfigExclusionsRequest reque
90103
@Override
91104
public ClusterState execute(ClusterState currentState) {
92105
assert resolvedExclusions == null : resolvedExclusions;
93-
resolvedExclusions = resolveVotingConfigExclusionsAndCheckMaximum(request, currentState);
106+
final int finalMaxVotingConfigExclusions = TransportAddVotingConfigExclusionsAction.this.maxVotingConfigExclusions;
107+
resolvedExclusions = resolveVotingConfigExclusionsAndCheckMaximum(request, currentState, finalMaxVotingConfigExclusions);
94108

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

@@ -149,9 +162,10 @@ public void onTimeout(TimeValue timeout) {
149162
}
150163

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

157171
@Override

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

Lines changed: 35 additions & 10 deletions
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;
@@ -80,6 +81,8 @@ public class TransportAddVotingConfigExclusionsActionTests extends ESTestCase {
8081

8182
private TransportService transportService;
8283
private ClusterStateObserver clusterStateObserver;
84+
private ClusterSettings clusterSettings;
85+
private int staticMaximum;
8386

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

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

122135
transportService.start();
123136
transportService.acceptIncomingRequests();
@@ -307,20 +320,32 @@ public void testSucceedsEvenIfAllExclusionsAlreadyAdded() throws InterruptedExce
307320
}
308321

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

317342
final int existingCount, newCount;
318343
if (randomBoolean()) {
319344
coordinationMetaDataBuilder.addVotingConfigExclusion(otherNode1Exclusion);
320-
existingCount = 2;
345+
existingCount = actualMaximum;
321346
newCount = 1;
322347
} else {
323-
existingCount = 1;
348+
existingCount = actualMaximum - 1;
324349
newCount = 2;
325350
}
326351

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

351376
public void testTimesOut() throws InterruptedException {

0 commit comments

Comments
 (0)