-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RCI] Check blocks while having index shard permit in TransportReplicationAction #35332
[RCI] Check blocks while having index shard permit in TransportReplicationAction #35332
Conversation
Pinging @elastic/es-distributed |
833df18
to
95cfc1e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some initial suggestions
...r/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java
Outdated
Show resolved
Hide resolved
@@ -310,6 +337,10 @@ protected void doRun() throws Exception { | |||
@Override | |||
public void onResponse(PrimaryShardReference primaryShardReference) { | |||
try { | |||
final ClusterState clusterState = clusterService.state(); | |||
if (handleBlockExceptions(clusterState, request, this::handleBlockException)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleBlockException re-resolves the index. I think we should always use the PrimaryShardReference here to make sure we always use the right one (with uuid and all).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
95cfc1e
to
c2a3ec2
Compare
@bleskes I updated the code, let me know what you think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking great. Left more comments
@@ -356,6 +382,11 @@ public void handleException(TransportException exp) { | |||
} | |||
} | |||
|
|||
private void handleBlockException(final ClusterBlockException blockException) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need this method? can't we just inline it in the two places we call it?
} | ||
return false; | ||
} | ||
|
||
private void handleBlockException(ClusterBlockException blockException) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
having two methods whos name differ with just an s is tricky. Can you maybe come up with something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do something very simple; I pushed 5cc18b25908f27c1ca8d3a3dfc00f49e6311d44c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++
@@ -249,6 +250,53 @@ protected ClusterBlockLevel globalBlockLevel() { | |||
assertListenerThrows("should fail with an IndexNotFoundException when no blocks checked", listener, IndexNotFoundException.class); | |||
} | |||
|
|||
public void testBlocksInPrimaryAction() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's important to test that there is a happens before relationship between acquiring all permits and checking/adding the block and check the block on each individual operation. Maybe add a test that acquires all the permits concurrently enables another operation and then adds a block? The test than checks that the operation picks up on the block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a totally valid test but I don't think I can write this test for now because the "all permit acquisition" logic is not exposed yet in IndexShard and TransportReplicationAction and also because the current tests in this class rely on a mocked IndexShard with no real IndexShardOperationPermits (but a simple atomic counter instead).
Once this PR is merged, I'd like to open a follow up PR which exposes the asyncBlockOperations(ActionListener<Releasable>)
added in #34902 in both IndexShard and TransportReplicationAction. It will help to write the test you're thinking of while using a non mocked IndexShard instance. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting close. I think I found another edge case (if I'm correct, we need to sharpen our testing).
...r/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java
Outdated
Show resolved
Hide resolved
final IndexMetaData indexMetaData = clusterState.metaData().getIndexSafe(primaryShardReference.routingEntry().index()); | ||
|
||
final ClusterBlockException blockException = blockExceptions(clusterState, indexMetaData.getIndex().getName()); | ||
if (blockException != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this doesn't go well if the cluster block is retryable (i.e., we don't retry). How about dealing with this in retryPrimaryException
and that means we can also remove the special handling in the reroute phase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at this before submitting this pull request and it looked good to me...
The ClusterBlockException
is thrown and caught by the surrounding try-catch block which calls the AsyncPrimaryAction.onFailure()
which sends back the exception to where it was executed ie the ReroutePhase.performAction()
which already takes care in handleException()
to retry the request if the exception is retryable.
I agree that we could remove the special handling in the reroute phase and only let the AsyncPrimaryAction to check for blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked via another channel and Boaz's suggestion makes perfect sense. In fact I thought that what was suggested was already implemented like this, but it wasn't.
@@ -249,6 +250,53 @@ protected ClusterBlockLevel globalBlockLevel() { | |||
assertListenerThrows("should fail with an IndexNotFoundException when no blocks checked", listener, IndexNotFoundException.class); | |||
} | |||
|
|||
public void testBlocksInPrimaryAction() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good!
@bleskes I updated the code and also adapted reroute phase tests, can you have another look please? Thanks |
@@ -696,12 +735,9 @@ public void onFailure(Exception e) { | |||
protected void doRun() { | |||
setPhase(task, "routing"); | |||
final ClusterState state = observer.setAndGetObservedState(); | |||
if (handleBlockExceptions(state)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we still want to shortcut on blocks here rather than wait for the primary to acquired a permit? Otherwise we just end up sending all requests to the primary rather than terminating them on the coordinating node (and also make them acquire a permit).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course. I've been too quick on this.
@bleskes I updated the code again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I left one comment about testing. No need for another round.
} | ||
{ | ||
setState(clusterService, | ||
ClusterState.builder(clusterService.state()).blocks(ClusterBlocks.builder().addGlobalBlock(retryableBlock))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to test index level blocks too here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I randomized the test so that is checks global or index level blocks.
ef4375d
to
bbaf292
Compare
bbaf292
to
ed7591b
Compare
Thanks @bleskes ! |
…ationAction (#35332) Today, the TransportReplicationAction checks the global level blocks and the index level blocks before routing the operation to the primary, in the ReroutePhase, and it happens at the very beginning of the transport replication action execution. For the upcoming rework of the Close Index API and in order to deal with primary relocation, we'll need to also check for blocks before executing the operation on the primary (while holding a permit) but before routing to the new primary. This pull request change the AsyncPrimaryAction so that it checks for replication action's blocks before executing the operation locally or before routing the primary action to the newly primary shard. The check is done while holding a PrimaryShardReference. Related to #33888
* master: (59 commits) SQL: Move internals from Joda to java.time (elastic#35649) Add HLRC docs for Get Lifecycle Policy (elastic#35612) Align RolloverStep's name with other step names (elastic#35655) Watcher: Use joda method to get local TZ (elastic#35608) Fix line length for org.elasticsearch.action.* files (elastic#35607) Remove use of AbstractComponent in server (elastic#35444) Deprecate types in count and msearch. (elastic#35421) Refactor an ambigious TermVectorsRequest constructor. (elastic#35614) [Scripting] Use Number as a return value for BucketAggregationScript (elastic#35653) Removes AbstractComponent from several classes (elastic#35566) [DOCS] Add beta warning to ILM pages. (elastic#35571) Deprecate types in validate query requests. (elastic#35575) Unmute BuildExamplePluginsIT Revert "AwaitsFix the RecoveryIT suite - see elastic#35597" Revert "[RCI] Check blocks while having index shard permit in TransportReplicationAction (elastic#35332)" Remove remaining line length violations for o.e.action.admin.cluster (elastic#35156) ML: Adjusing BWC version post backport to 6.6 (elastic#35605) [TEST] Replace fields in response with actual values Remove usages of CharSequence in Sets (elastic#35501) AwaitsFix the RecoveryIT suite - see elastic#35597 ...
After #35332 has been merged, we noticed some test failures like #35597 in which one or more replica shards failed to be promoted as primaries because the primary replica re-synchronization never succeed. After some digging it appeared that the execution of the resync action was blocked because of the presence of a global cluster block in the cluster state (in this case, the "no master" block), making the resync action to fail when executed on the primary. Until #35332 such failures never happened because the TransportResyncReplicationAction is skipping the reroute phase, the only place where blocks were checked. Now with #35332 blocks are checked during reroute and also during the execution of the transport replication action on the primary. After some internal discussion, we decided that the TransportResyncReplicationAction should never be blocked. This action is part of the replica to primary promotion and makes sure that replicas are in sync and should not be blocked when the cluster state has no master or when the index is read only. This commit changes the TransportResyncReplicationAction to make obvious that it does not honor blocks. It also adds a simple test that fails if the resync action is blocked during the primary action execution. Closes #35597
After #35332 has been merged, we noticed some test failures like #35597 in which one or more replica shards failed to be promoted as primaries because the primary replica re-synchronization never succeed. After some digging it appeared that the execution of the resync action was blocked because of the presence of a global cluster block in the cluster state (in this case, the "no master" block), making the resync action to fail when executed on the primary. Until #35332 such failures never happened because the TransportResyncReplicationAction is skipping the reroute phase, the only place where blocks were checked. Now with #35332 blocks are checked during reroute and also during the execution of the transport replication action on the primary. After some internal discussion, we decided that the TransportResyncReplicationAction should never be blocked. This action is part of the replica to primary promotion and makes sure that replicas are in sync and should not be blocked when the cluster state has no master or when the index is read only. This commit changes the TransportResyncReplicationAction to make obvious that it does not honor blocks. It also adds a simple test that fails if the resync action is blocked during the primary action execution. Closes #35597
… TransportReplicationAction (#35332)"" This reverts commit c70b8ac
Today, the
TransportReplicationAction
checks the global level blocks and the index level blocks before routing the operation to the primary, in theReroutePhase
, and it happens at the very beginning of the transport replication action execution. For the upcoming rework of the Close Index API and in order to deal with primary relocation, we'll need to also check for blocks before executing the operation on the primary (while holding a permit) but before routing to the new primary.This pull request change the
AsyncPrimaryAction
so that it checks for replication action's blocks before executing the operation locally or before routing the primary action to the newly primary shard. The check is done while holding aPrimaryShardReference
.Related to #33888