diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index a83aed602e1..94bea76fe4b 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -462,7 +462,6 @@ - diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java index f3146f6f771..df623aa8a5e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/FilterAllocationDecider.java @@ -158,13 +158,13 @@ public class FilterAllocationDecider extends AllocationDecider { private Decision shouldIndexFilter(IndexMetaData indexMd, RoutingNode node, RoutingAllocation allocation) { if (indexMd.requireFilters() != null) { - if (!indexMd.requireFilters().match(node.node())) { + if (indexMd.requireFilters().match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]", IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_PREFIX, indexMd.requireFilters()); } } if (indexMd.includeFilters() != null) { - if (!indexMd.includeFilters().match(node.node())) { + if (indexMd.includeFilters().match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]", IndexMetaData.INDEX_ROUTING_INCLUDE_GROUP_PREFIX, indexMd.includeFilters()); } @@ -180,13 +180,13 @@ public class FilterAllocationDecider extends AllocationDecider { private Decision shouldClusterFilter(RoutingNode node, RoutingAllocation allocation) { if (clusterRequireFilters != null) { - if (!clusterRequireFilters.match(node.node())) { + if (clusterRequireFilters.match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not match cluster setting [%s] filters [%s]", CLUSTER_ROUTING_REQUIRE_GROUP_PREFIX, clusterRequireFilters); } } if (clusterIncludeFilters != null) { - if (!clusterIncludeFilters.match(node.node())) { + if (clusterIncludeFilters.match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not cluster setting [%s] filters [%s]", CLUSTER_ROUTING_INCLUDE_GROUP_PREFIX, clusterIncludeFilters); } diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterRoutingTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterRoutingTests.java index 79473759f8f..86e8887688f 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterRoutingTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FilterRoutingTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.cluster.routing.allocation; -import org.apache.logging.log4j.Logger; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ESAllocationTestCase; @@ -27,48 +26,170 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.node.DiscoveryNodes.Builder; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingState; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import org.hamcrest.Matchers; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; +import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_ROUTING_EXCLUDE_GROUP_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_ROUTING_INCLUDE_GROUP_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_SETTING; import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING; import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED; +import static org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING; +import static org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING; +import static org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider.CLUSTER_ROUTING_REQUIRE_GROUP_SETTING; +import static org.elasticsearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider.CLUSTER_ROUTING_ALLOCATION_NODE_CONCURRENT_RECOVERIES_SETTING; import static org.hamcrest.Matchers.equalTo; public class FilterRoutingTests extends ESAllocationTestCase { - private final Logger logger = Loggers.getLogger(FilterRoutingTests.class); - public void testClusterFilters() { - AllocationService strategy = createAllocationService(Settings.builder() - .put("cluster.routing.allocation.include.tag1", "value1,value2") - .put("cluster.routing.allocation.exclude.tag1", "value3,value4") - .build()); + public void testClusterIncludeFiltersSingleAttribute() { + testClusterFilters(Settings.builder().put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value2"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4")))); + } + + public void testClusterIncludeFiltersMultipleAttributes() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1") + .put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag2", "value2"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag2", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag2", "value4")))); + } + + public void testClusterIncludeFiltersOptionalAttribute() { + testClusterFilters(Settings.builder().put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value2"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap())) + .add(newNode("node4", attrMap()))); + } + + public void testClusterIncludeFiltersWildcards() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "*incl*") + .put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag2", "*incl*"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "do_include_this"))) + .add(newNode("node2", attrMap("tag2", "also_include_this"))) + .add(newNode("node3", attrMap("tag1", "exclude_this"))) + .add(newNode("node4", attrMap("tag2", "also_exclude_this")))); + } + + public void testClusterExcludeFiltersSingleAttribute() { + testClusterFilters(Settings.builder().put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value3,value4"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4")))); + } + + public void testClusterExcludeFiltersMultipleAttributes() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value3") + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag2", "value4"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag2", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag2", "value4")))); + } + + public void testClusterExcludeFiltersOptionalAttribute() { + testClusterFilters(Settings.builder().put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value3,value4"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap())) + .add(newNode("node2", attrMap())) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4")))); + } + + public void testClusterExcludeFiltersWildcards() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "*excl*") + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag2", "*excl*"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "do_include_this"))) + .add(newNode("node2", attrMap("tag2", "also_include_this"))) + .add(newNode("node3", attrMap("tag1", "exclude_this"))) + .add(newNode("node4", attrMap("tag2", "also_exclude_this")))); + } + + public void testClusterIncludeAndExcludeFilters() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "*incl*") + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag2", "*excl*"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "do_include_this"))) + .add(newNode("node2", attrMap("tag1", "also_include_this", "tag2", "ok_by_tag2"))) + .add(newNode("node3", attrMap("tag1", "included_by_tag1", "tag2", "excluded_by_tag2"))) + .add(newNode("node4", attrMap("tag1", "excluded_by_tag1", "tag2", "included_by_tag2")))); + } + + public void testClusterRequireFilters() { + testClusterFilters(Settings.builder() + .put(CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag1", "req1") + .put(CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag2", "req2"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "req1", "tag2", "req2"))) + .add(newNode("node2", attrMap("tag1", "req1", "tag2", "req2"))) + .add(newNode("node3", attrMap("tag1", "req1"))) + .add(newNode("node4", attrMap("tag1", "other", "tag2", "req2")))); + } + + private static Map attrMap(String... keysValues) { + if (keysValues.length == 0) { + return emptyMap(); + } + if (keysValues.length == 2) { + return singletonMap(keysValues[0], keysValues[1]); + } + Map result = new HashMap<>(); + for (int i = 0; i < keysValues.length; i += 2) { + result.put(keysValues[i], keysValues[i + 1]); + } + return result; + } + + /** + * A test that creates a 2p1r index and which expects the given allocation service's settings only to allocate the shards of this index + * to `node1` and `node2`. + */ + private void testClusterFilters(Settings.Builder allocationServiceSettings, DiscoveryNodes.Builder nodes) { + final AllocationService strategy = createAllocationService(allocationServiceSettings.build()); logger.info("Building initial routing table"); - MetaData metaData = MetaData.builder() - .put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(1)) - .build(); + final MetaData metaData = MetaData.builder() + .put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(1)) + .build(); - RoutingTable initialRoutingTable = RoutingTable.builder() - .addAsNew(metaData.index("test")) - .build(); + final RoutingTable initialRoutingTable = RoutingTable.builder() + .addAsNew(metaData.index("test")) + .build(); - ClusterState clusterState = ClusterState.builder(org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).metaData(metaData).routingTable(initialRoutingTable).build(); + ClusterState clusterState = ClusterState.builder(CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)) + .metaData(metaData).routingTable(initialRoutingTable).nodes(nodes).build(); - logger.info("--> adding four nodes and performing rerouting"); - clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder() - .add(newNode("node1", singletonMap("tag1", "value1"))) - .add(newNode("node2", singletonMap("tag1", "value2"))) - .add(newNode("node3", singletonMap("tag1", "value3"))) - .add(newNode("node4", singletonMap("tag1", "value4"))) - ).build(); + logger.info("--> rerouting"); clusterState = strategy.reroute(clusterState, "reroute"); assertThat(clusterState.getRoutingNodes().shardsWithState(INITIALIZING).size(), equalTo(2)); @@ -79,41 +200,99 @@ public class FilterRoutingTests extends ESAllocationTestCase { clusterState = strategy.applyStartedShards(clusterState, clusterState.getRoutingNodes().shardsWithState(INITIALIZING)); logger.info("--> make sure shards are only allocated on tag1 with value1 and value2"); - List startedShards = clusterState.getRoutingNodes().shardsWithState(ShardRoutingState.STARTED); + final List startedShards = clusterState.getRoutingNodes().shardsWithState(ShardRoutingState.STARTED); assertThat(startedShards.size(), equalTo(4)); for (ShardRouting startedShard : startedShards) { assertThat(startedShard.currentNodeId(), Matchers.anyOf(equalTo("node1"), equalTo("node2"))); } } - public void testIndexFilters() { + public void testIndexIncludeFilters() { + testIndexFilters( + Settings.builder().put(INDEX_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value2"), + Settings.builder().put(INDEX_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value4"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4"))) + .add(newNode("node5", attrMap())) + ); + } + + public void testIndexExcludeFilters() { + testIndexFilters( + Settings.builder().put(INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value3,value4"), + Settings.builder().put(INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value2,value3"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap())) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4")))); + } + + public void testIndexIncludeThenExcludeFilters() { + testIndexFilters( + Settings.builder().put(INDEX_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value2"), + Settings.builder().put(INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value2,value3") + .putNull(INDEX_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap("tag1", "value2"))) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap()))); + } + + public void testIndexExcludeThenIncludeFilters() { + testIndexFilters( + Settings.builder().put(INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value3,value4"), + Settings.builder().put(INDEX_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "tag1", "value1,value4") + .putNull(INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1"))) + .add(newNode("node2", attrMap())) + .add(newNode("node3", attrMap("tag1", "value3"))) + .add(newNode("node4", attrMap("tag1", "value4")))); + } + + public void testIndexRequireFilters() { + testIndexFilters( + Settings.builder() + .put(INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag1", "value1") + .put(INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag2", "value2"), + Settings.builder() + .putNull(INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag2") + .put(INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "tag3", "value3"), + DiscoveryNodes.builder() + .add(newNode("node1", attrMap("tag1", "value1", "tag2", "value2", "tag3", "value3"))) + .add(newNode("node2", attrMap("tag1", "value1", "tag2", "value2", "tag3", "other"))) + .add(newNode("node3", attrMap("tag1", "other", "tag2", "value2", "tag3", "other"))) + .add(newNode("node4", attrMap("tag1", "value1", "tag2", "other", "tag3", "value3"))) + .add(newNode("node5", attrMap("tag2", "value2", "tag3", "value3"))) + .add(newNode("node6", attrMap()))); + } + + /** + * A test that creates a 2p1r index and expects the given index allocation settings only to allocate the shards to `node1` and `node2`; + * on updating the index allocation settings the shards should be relocated to nodes `node1` and `node4`. + */ + private void testIndexFilters(Settings.Builder initialIndexSettings, Settings.Builder updatedIndexSettings, Builder nodesBuilder) { AllocationService strategy = createAllocationService(Settings.builder() - .build()); + .build()); logger.info("Building initial routing table"); - MetaData initialMetaData = MetaData.builder() - .put(IndexMetaData.builder("test").settings(settings(Version.CURRENT) - .put("index.number_of_shards", 2) - .put("index.number_of_replicas", 1) - .put("index.routing.allocation.include.tag1", "value1,value2") - .put("index.routing.allocation.exclude.tag1", "value3,value4") - .build())) - .build(); + final MetaData initialMetaData = MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT) + .put("index.number_of_shards", 2).put("index.number_of_replicas", 1).put(initialIndexSettings.build()))).build(); - RoutingTable initialRoutingTable = RoutingTable.builder() - .addAsNew(initialMetaData.index("test")) - .build(); + final RoutingTable initialRoutingTable = RoutingTable.builder() + .addAsNew(initialMetaData.index("test")) + .build(); - ClusterState clusterState = ClusterState.builder(org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).metaData(initialMetaData).routingTable(initialRoutingTable).build(); + ClusterState clusterState = ClusterState.builder(CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)) + .metaData(initialMetaData).routingTable(initialRoutingTable).nodes(nodesBuilder).build(); - logger.info("--> adding two nodes and performing rerouting"); - clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder() - .add(newNode("node1", singletonMap("tag1", "value1"))) - .add(newNode("node2", singletonMap("tag1", "value2"))) - .add(newNode("node3", singletonMap("tag1", "value3"))) - .add(newNode("node4", singletonMap("tag1", "value4"))) - ).build(); + logger.info("--> rerouting"); clusterState = strategy.reroute(clusterState, "reroute"); assertThat(clusterState.getRoutingNodes().shardsWithState(INITIALIZING).size(), equalTo(2)); @@ -132,13 +311,11 @@ public class FilterRoutingTests extends ESAllocationTestCase { logger.info("--> switch between value2 and value4, shards should be relocating"); - IndexMetaData existingMetaData = clusterState.metaData().index("test"); - MetaData updatedMetaData = MetaData.builder() - .put(IndexMetaData.builder(existingMetaData).settings(Settings.builder().put(existingMetaData.getSettings()) - .put("index.routing.allocation.include.tag1", "value1,value4") - .put("index.routing.allocation.exclude.tag1", "value2,value3") - .build())) - .build(); + final IndexMetaData existingMetaData = clusterState.metaData().index("test"); + final MetaData updatedMetaData + = MetaData.builder().put(IndexMetaData.builder(existingMetaData).settings(Settings.builder() + .put(existingMetaData.getSettings()).put(updatedIndexSettings.build()).build())).build(); + clusterState = ClusterState.builder(clusterState).metaData(updatedMetaData).build(); clusterState = strategy.reroute(clusterState, "reroute"); assertThat(clusterState.getRoutingNodes().shardsWithState(ShardRoutingState.STARTED).size(), equalTo(2)); @@ -160,16 +337,17 @@ public class FilterRoutingTests extends ESAllocationTestCase { logger.info("Building initial routing table"); MetaData metaData = MetaData.builder() - .put(IndexMetaData.builder("test1").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(0)) - .put(IndexMetaData.builder("test2").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(0)) - .build(); + .put(IndexMetaData.builder("test1").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(0)) + .put(IndexMetaData.builder("test2").settings(settings(Version.CURRENT)).numberOfShards(2).numberOfReplicas(0)) + .build(); RoutingTable initialRoutingTable = RoutingTable.builder() - .addAsNew(metaData.index("test1")) - .addAsNew(metaData.index("test2")) - .build(); + .addAsNew(metaData.index("test1")) + .addAsNew(metaData.index("test2")) + .build(); - ClusterState clusterState = ClusterState.builder(org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).metaData(metaData).routingTable(initialRoutingTable).build(); + ClusterState clusterState = ClusterState.builder(CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)) + .metaData(metaData).routingTable(initialRoutingTable).build(); logger.info("--> adding two nodes and performing rerouting"); DiscoveryNode node1 = newNode("node1", singletonMap("tag1", "value1")); @@ -187,9 +365,9 @@ public class FilterRoutingTests extends ESAllocationTestCase { logger.info("--> disable allocation for node1 and reroute"); strategy = createAllocationService(Settings.builder() - .put("cluster.routing.allocation.node_concurrent_recoveries", "1") - .put("cluster.routing.allocation.exclude.tag1", "value1") - .build()); + .put(CLUSTER_ROUTING_ALLOCATION_NODE_CONCURRENT_RECOVERIES_SETTING.getKey(), "1") + .put(CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "tag1", "value1") + .build()); logger.info("--> move shards from node1 to node2"); clusterState = strategy.reroute(clusterState, "reroute");