2525import org .elasticsearch .cluster .routing .TestShardRouting ;
2626import org .elasticsearch .cluster .routing .allocation .RoutingAllocation ;
2727import org .elasticsearch .cluster .routing .allocation .WriteLoadConstraintSettings ;
28+ import org .elasticsearch .common .Strings ;
29+ import org .elasticsearch .common .regex .Regex ;
2830import org .elasticsearch .common .settings .Settings ;
2931import org .elasticsearch .index .Index ;
3032import 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