|
26 | 26 | import org.elasticsearch.client.ResponseException;
|
27 | 27 | import org.elasticsearch.cluster.metadata.IndexMetaData;
|
28 | 28 | import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
|
| 29 | +import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; |
29 | 30 | import org.elasticsearch.common.Booleans;
|
30 | 31 | import org.elasticsearch.common.settings.Settings;
|
31 | 32 | import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
|
36 | 37 | import org.elasticsearch.rest.action.document.RestUpdateAction;
|
37 | 38 | import org.elasticsearch.test.rest.yaml.ObjectPath;
|
38 | 39 | import org.hamcrest.Matcher;
|
| 40 | +import org.hamcrest.Matchers; |
39 | 41 |
|
40 | 42 | import java.io.IOException;
|
41 | 43 | import java.util.ArrayList;
|
@@ -448,6 +450,41 @@ public void testCloseIndexDuringRollingUpgrade() throws Exception {
|
448 | 450 | }
|
449 | 451 | }
|
450 | 452 |
|
| 453 | + /** |
| 454 | + * We test that a closed index makes no-op replica allocation/recovery only. |
| 455 | + */ |
| 456 | + public void testClosedIndexNoopRecovery() throws Exception { |
| 457 | + final String indexName = "closed_index_replica_allocation"; |
| 458 | + if (CLUSTER_TYPE == ClusterType.OLD) { |
| 459 | + createIndex(indexName, Settings.builder() |
| 460 | + .put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) |
| 461 | + .put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) |
| 462 | + .put(EnableAllocationDecider.INDEX_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), "none") |
| 463 | + .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "120s") |
| 464 | + .put("index.routing.allocation.include._name", "node-0") |
| 465 | + .build()); |
| 466 | + indexDocs(indexName, 0, randomInt(10)); |
| 467 | + // allocate replica to node-2 |
| 468 | + updateIndexSettings(indexName, |
| 469 | + Settings.builder().put("index.routing.allocation.include._name", "node-0,node-2,upgraded-node-*")); |
| 470 | + ensureGreen(indexName); |
| 471 | + closeIndex(indexName); |
| 472 | + } |
| 473 | + |
| 474 | + final Version indexVersionCreated = indexVersionCreated(indexName); |
| 475 | + if (indexVersionCreated.onOrAfter(Version.V_7_2_0)) { |
| 476 | + // index was created on a version that supports the replication of closed indices, |
| 477 | + // so we expect the index to be closed and replicated |
| 478 | + ensureGreen(indexName); |
| 479 | + assertClosedIndex(indexName, true); |
| 480 | + if (CLUSTER_TYPE != ClusterType.OLD && minimumNodeVersion().onOrAfter(Version.V_7_2_0)) { |
| 481 | + assertNoFileBasedRecovery(indexName, s-> s.startsWith("upgraded")); |
| 482 | + } |
| 483 | + } else { |
| 484 | + assertClosedIndex(indexName, false); |
| 485 | + } |
| 486 | + |
| 487 | + } |
451 | 488 | /**
|
452 | 489 | * Returns the version in which the given index has been created
|
453 | 490 | */
|
@@ -592,4 +629,36 @@ public void testUpdateDoc() throws Exception {
|
592 | 629 | client().performRequest(update);
|
593 | 630 | }
|
594 | 631 | }
|
| 632 | + |
| 633 | + private void assertNoFileBasedRecovery(String indexName, Predicate<String> targetNode) throws IOException { |
| 634 | + Map<String, Object> recoveries = entityAsMap(client() |
| 635 | + .performRequest(new Request("GET", indexName + "/_recovery?detailed=true"))); |
| 636 | + |
| 637 | + @SuppressWarnings("unchecked") |
| 638 | + List<Map<String, ?>> shards = (List<Map<String,?>>) XContentMapValues.extractValue(indexName + ".shards", recoveries); |
| 639 | + assertNotNull(shards); |
| 640 | + boolean foundReplica = false; |
| 641 | + for (Map<String, ?> shard : shards) { |
| 642 | + if (shard.get("primary") == Boolean.FALSE |
| 643 | + && targetNode.test((String) XContentMapValues.extractValue("target.name", shard))) { |
| 644 | + List<?> details = (List<?>) XContentMapValues.extractValue("index.files.details", shard); |
| 645 | + // once detailed recoveries works, remove this if. |
| 646 | + if (details == null) { |
| 647 | + long totalFiles = ((Number) XContentMapValues.extractValue("index.files.total", shard)).longValue(); |
| 648 | + long reusedFiles = ((Number) XContentMapValues.extractValue("index.files.reused", shard)).longValue(); |
| 649 | + logger.info("total [{}] reused [{}]", totalFiles, reusedFiles); |
| 650 | + assertEquals("must reuse all files, recoveries [" + recoveries + "]", totalFiles, reusedFiles); |
| 651 | + } else { |
| 652 | + assertNotNull(details); |
| 653 | + assertThat(details, Matchers.empty()); |
| 654 | + } |
| 655 | + |
| 656 | + long translogRecovered = ((Number) XContentMapValues.extractValue("translog.recovered", shard)).longValue(); |
| 657 | + assertEquals("must be noop, recoveries [" + recoveries + "]", 0, translogRecovered); |
| 658 | + foundReplica = true; |
| 659 | + } |
| 660 | + } |
| 661 | + |
| 662 | + assertTrue("must find replica", foundReplica); |
| 663 | + } |
595 | 664 | }
|
0 commit comments