4141import org .opensearch .action .support .master .AcknowledgedResponse ;
4242import org .opensearch .client .Client ;
4343import org .opensearch .cluster .ClusterState ;
44+ import org .opensearch .cluster .metadata .IndexMetadata ;
4445import org .opensearch .cluster .metadata .Metadata ;
4546import org .opensearch .common .Priority ;
4647import org .opensearch .common .network .NetworkModule ;
6869
6970import static org .opensearch .cluster .metadata .IndexMetadata .SETTING_NUMBER_OF_REPLICAS ;
7071import static org .opensearch .cluster .metadata .IndexMetadata .SETTING_NUMBER_OF_SHARDS ;
72+ import static org .opensearch .indices .ShardLimitValidator .SETTING_CLUSTER_MAX_SHARDS_PER_NODE ;
73+ import static org .opensearch .indices .ShardLimitValidator .SETTING_MAX_SHARDS_PER_CLUSTER_KEY ;
7174import static org .opensearch .test .NodeRoles .dataNode ;
7275import static org .opensearch .test .hamcrest .OpenSearchAssertions .assertAcked ;
7376import static org .hamcrest .Matchers .equalTo ;
7477import static org .hamcrest .Matchers .greaterThan ;
7578
7679@ OpenSearchIntegTestCase .ClusterScope (scope = OpenSearchIntegTestCase .Scope .TEST )
7780public class ClusterShardLimitIT extends OpenSearchIntegTestCase {
78- private static final String shardsPerNodeKey = ShardLimitValidator . SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ();
81+ private static final String shardsPerNodeKey = SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ();
7982 private static final String ignoreDotIndexKey = ShardLimitValidator .SETTING_CLUSTER_IGNORE_DOT_INDEXES .getKey ();
8083
8184 public void testSettingClusterMaxShards () {
8285 int shardsPerNode = between (1 , 500_000 );
83- setShardsPerNode (shardsPerNode );
86+ setMaxShardLimit (shardsPerNode , shardsPerNodeKey );
8487 }
8588
8689 public void testSettingIgnoreDotIndexes () {
@@ -118,7 +121,7 @@ public void testIndexCreationOverLimit() {
118121
119122 ShardCounts counts = ShardCounts .forDataNodeCount (dataNodes );
120123
121- setShardsPerNode (counts .getShardsPerNode ());
124+ setMaxShardLimit (counts .getShardsPerNode (), shardsPerNodeKey );
122125 // Create an index that will bring us up to the limit
123126 createIndex (
124127 "test" ,
@@ -155,7 +158,7 @@ public void testIndexCreationOverLimitForDotIndexesSucceeds() {
155158 int dataNodes = client ().admin ().cluster ().prepareState ().get ().getState ().getNodes ().getDataNodes ().size ();
156159
157160 // Setting the cluster.max_shards_per_node setting according to the data node count.
158- setShardsPerNode (dataNodes );
161+ setMaxShardLimit (dataNodes , shardsPerNodeKey );
159162 setIgnoreDotIndex (true );
160163
161164 /*
@@ -176,9 +179,7 @@ public void testIndexCreationOverLimitForDotIndexesSucceeds() {
176179
177180 // Getting cluster.max_shards_per_node setting
178181 ClusterState clusterState = client ().admin ().cluster ().prepareState ().get ().getState ();
179- String maxShardsPerNode = clusterState .getMetadata ()
180- .settings ()
181- .get (ShardLimitValidator .SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
182+ String maxShardsPerNode = clusterState .getMetadata ().settings ().get (SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
182183
183184 // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node
184185 assertEquals (dataNodes * Integer .parseInt (maxShardsPerNode ), currentActiveShards );
@@ -203,7 +204,7 @@ public void testIndexCreationOverLimitForDotIndexesFail() {
203204 int maxAllowedShards = dataNodes * dataNodes ;
204205
205206 // Setting the cluster.max_shards_per_node setting according to the data node count.
206- setShardsPerNode (dataNodes );
207+ setMaxShardLimit (dataNodes , shardsPerNodeKey );
207208
208209 /*
209210 Create an index that will bring us up to the limit. It would create index with primary equal to the
@@ -223,9 +224,7 @@ public void testIndexCreationOverLimitForDotIndexesFail() {
223224
224225 // Getting cluster.max_shards_per_node setting
225226 ClusterState clusterState = client ().admin ().cluster ().prepareState ().get ().getState ();
226- String maxShardsPerNode = clusterState .getMetadata ()
227- .settings ()
228- .get (ShardLimitValidator .SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
227+ String maxShardsPerNode = clusterState .getMetadata ().settings ().get (SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
229228
230229 // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node
231230 assertEquals (dataNodes * Integer .parseInt (maxShardsPerNode ), currentActiveShards );
@@ -247,6 +246,27 @@ public void testIndexCreationOverLimitForDotIndexesFail() {
247246 assertFalse (clusterState .getMetadata ().hasIndex (".test-index" ));
248247 }
249248
249+ public void testCreateIndexWithMaxClusterShardSetting () {
250+ int dataNodes = client ().admin ().cluster ().prepareState ().get ().getState ().getNodes ().getDataNodes ().size ();
251+ ClusterState clusterState = client ().admin ().cluster ().prepareState ().get ().getState ();
252+ setMaxShardLimit (dataNodes , shardsPerNodeKey );
253+
254+ int maxAllowedShards = dataNodes + 1 ;
255+ int extraShardCount = maxAllowedShards + 1 ;
256+ // Getting total active shards in the cluster.
257+ int currentActiveShards = client ().admin ().cluster ().prepareHealth ().get ().getActiveShards ();
258+ try {
259+ setMaxShardLimit (maxAllowedShards , SETTING_MAX_SHARDS_PER_CLUSTER_KEY );
260+ prepareCreate ("test_index_with_cluster_shard_limit" ).setSettings (
261+ Settings .builder ().put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , extraShardCount ).put (SETTING_NUMBER_OF_REPLICAS , 0 ).build ()
262+ ).get ();
263+ } catch (final IllegalArgumentException ex ) {
264+ verifyException (Math .min (maxAllowedShards , dataNodes * dataNodes ), currentActiveShards , extraShardCount , ex );
265+ } finally {
266+ setMaxShardLimit (-1 , SETTING_MAX_SHARDS_PER_CLUSTER_KEY );
267+ }
268+ }
269+
250270 /**
251271 * The test checks if the index starting with the .ds- can be created if the node has
252272 * number of shards equivalent to the cluster.max_shards_per_node and the cluster.ignore_Dot_indexes
@@ -258,7 +278,7 @@ public void testIndexCreationOverLimitForDataStreamIndexes() {
258278 int maxAllowedShards = dataNodes * dataNodes ;
259279
260280 // Setting the cluster.max_shards_per_node setting according to the data node count.
261- setShardsPerNode (dataNodes );
281+ setMaxShardLimit (dataNodes , shardsPerNodeKey );
262282 setIgnoreDotIndex (true );
263283
264284 /*
@@ -279,9 +299,7 @@ public void testIndexCreationOverLimitForDataStreamIndexes() {
279299
280300 // Getting cluster.max_shards_per_node setting
281301 ClusterState clusterState = client ().admin ().cluster ().prepareState ().get ().getState ();
282- String maxShardsPerNode = clusterState .getMetadata ()
283- .settings ()
284- .get (ShardLimitValidator .SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
302+ String maxShardsPerNode = clusterState .getMetadata ().settings ().get (SETTING_CLUSTER_MAX_SHARDS_PER_NODE .getKey ());
285303
286304 // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node
287305 assertEquals (dataNodes * Integer .parseInt (maxShardsPerNode ), currentActiveShards );
@@ -308,7 +326,7 @@ public void testIndexCreationOverLimitFromTemplate() {
308326
309327 final ShardCounts counts = ShardCounts .forDataNodeCount (dataNodes );
310328
311- setShardsPerNode (counts .getShardsPerNode ());
329+ setMaxShardLimit (counts .getShardsPerNode (), shardsPerNodeKey );
312330
313331 if (counts .getFirstIndexShards () > 0 ) {
314332 createIndex (
@@ -351,7 +369,7 @@ public void testIncreaseReplicasOverLimit() {
351369
352370 int firstShardCount = between (2 , 10 );
353371 int shardsPerNode = firstShardCount - 1 ;
354- setShardsPerNode (shardsPerNode );
372+ setMaxShardLimit (shardsPerNode , shardsPerNodeKey );
355373
356374 prepareCreate (
357375 "growing-should-fail" ,
@@ -397,7 +415,7 @@ public void testChangingMultipleIndicesOverLimit() {
397415 int secondIndexReplicas = dataNodes ;
398416
399417 int shardsPerNode = firstIndexFactor + (secondIndexFactor * (1 + secondIndexReplicas ));
400- setShardsPerNode (shardsPerNode );
418+ setMaxShardLimit (shardsPerNode , shardsPerNodeKey );
401419
402420 createIndex (
403421 "test-1-index" ,
@@ -448,7 +466,7 @@ public void testPreserveExistingSkipsCheck() {
448466
449467 int firstShardCount = between (2 , 10 );
450468 int shardsPerNode = firstShardCount - 1 ;
451- setShardsPerNode (shardsPerNode );
469+ setMaxShardLimit (shardsPerNode , shardsPerNodeKey );
452470
453471 prepareCreate (
454472 "test-index" ,
@@ -521,7 +539,7 @@ public void testRestoreSnapshotOverLimit() {
521539 cluster ().wipeIndices ("snapshot-index" );
522540
523541 // Reduce the shard limit and fill it up
524- setShardsPerNode (counts .getShardsPerNode ());
542+ setMaxShardLimit (counts .getShardsPerNode (), shardsPerNodeKey );
525543 createIndex (
526544 "test-fill" ,
527545 Settings .builder ()
@@ -570,7 +588,7 @@ public void testOpenIndexOverLimit() {
570588 assertTrue (closeIndexResponse .isAcknowledged ());
571589
572590 // Fill up the cluster
573- setShardsPerNode (counts .getShardsPerNode ());
591+ setMaxShardLimit (counts .getShardsPerNode (), shardsPerNodeKey );
574592 createIndex (
575593 "test-fill" ,
576594 Settings .builder ()
@@ -704,27 +722,34 @@ private int ensureMultipleDataNodes(int dataNodes) {
704722 return dataNodes ;
705723 }
706724
707- private void setShardsPerNode (int shardsPerNode ) {
725+ /**
726+ * Set max shard limit on either per node level or on cluster level.
727+ *
728+ * @param limit the limit value to set.
729+ * @param key node level or cluster level setting key.
730+ */
731+ private void setMaxShardLimit (int limit , String key ) {
708732 try {
709733 ClusterUpdateSettingsResponse response ;
710734 if (frequently ()) {
711735 response = client ().admin ()
712736 .cluster ()
713737 .prepareUpdateSettings ()
714- .setPersistentSettings (Settings .builder ().put (shardsPerNodeKey , shardsPerNode ).build ())
738+ .setPersistentSettings (Settings .builder ().put (key , limit ).build ())
715739 .get ();
716- assertEquals (shardsPerNode , response .getPersistentSettings ().getAsInt (shardsPerNodeKey , -1 ).intValue ());
740+ assertEquals (limit , response .getPersistentSettings ().getAsInt (key , -1 ).intValue ());
717741 } else {
718742 response = client ().admin ()
719743 .cluster ()
720744 .prepareUpdateSettings ()
721- .setTransientSettings (Settings .builder ().put (shardsPerNodeKey , shardsPerNode ).build ())
745+ .setTransientSettings (Settings .builder ().put (key , limit ).build ())
722746 .get ();
723- assertEquals (shardsPerNode , response .getTransientSettings ().getAsInt (shardsPerNodeKey , -1 ).intValue ());
747+ assertEquals (limit , response .getTransientSettings ().getAsInt (key , -1 ).intValue ());
724748 }
725749 } catch (IllegalArgumentException ex ) {
726750 fail (ex .getMessage ());
727751 }
752+
728753 }
729754
730755 private void setIgnoreDotIndex (boolean ignoreDotIndex ) {
0 commit comments