|
42 | 42 | import org.elasticsearch.cluster.routing.ShardIterator;
|
43 | 43 | import org.elasticsearch.cluster.routing.ShardRouting;
|
44 | 44 | import org.elasticsearch.cluster.routing.ShardRoutingState;
|
| 45 | +import org.elasticsearch.cluster.routing.allocation.AllocationService; |
| 46 | +import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; |
45 | 47 | import org.elasticsearch.common.collect.Tuple;
|
46 | 48 | import org.elasticsearch.common.io.stream.StreamInput;
|
47 | 49 | import org.elasticsearch.common.io.stream.StreamOutput;
|
|
53 | 55 | import org.elasticsearch.index.shard.ShardId;
|
54 | 56 | import org.elasticsearch.index.shard.ShardNotFoundException;
|
55 | 57 | import org.elasticsearch.rest.RestStatus;
|
| 58 | +import org.elasticsearch.test.ESAllocationTestCase; |
56 | 59 | import org.elasticsearch.test.ESTestCase;
|
57 | 60 | import org.elasticsearch.test.cluster.TestClusterService;
|
58 | 61 | import org.elasticsearch.test.transport.CapturingTransport;
|
|
67 | 70 |
|
68 | 71 | import java.io.IOException;
|
69 | 72 | import java.util.ArrayList;
|
| 73 | +import java.util.Arrays; |
70 | 74 | import java.util.HashMap;
|
71 | 75 | import java.util.HashSet;
|
72 | 76 | import java.util.List;
|
@@ -205,6 +209,56 @@ public void testNotStartedPrimary() throws InterruptedException, ExecutionExcept
|
205 | 209 | assertIndexShardCounter(1);
|
206 | 210 | }
|
207 | 211 |
|
| 212 | + /** |
| 213 | + * When relocating a primary shard, there is a cluster state update at the end of relocation where the active primary is switched from |
| 214 | + * the relocation source to the relocation target. If relocation source receives and processes this cluster state |
| 215 | + * before the relocation target, there is a time span where relocation source believes active primary to be on |
| 216 | + * relocation target and relocation target believes active primary to be on relocation source. This results in replication |
| 217 | + * requests being sent back and forth. |
| 218 | + * |
| 219 | + * This test checks that replication request is not routed back from relocation target to relocation source in case of |
| 220 | + * stale index routing table on relocation target. |
| 221 | + */ |
| 222 | + public void testNoRerouteOnStaleClusterState() throws InterruptedException, ExecutionException { |
| 223 | + final String index = "test"; |
| 224 | + final ShardId shardId = new ShardId(index, 0); |
| 225 | + ClusterState state = state(index, true, ShardRoutingState.RELOCATING); |
| 226 | + String relocationTargetNode = state.getRoutingTable().shardRoutingTable(shardId).primaryShard().relocatingNodeId(); |
| 227 | + state = ClusterState.builder(state).nodes(DiscoveryNodes.builder(state.nodes()).localNodeId(relocationTargetNode)).build(); |
| 228 | + clusterService.setState(state); |
| 229 | + logger.debug("--> relocation ongoing state:\n{}", clusterService.state().prettyPrint()); |
| 230 | + |
| 231 | + Request request = new Request(shardId).timeout("1ms").routedBasedOnClusterVersion(clusterService.state().version() + 1); |
| 232 | + PlainActionFuture<Response> listener = new PlainActionFuture<>(); |
| 233 | + TransportReplicationAction.ReroutePhase reroutePhase = action.new ReroutePhase(request, listener); |
| 234 | + reroutePhase.run(); |
| 235 | + assertListenerThrows("cluster state too old didn't cause a timeout", listener, UnavailableShardsException.class); |
| 236 | + |
| 237 | + request = new Request(shardId).routedBasedOnClusterVersion(clusterService.state().version() + 1); |
| 238 | + listener = new PlainActionFuture<>(); |
| 239 | + reroutePhase = action.new ReroutePhase(request, listener); |
| 240 | + reroutePhase.run(); |
| 241 | + assertFalse("cluster state too old didn't cause a retry", listener.isDone()); |
| 242 | + |
| 243 | + // finish relocation |
| 244 | + ShardRouting relocationTarget = clusterService.state().getRoutingTable().shardRoutingTable(shardId).shardsWithState(ShardRoutingState.INITIALIZING).get(0); |
| 245 | + AllocationService allocationService = ESAllocationTestCase.createAllocationService(); |
| 246 | + RoutingAllocation.Result result = allocationService.applyStartedShards(state, Arrays.asList(relocationTarget)); |
| 247 | + ClusterState updatedState = ClusterState.builder(clusterService.state()).routingResult(result).build(); |
| 248 | + |
| 249 | + clusterService.setState(updatedState); |
| 250 | + logger.debug("--> relocation complete state:\n{}", clusterService.state().prettyPrint()); |
| 251 | + |
| 252 | + IndexShardRoutingTable shardRoutingTable = clusterService.state().routingTable().index(index).shard(shardId.id()); |
| 253 | + final String primaryNodeId = shardRoutingTable.primaryShard().currentNodeId(); |
| 254 | + final List<CapturingTransport.CapturedRequest> capturedRequests = |
| 255 | + transport.getCapturedRequestsByTargetNodeAndClear().get(primaryNodeId); |
| 256 | + assertThat(capturedRequests, notNullValue()); |
| 257 | + assertThat(capturedRequests.size(), equalTo(1)); |
| 258 | + assertThat(capturedRequests.get(0).action, equalTo("testAction[p]")); |
| 259 | + assertIndexShardCounter(1); |
| 260 | + } |
| 261 | + |
208 | 262 | public void testUnknownIndexOrShardOnReroute() throws InterruptedException {
|
209 | 263 | final String index = "test";
|
210 | 264 | // no replicas in oder to skip the replication part
|
|
0 commit comments