Skip to content

Commit a2ef0e8

Browse files
committed
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 552edd8 commit a2ef0e8

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ public void onRecoveryFailure(RecoveryState state, RecoveryFailedException e, bo
699699
}
700700
}
701701

702-
private synchronized void handleRecoveryFailure(ShardRouting shardRouting, boolean sendShardFailure, Exception failure) {
702+
// package-private for testing
703+
synchronized void handleRecoveryFailure(ShardRouting shardRouting, boolean sendShardFailure, Exception failure) {
703704
failAndRemoveShard(shardRouting, sendShardFailure, "failed recovery", failure, clusterService.state());
704705
}
705706

@@ -708,7 +709,10 @@ private void failAndRemoveShard(ShardRouting shardRouting, boolean sendShardFail
708709
try {
709710
AllocatedIndex<? extends Shard> indexService = indicesService.indexService(shardRouting.shardId().getIndex());
710711
if (indexService != null) {
711-
indexService.removeShard(shardRouting.shardId().id(), message);
712+
Shard shard = indexService.getShardOrNull(shardRouting.shardId().id());
713+
if (shard != null && shard.routingEntry().isSameAllocation(shardRouting)) {
714+
indexService.removeShard(shardRouting.shardId().id(), message);
715+
}
712716
}
713717
} catch (ShardNotFoundException e) {
714718
// the node got closed on us, ignore it

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,40 @@ public void testInitializingPrimaryRemovesInitializingReplicaWithSameAID() {
255255

256256
}
257257

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

0 commit comments

Comments
 (0)