Skip to content

Commit f96a6ea

Browse files
committed
Always allow allocation of unassigned shards
1 parent 43f7c0d commit f96a6ea

File tree

2 files changed

+57
-14
lines changed

2 files changed

+57
-14
lines changed

server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDecider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, Routing
4343
return Decision.single(Decision.Type.YES, NAME, "Decider is disabled");
4444
}
4545

46+
// Never reject allocation of an unassigned shard
47+
if (shardRouting.assignedToNode() == false) {
48+
return Decision.single(Decision.Type.YES, NAME, "Shard is unassigned. Decider takes no action.");
49+
}
50+
4651
// Check whether the shard being relocated has any write load estimate. If it does not, then this decider has no opinion.
4752
var allShardWriteLoads = allocation.clusterInfo().getShardWriteLoads();
4853
var shardWriteLoad = allShardWriteLoads.get(shardRouting.shardId());

server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/WriteLoadConstraintDeciderTests.java

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.elasticsearch.cluster.routing.TestShardRouting;
2626
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
2727
import org.elasticsearch.cluster.routing.allocation.WriteLoadConstraintSettings;
28+
import org.elasticsearch.common.Strings;
29+
import org.elasticsearch.common.regex.Regex;
2830
import org.elasticsearch.common.settings.Settings;
2931
import org.elasticsearch.index.Index;
3032
import org.elasticsearch.index.shard.ShardId;
@@ -102,38 +104,65 @@ public void testWriteLoadDeciderCanAllocate() {
102104
)
103105
.build()
104106
);
105-
assertEquals(
107+
assertDecisionMatches(
106108
"Assigning a new shard to a node that is above the threshold should fail",
107-
Decision.Type.NO,
108109
writeLoadDecider.canAllocate(
109110
testHarness.shardRouting2,
110111
testHarness.exceedingThresholdRoutingNode,
111112
testHarness.routingAllocation
112-
).type()
113+
),
114+
Decision.Type.NO,
115+
"Node [*] with write thread pool utilization [0.99] already exceeds the high utilization threshold of [0.900000]. "
116+
+ "Cannot allocate shard [[test-index][1]] to node without risking increased write latencies."
113117
);
114-
assertEquals(
118+
assertDecisionMatches(
119+
"Unassigned shard should always be accepted",
120+
writeLoadDecider.canAllocate(
121+
testHarness.unassignedShardRouting,
122+
testHarness.exceedingThresholdRoutingNode,
123+
testHarness.routingAllocation
124+
),
125+
Decision.Type.YES,
126+
"Shard is unassigned. Decider takes no action."
127+
);
128+
assertDecisionMatches(
115129
"Assigning a new shard to a node that has capacity should succeed",
130+
writeLoadDecider.canAllocate(testHarness.shardRouting1, testHarness.belowThresholdRoutingNode, testHarness.routingAllocation),
116131
Decision.Type.YES,
117-
writeLoadDecider.canAllocate(testHarness.shardRouting1, testHarness.belowThresholdRoutingNode, testHarness.routingAllocation)
118-
.type()
132+
null
119133
);
120-
assertEquals(
134+
assertDecisionMatches(
121135
"Assigning a new shard without a write load estimate should _not_ be blocked by lack of capacity",
122-
Decision.Type.YES,
123136
writeLoadDecider.canAllocate(
124137
testHarness.thirdRoutingNoWriteLoad,
125138
testHarness.exceedingThresholdRoutingNode,
126139
testHarness.routingAllocation
127-
).type()
140+
),
141+
Decision.Type.YES,
142+
"Shard has no estimated write load. Decider takes no action."
128143
);
129-
assertEquals(
144+
assertDecisionMatches(
130145
"Assigning a new shard that would cause the node to exceed capacity should fail",
146+
writeLoadDecider.canAllocate(testHarness.shardRouting1, testHarness.nearThresholdRoutingNode, testHarness.routingAllocation),
131147
Decision.Type.NO,
132-
writeLoadDecider.canAllocate(testHarness.shardRouting1, testHarness.nearThresholdRoutingNode, testHarness.routingAllocation)
133-
.type()
148+
"The high utilization threshold of [0.900000] would be exceeded on node [*] with utilization [0.89] "
149+
+ "if shard [[test-index][0]] with estimated additional utilisation [0.06250] (write load [0.50000] / threads [8]) were "
150+
+ "assigned to it. Cannot allocate shard to node without risking increased write latencies."
134151
);
135152
}
136153

154+
private void assertDecisionMatches(String description, Decision decision, Decision.Type type, String explanationPattern) {
155+
assertEquals(description, type, decision.type());
156+
if (explanationPattern == null) {
157+
assertNull(decision.getExplanation());
158+
} else {
159+
assertTrue(
160+
Strings.format("Expected: \"%s\", got \"%s\"", explanationPattern, decision.getExplanation()),
161+
Regex.simpleMatch(explanationPattern, decision.getExplanation())
162+
);
163+
}
164+
}
165+
137166
/**
138167
* Carries all the cluster state objects needed for testing after {@link #createClusterStateAndRoutingAllocation} sets them up.
139168
*/
@@ -145,7 +174,8 @@ private record TestHarness(
145174
RoutingNode nearThresholdRoutingNode,
146175
ShardRouting shardRouting1,
147176
ShardRouting shardRouting2,
148-
ShardRouting thirdRoutingNoWriteLoad
177+
ShardRouting thirdRoutingNoWriteLoad,
178+
ShardRouting unassignedShardRouting
149179
) {}
150180

151181
/**
@@ -183,6 +213,7 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
183213
ShardId testShardId1 = new ShardId(testIndex, 0);
184214
ShardId testShardId2 = new ShardId(testIndex, 1);
185215
ShardId testShardId3NoWriteLoad = new ShardId(testIndex, 2);
216+
ShardId testShardId4Unassigned = new ShardId(testIndex, 3);
186217

187218
/**
188219
* Create a ClusterInfo that includes the node and shard level write load estimates for a variety of node capacity situations.
@@ -248,6 +279,12 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
248279
true,
249280
ShardRoutingState.STARTED
250281
);
282+
ShardRouting unassignedShardRouting = TestShardRouting.newShardRouting(
283+
testShardId4Unassigned,
284+
null,
285+
true,
286+
ShardRoutingState.UNASSIGNED
287+
);
251288

252289
RoutingNode exceedingThresholdRoutingNode = RoutingNodesHelper.routingNode(
253290
exceedingThresholdDiscoveryNode.getId(),
@@ -273,7 +310,8 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
273310
nearThresholdRoutingNode,
274311
shardRouting1,
275312
shardRouting2,
276-
thirdRoutingNoWriteLoad
313+
thirdRoutingNoWriteLoad,
314+
unassignedShardRouting
277315
);
278316
}
279317

0 commit comments

Comments
 (0)