-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-9113: Extract Producer to RecordCollector #7846
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
067cf6e
76b2731
ff672ef
7c1f06e
c47a66c
9d9c7da
2f67f35
38be1c1
d4290d4
2aa6673
edd0265
578d34c
412d69d
b94579f
7e632fc
347ea26
7d1cef5
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 |
|---|---|---|
|
|
@@ -1149,7 +1149,8 @@ public List<PartitionInfo> partitionsFor(String topic) { | |
| * block forever.</strong> | ||
| * <p> | ||
| * | ||
| * @throws InterruptException If the thread is interrupted while blocked | ||
| * @throws InterruptException If the thread is interrupted while blocked. | ||
| * @throws KafkaException If a Kafka related error occurs that does not belong to the public API exceptions. | ||
|
Contributor
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. Just a reminder of the possible exception thrown.
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. Makes sense, but at the same time, it's a little confusing because
Contributor
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. Yes that's indeed the intention, to treat interrupt exception specially. |
||
| */ | ||
| @Override | ||
| public void close() { | ||
|
|
@@ -1169,7 +1170,8 @@ public void close() { | |
| * | ||
| * @param timeout The maximum time to wait for producer to complete any pending requests. The value should be | ||
| * non-negative. Specifying a timeout of zero means do not wait for pending send requests to complete. | ||
| * @throws InterruptException If the thread is interrupted while blocked | ||
| * @throws InterruptException If the thread is interrupted while blocked. | ||
| * @throws KafkaException If a Kafka related error occurs that does not belong to the public API exceptions. | ||
| * @throws IllegalArgumentException If the <code>timeout</code> is negative. | ||
| * | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
|
|
||
|
|
||
| import org.apache.kafka.common.TopicPartition; | ||
| import org.apache.kafka.streams.processor.internals.Task; | ||
| import org.apache.kafka.streams.processor.TaskId; | ||
|
|
||
| /** | ||
| * Indicates that a task got migrated to another thread. | ||
|
|
@@ -28,43 +28,41 @@ public class TaskMigratedException extends StreamsException { | |
|
|
||
| private final static long serialVersionUID = 1L; | ||
|
|
||
| private final Task task; | ||
| private final TaskId taskId; | ||
|
Contributor
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 is a minor change: since the exception maybe thrown BEFORE the task is created now I've decided to just encode the task-id, which means we cannot print the full task topology but I think it is not a huge step backwards -- actually I think it is cleaner on log4j to not print the full topology after a second thought.
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. +1 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. prop: +1 for the motivation, however could we make this a separate minor PR to reduce the current PR size?
Contributor
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. @abbccdda My current proposal is to move all the PRs into a separate |
||
|
|
||
| // this is for unit test only | ||
| public TaskMigratedException() { | ||
| super("A task has been migrated unexpectedly", null); | ||
|
|
||
| this.task = null; | ||
| } | ||
|
|
||
| public TaskMigratedException(final Task task, | ||
| public TaskMigratedException(final TaskId taskId, | ||
| final TopicPartition topicPartition, | ||
| final long endOffset, | ||
| final long pos) { | ||
| super(String.format("Log end offset of %s should not change while restoring: old end offset %d, current offset %d", | ||
| topicPartition, | ||
| endOffset, | ||
| pos), | ||
| null); | ||
|
|
||
| this.task = task; | ||
| this(taskId, String.format("Log end offset of %s should not change while restoring: old end offset %d, current offset %d", | ||
| topicPartition, | ||
| endOffset, | ||
| pos), null); | ||
| } | ||
|
|
||
| public TaskMigratedException(final Task task) { | ||
| super(String.format("Task %s is unexpectedly closed during processing", task.id()), null); | ||
|
|
||
| this.task = task; | ||
| public TaskMigratedException(final TaskId taskId) { | ||
| this(taskId, String.format("Task %s is unexpectedly closed during processing", taskId), null); | ||
| } | ||
|
|
||
| public TaskMigratedException(final Task task, | ||
| public TaskMigratedException(final TaskId taskId, | ||
| final Throwable throwable) { | ||
| super(String.format("Client request for task %s has been fenced due to a rebalance", task.id()), throwable); | ||
| this(taskId, String.format("Client request for task %s has been fenced due to a rebalance", taskId), throwable); | ||
| } | ||
|
|
||
| this.task = task; | ||
| public TaskMigratedException(final TaskId taskId, | ||
| final String message, | ||
| final Throwable throwable) { | ||
| super(message, throwable); | ||
| this.taskId = taskId; | ||
| } | ||
|
|
||
| public Task migratedTask() { | ||
| return task; | ||
| public TaskId migratedTaskId() { | ||
| return taskId; | ||
| } | ||
|
|
||
| // this is for unit test only | ||
| // TODO K9113: remove this after we've refactored AssignedTasksTests | ||
| public TaskMigratedException() { | ||
|
vvcephei marked this conversation as resolved.
|
||
| this(null, "A task has been migrated unexpectedly", null); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,6 @@ | |
| import org.apache.kafka.streams.errors.LockException; | ||
| import org.apache.kafka.streams.errors.ProcessorStateException; | ||
| import org.apache.kafka.streams.errors.StreamsException; | ||
| import org.apache.kafka.streams.errors.TaskMigratedException; | ||
| import org.apache.kafka.streams.processor.ProcessorContext; | ||
| import org.apache.kafka.streams.processor.StateStore; | ||
| import org.apache.kafka.streams.processor.TaskId; | ||
|
|
@@ -43,7 +42,6 @@ public abstract class AbstractTask implements Task { | |
| final Set<TopicPartition> partitions; | ||
| final Consumer<byte[], byte[]> consumer; | ||
| final String logPrefix; | ||
| final boolean eosEnabled; | ||
| final Logger log; | ||
| final LogContext logContext; | ||
| final StateDirectory stateDirectory; | ||
|
|
@@ -61,37 +59,23 @@ public abstract class AbstractTask implements Task { | |
| final Set<TopicPartition> partitions, | ||
| final ProcessorTopology topology, | ||
| final Consumer<byte[], byte[]> consumer, | ||
| final ChangelogReader changelogReader, | ||
| final boolean isStandby, | ||
| final ProcessorStateManager stateMgr, | ||
| final StateDirectory stateDirectory, | ||
| final StreamsConfig config) { | ||
| this.id = id; | ||
| this.applicationId = config.getString(StreamsConfig.APPLICATION_ID_CONFIG); | ||
| this.partitions = new HashSet<>(partitions); | ||
| this.topology = topology; | ||
| this.consumer = consumer; | ||
| this.eosEnabled = StreamsConfig.EXACTLY_ONCE.equals(config.getString(StreamsConfig.PROCESSING_GUARANTEE_CONFIG)); | ||
| this.stateDirectory = stateDirectory; | ||
|
|
||
| this.stateMgr = stateMgr; | ||
|
|
||
| final String threadIdPrefix = String.format("stream-thread [%s] ", Thread.currentThread().getName()); | ||
| this.logPrefix = threadIdPrefix + String.format("%s [%s] ", isStandby ? "standby-task" : "task", id); | ||
| this.logContext = new LogContext(logPrefix); | ||
| this.log = logContext.logger(getClass()); | ||
|
|
||
| // create the processor state manager | ||
| try { | ||
| stateMgr = new ProcessorStateManager( | ||
| id, | ||
| partitions, | ||
| isStandby, | ||
| stateDirectory, | ||
| topology.storeToChangelogTopic(), | ||
| changelogReader, | ||
| eosEnabled, | ||
| logContext); | ||
| } catch (final IOException e) { | ||
| throw new ProcessorStateException(String.format("%sError while creating the state manager", logPrefix), e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -135,10 +119,6 @@ public String toString() { | |
| return toString(""); | ||
| } | ||
|
|
||
| public boolean isEosEnabled() { | ||
| return eosEnabled; | ||
| } | ||
|
|
||
| /** | ||
| * Produces a string representation containing useful information about a Task starting with the given indent. | ||
| * This is useful in debugging scenarios. | ||
|
|
@@ -169,19 +149,6 @@ public String toString(final String indent) { | |
| return sb.toString(); | ||
| } | ||
|
|
||
| /** | ||
| * Flush all state stores owned by this task | ||
| */ | ||
| void flushState() { | ||
| try { | ||
| stateMgr.flush(); | ||
|
Contributor
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. Push the stateMgr.flush to individual functions of tasks. |
||
| } catch (final ProcessorStateException e) { | ||
| if (e.getCause() instanceof RecoverableClientException) { | ||
| throw new TaskMigratedException(this, e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Package-private for testing only | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,7 +76,7 @@ List<TopicPartition> closeRevokedStandbyTasks(final Map<TaskId, Set<TopicPartiti | |
| } | ||
|
|
||
| try { | ||
| task.close(true, false); | ||
| task.close(true); | ||
|
Contributor
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 is another cleanup: |
||
| } catch (final RuntimeException e) { | ||
| log.error("Closing the standby task {} failed due to the following error:", task.id(), e); | ||
| } finally { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,9 +75,9 @@ boolean allTasksRunning() { | |
| @Override | ||
| void closeTask(final StreamTask task, final boolean clean) { | ||
| if (suspended.containsKey(task.id())) { | ||
| task.closeSuspended(clean, null); | ||
| task.closeSuspended(clean); | ||
| } else { | ||
| task.close(clean, false); | ||
| task.close(clean); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -155,7 +155,7 @@ private RuntimeException suspendRunningTasks(final Set<TaskId> runningTasksToSus | |
| firstException.compareAndSet(null, e); | ||
| try { | ||
| prevActiveTasks.remove(id); | ||
| task.close(false, false); | ||
| task.close(false); | ||
| } catch (final RuntimeException f) { | ||
| log.error( | ||
| "After suspending failed, closing the same stream task {} failed again due to the following error:", | ||
|
|
@@ -179,7 +179,7 @@ private RuntimeException closeNonRunningTasks(final Set<TaskId> nonRunningTasksT | |
|
|
||
| for (final TaskId id : nonRunningTasksToClose) { | ||
| final StreamTask task = created.get(id); | ||
| firstException.compareAndSet(null, closeNonRunning(false, task, closedTaskChangelogs)); | ||
| firstException.compareAndSet(null, closeNonRunning(true, task, closedTaskChangelogs)); | ||
| } | ||
|
|
||
| return firstException.get(); | ||
|
|
@@ -192,19 +192,17 @@ RuntimeException closeRestoringTasks(final Set<TaskId> restoringTasksToClose, | |
|
|
||
| for (final TaskId id : restoringTasksToClose) { | ||
| final StreamTask task = restoring.get(id); | ||
| firstException.compareAndSet(null, closeRestoring(false, task, closedTaskChangelogs)); | ||
| firstException.compareAndSet(null, closeRestoring(true, task, closedTaskChangelogs)); | ||
| } | ||
|
|
||
| return firstException.get(); | ||
| } | ||
|
|
||
| private RuntimeException closeRunning(final boolean isZombie, | ||
| final StreamTask task) { | ||
| private RuntimeException closeRunning(final boolean clean, final StreamTask task) { | ||
| removeTaskFromAllStateMaps(task, Collections.emptyMap()); | ||
|
|
||
| try { | ||
| final boolean clean = !isZombie; | ||
| task.close(clean, isZombie); | ||
| task.close(clean); | ||
| } catch (final RuntimeException e) { | ||
| log.error("Failed to close the stream task {}", task.id(), e); | ||
| return e; | ||
|
|
@@ -213,14 +211,14 @@ private RuntimeException closeRunning(final boolean isZombie, | |
| return null; | ||
| } | ||
|
|
||
| private RuntimeException closeNonRunning(final boolean isZombie, | ||
| private RuntimeException closeNonRunning(final boolean clean, | ||
| final StreamTask task, | ||
| final List<TopicPartition> closedTaskChangelogs) { | ||
| removeTaskFromAllStateMaps(task, Collections.emptyMap()); | ||
| closedTaskChangelogs.addAll(task.changelogPartitions()); | ||
|
|
||
| try { | ||
| task.close(false, isZombie); | ||
| task.close(clean); | ||
| } catch (final RuntimeException e) { | ||
| log.error("Failed to close the stream task {}", task.id(), e); | ||
| return e; | ||
|
|
@@ -230,14 +228,13 @@ private RuntimeException closeNonRunning(final boolean isZombie, | |
| } | ||
|
|
||
| // Since a restoring task has not had its topology initialized yet, we need only close the state manager | ||
| private RuntimeException closeRestoring(final boolean isZombie, | ||
| private RuntimeException closeRestoring(final boolean clean, | ||
| final StreamTask task, | ||
| final List<TopicPartition> closedTaskChangelogs) { | ||
| removeTaskFromAllStateMaps(task, Collections.emptyMap()); | ||
| closedTaskChangelogs.addAll(task.changelogPartitions()); | ||
|
|
||
| try { | ||
| final boolean clean = !isZombie; | ||
| task.closeStateManager(clean); | ||
| } catch (final RuntimeException e) { | ||
| log.error("Failed to close the restoring stream task {} due to the following error:", task.id(), e); | ||
|
|
@@ -247,13 +244,11 @@ private RuntimeException closeRestoring(final boolean isZombie, | |
| return null; | ||
| } | ||
|
|
||
| private RuntimeException closeSuspended(final boolean isZombie, | ||
| final StreamTask task) { | ||
| private RuntimeException closeSuspended(final boolean clean, final StreamTask task) { | ||
|
Contributor
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. My thoughts around those function cleanup are: after we've added the state into Task, then we would not have such functions any more but just a single
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. SGTM. I agree it makes sense to tackle that in a separate PR. |
||
| removeTaskFromAllStateMaps(task, Collections.emptyMap()); | ||
|
|
||
| try { | ||
| final boolean clean = !isZombie; | ||
| task.closeSuspended(clean, null); | ||
| task.closeSuspended(clean); | ||
| } catch (final RuntimeException e) { | ||
| log.error("Failed to close the suspended stream task {} due to the following error:", task.id(), e); | ||
| return e; | ||
|
|
@@ -270,7 +265,7 @@ RuntimeException closeNotAssignedSuspendedTasks(final Set<TaskId> revokedTasks) | |
| final StreamTask suspendedTask = suspended.get(revokedTask); | ||
|
|
||
| if (suspendedTask != null) { | ||
| firstException.compareAndSet(null, closeSuspended(false, suspendedTask)); | ||
| firstException.compareAndSet(null, closeSuspended(true, suspendedTask)); | ||
| } else { | ||
| log.debug("Revoked stream task {} could not be found in suspended, may have already been closed", revokedTask); | ||
| } | ||
|
|
@@ -287,16 +282,16 @@ RuntimeException closeAllTasksAsZombies() { | |
| for (final TaskId id : allAssignedTaskIds()) { | ||
| if (running.containsKey(id)) { | ||
| log.debug("Closing the zombie running stream task {}.", id); | ||
| firstException.compareAndSet(null, closeRunning(true, running.get(id))); | ||
| firstException.compareAndSet(null, closeRunning(false, running.get(id))); | ||
| } else if (created.containsKey(id)) { | ||
| log.debug("Closing the zombie created stream task {}.", id); | ||
| firstException.compareAndSet(null, closeNonRunning(true, created.get(id), changelogs)); | ||
| firstException.compareAndSet(null, closeNonRunning(false, created.get(id), changelogs)); | ||
| } else if (restoring.containsKey(id)) { | ||
| log.debug("Closing the zombie restoring stream task {}.", id); | ||
| firstException.compareAndSet(null, closeRestoring(true, restoring.get(id), changelogs)); | ||
| firstException.compareAndSet(null, closeRestoring(false, restoring.get(id), changelogs)); | ||
| } else if (suspended.containsKey(id)) { | ||
| log.debug("Closing the zombie suspended stream task {}.", id); | ||
| firstException.compareAndSet(null, closeSuspended(true, suspended.get(id))); | ||
| firstException.compareAndSet(null, closeSuspended(false, suspended.get(id))); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -330,7 +325,7 @@ boolean maybeResumeSuspendedTask(final TaskId taskId, | |
| return true; | ||
| } else { | ||
| log.warn("Couldn't resume stream task {} assigned partitions {}, task partitions {}", taskId, partitions, task.partitions()); | ||
| task.closeSuspended(true, null); | ||
| task.closeSuspended(true); | ||
| } | ||
| } | ||
| return false; | ||
|
|
||
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.
After KIP-360 this would not be thrown, but anyhow the caller like stream thread should always try to capture it since it cannot guarantee to always talk to a 2.5+ broker.