|
@@ -30,12 +30,16 @@ import org.elasticsearch.cluster.metadata.MetaData;
|
|
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
|
|
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
|
|
import org.elasticsearch.cluster.routing.RestoreSource;
|
|
|
+import org.elasticsearch.cluster.routing.RoutingNode;
|
|
|
import org.elasticsearch.cluster.routing.RoutingNodes;
|
|
|
import org.elasticsearch.cluster.routing.RoutingTable;
|
|
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
|
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
|
|
+import org.elasticsearch.cluster.routing.UnassignedInfo.AllocationStatus;
|
|
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
|
|
+import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
|
|
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
|
|
+import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
|
|
import org.elasticsearch.common.Nullable;
|
|
|
import org.elasticsearch.common.UUIDs;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
@@ -49,6 +53,7 @@ import org.junit.Before;
|
|
|
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
import static org.hamcrest.Matchers.anyOf;
|
|
@@ -192,6 +197,74 @@ public class PrimaryShardAllocatorTests extends ESAllocationTestCase {
|
|
|
assertClusterHealthStatus(allocation, ClusterHealthStatus.YELLOW);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Tests that when the nodes with prior copies of the given shard all return a decision of NO, but
|
|
|
+ * {@link AllocationDecider#canForceAllocatePrimary(ShardRouting, RoutingNode, RoutingAllocation)}
|
|
|
+ * returns a YES decision for at least one of those NO nodes, then we force allocate to one of them
|
|
|
+ */
|
|
|
+ public void testForceAllocatePrimary() {
|
|
|
+ testAllocator.addData(node1, ShardStateMetaData.NO_VERSION, "allocId1", randomBoolean());
|
|
|
+ AllocationDeciders deciders = new AllocationDeciders(Settings.EMPTY, new AllocationDecider[] {
|
|
|
+ // since the deciders return a NO decision for allocating a shard (due to the guaranteed NO decision from the second decider),
|
|
|
+ // the allocator will see if it can force assign the primary, where the decision will be YES
|
|
|
+ new TestAllocateDecision(randomBoolean() ? Decision.YES : Decision.NO), getNoDeciderThatAllowsForceAllocate()
|
|
|
+ });
|
|
|
+ RoutingAllocation allocation = routingAllocationWithOnePrimaryNoReplicas(deciders, false, Version.CURRENT, "allocId1");
|
|
|
+ boolean changed = testAllocator.allocateUnassigned(allocation);
|
|
|
+ assertTrue(changed);
|
|
|
+ assertTrue(allocation.routingNodes().unassigned().ignored().isEmpty());
|
|
|
+ assertEquals(allocation.routingNodes().shardsWithState(ShardRoutingState.INITIALIZING).size(), 1);
|
|
|
+ assertEquals(allocation.routingNodes().shardsWithState(ShardRoutingState.INITIALIZING).get(0).currentNodeId(), node1.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tests that when the nodes with prior copies of the given shard all return a decision of NO, and
|
|
|
+ * {@link AllocationDecider#canForceAllocatePrimary(ShardRouting, RoutingNode, RoutingAllocation)}
|
|
|
+ * returns a NO or THROTTLE decision for a node, then we do not force allocate to that node.
|
|
|
+ */
|
|
|
+ public void testDontAllocateOnNoOrThrottleForceAllocationDecision() {
|
|
|
+ testAllocator.addData(node1, ShardStateMetaData.NO_VERSION, "allocId1", randomBoolean());
|
|
|
+ boolean forceDecisionNo = randomBoolean();
|
|
|
+ AllocationDeciders deciders = new AllocationDeciders(Settings.EMPTY, new AllocationDecider[] {
|
|
|
+ // since both deciders here return a NO decision for allocating a shard,
|
|
|
+ // the allocator will see if it can force assign the primary, where the decision will be either NO or THROTTLE,
|
|
|
+ // so the shard will remain un-initialized
|
|
|
+ new TestAllocateDecision(Decision.NO), forceDecisionNo ? getNoDeciderThatDeniesForceAllocate() :
|
|
|
+ getNoDeciderThatThrottlesForceAllocate()
|
|
|
+ });
|
|
|
+ RoutingAllocation allocation = routingAllocationWithOnePrimaryNoReplicas(deciders, false, Version.CURRENT, "allocId1");
|
|
|
+ boolean changed = testAllocator.allocateUnassigned(allocation);
|
|
|
+ assertTrue(changed);
|
|
|
+ List<ShardRouting> ignored = allocation.routingNodes().unassigned().ignored();
|
|
|
+ assertEquals(ignored.size(), 1);
|
|
|
+ assertEquals(ignored.get(0).unassignedInfo().getLastAllocationStatus(),
|
|
|
+ forceDecisionNo ? AllocationStatus.DECIDERS_NO : AllocationStatus.DECIDERS_THROTTLED);
|
|
|
+ assertTrue(allocation.routingNodes().shardsWithState(ShardRoutingState.INITIALIZING).isEmpty());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tests that when the nodes with prior copies of the given shard return a THROTTLE decision,
|
|
|
+ * then we do not force allocate to that node but instead throttle.
|
|
|
+ */
|
|
|
+ public void testDontForceAllocateOnThrottleDecision() {
|
|
|
+ testAllocator.addData(node1, ShardStateMetaData.NO_VERSION, "allocId1", randomBoolean());
|
|
|
+ AllocationDeciders deciders = new AllocationDeciders(Settings.EMPTY, new AllocationDecider[] {
|
|
|
+ // since we have a NO decision for allocating a shard (because the second decider returns a NO decision),
|
|
|
+ // the allocator will see if it can force assign the primary, and in this case,
|
|
|
+ // the TestAllocateDecision's decision for force allocating is to THROTTLE (using
|
|
|
+ // the default behavior) so despite the other decider's decision to return YES for
|
|
|
+ // force allocating the shard, we still THROTTLE due to the decision from TestAllocateDecision
|
|
|
+ new TestAllocateDecision(Decision.THROTTLE), getNoDeciderThatAllowsForceAllocate()
|
|
|
+ });
|
|
|
+ RoutingAllocation allocation = routingAllocationWithOnePrimaryNoReplicas(deciders, false, Version.CURRENT, "allocId1");
|
|
|
+ boolean changed = testAllocator.allocateUnassigned(allocation);
|
|
|
+ assertTrue(changed);
|
|
|
+ List<ShardRouting> ignored = allocation.routingNodes().unassigned().ignored();
|
|
|
+ assertEquals(ignored.size(), 1);
|
|
|
+ assertEquals(ignored.get(0).unassignedInfo().getLastAllocationStatus(), AllocationStatus.DECIDERS_THROTTLED);
|
|
|
+ assertTrue(allocation.routingNodes().shardsWithState(ShardRoutingState.INITIALIZING).isEmpty());
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Tests that when there was a node that previously had the primary, it will be allocated to that same node again.
|
|
|
*/
|
|
@@ -542,11 +615,12 @@ public class PrimaryShardAllocatorTests extends ESAllocationTestCase {
|
|
|
assertClusterHealthStatus(allocation, ClusterHealthStatus.RED);
|
|
|
}
|
|
|
|
|
|
- private RoutingAllocation routingAllocationWithOnePrimaryNoReplicas(AllocationDeciders deciders, boolean asNew, Version version, String... activeAllocationIds) {
|
|
|
+ private RoutingAllocation routingAllocationWithOnePrimaryNoReplicas(AllocationDeciders deciders, boolean asNew, Version version,
|
|
|
+ String... activeAllocationIds) {
|
|
|
MetaData metaData = MetaData.builder()
|
|
|
.put(IndexMetaData.builder(shardId.getIndexName()).settings(settings(version))
|
|
|
- .numberOfShards(1).numberOfReplicas(0).putActiveAllocationIds(0, Sets.newHashSet(activeAllocationIds)))
|
|
|
- .build();
|
|
|
+ .numberOfShards(1).numberOfReplicas(0).putActiveAllocationIds(shardId.id(), Sets.newHashSet(activeAllocationIds)))
|
|
|
+ .build();
|
|
|
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
|
|
if (asNew) {
|
|
|
routingTableBuilder.addAsNew(metaData.index(shardId.getIndex()));
|
|
@@ -573,6 +647,28 @@ public class PrimaryShardAllocatorTests extends ESAllocationTestCase {
|
|
|
assertThat(clusterStateHealth.getStatus().ordinal(), lessThanOrEqualTo(expectedStatus.ordinal()));
|
|
|
}
|
|
|
|
|
|
+ private AllocationDecider getNoDeciderThatAllowsForceAllocate() {
|
|
|
+ return getNoDeciderWithForceAllocate(Decision.YES);
|
|
|
+ }
|
|
|
+
|
|
|
+ private AllocationDecider getNoDeciderThatThrottlesForceAllocate() {
|
|
|
+ return getNoDeciderWithForceAllocate(Decision.THROTTLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ private AllocationDecider getNoDeciderThatDeniesForceAllocate() {
|
|
|
+ return getNoDeciderWithForceAllocate(Decision.NO);
|
|
|
+ }
|
|
|
+
|
|
|
+ private AllocationDecider getNoDeciderWithForceAllocate(final Decision forceAllocateDecision) {
|
|
|
+ return new TestAllocateDecision(Decision.NO) {
|
|
|
+ @Override
|
|
|
+ public Decision canForceAllocatePrimary(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
|
|
|
+ assert shardRouting.primary() : "cannot force allocate a non-primary shard " + shardRouting;
|
|
|
+ return forceAllocateDecision;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
class TestAllocator extends PrimaryShardAllocator {
|
|
|
|
|
|
private Map<DiscoveryNode, TransportNodesListGatewayStartedShards.NodeGatewayStartedShards> data;
|