Skip to content

Commit f395f11

Browse files
authored
Increase minimum threshold in shard balancer (#115831)
Support for thresholds between 0.0 and 1.0 was deprecated in #92100. This commit removes this support in 9.0.
1 parent a7031d8 commit f395f11

File tree

5 files changed

+58
-51
lines changed

5 files changed

+58
-51
lines changed

docs/changelog/115831.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pr: 115831
2+
summary: Increase minimum threshold in shard balancer
3+
area: Allocation
4+
type: breaking
5+
issues: []
6+
breaking:
7+
title: Minimum shard balancer threshold is now 1.0
8+
area: Cluster and node setting
9+
details: >-
10+
Earlier versions of {es} accepted any non-negative value for `cluster.routing.allocation.balance.threshold`, but values smaller than
11+
`1.0` do not make sense and have been ignored since version 8.6.1. From 9.0.0 these nonsensical values are now forbidden.
12+
impact: Do not set `cluster.routing.allocation.balance.threshold` to a value less than `1.0`.
13+
notable: false

qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/FullClusterRestartIT.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import org.apache.http.util.EntityUtils;
1717
import org.elasticsearch.Build;
1818
import org.elasticsearch.client.Request;
19+
import org.elasticsearch.client.RequestOptions;
1920
import org.elasticsearch.client.Response;
2021
import org.elasticsearch.client.ResponseException;
2122
import org.elasticsearch.client.RestClient;
23+
import org.elasticsearch.client.WarningsHandler;
2224
import org.elasticsearch.cluster.metadata.IndexMetadata;
2325
import org.elasticsearch.cluster.metadata.MetadataIndexStateService;
2426
import org.elasticsearch.common.Strings;
@@ -27,6 +29,7 @@
2729
import org.elasticsearch.common.xcontent.support.XContentMapValues;
2830
import org.elasticsearch.core.Booleans;
2931
import org.elasticsearch.core.CheckedFunction;
32+
import org.elasticsearch.core.UpdateForV10;
3033
import org.elasticsearch.index.IndexSettings;
3134
import org.elasticsearch.index.IndexVersion;
3235
import org.elasticsearch.index.IndexVersions;
@@ -72,6 +75,7 @@
7275
import static java.util.stream.Collectors.toList;
7376
import static org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.SYSTEM_INDEX_ENFORCEMENT_INDEX_VERSION;
7477
import static org.elasticsearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING;
78+
import static org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator.THRESHOLD_SETTING;
7579
import static org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY;
7680
import static org.elasticsearch.test.MapMatcher.assertMap;
7781
import static org.elasticsearch.test.MapMatcher.matchesMap;
@@ -1949,4 +1953,35 @@ public static void assertNumHits(String index, int numHits, int totalShards) thr
19491953
assertThat(XContentMapValues.extractValue("_shards.successful", resp), equalTo(totalShards));
19501954
assertThat(extractTotalHits(resp), equalTo(numHits));
19511955
}
1956+
1957+
@UpdateForV10(owner = UpdateForV10.Owner.DISTRIBUTED_COORDINATION) // this test is just about v8->v9 upgrades, remove it in v10
1958+
public void testBalancedShardsAllocatorThreshold() throws Exception {
1959+
assumeTrue("test only applies for v8->v9 upgrades", getOldClusterTestVersion().getMajor() == 8);
1960+
1961+
final var chosenValue = randomFrom("0", "0.1", "0.5", "0.999");
1962+
1963+
if (isRunningAgainstOldCluster()) {
1964+
final var request = newXContentRequest(
1965+
HttpMethod.PUT,
1966+
"/_cluster/settings",
1967+
(builder, params) -> builder.startObject("persistent").field(THRESHOLD_SETTING.getKey(), chosenValue).endObject()
1968+
);
1969+
request.setOptions(RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE));
1970+
assertOK(client().performRequest(request));
1971+
}
1972+
1973+
final var clusterSettingsResponse = ObjectPath.createFromResponse(
1974+
client().performRequest(new Request("GET", "/_cluster/settings"))
1975+
);
1976+
1977+
final var settingsPath = "persistent." + THRESHOLD_SETTING.getKey();
1978+
final var settingValue = clusterSettingsResponse.evaluate(settingsPath);
1979+
1980+
if (isRunningAgainstOldCluster()) {
1981+
assertEquals(chosenValue, settingValue);
1982+
} else {
1983+
assertNull(settingValue);
1984+
assertNotNull(clusterSettingsResponse.<String>evaluate("persistent.archived." + THRESHOLD_SETTING.getKey()));
1985+
}
1986+
}
19521987
}

