Browse Source

Deprecated disable allocation decider which has the following options:
`allocation.disable_new_allocation`, `allocation.disable_allocation`, `allocation.disable_replica_allocation`,
in favour for the enable allocation decider which has a single option `allocation.enable` wich can be set to the following values:
`none`, `new_primaries`, `primaries` and `all` (default).

Closes #4488

Martijn van Groningen 11 years ago
parent
commit
e6f83248a2

+ 11 - 0
docs/reference/cluster/update-settings.asciidoc

@@ -93,6 +93,11 @@ There is a specific list of settings that can be updated, those include:
 [float]
 ===== Disable allocation
 
+added[1.0.0.RC1]
+
+All the disable allocation settings have been deprecated in favour for
+`cluster.routing.allocation.enable` setting.
+
 `cluster.routing.allocation.disable_allocation`::
      See <<modules-cluster>>.
 
@@ -102,6 +107,12 @@ There is a specific list of settings that can be updated, those include:
 `cluster.routing.allocation.disable_new_allocation`::
      See <<modules-cluster>>.
 
+[float]
+===== Enable allocation
+
+`cluster.routing.allocation.enable`::
+     See <<modules-cluster>>.
+
 [float]
 ===== Throttling allocation
 

+ 12 - 3
docs/reference/indices/update-settings.asciidoc

@@ -105,13 +105,22 @@ settings API:
     Only nodes matching all rules will be allowed to host shards from the index.
 
 `index.routing.allocation.disable_allocation`::
-    Disable allocation. Defaults to `false`.
+    Disable allocation. Defaults to `false`. Deprecated in favour for `index.routing.allocation.enable`.
 
 `index.routing.allocation.disable_new_allocation`::
-    Disable new allocation. Defaults to `false`.
+    Disable new allocation. Defaults to `false`. Deprecated in favour for `index.routing.allocation.enable`.
 
 `index.routing.allocation.disable_replica_allocation`::
-    Disable replica allocation. Defaults to `false`.
+    Disable replica allocation. Defaults to `false`. Deprecated in favour for `index.routing.allocation.enable`.
+
+added[1.0.0.RC1]
+
+`index.routing.allocation.enable`::
+    Enables shard allocation for a specific index. It can be set to:
+    * `all` (default) - Allows shard allocation for all shards.
+    * `primaries` - Allows shard allocation only for primary shards.
+    * `new_primaries` - Allows shard allocation only for primary shards for new indices.
+    * `none` - No shard allocation is allowed.
 
 `index.routing.allocation.total_shards_per_node`::
     Controls the total number of shards allowed to be allocated on a single node. Defaults to unbounded (`-1`).

+ 14 - 2
docs/reference/modules/cluster.asciidoc

@@ -35,12 +35,22 @@ The following settings may be used:
      How many concurrent recoveries are allowed to happen on a node. 
      Defaults to `2`.
 
+added[1.0.0.RC1]
+
+`cluster.routing.allocation.enable`::
+    Controls shard allocation for all indices, by allowing specific
+    kinds of shard to be allocated. Can be set to:
+    * `all` (default) - Allows shard allocation for all kinds of shards.
+    * `primaries` - Allows shard allocation only for primary shards.
+    * `new_primaries` - Allows shard allocation only for primary shards for new indices.
+    * `none` - No shard allocations of any kind are allowed for all indices.
 
 `cluster.routing.allocation.disable_new_allocation`::
        Allows to disable new primary allocations. Note, this will prevent 
        allocations for newly created indices. This setting really make 
        sense when dynamically updating it using the cluster update 
-       settings API.
+       settings API. This setting has been deprecated in favour
+       for `cluster.routing.allocation.enable`.
 
 
 `cluster.routing.allocation.disable_allocation`::
@@ -49,12 +59,14 @@ The following settings may be used:
         above). Note, a replica will still be promoted to primary if 
         one does not exist. This setting really make sense when 
         dynamically updating it using the cluster update settings API.
+        This setting has been deprecated in favour for `cluster.routing.allocation.enable`.
 
 
 `cluster.routing.allocation.disable_replica_allocation`::
       Allows to disable only replica allocation. Similar to the previous 
       setting, mainly make sense when using it dynamically using the 
