-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-8620: fix NPE due to race condition during shutdown while rebalancing #7021
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
Changes from all commits
c6a5997
1337616
d5b7b5c
b2326b4
58b7ce3
9c13d42
f1b1002
eb490a9
a99606b
5c6ec62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -190,14 +190,20 @@ State setState(final State newState) { | |
| oldState = state; | ||
|
|
||
| if (state == State.PENDING_SHUTDOWN && newState != State.DEAD) { | ||
| log.debug("Ignoring request to transit from PENDING_SHUTDOWN to {}: " + | ||
| "only DEAD state is a valid next state", newState); | ||
| // when the state is already in PENDING_SHUTDOWN, all other transitions will be | ||
| // refused but we do not throw exception here | ||
| return null; | ||
| } else if (state == State.DEAD) { | ||
| log.debug("Ignoring request to transit from DEAD to {}: " + | ||
| "no valid next state after DEAD", newState); | ||
|
abbccdda marked this conversation as resolved.
|
||
| // when the state is already in NOT_RUNNING, all its transitions | ||
| // will be refused but we do not throw exception here | ||
| return null; | ||
| } else if (state == State.PARTITIONS_REVOKED && newState == State.PARTITIONS_REVOKED) { | ||
| log.debug("Ignoring request to transit from PARTITIONS_REVOKED to PARTITIONS_REVOKED: " + | ||
| "self transition is not allowed"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking about this, I am wondering if we should just change the FSM to allow this transition and simplify the code here? \cc @guozhangwang
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this when tightening the FSM before but the unit tests reminds me of one thing: our current contract is that we only transit to PARTITIONS_REVOKED when calling onPartitionsRevoked, which is called only once at the beginning of the rebalance today, so keeping it strict is better just in case we have incorrect partial rebalance procedure. With KIP-429 this may be violated so we need to revisit our FSM once Streams adopt cooperative protocols. cc @ableegoldman who's working on this. |
||
| // when the state is already in PARTITIONS_REVOKED, its transition to itself will be | ||
| // refused but we do not throw exception here | ||
| return null; | ||
|
|
@@ -268,17 +274,23 @@ public void onPartitionsAssigned(final Collection<TopicPartition> assignment) { | |
| final long start = time.milliseconds(); | ||
| try { | ||
| if (streamThread.setState(State.PARTITIONS_ASSIGNED) == null) { | ||
| return; | ||
|
abbccdda marked this conversation as resolved.
|
||
| } | ||
| if (streamThread.assignmentErrorCode.get() == StreamsPartitionAssignor.Error.NONE.code()) { | ||
| log.debug( | ||
| "Skipping task creation in rebalance because we are already in {} state.", | ||
| streamThread.state() | ||
| ); | ||
| } else if (streamThread.assignmentErrorCode.get() != StreamsPartitionAssignor.Error.NONE.code()) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this a rather brittle fix? How does this code guarantee that after
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense |
||
| log.debug( | ||
| "Encountered assignment error during partition assignment: {}. Skipping task initialization", | ||
| streamThread.assignmentErrorCode | ||
| ); | ||
| } else { | ||
| log.debug("Creating tasks based on assignment."); | ||
| taskManager.createTasks(assignment); | ||
| } | ||
| } catch (final Throwable t) { | ||
| log.error( | ||
| "Error caught during partition assignment, " + | ||
| "will abort the current process and re-throw at the end of rebalance: {}", | ||
| t | ||
| ); | ||
| "will abort the current process and re-throw at the end of rebalance", t); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. |
||
| streamThread.setRebalanceException(t); | ||
| } finally { | ||
| log.info("partition assignment took {} ms.\n" + | ||
|
|
@@ -809,7 +821,6 @@ private void enforceRebalance() { | |
| // Visible for testing | ||
| void runOnce() { | ||
| final ConsumerRecords<byte[], byte[]> records; | ||
|
|
||
| now = time.milliseconds(); | ||
|
|
||
| if (state == State.PARTITIONS_ASSIGNED) { | ||
|
|
@@ -830,6 +841,15 @@ void runOnce() { | |
| throw new StreamsException(logPrefix + "Unexpected state " + state + " during normal iteration"); | ||
| } | ||
|
|
||
| // Shutdown hook could potentially be triggered and transit the thread state to PENDING_SHUTDOWN during #pollRequests(). | ||
| // The task manager internal states could be uninitialized if the state transition happens during #onPartitionsAssigned(). | ||
| // Should only proceed when the thread is still running after #pollRequests(), because no external state mutation | ||
| // could affect the task manager state beyond this point within #runOnce(). | ||
| if (!isRunning()) { | ||
| log.debug("State already transits to {}, skipping the run once call after poll request", state); | ||
| return; | ||
| } | ||
|
|
||
| final long pollLatency = advanceNowAndComputeLatency(); | ||
|
|
||
| if (records != null && !records.isEmpty()) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.