diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDecider.java index 289b003c1d08e..f9ad0d9f8f089 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDecider.java @@ -92,6 +92,12 @@ public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, Routing } final ProjectId projectId = allocation.getClusterState().metadata().projectFor(index).id(); + final IndexMetadata indexMetadata = allocation.getClusterState().metadata().getProject(projectId).index(index); + + if (hasIndexRoutingFilters(indexMetadata)) { + return allocation.decision(Decision.YES, NAME, "Decider is disabled for index level allocation filters."); + } + final Set eligibleNodes = new HashSet<>(); int totalShards = 0; String nomenclature = EMPTY; @@ -104,7 +110,6 @@ public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, Routing } else if (node.node().getRoles().contains(SEARCH_ROLE)) { collectEligibleNodes(allocation, eligibleNodes, SEARCH_ROLE); // Replicas only. - final IndexMetadata indexMetadata = allocation.getClusterState().metadata().getProject(projectId).index(index); totalShards = indexMetadata.getNumberOfShards() * indexMetadata.getNumberOfReplicas(); nomenclature = "search"; } @@ -169,4 +174,10 @@ private boolean hasFilters() { || (clusterIncludeFilters != null && clusterIncludeFilters.hasFilters()) || (clusterRequireFilters != null && clusterRequireFilters.hasFilters()); } + + private boolean hasIndexRoutingFilters(IndexMetadata indexMetadata) { + return indexMetadata.requireFilters() != null && indexMetadata.requireFilters().hasFilters() + || indexMetadata.excludeFilters() != null && indexMetadata.excludeFilters().hasFilters() + || indexMetadata.includeFilters() != null && indexMetadata.includeFilters().hasFilters(); + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDeciderTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDeciderTests.java index 5b094e42a40fb..adcd8a6ef1958 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDeciderTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/decider/IndexBalanceAllocationDeciderTests.java @@ -42,7 +42,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_PREFIX; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_CREATION_DATE; import static org.elasticsearch.cluster.routing.TestShardRouting.shardRoutingBuilder; import static org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_PREFIX; @@ -77,12 +81,12 @@ public class IndexBalanceAllocationDeciderTests extends ESAllocationTestCase { private List indexTier; private List searchIier; - private void setup(Settings settings) { + private void setup(Settings clusterSettings, Supplier indexSettings) { final String indexName = "IndexBalanceAllocationDeciderIndex"; final Map> nodeToShardRoutings = new HashMap<>(); Settings.Builder builder = Settings.builder() - .put(settings) + .put(clusterSettings) .put("stateless.enabled", "true") .put(IndexBalanceConstraintSettings.INDEX_BALANCE_DECIDER_ENABLED_SETTING.getKey(), "true"); @@ -111,10 +115,9 @@ private void setup(Settings settings) { indexMetadata = IndexMetadata.builder(indexName) .settings( - indexSettings(IndexVersion.current(), numberOfPrimaryShards, replicationFactor).put( - SETTING_CREATION_DATE, - System.currentTimeMillis() - ).build() + indexSettings(IndexVersion.current(), numberOfPrimaryShards, replicationFactor).put(indexSettings.get()) + .put(SETTING_CREATION_DATE, System.currentTimeMillis()) + .build() ) .timestampRange(IndexLongFieldRange.UNKNOWN) .eventIngestedRange(IndexLongFieldRange.UNKNOWN) @@ -211,8 +214,8 @@ private void setup(Settings settings) { } public void testCanAllocateUnderThresholdWithExcessShards() { - Settings settings = allowExcessShards(Settings.EMPTY); - setup(settings); + Settings clusterSettings = allowExcessShards(Settings.EMPTY); + setup(clusterSettings, () -> Settings.EMPTY); ShardRouting newIndexShardRouting = TestShardRouting.newShardRouting( new ShardId("newIndex", "uuid", 1), @@ -279,7 +282,7 @@ private void verifyCanAllocate() { } public void testCanAllocateExceedThreshold() { - setup(Settings.EMPTY); + setup(Settings.EMPTY, () -> Settings.EMPTY); int ideal = numberOfPrimaryShards / 2; int current = numberOfPrimaryShards / 2; @@ -316,11 +319,11 @@ public void testCanAllocateExceedThreshold() { } public void testCanAllocateHasDiscoveryNodeFilters() { - Settings settings = addRandomFilterSetting(Settings.EMPTY); + Settings clusterSettings = addRandomFilterSetting(Settings.EMPTY); if (randomBoolean()) { - settings = allowExcessShards(settings); + clusterSettings = allowExcessShards(clusterSettings); } - setup(settings); + setup(clusterSettings, () -> Settings.EMPTY); for (RoutingNode routingNode : indexTier) { assertDecisionMatches( @@ -341,6 +344,28 @@ public void testCanAllocateHasDiscoveryNodeFilters() { } } + public void testCanAllocateHasIndexRoutingFilters() { + setup(Settings.EMPTY, this::addRandomIndexRoutingFilters); + + for (RoutingNode routingNode : indexTier) { + assertDecisionMatches( + "Having DiscoveryNodeFilters disables this decider", + indexBalanceAllocationDecider.canAllocate(indexTierShardRouting, routingNode, routingAllocation), + Decision.Type.YES, + "Decider is disabled for index level allocation filters." + ); + } + + for (RoutingNode routingNode : searchIier) { + assertDecisionMatches( + "Having DiscoveryNodeFilters disables this decider", + indexBalanceAllocationDecider.canAllocate(searchTierShardRouting, routingNode, routingAllocation), + Decision.Type.YES, + "Decider is disabled for index level allocation filters." + ); + } + } + public Settings addRandomFilterSetting(Settings settings) { String setting = randomFrom( CLUSTER_ROUTING_REQUIRE_GROUP_PREFIX, @@ -362,4 +387,29 @@ public Settings allowExcessShards(Settings settings) { .build(); } + public Settings addRandomIndexRoutingFilters() { + String setting = randomFrom( + INDEX_ROUTING_REQUIRE_GROUP_PREFIX, + INDEX_ROUTING_INCLUDE_GROUP_PREFIX, + INDEX_ROUTING_EXCLUDE_GROUP_PREFIX + ); + String attribute = randomFrom("_ip", "_host", "_id"); + String ip = randomFrom("192.168.0.1", "192.168.0.2", "192.168.7.1", "10.17.0.1"); + String id = randomFrom(indexNodeOne.getId(), indexNodeTwo.getId(), searchNodeOne.getId(), searchNodeTwo.getId()); + String hostName = randomFrom( + indexNodeOne.getHostName(), + indexNodeTwo.getHostName(), + searchNodeOne.getHostName(), + searchNodeTwo.getHostName() + ); + + String value = switch (attribute) { + case "_ip" -> ip; + case "_host" -> hostName; + case "_id" -> id; + default -> throw new IllegalStateException("Unexpected value: " + attribute); + }; + return Settings.builder().put(setting + "." + attribute, value).build(); + } + }