server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,13 @@
3232
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
3333
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
3434
import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type;
35-
import org.elasticsearch.common.logging.DeprecationCategory;
36-
import org.elasticsearch.common.logging.DeprecationLogger;
3735
import org.elasticsearch.common.settings.ClusterSettings;
3836
import org.elasticsearch.common.settings.Setting;
3937
import org.elasticsearch.common.settings.Setting.Property;
4038
import org.elasticsearch.common.settings.Settings;
4139
import org.elasticsearch.common.util.Maps;
4240
import org.elasticsearch.common.util.set.Sets;
4341
import org.elasticsearch.core.Tuple;
44-
import org.elasticsearch.core.UpdateForV9;
4542
import org.elasticsearch.gateway.PriorityComparator;
4643
import org.elasticsearch.index.shard.ShardId;
4744
import org.elasticsearch.injection.guice.Inject;
@@ -109,7 +106,7 @@ public class BalancedShardsAllocator implements ShardsAllocator {
109106
public static final Setting<Float> THRESHOLD_SETTING = Setting.floatSetting(
110107
"cluster.routing.allocation.balance.threshold",
111108
1.0f,
112-
0.0f,
109+
1.0f,
113110
Property.Dynamic,
114111
Property.NodeScope
115112
);
@@ -140,34 +137,10 @@ public BalancedShardsAllocator(ClusterSettings clusterSettings, WriteLoadForecas
140137
clusterSettings.initializeAndWatch(INDEX_BALANCE_FACTOR_SETTING, value -> this.indexBalanceFactor = value);
141138
clusterSettings.initializeAndWatch(WRITE_LOAD_BALANCE_FACTOR_SETTING, value -> this.writeLoadBalanceFactor = value);
142139
clusterSettings.initializeAndWatch(DISK_USAGE_BALANCE_FACTOR_SETTING, value -> this.diskUsageBalanceFactor = value);
143-
clusterSettings.initializeAndWatch(THRESHOLD_SETTING, value -> this.threshold = ensureValidThreshold(value));
140+
clusterSettings.initializeAndWatch(THRESHOLD_SETTING, value -> this.threshold = value);
144141
this.writeLoadForecaster = writeLoadForecaster;
145142
}
146143

147-
/**
148-
* Clamp threshold to be at least 1, and log a critical deprecation warning if smaller values are given.
149-
*
150-
* Once {@link org.elasticsearch.Version#V_7_17_0} goes out of scope, start to properly reject such bad values.
151-
*/
152-
@UpdateForV9(owner = UpdateForV9.Owner.DISTRIBUTED_COORDINATION)
153-
private static float ensureValidThreshold(float threshold) {
154-
if (1.0f <= threshold) {
155-
return threshold;
156-
} else {
157-
DeprecationLogger.getLogger(BalancedShardsAllocator.class)
158-
.critical(
159-
DeprecationCategory.SETTINGS,
160-
"balance_threshold_too_small",
161-
"ignoring value [{}] for [{}] since it is smaller than 1.0; "
162-
+ "setting [{}] to a value smaller than 1.0 will be forbidden in a future release",
163-
threshold,
164-
THRESHOLD_SETTING.getKey(),
165-
THRESHOLD_SETTING.getKey()
166-
);
167-
return 1.0f;
168-
}
169-
}
170-
171144
@Override
172145
public void allocate(RoutingAllocation allocation) {
173146
assert allocation.ignoreDisable() == false;

server/src/test/java/org/elasticsearch/cluster/routing/allocation/BalancedSingleShardTests.java

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public void testNodeDecisionsRanking() {
246246
// return the same ranking as the current node
247247
ClusterState clusterState = ClusterStateCreationUtils.state(randomIntBetween(1, 10), new String[] { "idx" }, 1);
248248
ShardRouting shardToRebalance = clusterState.routingTable().index("idx").shardsWithState(ShardRoutingState.STARTED).get(0);
249-
MoveDecision decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet(), -1);
249+
MoveDecision decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet());
250250
int currentRanking = decision.getCurrentNodeRanking();
251251
assertEquals(1, currentRanking);
252252
for (NodeAllocationResult result : decision.getNodeDecisions()) {
@@ -258,7 +258,7 @@ public void testNodeDecisionsRanking() {
258258
clusterState = ClusterStateCreationUtils.state(1, new String[] { "idx" }, randomIntBetween(2, 10));
259259
shardToRebalance = clusterState.routingTable().index("idx").shardsWithState(ShardRoutingState.STARTED).get(0);
260260
clusterState = addNodesToClusterState(clusterState, randomIntBetween(1, 10));
261-
decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet(), 0.01f);
261+
decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet());
262262
for (NodeAllocationResult result : decision.getNodeDecisions()) {
263263
assertThat(result.getWeightRanking(), lessThan(decision.getCurrentNodeRanking()));
264264
}
@@ -285,7 +285,7 @@ public void testNodeDecisionsRanking() {
285285
}
286286
}
287287
clusterState = addNodesToClusterState(clusterState, 1);
288-
decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet(), 0.01f);
288+
decision = executeRebalanceFor(shardToRebalance, clusterState, emptySet());
289289
for (NodeAllocationResult result : decision.getNodeDecisions()) {
290290
if (result.getWeightRanking() < decision.getCurrentNodeRanking()) {
291291
// highest ranked node should not be any of the initial nodes
@@ -298,22 +298,13 @@ public void testNodeDecisionsRanking() {
298298
assertTrue(nodesWithTwoShards.contains(result.getNode().getId()));
299299
}
300300
}
301-
302-
assertCriticalWarnings("""
303-
ignoring value [0.01] for [cluster.routing.allocation.balance.threshold] since it is smaller than 1.0; setting \
304-
[cluster.routing.allocation.balance.threshold] to a value smaller than 1.0 will be forbidden in a future release""");
305301
}
306302

