Skip to content
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

Conversation

tlrx
Copy link
Member

@tlrx tlrx commented Nov 7, 2018

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

@tlrx tlrx added >enhancement :Distributed/Distributed A catch all label for anything in the Distributed Area. If you aren't sure, use this one. labels Nov 7, 2018
@tlrx tlrx requested review from bleskes and ywelsch November 7, 2018 09:37
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed

@tlrx tlrx changed the title [RCI] Check blocks while having indexing permit in TransportReplicationAction [RCI] Check blocks while having index shard permit in TransportReplicationAction Nov 7, 2018
@tlrx tlrx mentioned this pull request Nov 7, 2018
50 tasks
@tlrx tlrx requested a review from s1monw November 7, 2018 16:20
Copy link
Contributor

@bleskes bleskes left a 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

@@ -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)) {
Copy link
Contributor

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).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

@tlrx
Copy link
Member Author

tlrx commented Nov 8, 2018

@bleskes I updated the code, let me know what you think.

Copy link
Contributor

@bleskes bleskes left a 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) {
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Member Author

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 5cc18b2

Copy link
Contributor

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() {
Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!

Copy link
Contributor

@bleskes bleskes left a 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).

final IndexMetaData indexMetaData = clusterState.metaData().getIndexSafe(primaryShardReference.routingEntry().index());

final ClusterBlockException blockException = blockExceptions(clusterState, indexMetaData.getIndex().getName());
if (blockException != null) {
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!

tlrx added a commit to tlrx/elasticsearch that referenced this pull request Nov 9, 2018
@tlrx
Copy link
Member Author

tlrx commented Nov 12, 2018

@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)) {
Copy link
Contributor

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).

Copy link
Member Author

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.

@tlrx
Copy link
Member Author

tlrx commented Nov 12, 2018

@bleskes I updated the code again.

Copy link
Contributor

@bleskes bleskes left a 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)));
Copy link
Contributor

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

Copy link
Member Author

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.

@tlrx tlrx force-pushed the check-blocks-while-having-permit-in-transport-replication-action branch from ef4375d to bbaf292 Compare November 13, 2018 14:41
@tlrx tlrx changed the base branch from replicated-closed-indices to master November 13, 2018 15:56
@tlrx tlrx force-pushed the check-blocks-while-having-permit-in-transport-replication-action branch from bbaf292 to ed7591b Compare November 13, 2018 17:06
@tlrx tlrx merged commit 31567ce into elastic:master Nov 14, 2018
@tlrx tlrx deleted the check-blocks-while-having-permit-in-transport-replication-action branch November 14, 2018 08:44
@tlrx
Copy link
Member Author

tlrx commented Nov 14, 2018

Thanks @bleskes !

tlrx added a commit that referenced this pull request Nov 14, 2018
…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
tlrx added a commit that referenced this pull request Nov 16, 2018
tlrx added a commit that referenced this pull request Nov 16, 2018
@tlrx
Copy link
Member Author

tlrx commented Nov 16, 2018

This has been reverted from master in d3d7c01 and 6.x in c70b8ac

jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Nov 17, 2018
* 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
  ...
tlrx added a commit that referenced this pull request Nov 22, 2018
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
tlrx added a commit that referenced this pull request Nov 22, 2018
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
tlrx added a commit that referenced this pull request Nov 22, 2018
… TransportReplicationAction (#35332)""

This reverts commit c70b8ac
tlrx added a commit that referenced this pull request Nov 22, 2018
… TransportReplicationAction (#35332)""

This reverts commit d3d7c01
original-brownbear pushed a commit that referenced this pull request Nov 23, 2018
@jimczi jimczi added v7.0.0-beta1 and removed v7.0.0 labels Feb 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed/Distributed A catch all label for anything in the Distributed Area. If you aren't sure, use this one. >enhancement v6.6.0 v7.0.0-beta1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants