25
25
import org .elasticsearch .cluster .routing .TestShardRouting ;
26
26
import org .elasticsearch .cluster .routing .allocation .RoutingAllocation ;
27
27
import org .elasticsearch .cluster .routing .allocation .WriteLoadConstraintSettings ;
28
+ import org .elasticsearch .common .Strings ;
29
+ import org .elasticsearch .common .regex .Regex ;
28
30
import org .elasticsearch .common .settings .Settings ;
29
31
import org .elasticsearch .index .Index ;
30
32
import org .elasticsearch .index .shard .ShardId ;
@@ -107,38 +109,66 @@ public void testWriteLoadDeciderCanAllocate() {
107
109
)
108
110
.build ()
109
111
);
110
- assertEquals (
112
+ assertDecisionMatches (
111
113
"Assigning a new shard to a node that is above the threshold should fail" ,
112
- Decision .Type .NOT_PREFERRED ,
113
114
writeLoadDecider .canAllocate (
114
115
testHarness .shardRouting2 ,
115
116
testHarness .exceedingThresholdRoutingNode ,
116
117
testHarness .routingAllocation
117
- ).type ()
118
+ ),
119
+ Decision .Type .NOT_PREFERRED ,
120
+ "Node [*] with write thread pool utilization [0.99] already exceeds the high utilization threshold of [0.900000]. "
121
+ + "Cannot allocate shard [[test-index][1]] to node without risking increased write latencies."
118
122
);
119
- assertEquals (
123
+ assertDecisionMatches (
124
+ "Unassigned shard should always be accepted" ,
125
+ writeLoadDecider .canAllocate (
126
+ testHarness .unassignedShardRouting ,
127
+ randomFrom (testHarness .exceedingThresholdRoutingNode , testHarness .belowThresholdRoutingNode ),
128
+ testHarness .routingAllocation
129
+ ),
130
+ Decision .Type .YES ,
131
+ "Shard is unassigned. Decider takes no action."
132
+ );
133
+ assertDecisionMatches (
120
134
"Assigning a new shard to a node that has capacity should succeed" ,
135
+ writeLoadDecider .canAllocate (testHarness .shardRouting1 , testHarness .belowThresholdRoutingNode , testHarness .routingAllocation ),
121
136
Decision .Type .YES ,
122
- writeLoadDecider .canAllocate (testHarness .shardRouting1 , testHarness .belowThresholdRoutingNode , testHarness .routingAllocation )
123
- .type ()
137
+ null
124
138
);
125
- assertEquals (
139
+ assertDecisionMatches (
126
140
"Assigning a new shard without a write load estimate should _not_ be blocked by lack of capacity" ,
127
- Decision .Type .YES ,
128
141
writeLoadDecider .canAllocate (
129
142
testHarness .thirdRoutingNoWriteLoad ,
130
143
testHarness .exceedingThresholdRoutingNode ,
131
144
testHarness .routingAllocation
132
- ).type ()
145
+ ),
146
+ Decision .Type .YES ,
147
+ "Shard has no estimated write load. Decider takes no action."
133
148
);
134
- assertEquals (
149
+ assertDecisionMatches (
135
150
"Assigning a new shard that would cause the node to exceed capacity should fail" ,
151
+ writeLoadDecider .canAllocate (testHarness .shardRouting1 , testHarness .nearThresholdRoutingNode , testHarness .routingAllocation ),
136
152
Decision .Type .NOT_PREFERRED ,
137
- writeLoadDecider .canAllocate (testHarness .shardRouting1 , testHarness .nearThresholdRoutingNode , testHarness .routingAllocation )
138
- .type ()
153
+ "The high utilization threshold of [0.900000] would be exceeded on node [*] with utilization [0.89] "
154
+ + "if shard [[test-index][0]] with estimated additional utilisation [0.06250] (write load [0.50000] / threads [8]) were "
155
+ + "assigned to it. Cannot allocate shard to node without risking increased write latencies."
156
+
139
157
);
140
158
}
141
159
160
+ private void assertDecisionMatches (String description , Decision decision , Decision .Type type , String explanationPattern ) {
161
+ assertEquals (description , type , decision .type ());
162
+ if (explanationPattern == null ) {
163
+ assertNull (decision .getExplanation ());
164
+ } else {
165
+ assertTrue (
166
+ Strings .format ("Expected: \" %s\" , got \" %s\" " , explanationPattern , decision .getExplanation ()),
167
+ Regex .simpleMatch (explanationPattern , decision .getExplanation ())
168
+ );
169
+ }
170
+ }
171
+
142
172
/**
143
173
* Carries all the cluster state objects needed for testing after {@link #createClusterStateAndRoutingAllocation} sets them up.
144
174
*/
@@ -150,7 +180,8 @@ private record TestHarness(
150
180
RoutingNode nearThresholdRoutingNode ,
151
181
ShardRouting shardRouting1 ,
152
182
ShardRouting shardRouting2 ,
153
- ShardRouting thirdRoutingNoWriteLoad
183
+ ShardRouting thirdRoutingNoWriteLoad ,
184
+ ShardRouting unassignedShardRouting
154
185
) {}
155
186
156
187
/**
@@ -188,6 +219,7 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
188
219
ShardId testShardId1 = new ShardId (testIndex , 0 );
189
220
ShardId testShardId2 = new ShardId (testIndex , 1 );
190
221
ShardId testShardId3NoWriteLoad = new ShardId (testIndex , 2 );
222
+ ShardId testShardId4Unassigned = new ShardId (testIndex , 3 );
191
223
192
224
/**
193
225
* Create a ClusterInfo that includes the node and shard level write load estimates for a variety of node capacity situations.
@@ -213,6 +245,9 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
213
245
shardIdToWriteLoadEstimate .put (testShardId1 , 0.5 );
214
246
shardIdToWriteLoadEstimate .put (testShardId2 , 0.5 );
215
247
shardIdToWriteLoadEstimate .put (testShardId3NoWriteLoad , 0d );
248
+ if (randomBoolean ()) {
249
+ shardIdToWriteLoadEstimate .put (testShardId4Unassigned , randomDoubleBetween (0.0 , 2.0 , true ));
250
+ }
216
251
217
252
ClusterInfo clusterInfo = ClusterInfo .builder ()
218
253
.nodeUsageStatsForThreadPools (nodeIdToNodeUsageStatsForThreadPools )
@@ -253,6 +288,12 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
253
288
true ,
254
289
ShardRoutingState .STARTED
255
290
);
291
+ ShardRouting unassignedShardRouting = TestShardRouting .newShardRouting (
292
+ testShardId4Unassigned ,
293
+ null ,
294
+ true ,
295
+ ShardRoutingState .UNASSIGNED
296
+ );
256
297
257
298
RoutingNode exceedingThresholdRoutingNode = RoutingNodesHelper .routingNode (
258
299
exceedingThresholdDiscoveryNode .getId (),
@@ -266,8 +307,7 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
266
307
);
267
308
RoutingNode nearThresholdRoutingNode = RoutingNodesHelper .routingNode (
268
309
nearThresholdDiscoveryNode3 .getId (),
269
- nearThresholdDiscoveryNode3 ,
270
- new ShardRouting [] {}
310
+ nearThresholdDiscoveryNode3
271
311
);
272
312
273
313
return new TestHarness (
@@ -278,7 +318,8 @@ private TestHarness createClusterStateAndRoutingAllocation(String indexName) {
278
318
nearThresholdRoutingNode ,
279
319
shardRouting1 ,
280
320
shardRouting2 ,
281
- thirdRoutingNoWriteLoad
321
+ thirdRoutingNoWriteLoad ,
322
+ unassignedShardRouting
282
323
);
283
324
}
284
325
0 commit comments