-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-9441: Unify committing within TaskManager #8218
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
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 |
|---|---|---|
|
|
@@ -47,20 +47,20 @@ | |
| import static org.apache.kafka.streams.StreamsConfig.EXACTLY_ONCE; | ||
|
|
||
| class ActiveTaskCreator { | ||
| private final String applicationId; | ||
| private final InternalTopologyBuilder builder; | ||
| private final StreamsConfig config; | ||
| private final StreamsMetricsImpl streamsMetrics; | ||
| private final StateDirectory stateDirectory; | ||
| private final ChangelogReader storeChangelogReader; | ||
| private final Time time; | ||
| private final Logger log; | ||
| private final String threadId; | ||
| private final ThreadCache cache; | ||
| private final Producer<byte[], byte[]> threadProducer; | ||
| private final Time time; | ||
| private final KafkaClientSupplier clientSupplier; | ||
| private final Map<TaskId, Producer<byte[], byte[]>> taskProducers; | ||
| private final String threadId; | ||
| private final Logger log; | ||
| private final Sensor createTaskSensor; | ||
| private final String applicationId; | ||
| private final Producer<byte[], byte[]> threadProducer; | ||
| private final Map<TaskId, StreamsProducer> taskProducers; | ||
|
|
||
| private static String getThreadProducerClientId(final String threadClientId) { | ||
| return threadClientId + "-producer"; | ||
|
|
@@ -80,32 +80,44 @@ private static String getTaskProducerClientId(final String threadClientId, final | |
| final KafkaClientSupplier clientSupplier, | ||
| final String threadId, | ||
| final Logger log) { | ||
| applicationId = config.getString(StreamsConfig.APPLICATION_ID_CONFIG); | ||
| this.builder = builder; | ||
| this.config = config; | ||
| this.streamsMetrics = streamsMetrics; | ||
| this.stateDirectory = stateDirectory; | ||
| this.storeChangelogReader = storeChangelogReader; | ||
| this.cache = cache; | ||
| this.time = time; | ||
| this.clientSupplier = clientSupplier; | ||
| this.threadId = threadId; | ||
| this.log = log; | ||
|
|
||
| createTaskSensor = ThreadMetrics.createTaskSensor(threadId, streamsMetrics); | ||
| applicationId = config.getString(StreamsConfig.APPLICATION_ID_CONFIG); | ||
|
|
||
| if (EXACTLY_ONCE.equals(config.getString(StreamsConfig.PROCESSING_GUARANTEE_CONFIG))) { | ||
| threadProducer = null; | ||
| taskProducers = new HashMap<>(); | ||
| } else { | ||
| log.info("Creating thread producer client"); | ||
|
|
||
| final String threadProducerClientId = getThreadProducerClientId(threadId); | ||
| final Map<String, Object> producerConfigs = config.getProducerConfigs(threadProducerClientId); | ||
| log.info("Creating thread producer client"); | ||
|
|
||
| threadProducer = clientSupplier.getProducer(producerConfigs); | ||
| taskProducers = Collections.emptyMap(); | ||
| } | ||
| } | ||
|
|
||
| StreamsProducer streamsProducerForTask(final TaskId taskId) { | ||
|
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. We need a unit test for this function.
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.
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. Correct. For eos-beta there will be one |
||
| if (threadProducer != null) { | ||
| throw new IllegalStateException("Producer per thread is used"); | ||
| } | ||
|
|
||
| this.cache = cache; | ||
| this.threadId = threadId; | ||
| this.clientSupplier = clientSupplier; | ||
|
|
||
| createTaskSensor = ThreadMetrics.createTaskSensor(threadId, streamsMetrics); | ||
| final StreamsProducer taskProducer = taskProducers.get(taskId); | ||
| if (taskProducer == null) { | ||
| throw new IllegalStateException("Unknown TaskId: " + taskId); | ||
| } | ||
| return taskProducer; | ||
| } | ||
|
|
||
| Collection<Task> createTasks(final Consumer<byte[], byte[]> consumer, | ||
|
|
@@ -132,23 +144,27 @@ Collection<Task> createTasks(final Consumer<byte[], byte[]> consumer, | |
| partitions | ||
| ); | ||
|
|
||
| final StreamsProducer streamsProducer; | ||
| if (threadProducer == null) { | ||
| final String taskProducerClientId = getTaskProducerClientId(threadId, taskId); | ||
| final Map<String, Object> producerConfigs = config.getProducerConfigs(taskProducerClientId); | ||
| producerConfigs.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, applicationId + "-" + taskId); | ||
| log.info("Creating producer client for task {}", taskId); | ||
| taskProducers.put(taskId, clientSupplier.getProducer(producerConfigs)); | ||
| streamsProducer = new StreamsProducer( | ||
| clientSupplier.getProducer(producerConfigs), | ||
| true, | ||
| applicationId, | ||
| logContext); | ||
| taskProducers.put(taskId, streamsProducer); | ||
| } else { | ||
| streamsProducer = new StreamsProducer(threadProducer, false, null, logContext); | ||
| } | ||
|
|
||
| final RecordCollector recordCollector = new RecordCollectorImpl( | ||
| logContext, | ||
| taskId, | ||
| consumer, | ||
| threadProducer != null ? | ||
| new StreamsProducer(threadProducer, false, logContext, applicationId) : | ||
| new StreamsProducer(taskProducers.get(taskId), true, logContext, applicationId), | ||
| streamsProducer, | ||
| config.defaultProductionExceptionHandler(), | ||
| EXACTLY_ONCE.equals(config.getString(StreamsConfig.PROCESSING_GUARANTEE_CONFIG)), | ||
| streamsMetrics | ||
| ); | ||
|
|
||
|
|
@@ -178,18 +194,18 @@ void closeThreadProducerIfNeeded() { | |
| try { | ||
| threadProducer.close(); | ||
| } catch (final RuntimeException e) { | ||
| throw new StreamsException("Thread Producer encounter unexpected error trying to close", e); | ||
| throw new StreamsException("Thread Producer encounter error trying to close", e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void closeAndRemoveTaskProducerIfNeeded(final TaskId id) { | ||
| final Producer<byte[], byte[]> producer = taskProducers.remove(id); | ||
| if (producer != null) { | ||
| final StreamsProducer taskProducer = taskProducers.remove(id); | ||
| if (taskProducer != null) { | ||
| try { | ||
| producer.close(); | ||
| taskProducer.kafkaProducer().close(); | ||
|
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. nit: we can have a wrapped StreamsProducer#close / metrics, and then #kafkaProducer would be for testing-only.
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. We could, but the idea was that
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. Hmm, I think moving forward we would create and maintain both the single thread-producer and task-producers as StreamsProducer objects right?
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. SGTM |
||
| } catch (final RuntimeException e) { | ||
| throw new StreamsException("[" + id + "] Producer encounter unexpected error trying to close", e); | ||
| throw new StreamsException("[" + id + "] Producer encounter error trying to close", e); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -205,8 +221,8 @@ Map<MetricName, Metric> producerMetrics() { | |
| // When EOS is turned on, each task will have its own producer client | ||
| // and the producer object passed in here will be null. We would then iterate through | ||
| // all the active tasks and add their metrics to the output metrics map. | ||
| for (final Map.Entry<TaskId, Producer<byte[], byte[]>> entry : taskProducers.entrySet()) { | ||
| final Map<MetricName, ? extends Metric> taskProducerMetrics = entry.getValue().metrics(); | ||
| for (final Map.Entry<TaskId, StreamsProducer> entry : taskProducers.entrySet()) { | ||
| final Map<MetricName, ? extends Metric> taskProducerMetrics = entry.getValue().kafkaProducer().metrics(); | ||
| result.putAll(taskProducerMetrics); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,6 @@ | |
| */ | ||
| package org.apache.kafka.streams.processor.internals; | ||
|
|
||
| import org.apache.kafka.clients.consumer.OffsetAndMetadata; | ||
| import org.apache.kafka.clients.producer.Producer; | ||
| import org.apache.kafka.common.TopicPartition; | ||
| import org.apache.kafka.common.header.Headers; | ||
|
|
@@ -45,8 +44,6 @@ <K, V> void send(final String topic, | |
| final Serializer<V> valueSerializer, | ||
| final StreamPartitioner<? super K, ? super V> partitioner); | ||
|
|
||
| void commit(final Map<TopicPartition, OffsetAndMetadata> offsets); | ||
|
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. We moved this to
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. Please see my other comment above --- I think it is cleaner to just call In the next PR where we have the thread-producer, we could then only create a single WDYT?
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. After syncing offline about this, I think I'm convinced now that moving this logic into TaskManager is better. |
||
|
|
||
| /** | ||
| * Initialize the internal {@link Producer}; note this function should be made 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 could log thread-id here for easier log search.
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.
The
threadIdis already added to the log prefix when thelogobject is created inStreamsThread