-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-9441: remove prepareClose() to simplify task management #8833
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
139bceb
1be5d92
54f24e4
120fb8d
141c4d0
c4b2666
9e98592
329e187
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 |
|---|---|---|
|
|
@@ -17,13 +17,13 @@ | |
| package org.apache.kafka.streams.processor.internals; | ||
|
|
||
| import org.apache.kafka.clients.consumer.ConsumerRecord; | ||
| import org.apache.kafka.clients.consumer.OffsetAndMetadata; | ||
| import org.apache.kafka.common.TopicPartition; | ||
| import org.apache.kafka.common.metrics.Sensor; | ||
| import org.apache.kafka.common.utils.LogContext; | ||
| import org.apache.kafka.streams.StreamsConfig; | ||
| import org.apache.kafka.streams.StreamsMetrics; | ||
| import org.apache.kafka.streams.errors.StreamsException; | ||
| import org.apache.kafka.streams.errors.TaskMigratedException; | ||
| import org.apache.kafka.streams.processor.TaskId; | ||
| import org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl; | ||
| import org.apache.kafka.streams.processor.internals.metrics.ThreadMetrics; | ||
|
|
@@ -99,6 +99,8 @@ public void initializeIfNeeded() { | |
| processorContext.initialize(); | ||
|
|
||
| log.info("Initialized"); | ||
| } else if (state() == State.RESTORING) { | ||
| throw new IllegalStateException("Illegal state " + state() + " while initializing standby task " + id); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -107,18 +109,21 @@ public void completeRestoration() { | |
| throw new IllegalStateException("Standby task " + id + " should never be completing restoration"); | ||
| } | ||
|
|
||
| @Override | ||
| public void prepareSuspend() { | ||
| log.trace("No-op prepareSuspend with state {}", state()); | ||
| } | ||
|
|
||
| @Override | ||
| public void suspend() { | ||
| log.trace("No-op suspend with state {}", state()); | ||
| if (state() == State.RUNNING) { | ||
| transitionTo(State.SUSPENDED); | ||
| } else if (state() == State.RESTORING) { | ||
| throw new IllegalStateException("Illegal state " + state() + " while suspending standby task " + id); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void resume() { | ||
| if (state() == State.RESTORING) { | ||
| throw new IllegalStateException("Illegal state " + state() + " while resuming standby task " + id); | ||
| } | ||
| log.trace("No-op resume with state {}", state()); | ||
| } | ||
|
|
||
|
|
@@ -129,18 +134,20 @@ public void resume() { | |
| * @throws StreamsException fatal error, should close the thread | ||
| */ | ||
| @Override | ||
| public void prepareCommit() { | ||
| if (state() == State.RUNNING) { | ||
| public Map<TopicPartition, OffsetAndMetadata> prepareCommit() { | ||
| if (state() == State.RUNNING || state() == State.SUSPENDED) { | ||
| stateMgr.flush(); | ||
| log.info("Task ready for committing"); | ||
| } else { | ||
| throw new IllegalStateException("Illegal state " + state() + " while preparing standby task " + id + " for committing "); | ||
| } | ||
|
|
||
| return Collections.emptyMap(); | ||
| } | ||
|
|
||
| @Override | ||
| public void postCommit() { | ||
| if (state() == State.RUNNING) { | ||
| if (state() == State.RUNNING || state() == State.SUSPENDED) { | ||
| // since there's no written offsets we can checkpoint with empty map, | ||
| // and the state current offset would be used to checkpoint | ||
| stateMgr.checkpoint(Collections.emptyMap()); | ||
|
|
@@ -151,73 +158,24 @@ public void postCommit() { | |
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void prepareCloseClean() { | ||
| prepareClose(true); | ||
|
|
||
| log.info("Prepared clean close"); | ||
| } | ||
|
|
||
| @Override | ||
| public void prepareCloseDirty() { | ||
| prepareClose(false); | ||
|
|
||
| log.info("Prepared dirty close"); | ||
| } | ||
|
|
||
| /** | ||
| * 1. commit if we are running and clean close; | ||
| * 2. close the state manager. | ||
| * | ||
| * @throws TaskMigratedException all the task has been migrated | ||
| * @throws StreamsException fatal error, should close the thread | ||
| */ | ||
| private void prepareClose(final boolean clean) { | ||
|
Member
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. This logic is now followed in |
||
| switch (state()) { | ||
| case CREATED: | ||
| case CLOSED: | ||
| log.trace("Skip prepare closing since state is {}", state()); | ||
| return; | ||
|
|
||
| case RUNNING: | ||
| if (clean) { | ||
| stateMgr.flush(); | ||
| } | ||
|
|
||
| break; | ||
|
|
||
| case RESTORING: | ||
| case SUSPENDED: | ||
| throw new IllegalStateException("Illegal state " + state() + " while closing standby task " + id); | ||
|
|
||
| default: | ||
| throw new IllegalStateException("Unknown state " + state() + " while closing standby task " + id); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void closeClean() { | ||
| close(true); | ||
|
|
||
| log.info("Closed clean"); | ||
| } | ||
|
|
||
| @Override | ||
| public void closeDirty() { | ||
| close(false); | ||
|
|
||
| log.info("Closed dirty"); | ||
| } | ||
|
|
||
| @Override | ||
| public void closeAndRecycleState() { | ||
| prepareClose(true); | ||
| suspend(); | ||
| prepareCommit(); | ||
|
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. Why call
Member
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. Both now do what
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. I just mean, why not inline that? I'm just imagining coming back to this code in a few months and wondering why we need to suspend a task before recycling, or why we call
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. Nevermind, I see that's the pattern we follow everywhere else |
||
|
|
||
| if (state() == State.CREATED || state() == State.RUNNING) { | ||
| // since there's no written offsets we can checkpoint with empty map, | ||
| // and the state current offset would be used to checkpoint | ||
| stateMgr.checkpoint(Collections.emptyMap()); | ||
| offsetSnapshotSinceLastCommit = new HashMap<>(stateMgr.changelogOffsets()); | ||
| if (state() == State.CREATED || state() == State.SUSPENDED) { | ||
|
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. The comment below is not accurate anymore: we do not write checkpoint during recycle actually. EDIT: actually, the updated offsetSnapshotSinceLastCommit seems not used since after this function we would create a new StreamTask and in between we do not check if commitNeeded at all. Could we remove line 175 then? |
||
| stateMgr.recycle(); | ||
| } else { | ||
| throw new IllegalStateException("Illegal state " + state() + " while closing standby task " + id); | ||
|
|
@@ -232,13 +190,7 @@ public void closeAndRecycleState() { | |
| private void close(final boolean clean) { | ||
| switch (state()) { | ||
| case CREATED: | ||
| case RUNNING: | ||
| if (clean) { | ||
| // since there's no written offsets we can checkpoint with empty map, | ||
| // and the state current offset would be used to checkpoint | ||
| stateMgr.checkpoint(Collections.emptyMap()); | ||
| offsetSnapshotSinceLastCommit = new HashMap<>(stateMgr.changelogOffsets()); | ||
| } | ||
| case SUSPENDED: | ||
|
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. Ditto for line 195: we do not need to update the snapshot since we are closing the task already. |
||
| executeAndMaybeSwallow( | ||
| clean, | ||
| () -> StateManagerUtil.closeStateManager( | ||
|
|
@@ -260,8 +212,8 @@ private void close(final boolean clean) { | |
| log.trace("Skip closing since state is {}", state()); | ||
| return; | ||
|
|
||
| case RESTORING: | ||
| case SUSPENDED: | ||
| case RESTORING: // a StandbyTask is never in RESTORING state | ||
| case RUNNING: | ||
| throw new IllegalStateException("Illegal state " + state() + " while closing standby task " + id); | ||
|
|
||
| default: | ||
|
|
||
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 want to throw here if the current state is
CLOSED?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.
My proposal is, to keep the methods idempotent.
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.
nit: we can throw illegal-state if the state() == RESTORING since it should never happen.
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.
Why not just make
suspenda no-op if the task is RESTORING? That seems more in line with how we handle things elsewhereThere 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.
For
StandbyTasks, we never restore. When we do the state transition, we away make two transitions directly after each other fromCREATE -> RESTORING -> RUNNING-- thus, stateRESTORINGis an invalid state for standby tasks.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.
Right, by "check for RESTORING" I meant "throw an exception if state is restoring". It seems odd to check for RESTORING during
suspendbut not in any other StandbyTask method. Either it can never be in RESTORING and we are completely sure of that, and shouldn't check for RESTORING, or we should always check whether it's RESTORING and not just duringsuspend(eg also inpostCommit)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.
Just to clarify, I would support doing the former, ie don't check whether it's RESTORING here at all. But we should at least be consistent
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.
Ah, I see. I guess we check it in almost all method though. (we just missed
initializeIfNeededandresume()-- will add it there).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 do this check implicitly for some case already, ie:
ie, only
RUNNINGis a valid state, and all others are invalid. Thus, it seems to be consistent if we add those checks elsewhere (or, what would be odd, excludeRESTORINGfrom those implicit checks).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.
Cool 👍