/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.bwcompat; import com.google.common.base.Predicate; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.Version; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.cluster.routing.allocation.decider.ConcurrentRebalanceAllocationDecider; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESBackcompatTestCase; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.junit.annotations.TestLogging; import org.junit.BeforeClass; import java.util.ArrayList; import java.util.List; import static org.elasticsearch.action.admin.indices.upgrade.UpgradeAssertions.assertNotUpgraded; import static org.elasticsearch.action.admin.indices.upgrade.UpgradeAssertions.assertUpgraded; import static org.elasticsearch.action.admin.indices.upgrade.UpgradeAssertions.hasAncientSegments; import static org.elasticsearch.action.admin.indices.upgrade.UpgradeAssertions.isUpgraded; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST) // test scope since we set cluster wide settings @TestLogging("level:DEBUG") // hack to enable root level debug logic. TODO fix to be "_root:DEBUG" public class UpgradeIT extends ESBackcompatTestCase { @BeforeClass public static void checkUpgradeVersion() { final boolean luceneVersionMatches = (globalCompatibilityVersion().luceneVersion.major == Version.CURRENT.luceneVersion.major && globalCompatibilityVersion().luceneVersion.minor == Version.CURRENT.luceneVersion.minor); assumeFalse("lucene versions must be different to run upgrade test", luceneVersionMatches); } @Override protected int minExternalNodes() { return 2; } @Override protected int maximumNumberOfReplicas() { return Math.max(0, Math.min(backwardsCluster().numBackwardsDataNodes(), backwardsCluster().numNewDataNodes()) - 1); } public void testUpgrade() throws Exception { // allow the cluster to rebalance quickly - 2 concurrent rebalance are default we can do higher Settings.Builder builder = Settings.builder(); builder.put(ConcurrentRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CLUSTER_CONCURRENT_REBALANCE, 100); client().admin().cluster().prepareUpdateSettings().setPersistentSettings(builder).get(); int numIndexes = randomIntBetween(2, 4); String[] indexNames = new String[numIndexes]; for (int i = 0; i < numIndexes; ++i) { final String indexName = "test" + i; indexNames[i] = indexName; Settings settings = Settings.builder() .put("index.routing.allocation.exclude._name", backwardsCluster().newNodePattern()) // don't allow any merges so that we can check segments are upgraded // by the upgrader, and not just regular merging .put("index.merge.policy.segments_per_tier", 1000000f) .put(indexSettings()) .build(); assertAcked(prepareCreate(indexName).setSettings(settings)); ensureGreen(indexName); assertAllShardsOnNodes(indexName, backwardsCluster().backwardsNodePattern()); int numDocs = scaledRandomIntBetween(100, 1000); List<IndexRequestBuilder> docs = new ArrayList<>(); for (int j = 0; j < numDocs; ++j) { String id = Integer.toString(j); docs.add(client().prepareIndex(indexName, "type1", id).setSource("text", "sometext")); } indexRandom(true, docs); ensureGreen(indexName); assertEquals(0, flush(indexName).getFailedShards()); // index more docs that won't be flushed numDocs = scaledRandomIntBetween(100, 1000); docs = new ArrayList<>(); for (int j = 0; j < numDocs; ++j) { String id = Integer.toString(j); docs.add(client().prepareIndex(indexName, "type2", id).setSource("text", "someothertext")); } indexRandom(true, docs); ensureGreen(indexName); } logger.debug("--> Upgrading nodes"); backwardsCluster().allowOnAllNodes(indexNames); ensureGreen(); // disable allocation entirely until all nodes are upgraded builder = Settings.builder(); builder.put(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE, EnableAllocationDecider.Allocation.NONE); client().admin().cluster().prepareUpdateSettings().setTransientSettings(builder).get(); backwardsCluster().upgradeAllNodes(); builder = Settings.builder(); // disable rebalanceing entirely for the time being otherwise we might get relocations / rebalance from nodes with old segments builder.put(EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE, EnableAllocationDecider.Rebalance.NONE); builder.put(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE, EnableAllocationDecider.Allocation.ALL); client().admin().cluster().prepareUpdateSettings().setTransientSettings(builder).get(); ensureGreen(); logger.info("--> Nodes upgrade complete"); logSegmentsState(); assertNotUpgraded(client()); final String indexToUpgrade = "test" + randomInt(numIndexes - 1); // This test fires up another node running an older version of ES, but because wire protocol changes across major ES versions, it // means we can never generate ancient segments in this test (unless Lucene major version bumps but ES major version does not): assertFalse(hasAncientSegments(client(), indexToUpgrade)); logger.info("--> Running upgrade on index " + indexToUpgrade); assertNoFailures(client().admin().indices().prepareUpgrade(indexToUpgrade).get()); awaitBusy(new Predicate<Object>() { @Override public boolean apply(Object o) { try { return isUpgraded(client(), indexToUpgrade); } catch (Exception e) { throw ExceptionsHelper.convertToRuntime(e); } } }); logger.info("--> Single index upgrade complete"); logger.info("--> Running upgrade on the rest of the indexes"); assertNoFailures(client().admin().indices().prepareUpgrade().get()); logSegmentsState(); logger.info("--> Full upgrade complete"); assertUpgraded(client()); } static class UpgradeStatus { public final String indexName; public final int totalBytes; public final int toUpgradeBytes; public final int toUpgradeBytesAncient; public UpgradeStatus(String indexName, int totalBytes, int toUpgradeBytes, int toUpgradeBytesAncient) { this.indexName = indexName; this.totalBytes = totalBytes; this.toUpgradeBytes = toUpgradeBytes; this.toUpgradeBytesAncient = toUpgradeBytesAncient; assert toUpgradeBytesAncient <= toUpgradeBytes; } } }