-      cluster update settings API.
+      cluster update settings API. This setting has been deprecated in
+      favour for `cluster.routing.allocation.enable`.
 
 
 `indices.recovery.concurrent_streams`::

+ 1 - 0
src/main/java/org/elasticsearch/cluster/routing/allocation/decider/AllocationDecidersModule.java

@@ -70,6 +70,7 @@ public class AllocationDecidersModule extends AbstractModule {
             add(RebalanceOnlyWhenActiveAllocationDecider.class).
             add(ClusterRebalanceAllocationDecider.class).
             add(ConcurrentRebalanceAllocationDecider.class).
+            add(EnableAllocationDecider.class). // new enable allocation logic should proceed old disable allocation logic
             add(DisableAllocationDecider.class).
             add(AwarenessAllocationDecider.class).
             add(ShardsLimitAllocationDecider.class).

+ 3 - 0
src/main/java/org/elasticsearch/cluster/routing/allocation/decider/DisableAllocationDecider.java

@@ -50,7 +50,10 @@ import org.elasticsearch.node.settings.NodeSettingsService;
  * {@link RoutingAllocation#ignoreDisable()}. Which is set if allocation are
  * explicit.
  * </p>
+ *
+ * @deprecated In favour for {@link EnableAllocationDecider}.
  */
+@Deprecated
 public class DisableAllocationDecider extends AllocationDecider {
 
     public static final String CLUSTER_ROUTING_ALLOCATION_DISABLE_NEW_ALLOCATION = "cluster.routing.allocation.disable_new_allocation";

+ 122 - 0
src/main/java/org/elasticsearch/cluster/routing/allocation/decider/EnableAllocationDecider.java

@@ -0,0 +1,122 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.cluster.routing.allocation.decider;
+
+import org.elasticsearch.ElasticsearchIllegalArgumentException;
+import org.elasticsearch.ElasticsearchIllegalStateException;
+import org.elasticsearch.cluster.routing.RoutingNode;
+import org.elasticsearch.cluster.routing.ShardRouting;
+import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.node.settings.NodeSettingsService;
+
+import java.util.Locale;
+
+/**
+ * This allocation decider allows shard allocations via the cluster wide settings {@link #CLUSTER_ROUTING_ALLOCATION_ENABLE}
+ * and the per index setting {@link #INDEX_ROUTING_ALLOCATION_ENABLE}. The per index settings overrides the cluster wide
+ * setting. Depending on the
+ *
+ * Both settings can have the following values:
+ * <ul>
+ *     <li> <code>NONE</code>, no shard allocation is allowed.
+ *     <li> <code>NEW_PRIMARIES</code> only primary shards of new indices are allowed to be allocated
+ *     <li> <code>PRIMARIES</code> only primary shards (of any index) are allowed to be allocated
+ *     <li> <code>ALL</code> all shards are allowed to be allocated
+ * </ul>
+ */
+public class EnableAllocationDecider extends AllocationDecider implements NodeSettingsService.Listener {
+
+    public static final String CLUSTER_ROUTING_ALLOCATION_ENABLE = "cluster.routing.allocation.enable";
+    public static final String INDEX_ROUTING_ALLOCATION_ENABLE = "index.routing.allocation.enable";
+
+    private volatile Allocation enable;
+
+    @Inject
+    public EnableAllocationDecider(Settings settings, NodeSettingsService nodeSettingsService) {
+        super(settings);
+        this.enable = Allocation.parse(settings.get(CLUSTER_ROUTING_ALLOCATION_ENABLE, Allocation.ALL.name()));
+        nodeSettingsService.addListener(this);
+    }
+
+    @Override
+    public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
+        if (allocation.ignoreDisable()) {
+            return Decision.YES;
+        }
+
+        Settings indexSettings = allocation.routingNodes().metaData().index(shardRouting.index()).settings();
+        String enableIndexValue = indexSettings.get(INDEX_ROUTING_ALLOCATION_ENABLE);
+        final Allocation enable;
+        if (enableIndexValue != null) {
+            enable = Allocation.parse(enableIndexValue);
+        } else {
+            enable = this.enable;
+        }
+        switch (enable) {
+            case ALL:
+                return Decision.YES;
+            case NONE:
+                return Decision.NO;
+            case NEW_PRIMARIES:
+                if (shardRouting.primary() && !allocation.routingNodes().routingTable().index(shardRouting.index()).shard(shardRouting.id()).primaryAllocatedPostApi()) {
+                    return Decision.YES;
+                } else {
+                    return Decision.NO;
+                }
+            case PRIMARIES:
+                return shardRouting.primary() ? Decision.YES : Decision.NO;
+            default:
+                throw new ElasticsearchIllegalStateException("Unknown allocation option");
+        }
+    }
+
+    @Override
+    public void onRefreshSettings(Settings settings) {
+        Allocation enable = Allocation.parse(settings.get(CLUSTER_ROUTING_ALLOCATION_ENABLE, this.enable.name()));
+        if (enable != this.enable) {
+            logger.info("updating [cluster.routing.allocation.enable] from [{}] to [{}]", this.enable, enable);
+            EnableAllocationDecider.this.enable = enable;
+        }
+    }
+
+    public enum Allocation {
+
+        NONE,
+        NEW_PRIMARIES,
+        PRIMARIES,
+        ALL;
+
+        public static Allocation parse(String strValue) {
+            if (strValue == null) {
+                return null;
+            } else {
+                strValue = strValue.toUpperCase(Locale.ROOT);
+                try {
+                    return Allocation.valueOf(strValue);
+                } catch (IllegalArgumentException e) {
+                    throw new ElasticsearchIllegalArgumentException("Illegal allocation.enable value [" + strValue + "]");
+                }
+            }
+        }
+    }
+
+}

+ 1 - 0
src/main/java/org/elasticsearch/cluster/settings/ClusterDynamicSettingsModule.java

@@ -47,6 +47,7 @@ public class ClusterDynamicSettingsModule extends AbstractModule {
         clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, Validator.FLOAT);
         clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_THRESHOLD, Validator.NON_NEGATIVE_FLOAT);
         clusterDynamicSettings.addDynamicSetting(ConcurrentRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CLUSTER_CONCURRENT_REBALANCE, Validator.INTEGER);
+        clusterDynamicSettings.addDynamicSetting(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE);
         clusterDynamicSettings.addDynamicSetting(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_NEW_ALLOCATION);
         clusterDynamicSettings.addDynamicSetting(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_ALLOCATION);
         clusterDynamicSettings.addDynamicSetting(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_REPLICA_ALLOCATION);

+ 2 - 0
src/main/java/org/elasticsearch/index/settings/IndexDynamicSettingsModule.java

@@ -21,6 +21,7 @@ package org.elasticsearch.index.settings;
 
 import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.cluster.routing.allocation.decider.DisableAllocationDecider;
+import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider;
 import org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider;
 import org.elasticsearch.cluster.routing.allocation.decider.ShardsLimitAllocationDecider;
 import org.elasticsearch.cluster.settings.DynamicSettings;
@@ -55,6 +56,7 @@ public class IndexDynamicSettingsModule extends AbstractModule {
         indexDynamicSettings.addDynamicSetting(FilterAllocationDecider.INDEX_ROUTING_REQUIRE_GROUP + "*");
         indexDynamicSettings.addDynamicSetting(FilterAllocationDecider.INDEX_ROUTING_INCLUDE_GROUP + "*");
         indexDynamicSettings.addDynamicSetting(FilterAllocationDecider.INDEX_ROUTING_EXCLUDE_GROUP + "*");
+        indexDynamicSettings.addDynamicSetting(EnableAllocationDecider.INDEX_ROUTING_ALLOCATION_ENABLE);
         indexDynamicSettings.addDynamicSetting(DisableAllocationDecider.INDEX_ROUTING_ALLOCATION_DISABLE_ALLOCATION);
         indexDynamicSettings.addDynamicSetting(DisableAllocationDecider.INDEX_ROUTING_ALLOCATION_DISABLE_NEW_ALLOCATION);
         indexDynamicSettings.addDynamicSetting(DisableAllocationDecider.INDEX_ROUTING_ALLOCATION_DISABLE_REPLICA_ALLOCATION);

+ 27 - 3
src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteTests.java

@@ -26,6 +26,7 @@ import org.elasticsearch.cluster.routing.ShardRoutingState;
 import org.elasticsearch.cluster.routing.allocation.command.AllocateAllocationCommand;
 import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand;
 import org.elasticsearch.cluster.routing.allocation.decider.DisableAllocationDecider;
+import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider;
 import org.elasticsearch.common.Priority;
 import org.elasticsearch.common.io.FileSystemUtils;
 import org.elasticsearch.common.logging.ESLogger;
@@ -51,14 +52,25 @@ public class ClusterRerouteTests extends ElasticsearchIntegrationTest {
 
     private final ESLogger logger = Loggers.getLogger(ClusterRerouteTests.class);
 
-
     @Test
-    public void rerouteWithCommands() throws Exception {
+    public void rerouteWithCommands_disableAllocationSettings() throws Exception {
         Settings commonSettings = settingsBuilder()
                 .put(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_NEW_ALLOCATION, true)
                 .put(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_ALLOCATION, true)
                 .build();
+        rerouteWithCommands(commonSettings);
+    }
+
+    @Test
+    public void rerouteWithCommands_enableAllocationSettings() throws Exception {
+        Settings commonSettings = settingsBuilder()
+                .put(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE, EnableAllocationDecider.Allocation.NONE.name())
+                .put("gateway.type", "local")
+                .build();
+        rerouteWithCommands(commonSettings);
+    }
 
+    private void rerouteWithCommands(Settings commonSettings) throws Exception {
         String node_1 = cluster().startNode(commonSettings);
         String node_2 = cluster().startNode(commonSettings);
 
@@ -116,13 +128,25 @@ public class ClusterRerouteTests extends ElasticsearchIntegrationTest {
     }
 
     @Test
-    public void rerouteWithAllocateLocalGateway() throws Exception {
+    public void rerouteWithAllocateLocalGateway_disableAllocationSettings() throws Exception {
         Settings commonSettings = settingsBuilder()
                 .put(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_NEW_ALLOCATION, true)
                 .put(DisableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_DISABLE_ALLOCATION, true)
                 .put("gateway.type", "local")
                 .build();
+        rerouteWithAllocateLocalGateway(commonSettings);
+    }
+
+    @Test
+    public void rerouteWithAllocateLocalGateway_enableAllocationSettings() throws Exception {
+        Settings commonSettings = settingsBuilder()
+                .put(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE, EnableAllocationDecider.Allocation.NONE.name())
+                .put("gateway.type", "local")
+                .build();
+        rerouteWithAllocateLocalGateway(commonSettings);
+    }
 
+    private void rerouteWithAllocateLocalGateway(Settings commonSettings) throws Exception {
         logger.info("--> starting 2 nodes");
         String node_1 = cluster().startNode(commonSettings);
         cluster().startNode(commonSettings);

+ 148 - 0
src/test/java/org/elasticsearch/cluster/routing/allocation/decider/EnableAllocationTests.java

@@ -0,0 +1,148 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.cluster.routing.allocation.decider;
+
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.cluster.metadata.MetaData;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
+import org.elasticsearch.cluster.routing.RoutingTable;
+import org.elasticsearch.cluster.routing.allocation.AllocationService;
+import org.elasticsearch.common.logging.ESLogger;
+import org.elasticsearch.common.logging.Loggers;
+import org.elasticsearch.common.settings.ImmutableSettings;
+import org.elasticsearch.test.ElasticsearchAllocationTestCase;
+import org.junit.Test;
+
+import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING;
+import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED;
+import static org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider.*;
+import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
+import static org.hamcrest.Matchers.equalTo;
+
+/**
+ */
+public class EnableAllocationTests extends ElasticsearchAllocationTestCase {
+
+    private final ESLogger logger = Loggers.getLogger(EnableAllocationTests.class);
+
+    @Test
+    public void testClusterEnableNone() {
+        AllocationService strategy = createAllocationService(settingsBuilder()
+                .put(CLUSTER_ROUTING_ALLOCATION_ENABLE, Allocation.NONE.name())
+                .build());
+
+        logger.info("Building initial routing table");
+
+        MetaData metaData = MetaData.builder()
+                .put(IndexMetaData.builder("test").numberOfShards(1).numberOfReplicas(1))
+                .build();
+
+        RoutingTable routingTable = RoutingTable.builder()
+                .addAsNew(metaData.index("test"))
+                .build();
+
+        ClusterState clusterState = ClusterState.builder().metaData(metaData).routingTable(routingTable).build();
+
+        logger.info("--> adding two nodes and do rerouting");
+        clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder()
+                .put(newNode("node1"))
+                .put(newNode("node2"))
+        ).build();
+        routingTable = strategy.reroute(clusterState).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+        assertThat(clusterState.routingNodes().shardsWithState(INITIALIZING).size(), equalTo(0));
+
+    }
+
+    @Test
+    public void testClusterEnableOnlyPrimaries() {
+        AllocationService strategy = createAllocationService(settingsBuilder()
+                .put(CLUSTER_ROUTING_ALLOCATION_ENABLE, Allocation.PRIMARIES.name())
+                .build());
+
+        logger.info("Building initial routing table");
+
+        MetaData metaData = MetaData.builder()
+                .put(IndexMetaData.builder("test").numberOfShards(1).numberOfReplicas(1))
+                .build();
+
+        RoutingTable routingTable = RoutingTable.builder()
+                .addAsNew(metaData.index("test"))
+                .build();
+
+        ClusterState clusterState = ClusterState.builder().metaData(metaData).routingTable(routingTable).build();
+
+        logger.info("--> adding two nodes do rerouting");
+        clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder()
+                .put(newNode("node1"))
+                .put(newNode("node2"))
+        ).build();
+        routingTable = strategy.reroute(clusterState).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+        assertThat(clusterState.routingNodes().shardsWithState(INITIALIZING).size(), equalTo(1));
+
+        logger.info("--> start the shards (primaries)");
+        routingTable = strategy.applyStartedShards(clusterState, clusterState.routingNodes().shardsWithState(INITIALIZING)).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+
+        assertThat(clusterState.routingNodes().shardsWithState(INITIALIZING).size(), equalTo(0));
+    }
+
+    @Test
+    public void testIndexEnableNone() {
+        AllocationService strategy = createAllocationService(settingsBuilder()
+                .build());
+
+        MetaData metaData = MetaData.builder()
+                .put(IndexMetaData.builder("disabled").settings(ImmutableSettings.builder()
+                        .put(INDEX_ROUTING_ALLOCATION_ENABLE, Allocation.NONE.name()))
+                        .numberOfShards(1).numberOfReplicas(1))
+                .put(IndexMetaData.builder("enabled").numberOfShards(1).numberOfReplicas(1))
+                .build();
+
+        RoutingTable routingTable = RoutingTable.builder()
+                .addAsNew(metaData.index("disabled"))
+                .addAsNew(metaData.index("enabled"))
+                .build();
+
+        ClusterState clusterState = ClusterState.builder().metaData(metaData).routingTable(routingTable).build();
+
+        logger.info("--> adding two nodes and do rerouting");
+        clusterState = ClusterState.builder(clusterState).nodes(DiscoveryNodes.builder()
+                .put(newNode("node1"))
+                .put(newNode("node2"))
+        ).build();
+        routingTable = strategy.reroute(clusterState).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+        assertThat(clusterState.routingNodes().shardsWithState(INITIALIZING).size(), equalTo(1));
+        logger.info("--> start the shards (primaries)");
+        routingTable = strategy.applyStartedShards(clusterState, clusterState.routingNodes().shardsWithState(INITIALIZING)).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+        logger.info("--> start the shards (replicas)");
+        routingTable = strategy.applyStartedShards(clusterState, clusterState.routingNodes().shardsWithState(INITIALIZING)).routingTable();
+        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
+
+        logger.info("--> verify only enabled index has been routed");
+        assertThat(clusterState.readOnlyRoutingNodes().shardsWithState("enabled", STARTED).size(), equalTo(2));
+        assertThat(clusterState.readOnlyRoutingNodes().shardsWithState("disabled", STARTED).size(), equalTo(0));
+    }
+
+}