Skip to content

Commit f400f2f

Browse files
authored
Check allocation id when failing shard on recovery (#50656)
A failure of a recovering shard can race with a new allocation of the shard, and cause the new allocation to be failed as well. This can result in a shard being marked as initializing in the cluster state, but not exist on the node anymore. Closes #50508
1 parent c68c78f commit f400f2f

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,8 @@ public void onRecoveryFailure(RecoveryState state, RecoveryFailedException e, bo
717717
}
718718
}
719719

720-
private synchronized void handleRecoveryFailure(ShardRouting shardRouting, boolean sendShardFailure, Exception failure) {
720+
// package-private for testing
721+
synchronized void handleRecoveryFailure(ShardRouting shardRouting, boolean sendShardFailure, Exception failure) {
721722
failAndRemoveShard(shardRouting, sendShardFailure, "failed recovery", failure, clusterService.state());
722723
}
723724

@@ -726,7 +727,10 @@ private void failAndRemoveShard(ShardRouting shardRouting, boolean sendShardFail
726727
try {
727728
AllocatedIndex<? extends Shard> indexService = indicesService.indexService(shardRouting.shardId().getIndex());
728729
if (indexService != null) {
729-
indexService.removeShard(shardRouting.shardId().id(), message);
730+
Shard shard = indexService.getShardOrNull(shardRouting.shardId().id());
731+
if (shard != null && shard.routingEntry().isSameAllocation(shardRouting)) {
732+
indexService.removeShard(shardRouting.shardId().id(), message);
733+
}
730734
}
731735
} catch (ShardNotFoundException e) {
732736
// the node got closed on us, ignore it

server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java

+34
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,40 @@ public void testInitializingPrimaryRemovesInitializingReplicaWithSameAID() {
256256

257257
}
258258

259+
public void testRecoveryFailures() {
260+
disableRandomFailures();
261+
String index = "index_" + randomAlphaOfLength(8).toLowerCase(Locale.ROOT);
262+
ClusterState state = ClusterStateCreationUtils.state(index, randomBoolean(),
263+
ShardRoutingState.STARTED, ShardRoutingState.INITIALIZING);
264+
265+
// the initial state which is derived from the newly created cluster state but doesn't contain the index
266+
ClusterState previousState = ClusterState.builder(state)
267+
.metaData(MetaData.builder(state.metaData()).remove(index))
268+
.routingTable(RoutingTable.builder().build())
269+
.build();
270+
271+
// pick a data node to simulate the adding an index cluster state change event on, that has shards assigned to it
272+
final ShardRouting shardRouting = state.routingTable().index(index).shard(0).replicaShards().get(0);
273+
final ShardId shardId = shardRouting.shardId();
274+
DiscoveryNode node = state.nodes().get(shardRouting.currentNodeId());
275+
276+
// simulate the cluster state change on the node
277+
ClusterState localState = adaptClusterStateToLocalNode(state, node);
278+
ClusterState previousLocalState = adaptClusterStateToLocalNode(previousState, node);
279+
IndicesClusterStateService indicesCSSvc = createIndicesClusterStateService(node, RecordingIndicesService::new);
280+
indicesCSSvc.start();
281+
indicesCSSvc.applyClusterState(new ClusterChangedEvent("cluster state change that adds the index", localState, previousLocalState));
282+
283+
assertNotNull(indicesCSSvc.indicesService.getShardOrNull(shardId));
284+
285+
// check that failing unrelated allocation does not remove shard
286+
indicesCSSvc.handleRecoveryFailure(shardRouting.reinitializeReplicaShard(), false, new Exception("dummy"));
287+
assertNotNull(indicesCSSvc.indicesService.getShardOrNull(shardId));
288+
289+
indicesCSSvc.handleRecoveryFailure(shardRouting, false, new Exception("dummy"));
290+
assertNull(indicesCSSvc.indicesService.getShardOrNull(shardId));
291+
}
292+
259293
public ClusterState randomInitialClusterState(Map<DiscoveryNode, IndicesClusterStateService> clusterStateServiceMap,
260294
Supplier<MockIndicesService> indicesServiceSupplier) {
261295
List<DiscoveryNode> allNodes = new ArrayList<>();

0 commit comments

Comments
 (0)