307303
private MoveDecision executeRebalanceFor(
308304
final ShardRouting shardRouting,
309305
final ClusterState clusterState,
310-
final Set<String> noDecisionNodes,
311-
final float threshold
306+
final Set<String> noDecisionNodes
312307
) {
313-
Settings settings = Settings.EMPTY;
314-
if (Float.compare(-1.0f, threshold) != 0) {
315-
settings = Settings.builder().put(BalancedShardsAllocator.THRESHOLD_SETTING.getKey(), threshold).build();
316-
}
317308
AllocationDecider allocationDecider = new AllocationDecider() {
318309
@Override
319310
public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
@@ -329,7 +320,7 @@ public Decision canRebalance(ShardRouting shardRouting, RoutingAllocation alloca
329320
return Decision.YES;
330321
}
331322
};
332-
BalancedShardsAllocator allocator = new BalancedShardsAllocator(settings);
323+
BalancedShardsAllocator allocator = new BalancedShardsAllocator(Settings.EMPTY);
333324
RoutingAllocation routingAllocation = newRoutingAllocation(
334325
new AllocationDeciders(Arrays.asList(allocationDecider, rebalanceDecider)),
335326
clusterState

server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocatorTests.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,10 @@ public void testGetIndexDiskUsageInBytes() {
441441

442442
public void testThresholdLimit() {
443443
final var badValue = (float) randomDoubleBetween(0.0, Math.nextDown(1.0f), true);
444-
assertEquals(
445-
1.0f,
446-
new BalancedShardsAllocator(Settings.builder().put(BalancedShardsAllocator.THRESHOLD_SETTING.getKey(), badValue).build())
447-
.getThreshold(),
448-
0.0f
444+
expectThrows(
445+
IllegalArgumentException.class,
446+
() -> new BalancedShardsAllocator(Settings.builder().put(BalancedShardsAllocator.THRESHOLD_SETTING.getKey(), badValue).build())
449447
);
450-
assertCriticalWarnings("ignoring value [" + badValue + """
451-
] for [cluster.routing.allocation.balance.threshold] since it is smaller than 1.0; setting \
452-
[cluster.routing.allocation.balance.threshold] to a value smaller than 1.0 will be forbidden in a future release""");
453448

454449
final var goodValue = (float) randomDoubleBetween(1.0, 10.0, true);
455450
assertEquals(

0 commit comments

Comments
 (0)