|
@@ -9,6 +9,7 @@
|
|
|
package org.elasticsearch.cluster.routing.allocation;
|
|
|
|
|
|
import org.elasticsearch.Version;
|
|
|
+import org.elasticsearch.cluster.ClusterInfo;
|
|
|
import org.elasticsearch.cluster.ClusterName;
|
|
|
import org.elasticsearch.cluster.ClusterState;
|
|
|
import org.elasticsearch.cluster.ESAllocationTestCase;
|
|
@@ -25,10 +26,12 @@ import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
|
|
import org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.snapshots.EmptySnapshotsInfoService;
|
|
|
+import org.elasticsearch.snapshots.SnapshotShardSizeInfo;
|
|
|
import org.elasticsearch.test.gateway.TestGatewayAllocator;
|
|
|
|
|
|
-import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.function.Consumer;
|
|
|
|
|
|
import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING;
|
|
|
import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED;
|
|
@@ -39,44 +42,34 @@ import static org.hamcrest.Matchers.not;
|
|
|
|
|
|
public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
|
|
|
- private AllocationService strategy;
|
|
|
-
|
|
|
- @Override
|
|
|
- public void setUp() throws Exception {
|
|
|
- super.setUp();
|
|
|
- strategy = new AllocationService(
|
|
|
- new AllocationDeciders(Collections.singleton(new MaxRetryAllocationDecider())),
|
|
|
- new TestGatewayAllocator(),
|
|
|
- new BalancedShardsAllocator(Settings.EMPTY),
|
|
|
- EmptyClusterInfoService.INSTANCE,
|
|
|
- EmptySnapshotsInfoService.INSTANCE
|
|
|
- );
|
|
|
- }
|
|
|
+ private final MaxRetryAllocationDecider decider = new MaxRetryAllocationDecider();
|
|
|
+ private final AllocationService strategy = new AllocationService(
|
|
|
+ new AllocationDeciders(List.of(decider)),
|
|
|
+ new TestGatewayAllocator(),
|
|
|
+ new BalancedShardsAllocator(Settings.EMPTY),
|
|
|
+ EmptyClusterInfoService.INSTANCE,
|
|
|
+ EmptySnapshotsInfoService.INSTANCE
|
|
|
+ );
|
|
|
|
|
|
private ClusterState createInitialClusterState() {
|
|
|
- Metadata.Builder metaBuilder = Metadata.builder();
|
|
|
- metaBuilder.put(IndexMetadata.builder("idx").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0));
|
|
|
- Metadata metadata = metaBuilder.build();
|
|
|
- RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
|
|
- routingTableBuilder.addAsNew(metadata.index("idx"));
|
|
|
+ Metadata metadata = Metadata.builder()
|
|
|
+ .put(IndexMetadata.builder("idx").settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(0))
|
|
|
+ .build();
|
|
|
+ RoutingTable routingTable = RoutingTable.builder().addAsNew(metadata.index("idx")).build();
|
|
|
|
|
|
- RoutingTable routingTable = routingTableBuilder.build();
|
|
|
ClusterState clusterState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY))
|
|
|
.metadata(metadata)
|
|
|
.routingTable(routingTable)
|
|
|
- .build();
|
|
|
- clusterState = ClusterState.builder(clusterState)
|
|
|
.nodes(DiscoveryNodes.builder().add(newNode("node1")).add(newNode("node2")))
|
|
|
.build();
|
|
|
- RoutingTable prevRoutingTable = routingTable;
|
|
|
- routingTable = strategy.reroute(clusterState, "reroute").routingTable();
|
|
|
- clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
|
|
|
|
|
- assertEquals(prevRoutingTable.index("idx").size(), 1);
|
|
|
- assertEquals(prevRoutingTable.index("idx").shard(0).shard(0).state(), UNASSIGNED);
|
|
|
+ assertEquals(clusterState.routingTable().index("idx").size(), 1);
|
|
|
+ assertEquals(clusterState.routingTable().index("idx").shard(0).shard(0).state(), UNASSIGNED);
|
|
|
|
|
|
- assertEquals(routingTable.index("idx").size(), 1);
|
|
|
- assertEquals(routingTable.index("idx").shard(0).shard(0).state(), INITIALIZING);
|
|
|
+ clusterState = strategy.reroute(clusterState, "reroute");
|
|
|
+
|
|
|
+ assertEquals(clusterState.routingTable().index("idx").size(), 1);
|
|
|
+ assertEquals(clusterState.routingTable().index("idx").shard(0).shard(0).state(), INITIALIZING);
|
|
|
return clusterState;
|
|
|
}
|
|
|
|
|
@@ -86,15 +79,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
final int retries = MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY.get(Settings.EMPTY);
|
|
|
// now fail it N-1 times
|
|
|
for (int i = 0; i < retries - 1; i++) {
|
|
|
- List<FailedShard> failedShards = Collections.singletonList(
|
|
|
- new FailedShard(
|
|
|
- routingTable.index("idx").shard(0).shard(0),
|
|
|
- "boom" + i,
|
|
|
- new UnsupportedOperationException(),
|
|
|
- randomBoolean()
|
|
|
- )
|
|
|
- );
|
|
|
- ClusterState newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ ClusterState newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom" + i);
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -104,10 +89,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
assertThat(routingTable.index("idx").shard(0).shard(0).unassignedInfo().getMessage(), containsString("boom" + i));
|
|
|
}
|
|
|
// now we go and check that we are actually stick to unassigned on the next failure
|
|
|
- List<FailedShard> failedShards = Collections.singletonList(
|
|
|
- new FailedShard(routingTable.index("idx").shard(0).shard(0), "boom", new UnsupportedOperationException(), randomBoolean())
|
|
|
- );
|
|
|
- ClusterState newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ ClusterState newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom");
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -130,11 +112,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
|
|
|
// again fail it N-1 times
|
|
|
for (int i = 0; i < retries - 1; i++) {
|
|
|
- failedShards = Collections.singletonList(
|
|
|
- new FailedShard(routingTable.index("idx").shard(0).shard(0), "boom", new UnsupportedOperationException(), randomBoolean())
|
|
|
- );
|
|
|
-
|
|
|
- newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom");
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -145,10 +123,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
}
|
|
|
|
|
|
// now we go and check that we are actually stick to unassigned on the next failure
|
|
|
- failedShards = Collections.singletonList(
|
|
|
- new FailedShard(routingTable.index("idx").shard(0).shard(0), "boom", new UnsupportedOperationException(), randomBoolean())
|
|
|
- );
|
|
|
- newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom");
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -164,15 +139,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
final int retries = MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY.get(Settings.EMPTY);
|
|
|
// now fail it N-1 times
|
|
|
for (int i = 0; i < retries - 1; i++) {
|
|
|
- List<FailedShard> failedShards = Collections.singletonList(
|
|
|
- new FailedShard(
|
|
|
- routingTable.index("idx").shard(0).shard(0),
|
|
|
- "boom" + i,
|
|
|
- new UnsupportedOperationException(),
|
|
|
- randomBoolean()
|
|
|
- )
|
|
|
- );
|
|
|
- ClusterState newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ ClusterState newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom" + i);
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -184,15 +151,12 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
// MaxRetryAllocationDecider#canForceAllocatePrimary should return YES decisions because canAllocate returns YES here
|
|
|
assertEquals(
|
|
|
Decision.Type.YES,
|
|
|
- new MaxRetryAllocationDecider().canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
+ decider.canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
);
|
|
|
}
|
|
|
// now we go and check that we are actually stick to unassigned on the next failure
|
|
|
{
|
|
|
- List<FailedShard> failedShards = Collections.singletonList(
|
|
|
- new FailedShard(routingTable.index("idx").shard(0).shard(0), "boom", new UnsupportedOperationException(), randomBoolean())
|
|
|
- );
|
|
|
- ClusterState newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ ClusterState newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "boom");
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -204,7 +168,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
// MaxRetryAllocationDecider#canForceAllocatePrimary should return a NO decision because canAllocate returns NO here
|
|
|
assertEquals(
|
|
|
Decision.Type.NO,
|
|
|
- new MaxRetryAllocationDecider().canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
+ decider.canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -240,11 +204,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
// bumped up the max retry count, so canForceAllocatePrimary should return a YES decision
|
|
|
assertEquals(
|
|
|
Decision.Type.YES,
|
|
|
- new MaxRetryAllocationDecider().canForceAllocatePrimary(
|
|
|
- routingTable.index("idx").shard(0).shard(0),
|
|
|
- null,
|
|
|
- newRoutingAllocation(clusterState)
|
|
|
- ).type()
|
|
|
+ decider.canForceAllocatePrimary(routingTable.index("idx").shard(0).shard(0), null, newRoutingAllocation(clusterState)).type()
|
|
|
);
|
|
|
|
|
|
// now we start the shard
|
|
@@ -257,10 +217,7 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
assertEquals(routingTable.index("idx").shard(0).shard(0).state(), STARTED);
|
|
|
|
|
|
// now fail again and see if it has a new counter
|
|
|
- List<FailedShard> failedShards = Collections.singletonList(
|
|
|
- new FailedShard(routingTable.index("idx").shard(0).shard(0), "ZOOOMG", new UnsupportedOperationException(), randomBoolean())
|
|
|
- );
|
|
|
- newState = strategy.applyFailedShards(clusterState, failedShards, List.of());
|
|
|
+ newState = applyShardFailure(clusterState, routingTable.index("idx").shard(0).shard(0), "ZOOOMG");
|
|
|
assertThat(newState, not(equalTo(clusterState)));
|
|
|
clusterState = newState;
|
|
|
routingTable = newState.routingTable();
|
|
@@ -272,10 +229,83 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
// Counter reset, so MaxRetryAllocationDecider#canForceAllocatePrimary should return a YES decision
|
|
|
assertEquals(
|
|
|
Decision.Type.YES,
|
|
|
- new MaxRetryAllocationDecider().canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
+ decider.canForceAllocatePrimary(unassignedPrimary, null, newRoutingAllocation(clusterState)).type()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testFailedRelocation() {
|
|
|
+ ClusterState clusterState = createInitialClusterState();
|
|
|
+ clusterState = startInitializingShardsAndReroute(strategy, clusterState);
|
|
|
+
|
|
|
+ int retries = MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY.get(Settings.EMPTY);
|
|
|
+
|
|
|
+ // shard could be relocated while retries are not exhausted
|
|
|
+ for (int i = 0; i < retries; i++) {
|
|
|
+ clusterState = withRoutingAllocation(clusterState, allocation -> {
|
|
|
+ var source = allocation.routingTable().index("idx").shard(0).shard(0);
|
|
|
+ var targetNodeId = Objects.equals(source.currentNodeId(), "node1") ? "node2" : "node1";
|
|
|
+ assertThat(decider.canAllocate(source, allocation).type(), equalTo(Decision.Type.YES));
|
|
|
+ allocation.routingNodes().relocateShard(source, targetNodeId, 0, allocation.changes());
|
|
|
+ });
|
|
|
+ clusterState = applyShardFailure(
|
|
|
+ clusterState,
|
|
|
+ clusterState.getRoutingTable().index("idx").shard(0).shard(0).getTargetRelocatingShard(),
|
|
|
+ "boom" + i
|
|
|
+ );
|
|
|
+
|
|
|
+ var relocationFailureInfo = clusterState.getRoutingTable().index("idx").shard(0).shard(0).relocationFailureInfo();
|
|
|
+ assertThat(relocationFailureInfo.failedRelocations(), equalTo(i + 1));
|
|
|
+ }
|
|
|
+
|
|
|
+ // shard could not be relocated when retries are exhausted
|
|
|
+ withRoutingAllocation(clusterState, allocation -> {
|
|
|
+ var source = allocation.routingTable().index("idx").shard(0).shard(0);
|
|
|
+ assertThat(decider.canAllocate(source, allocation).type(), equalTo(Decision.Type.NO));
|
|
|
+ });
|
|
|
+
|
|
|
+ // manually reset retry count
|
|
|
+ clusterState = strategy.reroute(clusterState, new AllocationCommands(), false, true).clusterState();
|
|
|
+
|
|
|
+ // shard could be relocated again
|
|
|
+ withRoutingAllocation(clusterState, allocation -> {
|
|
|
+ var source = allocation.routingTable().index("idx").shard(0).shard(0);
|
|
|
+ assertThat(decider.canAllocate(source, allocation).type(), equalTo(Decision.Type.YES));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState applyShardFailure(ClusterState clusterState, ShardRouting shardRouting, String message) {
|
|
|
+ return strategy.applyFailedShards(
|
|
|
+ clusterState,
|
|
|
+ List.of(new FailedShard(shardRouting, message, new RuntimeException("test"), randomBoolean())),
|
|
|
+ List.of()
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ private static ClusterState withRoutingAllocation(ClusterState clusterState, Consumer<RoutingAllocation> block) {
|
|
|
+ RoutingAllocation allocation = new RoutingAllocation(
|
|
|
+ null,
|
|
|
+ clusterState.mutableRoutingNodes(),
|
|
|
+ clusterState,
|
|
|
+ ClusterInfo.EMPTY,
|
|
|
+ SnapshotShardSizeInfo.EMPTY,
|
|
|
+ 0L
|
|
|
+ );
|
|
|
+ block.accept(allocation);
|
|
|
+ return updateClusterState(clusterState, allocation);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static ClusterState updateClusterState(ClusterState state, RoutingAllocation allocation) {
|
|
|
+ assert allocation.metadata() == state.metadata();
|
|
|
+ if (allocation.routingNodesChanged() == false) {
|
|
|
+ return state;
|
|
|
+ }
|
|
|
+ final RoutingTable newRoutingTable = RoutingTable.of(state.routingTable().version(), allocation.routingNodes());
|
|
|
+ final Metadata newMetadata = allocation.updateMetadataWithRoutingChanges(newRoutingTable);
|
|
|
+ assert newRoutingTable.validate(newMetadata);
|
|
|
+
|
|
|
+ return state.copyAndUpdate(builder -> builder.routingTable(newRoutingTable).metadata(newMetadata));
|
|
|
+ }
|
|
|
+
|
|
|
private RoutingAllocation newRoutingAllocation(ClusterState clusterState) {
|
|
|
final var routingAllocation = new RoutingAllocation(null, clusterState, null, null, 0);
|
|
|
if (randomBoolean()) {
|
|
@@ -283,5 +313,4 @@ public class MaxRetryAllocationDeciderTests extends ESAllocationTestCase {
|
|
|
}
|
|
|
return routingAllocation;
|
|
|
}
|
|
|
-
|
|
|
}
|