Protect shard splitting from illegal target shards (#27468)
While we have an assertion that checks if the number of routing shards is a multiple of the number of shards we need a real hard exception that checks this way earlier. This change adds a check and test that is executed before we create the index. Relates to #26931
This commit is contained in:
parent
29450de7b5
commit
ea35abca28
|
@ -1343,6 +1343,12 @@ public class IndexMetaData implements Diffable<IndexMetaData>, ToXContentFragmen
|
||||||
+ "] must be less that the number of target shards [" + numTargetShards + "]");
|
+ "] must be less that the number of target shards [" + numTargetShards + "]");
|
||||||
}
|
}
|
||||||
int routingFactor = getRoutingFactor(numSourceShards, numTargetShards);
|
int routingFactor = getRoutingFactor(numSourceShards, numTargetShards);
|
||||||
|
// now we verify that the numRoutingShards is valid in the source index
|
||||||
|
int routingNumShards = sourceIndexMetadata.getRoutingNumShards();
|
||||||
|
if (routingNumShards % numTargetShards != 0) {
|
||||||
|
throw new IllegalStateException("the number of routing shards ["
|
||||||
|
+ routingNumShards + "] must be a multiple of the target shards [" + numTargetShards + "]");
|
||||||
|
}
|
||||||
// this is just an additional assertion that ensures we are a factor of the routing num shards.
|
// this is just an additional assertion that ensures we are a factor of the routing num shards.
|
||||||
assert getRoutingFactor(numTargetShards, sourceIndexMetadata.getRoutingNumShards()) >= 0;
|
assert getRoutingFactor(numTargetShards, sourceIndexMetadata.getRoutingNumShards()) >= 0;
|
||||||
return new ShardId(sourceIndexMetadata.getIndex(), shardId/routingFactor);
|
return new ShardId(sourceIndexMetadata.getIndex(), shardId/routingFactor);
|
||||||
|
|
|
@ -118,6 +118,8 @@ public class IndexMetaDataTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSelectResizeShards() {
|
public void testSelectResizeShards() {
|
||||||
|
int numTargetShards = randomFrom(4, 6, 8, 12);
|
||||||
|
|
||||||
IndexMetaData split = IndexMetaData.builder("foo")
|
IndexMetaData split = IndexMetaData.builder("foo")
|
||||||
.settings(Settings.builder()
|
.settings(Settings.builder()
|
||||||
.put("index.version.created", 1)
|
.put("index.version.created", 1)
|
||||||
|
@ -125,6 +127,7 @@ public class IndexMetaDataTests extends ESTestCase {
|
||||||
.put("index.number_of_replicas", 0)
|
.put("index.number_of_replicas", 0)
|
||||||
.build())
|
.build())
|
||||||
.creationDate(randomLong())
|
.creationDate(randomLong())
|
||||||
|
.setRoutingNumShards(numTargetShards * 2)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
IndexMetaData shrink = IndexMetaData.builder("foo")
|
IndexMetaData shrink = IndexMetaData.builder("foo")
|
||||||
|
@ -135,7 +138,6 @@ public class IndexMetaDataTests extends ESTestCase {
|
||||||
.build())
|
.build())
|
||||||
.creationDate(randomLong())
|
.creationDate(randomLong())
|
||||||
.build();
|
.build();
|
||||||
int numTargetShards = randomFrom(4, 6, 8, 12);
|
|
||||||
int shard = randomIntBetween(0, numTargetShards-1);
|
int shard = randomIntBetween(0, numTargetShards-1);
|
||||||
assertEquals(Collections.singleton(IndexMetaData.selectSplitShard(shard, split, numTargetShards)),
|
assertEquals(Collections.singleton(IndexMetaData.selectSplitShard(shard, split, numTargetShards)),
|
||||||
IndexMetaData.selectRecoverFromShards(shard, split, numTargetShards));
|
IndexMetaData.selectRecoverFromShards(shard, split, numTargetShards));
|
||||||
|
@ -173,6 +175,9 @@ public class IndexMetaDataTests extends ESTestCase {
|
||||||
|
|
||||||
assertEquals("the number of source shards [2] must be a must be a factor of [3]",
|
assertEquals("the number of source shards [2] must be a must be a factor of [3]",
|
||||||
expectThrows(IllegalArgumentException.class, () -> IndexMetaData.selectSplitShard(0, metaData, 3)).getMessage());
|
expectThrows(IllegalArgumentException.class, () -> IndexMetaData.selectSplitShard(0, metaData, 3)).getMessage());
|
||||||
|
|
||||||
|
assertEquals("the number of routing shards [4] must be a multiple of the target shards [8]",
|
||||||
|
expectThrows(IllegalStateException.class, () -> IndexMetaData.selectSplitShard(0, metaData, 8)).getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexFormat() {
|
public void testIndexFormat() {
|
||||||
|
|
|
@ -56,10 +56,12 @@ import static org.hamcrest.Matchers.endsWith;
|
||||||
public class MetaDataCreateIndexServiceTests extends ESTestCase {
|
public class MetaDataCreateIndexServiceTests extends ESTestCase {
|
||||||
|
|
||||||
private ClusterState createClusterState(String name, int numShards, int numReplicas, Settings settings) {
|
private ClusterState createClusterState(String name, int numShards, int numReplicas, Settings settings) {
|
||||||
|
int numRoutingShards = settings.getAsInt(IndexMetaData.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), numShards);
|
||||||
MetaData.Builder metaBuilder = MetaData.builder();
|
MetaData.Builder metaBuilder = MetaData.builder();
|
||||||
IndexMetaData indexMetaData = IndexMetaData.builder(name).settings(settings(Version.CURRENT)
|
IndexMetaData indexMetaData = IndexMetaData.builder(name).settings(settings(Version.CURRENT)
|
||||||
.put(settings))
|
.put(settings))
|
||||||
.numberOfShards(numShards).numberOfReplicas(numReplicas).build();
|
.numberOfShards(numShards).numberOfReplicas(numReplicas)
|
||||||
|
.setRoutingNumShards(numRoutingShards).build();
|
||||||
metaBuilder.put(indexMetaData, false);
|
metaBuilder.put(indexMetaData, false);
|
||||||
MetaData metaData = metaBuilder.build();
|
MetaData metaData = metaBuilder.build();
|
||||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||||
|
@ -204,10 +206,13 @@ public class MetaDataCreateIndexServiceTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
).getMessage());
|
).getMessage());
|
||||||
|
|
||||||
|
int targetShards;
|
||||||
|
do {
|
||||||
|
targetShards = randomIntBetween(numShards+1, 100);
|
||||||
|
} while (isSplitable(numShards, targetShards) == false);
|
||||||
ClusterState clusterState = ClusterState.builder(createClusterState("source", numShards, 0,
|
ClusterState clusterState = ClusterState.builder(createClusterState("source", numShards, 0,
|
||||||
Settings.builder().put("index.blocks.write", true).build())).nodes(DiscoveryNodes.builder().add(newNode("node1")))
|
Settings.builder().put("index.blocks.write", true).put("index.number_of_routing_shards", targetShards).build()))
|
||||||
.build();
|
.nodes(DiscoveryNodes.builder().add(newNode("node1"))).build();
|
||||||
AllocationService service = new AllocationService(Settings.builder().build(), new AllocationDeciders(Settings.EMPTY,
|
AllocationService service = new AllocationService(Settings.builder().build(), new AllocationDeciders(Settings.EMPTY,
|
||||||
Collections.singleton(new MaxRetryAllocationDecider(Settings.EMPTY))),
|
Collections.singleton(new MaxRetryAllocationDecider(Settings.EMPTY))),
|
||||||
new TestGatewayAllocator(), new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE);
|
new TestGatewayAllocator(), new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE);
|
||||||
|
@ -218,10 +223,7 @@ public class MetaDataCreateIndexServiceTests extends ESTestCase {
|
||||||
routingTable = service.applyStartedShards(clusterState,
|
routingTable = service.applyStartedShards(clusterState,
|
||||||
routingTable.index("source").shardsWithState(ShardRoutingState.INITIALIZING)).routingTable();
|
routingTable.index("source").shardsWithState(ShardRoutingState.INITIALIZING)).routingTable();
|
||||||
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
|
||||||
int targetShards;
|
|
||||||
do {
|
|
||||||
targetShards = randomIntBetween(numShards+1, 100);
|
|
||||||
} while (isSplitable(numShards, targetShards) == false);
|
|
||||||
MetaDataCreateIndexService.validateSplitIndex(clusterState, "source", Collections.emptySet(), "target",
|
MetaDataCreateIndexService.validateSplitIndex(clusterState, "source", Collections.emptySet(), "target",
|
||||||
Settings.builder().put("index.number_of_shards", targetShards).build());
|
Settings.builder().put("index.number_of_shards", targetShards).build());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
---
|
---
|
||||||
"Split index via API":
|
setup:
|
||||||
- skip:
|
|
||||||
version: " - 6.0.99"
|
|
||||||
reason: Added in 6.1.0
|
|
||||||
- do:
|
- do:
|
||||||
indices.create:
|
indices.create:
|
||||||
index: source
|
index: source
|
||||||
|
@ -33,6 +30,12 @@
|
||||||
id: "3"
|
id: "3"
|
||||||
body: { "foo": "hello world 3" }
|
body: { "foo": "hello world 3" }
|
||||||
|
|
||||||
|
---
|
||||||
|
"Split index via API":
|
||||||
|
- skip:
|
||||||
|
version: " - 6.0.99"
|
||||||
|
reason: Added in 6.1.0
|
||||||
|
|
||||||
# make it read-only
|
# make it read-only
|
||||||
- do:
|
- do:
|
||||||
indices.put_settings:
|
indices.put_settings:
|
||||||
|
@ -97,5 +100,38 @@
|
||||||
- match: { _id: "3" }
|
- match: { _id: "3" }
|
||||||
- match: { _source: { foo: "hello world 3" } }
|
- match: { _source: { foo: "hello world 3" } }
|
||||||
|
|
||||||
|
---
|
||||||
|
"Create illegal split indices":
|
||||||
|
- skip:
|
||||||
|
version: " - 6.99.99"
|
||||||
|
reason: fixed in 7.0.0
|
||||||
|
|
||||||
|
# try to do an illegal split with number_of_routing_shards set
|
||||||
|
- do:
|
||||||
|
catch: /illegal_argument_exception/
|
||||||
|
indices.split:
|
||||||
|
index: "source"
|
||||||
|
target: "target"
|
||||||
|
wait_for_active_shards: 1
|
||||||
|
master_timeout: 10s
|
||||||
|
body:
|
||||||
|
settings:
|
||||||
|
index.number_of_replicas: 0
|
||||||
|
index.number_of_shards: 2
|
||||||
|
index.number_of_routing_shards: 4
|
||||||
|
|
||||||
|
# try to do an illegal split with illegal number_of_shards
|
||||||
|
- do:
|
||||||
|
catch: /illegal_state_exception/
|
||||||
|
indices.split:
|
||||||
|
index: "source"
|
||||||
|
target: "target"
|
||||||
|
wait_for_active_shards: 1
|
||||||
|
master_timeout: 10s
|
||||||
|
body:
|
||||||
|
settings:
|
||||||
|
index.number_of_replicas: 0
|
||||||
|
index.number_of_shards: 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue