-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-10340: Proactively close producer when cancelling source tasks #10016
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
70e0489
ae991e2
8f99829
83b3040
cf26753
a442a0a
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 | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -60,6 +60,7 @@ | |||||||||
| import java.util.Map; | ||||||||||
| import java.util.concurrent.CountDownLatch; | ||||||||||
| import java.util.concurrent.ExecutionException; | ||||||||||
| import java.util.concurrent.Executor; | ||||||||||
| import java.util.concurrent.Future; | ||||||||||
| import java.util.concurrent.TimeUnit; | ||||||||||
| import java.util.concurrent.TimeoutException; | ||||||||||
|
|
@@ -86,6 +87,7 @@ class WorkerSourceTask extends WorkerTask { | |||||||||
| private final TopicAdmin admin; | ||||||||||
| private final CloseableOffsetStorageReader offsetReader; | ||||||||||
| private final OffsetStorageWriter offsetWriter; | ||||||||||
| private final Executor closeExecutor; | ||||||||||
| private final SourceTaskMetricsGroup sourceTaskMetricsGroup; | ||||||||||
| private final AtomicReference<Exception> producerSendException; | ||||||||||
| private final boolean isTopicTrackingEnabled; | ||||||||||
|
|
@@ -123,7 +125,8 @@ public WorkerSourceTask(ConnectorTaskId id, | |||||||||
| ClassLoader loader, | ||||||||||
| Time time, | ||||||||||
| RetryWithToleranceOperator retryWithToleranceOperator, | ||||||||||
| StatusBackingStore statusBackingStore) { | ||||||||||
| StatusBackingStore statusBackingStore, | ||||||||||
| Executor closeExecutor) { | ||||||||||
|
|
||||||||||
| super(id, statusListener, initialState, loader, connectMetrics, | ||||||||||
| retryWithToleranceOperator, time, statusBackingStore); | ||||||||||
|
|
@@ -139,6 +142,7 @@ public WorkerSourceTask(ConnectorTaskId id, | |||||||||
| this.admin = admin; | ||||||||||
| this.offsetReader = offsetReader; | ||||||||||
| this.offsetWriter = offsetWriter; | ||||||||||
| this.closeExecutor = closeExecutor; | ||||||||||
|
|
||||||||||
| this.toSend = null; | ||||||||||
| this.lastSendFailed = false; | ||||||||||
|
|
@@ -171,13 +175,9 @@ protected void close() { | |||||||||
| log.warn("Could not stop task", t); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| if (producer != null) { | ||||||||||
| try { | ||||||||||
| producer.close(Duration.ofSeconds(30)); | ||||||||||
| } catch (Throwable t) { | ||||||||||
| log.warn("Could not close producer", t); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| closeProducer(Duration.ofSeconds(30)); | ||||||||||
|
|
||||||||||
| if (admin != null) { | ||||||||||
| try { | ||||||||||
| admin.close(Duration.ofSeconds(30)); | ||||||||||
|
|
@@ -202,6 +202,14 @@ public void removeMetrics() { | |||||||||
| public void cancel() { | ||||||||||
| super.cancel(); | ||||||||||
| offsetReader.close(); | ||||||||||
| // We proactively close the producer here as the main work thread for the task may | ||||||||||
| // be blocked indefinitely in a call to Producer::send if automatic topic creation is | ||||||||||
| // not enabled on either the connector or the Kafka cluster. Closing the producer should | ||||||||||
| // unblock it in that case and allow shutdown to proceed normally. | ||||||||||
| // With a duration of 0, the producer's own shutdown logic should be fairly quick, | ||||||||||
| // but closing user-pluggable classes like interceptors may lag indefinitely. So, we | ||||||||||
| // call close on a separate thread in order to avoid blocking the herder's tick thread. | ||||||||||
| closeExecutor.execute(() -> closeProducer(Duration.ZERO)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| @Override | ||||||||||
|
|
@@ -259,6 +267,16 @@ public void execute() { | |||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private void closeProducer(Duration duration) { | ||||||||||
| if (producer != null) { | ||||||||||
| try { | ||||||||||
| producer.close(duration); | ||||||||||
| } catch (Throwable 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. The producer's
Suggested change
Two things. First, the producer throws
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. Right now there aren't any code paths that lead to the worker's executor being shut down. One might be added in the future, but it seems a little premature to try to catch it right now and may mislead readers of the code base. Plus, if an
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, that seems to have been done a long time ago. I wonder if that was an oversight, or whether that was intentional since in Connect the But in our test cases that use
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've logged https://issues.apache.org/jira/browse/KAFKA-12380 for shutting down the worker's executor. Again, it's not an issue in runtime, but a potential issue in our tests. |
||||||||||
| log.warn("Could not close producer for {}", id, t); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private void maybeThrowProducerSendException() { | ||||||||||
| if (producerSendException.get() != null) { | ||||||||||
| throw new ConnectException( | ||||||||||
|
|
@@ -488,7 +506,9 @@ public boolean commitOffsets() { | |||||||||
| while (!outstandingMessages.isEmpty()) { | ||||||||||
| try { | ||||||||||
| long timeoutMs = timeout - time.milliseconds(); | ||||||||||
| if (timeoutMs <= 0) { | ||||||||||
| // If the task has been cancelled, no more records will be sent from the producer; in that case, if any outstanding messages remain, | ||||||||||
| // we can stop flushing immediately | ||||||||||
| if (isCancelled() || timeoutMs <= 0) { | ||||||||||
| log.error("{} Failed to flush, timed out while waiting for producer to flush outstanding {} messages", this, outstandingMessages.size()); | ||||||||||
| finishFailedFlush(); | ||||||||||
| recordCommitFailure(time.milliseconds() - started, null); | ||||||||||
|
|
||||||||||
Uh oh!
There was an error while loading. Please reload this page.