|
|
@@ -0,0 +1,305 @@
|
|
|
+/*
|
|
|
+ * 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.action.support;
|
|
|
+
|
|
|
+import com.carrotsearch.hppc.cursors.ObjectCursor;
|
|
|
+import org.elasticsearch.Version;
|
|
|
+import org.elasticsearch.cluster.ClusterName;
|
|
|
+import org.elasticsearch.cluster.ClusterState;
|
|
|
+import org.elasticsearch.cluster.metadata.IndexMetaData;
|
|
|
+import org.elasticsearch.cluster.metadata.MetaData;
|
|
|
+import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
|
|
+import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
|
|
+import org.elasticsearch.cluster.routing.RoutingTable;
|
|
|
+import org.elasticsearch.cluster.routing.ShardRouting;
|
|
|
+import org.elasticsearch.common.UUIDs;
|
|
|
+import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
|
|
|
+import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
|
|
+import org.elasticsearch.test.ESTestCase;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+
|
|
|
+import static org.hamcrest.Matchers.equalTo;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Tests for the {@link ActiveShardCount} class
|
|
|
+ */
|
|
|
+public class ActiveShardCountTests extends ESTestCase {
|
|
|
+
|
|
|
+ public void testFromIntValue() {
|
|
|
+ assertSame(ActiveShardCount.from(0), ActiveShardCount.NONE);
|
|
|
+ final int value = randomIntBetween(1, 50);
|
|
|
+ IndexMetaData indexMetaData = IndexMetaData.builder("test")
|
|
|
+ .settings(settings(Version.CURRENT))
|
|
|
+ .numberOfShards(1)
|
|
|
+ .numberOfReplicas(0)
|
|
|
+ .build();
|
|
|
+ assertEquals(ActiveShardCount.from(value).resolve(indexMetaData), value);
|
|
|
+ expectThrows(IllegalArgumentException.class, () -> ActiveShardCount.from(randomIntBetween(-10, -1)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testResolve() {
|
|
|
+ // one shard
|
|
|
+ IndexMetaData indexMetaData = IndexMetaData.builder("test")
|
|
|
+ .settings(settings(Version.CURRENT))
|
|
|
+ .numberOfShards(1)
|
|
|
+ .numberOfReplicas(0)
|
|
|
+ .build();
|
|
|
+ assertThat(ActiveShardCount.ALL.resolve(indexMetaData), equalTo(1));
|
|
|
+ assertThat(ActiveShardCount.DEFAULT.resolve(indexMetaData), equalTo(1));
|
|
|
+ assertThat(ActiveShardCount.NONE.resolve(indexMetaData), equalTo(0));
|
|
|
+ final int value = randomIntBetween(2, 20);
|
|
|
+ assertThat(ActiveShardCount.from(value).resolve(indexMetaData), equalTo(value));
|
|
|
+
|
|
|
+ // more than one shard
|
|
|
+ final int numNewShards = randomIntBetween(1, 20);
|
|
|
+ indexMetaData = IndexMetaData.builder("test")
|
|
|
+ .settings(settings(Version.CURRENT))
|
|
|
+ .numberOfShards(1)
|
|
|
+ .numberOfReplicas(numNewShards)
|
|
|
+ .build();
|
|
|
+ assertThat(ActiveShardCount.ALL.resolve(indexMetaData), equalTo(numNewShards + 1));
|
|
|
+ assertThat(ActiveShardCount.DEFAULT.resolve(indexMetaData), equalTo(1));
|
|
|
+ assertThat(ActiveShardCount.NONE.resolve(indexMetaData), equalTo(0));
|
|
|
+ assertThat(ActiveShardCount.from(value).resolve(indexMetaData), equalTo(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testSerialization() throws IOException {
|
|
|
+ doWriteRead(ActiveShardCount.ALL);
|
|
|
+ doWriteRead(ActiveShardCount.DEFAULT);
|
|
|
+ doWriteRead(ActiveShardCount.NONE);
|
|
|
+ doWriteRead(ActiveShardCount.from(randomIntBetween(1, 50)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testParseString() {
|
|
|
+ assertSame(ActiveShardCount.parseString("all"), ActiveShardCount.ALL);
|
|
|
+ assertSame(ActiveShardCount.parseString(null), ActiveShardCount.DEFAULT);
|
|
|
+ assertSame(ActiveShardCount.parseString("0"), ActiveShardCount.NONE);
|
|
|
+ int value = randomIntBetween(1, 50);
|
|
|
+ assertEquals(ActiveShardCount.parseString(value + ""), ActiveShardCount.from(value));
|
|
|
+ expectThrows(IllegalArgumentException.class, () -> ActiveShardCount.parseString(randomAsciiOfLengthBetween(4, 8)));
|
|
|
+ expectThrows(IllegalArgumentException.class, () -> ActiveShardCount.parseString("-1")); // magic numbers not exposed through API
|
|
|
+ expectThrows(IllegalArgumentException.class, () -> ActiveShardCount.parseString("-2"));
|
|
|
+ expectThrows(IllegalArgumentException.class, () -> ActiveShardCount.parseString(randomIntBetween(-10, -3) + ""));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void doWriteRead(ActiveShardCount activeShardCount) throws IOException {
|
|
|
+ final BytesStreamOutput out = new BytesStreamOutput();
|
|
|
+ activeShardCount.writeTo(out);
|
|
|
+ final ByteBufferStreamInput in = new ByteBufferStreamInput(ByteBuffer.wrap(out.bytes().toBytesRef().bytes));
|
|
|
+ ActiveShardCount readActiveShardCount = ActiveShardCount.readFrom(in);
|
|
|
+ if (activeShardCount == ActiveShardCount.DEFAULT
|
|
|
+ || activeShardCount == ActiveShardCount.ALL
|
|
|
+ || activeShardCount == ActiveShardCount.NONE) {
|
|
|
+ assertSame(activeShardCount, readActiveShardCount);
|
|
|
+ } else {
|
|
|
+ assertEquals(activeShardCount, readActiveShardCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testEnoughShardsActiveZero() {
|
|
|
+ final String indexName = "test-idx";
|
|
|
+ final int numberOfShards = randomIntBetween(1, 5);
|
|
|
+ final int numberOfReplicas = randomIntBetween(4, 7);
|
|
|
+ final ActiveShardCount waitForActiveShards = ActiveShardCount.from(0);
|
|
|
+ ClusterState clusterState = initializeWithNewIndex(indexName, numberOfShards, numberOfReplicas);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startPrimaries(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startLessThanWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startAllShards(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testEnoughShardsActiveLevelOne() {
|
|
|
+ runTestForOneActiveShard(ActiveShardCount.ONE);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testEnoughShardsActiveLevelDefault() {
|
|
|
+ // default is 1
|
|
|
+ runTestForOneActiveShard(ActiveShardCount.DEFAULT);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testEnoughShardsActiveRandom() {
|
|
|
+ final String indexName = "test-idx";
|
|
|
+ final int numberOfShards = randomIntBetween(1, 5);
|
|
|
+ final int numberOfReplicas = randomIntBetween(4, 7);
|
|
|
+ final ActiveShardCount waitForActiveShards = ActiveShardCount.from(randomIntBetween(2, numberOfReplicas));
|
|
|
+ ClusterState clusterState = initializeWithNewIndex(indexName, numberOfShards, numberOfReplicas);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startPrimaries(clusterState, indexName);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startLessThanWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startAllShards(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testEnoughShardsActiveLevelAll() {
|
|
|
+ final String indexName = "test-idx";
|
|
|
+ final int numberOfShards = randomIntBetween(1, 5);
|
|
|
+ final int numberOfReplicas = randomIntBetween(4, 7);
|
|
|
+ // both values should represent "all"
|
|
|
+ final ActiveShardCount waitForActiveShards = randomBoolean() ? ActiveShardCount.from(numberOfReplicas + 1) : ActiveShardCount.ALL;
|
|
|
+ ClusterState clusterState = initializeWithNewIndex(indexName, numberOfShards, numberOfReplicas);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startPrimaries(clusterState, indexName);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startLessThanWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startAllShards(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void runTestForOneActiveShard(final ActiveShardCount activeShardCount) {
|
|
|
+ final String indexName = "test-idx";
|
|
|
+ final int numberOfShards = randomIntBetween(1, 5);
|
|
|
+ final int numberOfReplicas = randomIntBetween(4, 7);
|
|
|
+ assert activeShardCount == ActiveShardCount.ONE || activeShardCount == ActiveShardCount.DEFAULT;
|
|
|
+ final ActiveShardCount waitForActiveShards = activeShardCount;
|
|
|
+ ClusterState clusterState = initializeWithNewIndex(indexName, numberOfShards, numberOfReplicas);
|
|
|
+ assertFalse(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startPrimaries(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startLessThanWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startWaitOnShards(clusterState, indexName, waitForActiveShards);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ clusterState = startAllShards(clusterState, indexName);
|
|
|
+ assertTrue(waitForActiveShards.enoughShardsActive(clusterState, indexName));
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState initializeWithNewIndex(final String indexName, final int numShards, final int numReplicas) {
|
|
|
+ // initial index creation and new routing table info
|
|
|
+ final IndexMetaData indexMetaData = IndexMetaData.builder(indexName)
|
|
|
+ .settings(settings(Version.CURRENT)
|
|
|
+ .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()))
|
|
|
+ .numberOfShards(numShards)
|
|
|
+ .numberOfReplicas(numReplicas)
|
|
|
+ .build();
|
|
|
+ final MetaData metaData = MetaData.builder().put(indexMetaData, true).build();
|
|
|
+ final RoutingTable routingTable = RoutingTable.builder().addAsNew(indexMetaData).build();
|
|
|
+ return ClusterState.builder(new ClusterName("test_cluster")).metaData(metaData).routingTable(routingTable).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState startPrimaries(final ClusterState clusterState, final String indexName) {
|
|
|
+ RoutingTable routingTable = clusterState.routingTable();
|
|
|
+ IndexRoutingTable indexRoutingTable = routingTable.index(indexName);
|
|
|
+ IndexRoutingTable.Builder newIndexRoutingTable = IndexRoutingTable.builder(indexRoutingTable.getIndex());
|
|
|
+ for (final ObjectCursor<IndexShardRoutingTable> shardEntry : indexRoutingTable.getShards().values()) {
|
|
|
+ final IndexShardRoutingTable shardRoutingTable = shardEntry.value;
|
|
|
+ for (ShardRouting shardRouting : shardRoutingTable.getShards()) {
|
|
|
+ if (shardRouting.primary()) {
|
|
|
+ shardRouting = shardRouting.initialize(randomAsciiOfLength(8), null, shardRouting.getExpectedShardSize())
|
|
|
+ .moveToStarted();
|
|
|
+ }
|
|
|
+ newIndexRoutingTable.addShard(shardRouting);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ routingTable = RoutingTable.builder(routingTable).add(newIndexRoutingTable).build();
|
|
|
+ return ClusterState.builder(clusterState).routingTable(routingTable).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState startLessThanWaitOnShards(final ClusterState clusterState, final String indexName,
|
|
|
+ final ActiveShardCount waitForActiveShards) {
|
|
|
+ RoutingTable routingTable = clusterState.routingTable();
|
|
|
+ IndexRoutingTable indexRoutingTable = routingTable.index(indexName);
|
|
|
+ IndexRoutingTable.Builder newIndexRoutingTable = IndexRoutingTable.builder(indexRoutingTable.getIndex());
|
|
|
+ for (final ObjectCursor<IndexShardRoutingTable> shardEntry : indexRoutingTable.getShards().values()) {
|
|
|
+ final IndexShardRoutingTable shardRoutingTable = shardEntry.value;
|
|
|
+ assert shardRoutingTable.getSize() > 2;
|
|
|
+ // want less than half, and primary is already started
|
|
|
+ int numToStart = waitForActiveShards.resolve(clusterState.metaData().index(indexName)) - 2;
|
|
|
+ for (ShardRouting shardRouting : shardRoutingTable.getShards()) {
|
|
|
+ if (shardRouting.primary()) {
|
|
|
+ assertTrue(shardRouting.active());
|
|
|
+ } else {
|
|
|
+ if (numToStart > 0) {
|
|
|
+ shardRouting = shardRouting.initialize(randomAsciiOfLength(8), null, shardRouting.getExpectedShardSize())
|
|
|
+ .moveToStarted();
|
|
|
+ numToStart--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newIndexRoutingTable.addShard(shardRouting);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ routingTable = RoutingTable.builder(routingTable).add(newIndexRoutingTable).build();
|
|
|
+ return ClusterState.builder(clusterState).routingTable(routingTable).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState startWaitOnShards(final ClusterState clusterState, final String indexName,
|
|
|
+ final ActiveShardCount waitForActiveShards) {
|
|
|
+ RoutingTable routingTable = clusterState.routingTable();
|
|
|
+ IndexRoutingTable indexRoutingTable = routingTable.index(indexName);
|
|
|
+ IndexRoutingTable.Builder newIndexRoutingTable = IndexRoutingTable.builder(indexRoutingTable.getIndex());
|
|
|
+ for (final ObjectCursor<IndexShardRoutingTable> shardEntry : indexRoutingTable.getShards().values()) {
|
|
|
+ final IndexShardRoutingTable shardRoutingTable = shardEntry.value;
|
|
|
+ assert shardRoutingTable.getSize() > 2;
|
|
|
+ int numToStart = waitForActiveShards.resolve(clusterState.metaData().index(indexName)) - 1; // primary is already started
|
|
|
+ for (ShardRouting shardRouting : shardRoutingTable.getShards()) {
|
|
|
+ if (shardRouting.primary()) {
|
|
|
+ assertTrue(shardRouting.active());
|
|
|
+ } else {
|
|
|
+ if (shardRouting.active() == false) {
|
|
|
+ if (numToStart > 0) {
|
|
|
+ shardRouting = shardRouting.initialize(randomAsciiOfLength(8), null, shardRouting.getExpectedShardSize())
|
|
|
+ .moveToStarted();
|
|
|
+ numToStart--;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ numToStart--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newIndexRoutingTable.addShard(shardRouting);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ routingTable = RoutingTable.builder(routingTable).add(newIndexRoutingTable).build();
|
|
|
+ return ClusterState.builder(clusterState).routingTable(routingTable).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private ClusterState startAllShards(final ClusterState clusterState, final String indexName) {
|
|
|
+ RoutingTable routingTable = clusterState.routingTable();
|
|
|
+ IndexRoutingTable indexRoutingTable = routingTable.index(indexName);
|
|
|
+ IndexRoutingTable.Builder newIndexRoutingTable = IndexRoutingTable.builder(indexRoutingTable.getIndex());
|
|
|
+ for (final ObjectCursor<IndexShardRoutingTable> shardEntry : indexRoutingTable.getShards().values()) {
|
|
|
+ final IndexShardRoutingTable shardRoutingTable = shardEntry.value;
|
|
|
+ for (ShardRouting shardRouting : shardRoutingTable.getShards()) {
|
|
|
+ if (shardRouting.primary()) {
|
|
|
+ assertTrue(shardRouting.active());
|
|
|
+ } else {
|
|
|
+ if (shardRouting.active() == false) {
|
|
|
+ shardRouting = shardRouting.initialize(randomAsciiOfLength(8), null, shardRouting.getExpectedShardSize())
|
|
|
+ .moveToStarted();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newIndexRoutingTable.addShard(shardRouting);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ routingTable = RoutingTable.builder(routingTable).add(newIndexRoutingTable).build();
|
|
|
+ return ClusterState.builder(clusterState).routingTable(routingTable).build();
|
|
|
+ }
|
|
|
+
|
|
|
+}
|