Skip to content

[#1433] fix(server): Race conditions with ShuffleServer state - #1434

Merged
xianjingfeng merged 3 commits into
apache:masterfrom
EnricoMi:shuffle-server-state-race-conditions
Jan 12, 2024
Merged

[#1433] fix(server): Race conditions with ShuffleServer state#1434
xianjingfeng merged 3 commits into
apache:masterfrom
EnricoMi:shuffle-server-state-race-conditions

Conversation

@EnricoMi

@EnricoMi EnricoMi commented Jan 10, 2024

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Make state transitions in ShuffleServer robust.

Why are the changes needed?

Avoids the following race conditions / undesired state transitions:

  • ShuffleServer is decommissioning, then getting unhealthy, which stops decommissioning
  • ShuffleServer is in any non-active state, getting unhealthy, turns active when healthy

Fix: #1433

Does this PR introduce any user-facing change?

No.

How was this patch tested?

Untested.

Avoids the following race conditions / undesired state changes:
- ShuffleServer is decommissioning, then getting unhealthy, which stops decommissioning
- ShuffleServer is in any non-active state, getting unhealthy, turns active when healthy
return serverStatus.get();
}

public void setServerStatus(ServerStatus serverStatus) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is likely to introduce race-conditions.

LOG.info("Shuffle Server is decommissioning. Nothing needs to be done.");
return;
}
if (!ServerStatus.ACTIVE.equals(serverStatus.get())) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

State could change between serverStatus.get() and serverStatus.set(ServerStatus.DECOMMISSIONING).

while (isDecommissioning()) {
remainApplicationNum = shuffleTaskManager.getAppIds().size();
if (remainApplicationNum == 0) {
serverStatus.set(ServerStatus.DECOMMISSIONED);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

State could change between isDecommissioning() and serverStatus.set(ServerStatus.DECOMMISSIONED).

serverStatus.set(ServerStatus.ACTIVE);
return;
}
serverStatus.set(ServerStatus.ACTIVE);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

State could change between isDecommissioning() and serverStatus.set(ServerStatus.ACTIVE).

Comment on lines 486 to +488
public boolean isDecommissioning() {
return ServerStatus.DECOMMISSIONING.equals(serverStatus.get())
|| ServerStatus.DECOMMISSIONED.equals(serverStatus.get());
return ServerStatus.DECOMMISSIONING.equals(serverStatus.get());
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

better reflects the name of the method, no need to bind the while condition in waitDecommissionFinish on DECOMMISSIONED state.

for (Checker checker : checkers) {
if (!checker.checkIsHealthy()) {
serverStatus.set(ServerStatus.UNHEALTHY);
serverStatus.compareAndSet(ServerStatus.ACTIVE, ServerStatus.UNHEALTHY);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The HealthCheck should only transition from ACTIVE to UNHEALTHY and back. Otherwise, it could stop the decommissioning phase.

@roryqi
roryqi requested a review from xianjingfeng January 10, 2024 11:35
@EnricoMi EnricoMi changed the title [#1433] fix: Race conditions with ShuffleServer state [#1433] fix(server): Race conditions with ShuffleServer state Jan 10, 2024
boolean wasDecommissioning =
serverStatus.compareAndSet(ServerStatus.DECOMMISSIONING, ServerStatus.DECOMMISSIONED);
if (!wasDecommissioning) {
break;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should print some logs here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We are leaving the lop here and there will be a log when exiting this method:

LOG.info("Decommission exiting, remaining {} applications not finished.", remainApplicationNum);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remainApplicationNum is 0, so this line will not be reached. right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What about

Suggested change
break;
LOG.info("Ready to decommission but decommissioning state left unexpectedly.");
break;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about

It is ok for me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You are right, we won't see that existing log message.

I have moved the "All applications finished. Current status is " + serverStatus message up so it is printed in any case logging the current stats, and added the suggested additional log line.

@xianjingfeng xianjingfeng left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Thanks @EnricoMi

@EnricoMi

Copy link
Copy Markdown
Contributor Author

I presume Java 8 support is still a requirement?

@xianjingfeng

Copy link
Copy Markdown
Member

I presume Java 8 support is still a requirement?

Sorry. I don't get your point.

@EnricoMi

Copy link
Copy Markdown
Contributor Author

I presume Java 8 support is still a requirement?

Sorry. I don't get your point.

In some places it would be preferably to use AtomicReference<ServerStatus>.compareAndExchange rather than AtomicReference<ServerStatus>.compareAndSet, but that flags up as Usage of API documented as @since 1.9+. If Uniffle is required to support Java 8, then using compareAndExchange is prohibited.

@xianjingfeng

Copy link
Copy Markdown
Member

I presume Java 8 support is still a requirement?

Sorry. I don't get your point.

In some places it would be preferably to use AtomicReference<ServerStatus>.compareAndExchange rather than AtomicReference<ServerStatus>.compareAndSet, but that flags up as Usage of API documented as @since 1.9+. If Uniffle is required to support Java 8, then using compareAndExchange is prohibited.

Get it. We still need to support Java 8. Please see #674

@xianjingfeng
xianjingfeng merged commit 0630dc5 into apache:master Jan 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Race conditions with ShuffleServer state

2 participants