Add assertion that checks that the same shard with same id is not added to same node (#21498)

Adds an assertion that checks that the same shard with same id is not added to same node. Previously we would just silently ignore the second shard being added.
This commit is contained in:
Yannick Welsch 2016-11-14 15:14:14 +01:00 committed by GitHub
commit 8655cd7182
6 changed files with 77 additions and 86 deletions

View File

@ -33,6 +33,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@ -572,14 +573,6 @@ public class IndexShardRoutingTable implements Iterable<ShardRouting> {
}
public Builder addShard(ShardRouting shardEntry) {
for (ShardRouting shard : shards) {
// don't add two that map to the same node id
// we rely on the fact that a node does not have primary and backup of the same shard
if (shard.assignedToNode() && shardEntry.assignedToNode()
&& shard.currentNodeId().equals(shardEntry.currentNodeId())) {
return this;
}
}
shards.add(shardEntry);
return this;
}
@ -590,9 +583,28 @@ public class IndexShardRoutingTable implements Iterable<ShardRouting> {
}
public IndexShardRoutingTable build() {
// don't allow more than one shard copy with same id to be allocated to same node
assert distinctNodes(shards) : "more than one shard with same id assigned to same node (shards: " + shards + ")";
return new IndexShardRoutingTable(shardId, Collections.unmodifiableList(new ArrayList<>(shards)));
}
static boolean distinctNodes(List<ShardRouting> shards) {
Set<String> nodes = new HashSet<>();
for (ShardRouting shard : shards) {
if (shard.assignedToNode()) {
if (nodes.add(shard.currentNodeId()) == false) {
return false;
}
if (shard.relocating()) {
if (nodes.add(shard.relocatingNodeId()) == false) {
return false;
}
}
}
}
return true;
}
public static IndexShardRoutingTable readFrom(StreamInput in) throws IOException {
Index index = new Index(in);
return readFromThin(in, index);

View File

@ -31,6 +31,7 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.metadata.RepositoriesMetaData;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes;
@ -56,6 +57,7 @@ import org.elasticsearch.test.ESIntegTestCase;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
@ -238,13 +240,19 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
for (int i = 0; i < shardCount; i++) {
IndexShardRoutingTable.Builder indexShard = new IndexShardRoutingTable.Builder(new ShardId(index, "_na_", i));
int replicaCount = randomIntBetween(1, 10);
Set<String> availableNodeIds = Sets.newHashSet(nodeIds);
for (int j = 0; j < replicaCount; j++) {
UnassignedInfo unassignedInfo = null;
if (randomInt(5) == 1) {
unassignedInfo = new UnassignedInfo(randomReason(), randomAsciiOfLength(10));
}
if (availableNodeIds.isEmpty()) {
break;
}
String nodeId = randomFrom(availableNodeIds);
availableNodeIds.remove(nodeId);
indexShard.addShard(
TestShardRouting.newShardRouting(index, i, randomFrom(nodeIds), null, j == 0,
TestShardRouting.newShardRouting(index, i, nodeId, null, j == 0,
ShardRoutingState.fromValue((byte) randomIntBetween(2, 3)), unassignedInfo));
}
builder.addIndexShard(indexShard.build());
@ -258,8 +266,20 @@ public class ClusterStateDiffIT extends ESIntegTestCase {
private IndexRoutingTable randomChangeToIndexRoutingTable(IndexRoutingTable original, String[] nodes) {
IndexRoutingTable.Builder builder = IndexRoutingTable.builder(original.getIndex());
for (ObjectCursor<IndexShardRoutingTable> indexShardRoutingTable : original.shards().values()) {
Set<String> availableNodes = Sets.newHashSet(nodes);
for (ShardRouting shardRouting : indexShardRoutingTable.value.shards()) {
final ShardRouting updatedShardRouting = randomChange(shardRouting, nodes);
availableNodes.remove(shardRouting.currentNodeId());
if (shardRouting.relocating()) {
availableNodes.remove(shardRouting.relocatingNodeId());
}
}
for (ShardRouting shardRouting : indexShardRoutingTable.value.shards()) {
final ShardRouting updatedShardRouting = randomChange(shardRouting, availableNodes);
availableNodes.remove(updatedShardRouting.currentNodeId());
if (shardRouting.relocating()) {
availableNodes.remove(updatedShardRouting.relocatingNodeId());
}
builder.addShard(updatedShardRouting);
}
}

View File

@ -353,7 +353,7 @@ public class ClusterStateHealthTests extends ESTestCase {
final int numberOfReplicas,
final boolean withPrimaryAllocationFailures) {
// generate random node ids
final List<String> nodeIds = new ArrayList<>();
final Set<String> nodeIds = new HashSet<>();
final int numNodes = randomIntBetween(numberOfReplicas + 1, 10);
for (int i = 0; i < numNodes; i++) {
nodeIds.add(randomAsciiOfLength(8));
@ -372,7 +372,7 @@ public class ClusterStateHealthTests extends ESTestCase {
for (final ShardRouting shardRouting : shardRoutingTable.getShards()) {
if (shardRouting.primary()) {
newIndexRoutingTable.addShard(
shardRouting.initialize(nodeIds.get(randomIntBetween(0, numNodes - 1)), null, shardRouting.getExpectedShardSize())
shardRouting.initialize(randomFrom(nodeIds), null, shardRouting.getExpectedShardSize())
);
} else {
newIndexRoutingTable.addShard(shardRouting);
@ -460,17 +460,15 @@ public class ClusterStateHealthTests extends ESTestCase {
newIndexRoutingTable = IndexRoutingTable.builder(indexRoutingTable.getIndex());
for (final ObjectCursor<IndexShardRoutingTable> shardEntry : indexRoutingTable.getShards().values()) {
final IndexShardRoutingTable shardRoutingTable = shardEntry.value;
final String primaryNodeId = shardRoutingTable.primaryShard().currentNodeId();
Set<String> allocatedNodes = new HashSet<>();
allocatedNodes.add(primaryNodeId);
for (final ShardRouting shardRouting : shardRoutingTable.getShards()) {
if (shardRouting.primary() == false) {
// give the replica a different node id than the primary
final String primaryNodeId = shardRoutingTable.primaryShard().currentNodeId();
String replicaNodeId;
do {
replicaNodeId = nodeIds.get(randomIntBetween(0, numNodes - 1));
} while (primaryNodeId.equals(replicaNodeId));
newIndexRoutingTable.addShard(
shardRouting.initialize(replicaNodeId, null, shardRouting.getExpectedShardSize())
);
String replicaNodeId = randomFrom(Sets.difference(nodeIds, allocatedNodes));
newIndexRoutingTable.addShard(shardRouting.initialize(replicaNodeId, null, shardRouting.getExpectedShardSize()));
allocatedNodes.add(replicaNodeId);
} else {
newIndexRoutingTable.addShard(shardRouting);
}

View File

@ -19,6 +19,8 @@
package org.elasticsearch.cluster.routing;
import java.util.Set;
import static org.elasticsearch.test.ESTestCase.randomAsciiOfLength;
import static org.elasticsearch.test.ESTestCase.randomFrom;
import static org.elasticsearch.test.ESTestCase.randomInt;
@ -31,7 +33,7 @@ public final class RandomShardRoutingMutator {
}
public static ShardRouting randomChange(ShardRouting shardRouting, String[] nodes) {
public static ShardRouting randomChange(ShardRouting shardRouting, Set<String> nodes) {
switch (randomInt(2)) {
case 0:
if (shardRouting.unassigned() == false && shardRouting.primary() == false) {
@ -42,7 +44,7 @@ public final class RandomShardRoutingMutator {
}
break;
case 1:
if (shardRouting.unassigned()) {
if (shardRouting.unassigned() && nodes.isEmpty() == false) {
shardRouting = shardRouting.initialize(randomFrom(nodes), null, -1);
}
break;

View File

@ -28,9 +28,12 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.node.DiscoveryNodes.Builder;
import org.elasticsearch.cluster.routing.allocation.AllocationService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.shard.ShardId;
import org.junit.Before;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
@ -328,6 +331,19 @@ public class RoutingTableTests extends ESAllocationTestCase {
expectThrows(IllegalStateException.class, () -> indexRoutingTable.validate(metaData4));
}
public void testDistinctNodes() {
ShardId shardId = new ShardId(new Index("index", "uuid"), 0);
ShardRouting routing1 = TestShardRouting.newShardRouting(shardId, "node1", randomBoolean(), ShardRoutingState.STARTED);
ShardRouting routing2 = TestShardRouting.newShardRouting(shardId, "node2", randomBoolean(), ShardRoutingState.STARTED);
ShardRouting routing3 = TestShardRouting.newShardRouting(shardId, "node1", randomBoolean(), ShardRoutingState.STARTED);
ShardRouting routing4 = TestShardRouting.newShardRouting(shardId, "node3", "node2", randomBoolean(), ShardRoutingState.RELOCATING);
assertTrue(IndexShardRoutingTable.Builder.distinctNodes(Arrays.asList(routing1, routing2)));
assertFalse(IndexShardRoutingTable.Builder.distinctNodes(Arrays.asList(routing1, routing3)));
assertFalse(IndexShardRoutingTable.Builder.distinctNodes(Arrays.asList(routing1, routing2, routing3)));
assertTrue(IndexShardRoutingTable.Builder.distinctNodes(Arrays.asList(routing1, routing4)));
assertFalse(IndexShardRoutingTable.Builder.distinctNodes(Arrays.asList(routing2, routing4)));
}
/** reverse engineer the in sync aid based on the given indexRoutingTable **/
public static IndexMetaData updateActiveAllocations(IndexRoutingTable indexRoutingTable, IndexMetaData indexMetaData) {
IndexMetaData.Builder imdBuilder = IndexMetaData.builder(indexMetaData);

View File

@ -71,13 +71,7 @@ public class IndicesStoreTests extends ESTestCase {
}
public void testShardCanBeDeletedNoShardRouting() throws Exception {
int numShards = randomIntBetween(1, 7);
int numReplicas = randomInt(2);
ClusterState.Builder clusterState = ClusterState.builder(new ClusterName("test"));
clusterState.metaData(MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(numShards).numberOfReplicas(numReplicas)));
IndexShardRoutingTable.Builder routingTable = new IndexShardRoutingTable.Builder(new ShardId("test", "_na_", 1));
assertFalse(IndicesStore.shardCanBeDeleted(localNode.getId(), routingTable.build()));
}
@ -85,8 +79,6 @@ public class IndicesStoreTests extends ESTestCase {
int numShards = randomIntBetween(1, 7);
int numReplicas = randomInt(2);
ClusterState.Builder clusterState = ClusterState.builder(new ClusterName("test"));
clusterState.metaData(MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(numShards).numberOfReplicas(numReplicas)));
IndexShardRoutingTable.Builder routingTable = new IndexShardRoutingTable.Builder(new ShardId("test", "_na_", 1));
for (int i = 0; i < numShards; i++) {
@ -102,7 +94,8 @@ public class IndicesStoreTests extends ESTestCase {
if (state == ShardRoutingState.UNASSIGNED) {
unassignedInfo = new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, null);
}
routingTable.addShard(TestShardRouting.newShardRouting("test", i, randomBoolean() ? localNode.getId() : randomAsciiOfLength(10), null, j == 0, state, unassignedInfo));
String relocatingNodeId = state == ShardRoutingState.RELOCATING ? randomAsciiOfLength(10) : null;
routingTable.addShard(TestShardRouting.newShardRouting("test", i, randomAsciiOfLength(10), relocatingNodeId, j == 0, state, unassignedInfo));
}
}
@ -113,69 +106,19 @@ public class IndicesStoreTests extends ESTestCase {
int numShards = randomIntBetween(1, 7);
int numReplicas = randomInt(2);
ClusterState.Builder clusterState = ClusterState.builder(new ClusterName("test"));
clusterState.metaData(MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(numShards).numberOfReplicas(numReplicas)));
clusterState.nodes(DiscoveryNodes.builder().localNodeId(localNode.getId()).add(localNode).add(new DiscoveryNode("xyz",
buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT)));
IndexShardRoutingTable.Builder routingTable = new IndexShardRoutingTable.Builder(new ShardId("test", "_na_", 1));
int localShardId = randomInt(numShards - 1);
for (int i = 0; i < numShards; i++) {
String nodeId = i == localShardId ? localNode.getId() : randomBoolean() ? "abc" : "xyz";
String relocationNodeId = randomBoolean() ? null : randomBoolean() ? localNode.getId() : "xyz";
routingTable.addShard(TestShardRouting.newShardRouting("test", i, nodeId, relocationNodeId, true, ShardRoutingState.STARTED));
int localNodeIndex = randomInt(numReplicas);
boolean primaryOnLocalNode = i == localShardId && localNodeIndex == numReplicas;
routingTable.addShard(TestShardRouting.newShardRouting("test", i, primaryOnLocalNode ? localNode.getId() : randomAsciiOfLength(10), true, ShardRoutingState.STARTED));
for (int j = 0; j < numReplicas; j++) {
routingTable.addShard(TestShardRouting.newShardRouting("test", i, nodeId, relocationNodeId, false, ShardRoutingState.STARTED));
boolean replicaOnLocalNode = i == localShardId && localNodeIndex == j;
routingTable.addShard(TestShardRouting.newShardRouting("test", i, replicaOnLocalNode ? localNode.getId() : randomAsciiOfLength(10), false, ShardRoutingState.STARTED));
}
}
// Shard exists locally, can't delete shard
assertFalse(IndicesStore.shardCanBeDeleted(localNode.getId(), routingTable.build()));
}
public void testShardCanBeDeletedNodeVersion() throws Exception {
int numShards = randomIntBetween(1, 7);
int numReplicas = randomInt(2);
// Most of the times don't test bwc and use current version
final Version nodeVersion = randomBoolean() ? CURRENT : randomVersion(random());
ClusterState.Builder clusterState = ClusterState.builder(new ClusterName("test"));
clusterState.metaData(MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(numShards).numberOfReplicas(numReplicas)));
clusterState.nodes(DiscoveryNodes.builder().localNodeId(localNode.getId()).add(localNode).add(new DiscoveryNode("xyz",
buildNewFakeTransportAddress(), emptyMap(), emptySet(), nodeVersion)));
IndexShardRoutingTable.Builder routingTable = new IndexShardRoutingTable.Builder(new ShardId("test", "_na_", 1));
for (int i = 0; i < numShards; i++) {
routingTable.addShard(TestShardRouting.newShardRouting("test", i, "xyz", null, true, ShardRoutingState.STARTED));
for (int j = 0; j < numReplicas; j++) {
routingTable.addShard(TestShardRouting.newShardRouting("test", i, "xyz", null, false, ShardRoutingState.STARTED));
}
}
// shard exist on other node (abc)
assertTrue(IndicesStore.shardCanBeDeleted(localNode.getId(), routingTable.build()));
}
public void testShardCanBeDeletedRelocatingNode() throws Exception {
int numShards = randomIntBetween(1, 7);
int numReplicas = randomInt(2);
ClusterState.Builder clusterState = ClusterState.builder(new ClusterName("test"));
clusterState.metaData(MetaData.builder().put(IndexMetaData.builder("test").settings(settings(Version.CURRENT)).numberOfShards(numShards).numberOfReplicas(numReplicas)));
final Version nodeVersion = randomBoolean() ? CURRENT : randomVersion(random());
clusterState.nodes(DiscoveryNodes.builder().localNodeId(localNode.getId())
.add(localNode)
.add(new DiscoveryNode("xyz", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT))
.add(new DiscoveryNode("def", buildNewFakeTransportAddress(), emptyMap(), emptySet(), nodeVersion) // <-- only set relocating, since we're testing that in this test
));
IndexShardRoutingTable.Builder routingTable = new IndexShardRoutingTable.Builder(new ShardId("test", "_na_", 1));
for (int i = 0; i < numShards; i++) {
routingTable.addShard(TestShardRouting.newShardRouting("test", i, "xyz", "def", true, ShardRoutingState.STARTED));
for (int j = 0; j < numReplicas; j++) {
routingTable.addShard(TestShardRouting.newShardRouting("test", i, "xyz", "def", false, ShardRoutingState.STARTED));
}
}
// shard exist on other node (abc and def)
assertTrue(IndicesStore.shardCanBeDeleted(localNode.getId(), routingTable.build()));
}
}