|
22 | 22 | import com.carrotsearch.hppc.cursors.IntObjectCursor; |
23 | 23 | import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteRequestBuilder; |
24 | 24 | import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresResponse; |
| 25 | +import org.elasticsearch.action.index.IndexResponse; |
25 | 26 | import org.elasticsearch.action.support.ActiveShardCount; |
26 | 27 | import org.elasticsearch.cluster.ClusterState; |
27 | 28 | import org.elasticsearch.cluster.metadata.IndexMetaData; |
|
31 | 32 | import org.elasticsearch.common.settings.Settings; |
32 | 33 | import org.elasticsearch.common.util.set.Sets; |
33 | 34 | import org.elasticsearch.gateway.GatewayAllocator; |
| 35 | +import org.elasticsearch.index.shard.IndexShard; |
| 36 | +import org.elasticsearch.index.shard.IndexShardTestCase; |
| 37 | +import org.elasticsearch.index.shard.ShardId; |
| 38 | +import org.elasticsearch.indices.IndicesService; |
34 | 39 | import org.elasticsearch.plugins.Plugin; |
35 | 40 | import org.elasticsearch.test.ESIntegTestCase; |
36 | 41 | import org.elasticsearch.test.InternalTestCluster; |
|
43 | 48 | import java.util.Arrays; |
44 | 49 | import java.util.Collection; |
45 | 50 | import java.util.Collections; |
| 51 | +import java.util.HashSet; |
46 | 52 | import java.util.List; |
| 53 | +import java.util.Set; |
47 | 54 | import java.util.concurrent.ExecutionException; |
| 55 | +import java.util.concurrent.TimeUnit; |
48 | 56 |
|
| 57 | +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; |
| 58 | +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; |
49 | 59 | import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; |
50 | 60 | import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; |
51 | 61 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; |
52 | 62 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; |
53 | 63 | import static org.hamcrest.Matchers.empty; |
54 | 64 | import static org.hamcrest.Matchers.equalTo; |
| 65 | +import static org.hamcrest.Matchers.hasSize; |
| 66 | +import static org.hamcrest.Matchers.isIn; |
| 67 | +import static org.hamcrest.Matchers.not; |
55 | 68 |
|
56 | 69 | @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) |
57 | 70 | public class PrimaryAllocationIT extends ESIntegTestCase { |
@@ -309,4 +322,71 @@ public void testForceAllocatePrimaryOnNoDecision() throws Exception { |
309 | 322 | assertEquals(1, client().admin().cluster().prepareState().get().getState() |
310 | 323 | .routingTable().index(indexName).shardsWithState(ShardRoutingState.STARTED).size()); |
311 | 324 | } |
| 325 | + |
| 326 | + /** |
| 327 | + * This test asserts that replicas failed to execute resync operations will be failed but not marked as stale. |
| 328 | + */ |
| 329 | + public void testPrimaryReplicaResyncFailed() throws Exception { |
| 330 | + String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); |
| 331 | + final int numberOfReplicas = between(2, 3); |
| 332 | + final String oldPrimary = internalCluster().startDataOnlyNode(); |
| 333 | + assertAcked( |
| 334 | + prepareCreate("test", Settings.builder().put(indexSettings()) |
| 335 | + .put(SETTING_NUMBER_OF_SHARDS, 1) |
| 336 | + .put(SETTING_NUMBER_OF_REPLICAS, numberOfReplicas))); |
| 337 | + final ShardId shardId = new ShardId(clusterService().state().metaData().index("test").getIndex(), 0); |
| 338 | + final Set<String> replicaNodes = new HashSet<>(internalCluster().startDataOnlyNodes(numberOfReplicas)); |
| 339 | + ensureGreen(); |
| 340 | + assertAcked( |
| 341 | + client(master).admin().cluster().prepareUpdateSettings() |
| 342 | + .setTransientSettings(Settings.builder().put("cluster.routing.allocation.enable", "none")).get()); |
| 343 | + logger.info("--> Indexing with gap in seqno to ensure that some operations will be replayed in resync"); |
| 344 | + long numDocs = scaledRandomIntBetween(5, 50); |
| 345 | + for (int i = 0; i < numDocs; i++) { |
| 346 | + IndexResponse indexResult = index("test", "doc", Long.toString(i)); |
| 347 | + assertThat(indexResult.getShardInfo().getSuccessful(), equalTo(numberOfReplicas + 1)); |
| 348 | + } |
| 349 | + final IndexShard oldPrimaryShard = internalCluster().getInstance(IndicesService.class, oldPrimary).getShardOrNull(shardId); |
| 350 | + IndexShardTestCase.getEngine(oldPrimaryShard).getLocalCheckpointTracker().generateSeqNo(); // Make gap in seqno. |
| 351 | + long moreDocs = scaledRandomIntBetween(1, 10); |
| 352 | + for (int i = 0; i < moreDocs; i++) { |
| 353 | + IndexResponse indexResult = index("test", "doc", Long.toString(numDocs + i)); |
| 354 | + assertThat(indexResult.getShardInfo().getSuccessful(), equalTo(numberOfReplicas + 1)); |
| 355 | + } |
| 356 | + final Set<String> replicasSide1 = Sets.newHashSet(randomSubsetOf(between(1, numberOfReplicas - 1), replicaNodes)); |
| 357 | + final Set<String> replicasSide2 = Sets.difference(replicaNodes, replicasSide1); |
| 358 | + NetworkDisruption partition = new NetworkDisruption(new TwoPartitions(replicasSide1, replicasSide2), new NetworkDisconnect()); |
| 359 | + internalCluster().setDisruptionScheme(partition); |
| 360 | + logger.info("--> isolating some replicas during primary-replica resync"); |
| 361 | + partition.startDisrupting(); |
| 362 | + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(oldPrimary)); |
| 363 | + // Checks that we fails replicas in one side but not mark them as stale. |
| 364 | + assertBusy(() -> { |
| 365 | + ClusterState state = client(master).admin().cluster().prepareState().get().getState(); |
| 366 | + final IndexShardRoutingTable shardRoutingTable = state.routingTable().shardRoutingTable(shardId); |
| 367 | + final String newPrimaryNode = state.getRoutingNodes().node(shardRoutingTable.primary.currentNodeId()).node().getName(); |
| 368 | + assertThat(newPrimaryNode, not(equalTo(oldPrimary))); |
| 369 | + Set<String> selectedPartition = replicasSide1.contains(newPrimaryNode) ? replicasSide1 : replicasSide2; |
| 370 | + assertThat(shardRoutingTable.activeShards(), hasSize(selectedPartition.size())); |
| 371 | + for (ShardRouting activeShard : shardRoutingTable.activeShards()) { |
| 372 | + assertThat(state.getRoutingNodes().node(activeShard.currentNodeId()).node().getName(), isIn(selectedPartition)); |
| 373 | + } |
| 374 | + assertThat(state.metaData().index("test").inSyncAllocationIds(shardId.id()), hasSize(numberOfReplicas + 1)); |
| 375 | + }, 1, TimeUnit.MINUTES); |
| 376 | + assertAcked( |
| 377 | + client(master).admin().cluster().prepareUpdateSettings() |
| 378 | + .setTransientSettings(Settings.builder().put("cluster.routing.allocation.enable", "all")).get()); |
| 379 | + partition.stopDisrupting(); |
| 380 | + logger.info("--> stop disrupting network and re-enable allocation"); |
| 381 | + assertBusy(() -> { |
| 382 | + ClusterState state = client(master).admin().cluster().prepareState().get().getState(); |
| 383 | + assertThat(state.routingTable().shardRoutingTable(shardId).activeShards(), hasSize(numberOfReplicas)); |
| 384 | + assertThat(state.metaData().index("test").inSyncAllocationIds(shardId.id()), hasSize(numberOfReplicas + 1)); |
| 385 | + for (String node : replicaNodes) { |
| 386 | + IndexShard shard = internalCluster().getInstance(IndicesService.class, node).getShardOrNull(shardId); |
| 387 | + assertThat(shard.getLocalCheckpoint(), equalTo(numDocs + moreDocs)); |
| 388 | + } |
| 389 | + }); |
| 390 | + } |
| 391 | + |
312 | 392 | } |
0 commit comments