-
Notifications
You must be signed in to change notification settings - Fork 683
fix(connect): correct KAFKA-17719 fix to prevent rebalance storm #3286
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
85f55ed
1186a32
314cd31
52ad1ef
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -550,10 +550,41 @@ public void removeConnectorConfig(String connector) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("Removing connector configuration for connector '{}'", connector); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Timer timer = time.timer(READ_WRITE_TOTAL_TIMEOUT_MS); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<ProducerKeyValue> keyValues = Arrays.asList( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new ProducerKeyValue(CONNECTOR_KEY(connector), null), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new ProducerKeyValue(TARGET_STATE_KEY(connector), null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<ProducerKeyValue> keyValues = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyValues.add(new ProducerKeyValue(CONNECTOR_KEY(connector), null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyValues.add(new ProducerKeyValue(TARGET_STATE_KEY(connector), null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Write tombstones for all task config keys and the commit key so that log compaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // will eventually remove them. Without these tombstones, orphaned task configs and commit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // records would persist indefinitely in the config topic after the connector is deleted, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // which is the root cause of KAFKA-16838. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+557
to
+560
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synchronized (lock) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Integer taskCount = connectorTaskCounts.get(connector); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (taskCount != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Also collect task IDs from taskConfigs and deferredTaskUpdates to handle the case | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // where task count was reduced (old task keys with higher indices still exist) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Set<Integer> allTaskIds = new HashSet<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (int i = 0; i < taskCount; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allTaskIds.add(i); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (ConnectorTaskId taskId : taskConfigs.keySet()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (taskId.connector().equals(connector)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allTaskIds.add(taskId.task()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<ConnectorTaskId, Map<String, String>> deferred = deferredTaskUpdates.get(connector); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (deferred != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (ConnectorTaskId taskId : deferred.keySet()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allTaskIds.add(taskId.task()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (int taskId : allTaskIds) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyValues.add(new ProducerKeyValue(TASK_KEY(new ConnectorTaskId(connector, taskId)), null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyValues.add(new ProducerKeyValue(COMMIT_TASKS_KEY(connector), null)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+563
to
+585
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (taskCount != null) { | |
| // Also collect task IDs from taskConfigs and deferredTaskUpdates to handle the case | |
| // where task count was reduced (old task keys with higher indices still exist) | |
| Set<Integer> allTaskIds = new HashSet<>(); | |
| for (int i = 0; i < taskCount; i++) { | |
| allTaskIds.add(i); | |
| } | |
| for (ConnectorTaskId taskId : taskConfigs.keySet()) { | |
| if (taskId.connector().equals(connector)) { | |
| allTaskIds.add(taskId.task()); | |
| } | |
| } | |
| Map<ConnectorTaskId, Map<String, String>> deferred = deferredTaskUpdates.get(connector); | |
| if (deferred != null) { | |
| for (ConnectorTaskId taskId : deferred.keySet()) { | |
| allTaskIds.add(taskId.task()); | |
| } | |
| } | |
| for (int taskId : allTaskIds) { | |
| keyValues.add(new ProducerKeyValue(TASK_KEY(new ConnectorTaskId(connector, taskId)), null)); | |
| } | |
| keyValues.add(new ProducerKeyValue(COMMIT_TASKS_KEY(connector), null)); | |
| } | |
| // Collect task IDs from all available sources so cleanup still works even if | |
| // connectorTaskCounts does not currently contain an entry for this connector. | |
| Set<Integer> allTaskIds = new HashSet<>(); | |
| if (taskCount != null) { | |
| for (int i = 0; i < taskCount; i++) { | |
| allTaskIds.add(i); | |
| } | |
| } | |
| for (ConnectorTaskId taskId : taskConfigs.keySet()) { | |
| if (taskId.connector().equals(connector)) { | |
| allTaskIds.add(taskId.task()); | |
| } | |
| } | |
| Map<ConnectorTaskId, Map<String, String>> deferred = deferredTaskUpdates.get(connector); | |
| if (deferred != null) { | |
| for (ConnectorTaskId taskId : deferred.keySet()) { | |
| allTaskIds.add(taskId.task()); | |
| } | |
| } | |
| for (int taskId : allTaskIds) { | |
| keyValues.add(new ProducerKeyValue(TASK_KEY(new ConnectorTaskId(connector, taskId)), null)); | |
| } | |
| keyValues.add(new ProducerKeyValue(COMMIT_TASKS_KEY(connector), null)); |
Copilot
AI
Apr 5, 2026
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.
applyDeferredTaskConfigs can apply deferred task configs and update connector state, but it never triggers updateListener.onTaskConfigUpdate for the tasks it applied. If a tasks commit is received before the connector config while the store is started, the earlier commit is deferred (no listener call), and this method later applies the configs without notifying the herder, so tasks may never be reconfigured. Consider collecting the applied task IDs here (or returning them) and invoking onTaskConfigUpdate after releasing the lock, similar to processTasksCommitRecord.
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.
publishConnectorTaskConfigs returns early when task configs are unchanged, but it does not invoke the provided callback. Since callers (e.g., reconfigureConnector) treat the callback as the completion signal, this can leave request/retry logic waiting indefinitely. Call cb.onCompletion(null, null) before returning in the unchanged-configs case.