chickenchickenlove commented on code in PR #23913:
URL: https://github.com/apache/kafka/pull/23913#discussion_r4434569483


##########
coordinator-common/src/main/java/org/apache/kafka/coordinator/common/runtime/CoordinatorTimerImpl.java:
##########
@@ -166,6 +225,30 @@ public boolean isScheduled(String key) {
     }
 
     public void cancelAll() {
+        states.clear();
+        cancelTasks();
+    }
+
+    /**
+     * Rebuilds the scheduled tasks after the supplied snapshot registry has 
been reverted.
+     * This must be called under the same external synchronization as the 
registry rollback.
+     * It preserves the restored states and their original deadlines.
+     */
+    public void onRollback() {
+        cancelTasks();
+        // A timer implementation may execute an expired task immediately, so 
iterate over
+        // a copy in case that task updates the states while we are 
rescheduling them.
+        new HashMap<>(states).forEach(
+            (key, state) ->
+                scheduleTask(
+                    key,
+                    state,
+                    Math.max(0, state.deadlineMs() - time.hiResClockMs())
+                )
+        );
+    }

Review Comment:
   @squah-confluent 
   Thanks, that makes sense. I agree that timers scheduled solely to restore 
previous timer state should no longer need manual recovery once we have common 
handling for this. I’m also open to reverting heartbeat timeout extensions for 
simplicity, as you suggested.
   
   My remaining concern is that some timers scheduled by failure callbacks 
represent new recovery actions rather than restoration of previous timer state. 
For example, when persisting a classic group’s assignment fails during 
SyncGroup, the failure callback can initiate a new rebalance and schedule a 
join timeout. That timer would still be needed after the failed write is rolled 
back.
   
   This makes me think we need to distinguish changes that belong to the failed 
write from changes that should survive it, while also associating 
write-dependent changes with the correct batch.
   
   My understanding is that the core goal of this ticket is to keep timers 
consistent with the coordinator state after a rollback. For example, if a local 
log append fails and a member removal is rolled back, the cancellation of that 
member’s session timer should also be reverted.
   
   I agree that code which manually restores previous timer state should be 
replaced by common handling. However, not all timer changes depend on record 
writes in the same way. For example, a timer scheduled to start a new recovery 
action after a failure needs to be distinguished from changes belonging to the 
failed write.
   
   Also, operation boundaries do not always align with batch boundaries:
   
   1. Even an atomic operation may change timers while generating its records, 
then flush an existing batch before appending its own records.
   2. A non-atomic operation may have its records split across multiple 
batches, with only a prefix successfully written.
   
   Therefore, I don’t think it is sufficient to apply all pending timer changes 
whenever a batch succeeds, or discard them all whenever an operation fails. We 
need to associate each timer change with the write it actually depends on.
   
   Would the following approach make sense?
   
   1. Keep the existing immediate scheduling and cancellation behavior for 
timer changes that are independent of record writes.
   2. Explicitly associate write-dependent timer changes with the operation 
generating the records, and keep those changes pending.
   3. Have CoordinatorRuntime attach those changes to the batch that actually 
contains the corresponding records, and invoke common success/failure handling 
based on the local append outcome.
   
   These callbacks would need to be distinct from the existing appendFuture, 
whose completion also depends on the high watermark and can fail due to an 
operation timeout. For non-atomic operations, a single final callback cannot 
represent partial success, so we would need either explicit record dependency 
boundaries or a restriction on the initial scope of support.
   
   This would let callers explicitly declare write dependencies while keeping 
the application, discarding, and expiration handling of pending timer changes 
in a common layer. 
   
   
   What do you think, @squah-confluent @dajac ?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to