aliehsaeedii commented on code in PR #22676:
URL: https://github.com/apache/kafka/pull/22676#discussion_r3481033598


##########
group-coordinator/src/main/java/org/apache/kafka/coordinator/group/GroupCoordinatorService.java:
##########
@@ -911,22 +915,27 @@ private void recordPluginDeleteOutcome(int attempted, int 
errors) {
     }
 
     /**
-     * Conditional metadata write that clears {@code 
StoredDescriptionTopologyEpoch} for
-     * {@code groupId} only when the persisted value still equals {@code 
expectedStoredEpoch}.
-     * Mismatches and missing groups are silently ignored by the shard-side 
method. Runtime
-     * write failures (NOT_COORDINATOR etc.) are logged here and swallowed so 
a single failed
-     * write does not poison the cycle's allOf — the next cycle will retry 
naturally because
-     * the persisted storedEpoch is still non-default.
+     * Batched conditional metadata write that clears {@code 
StoredDescriptionTopologyEpoch}
+     * for every entry in {@code expectedStoredEpochByGroupId}, but only for 
the entries whose
+     * persisted value still equals the supplied epoch. Mismatches and missing 
groups are
+     * silently ignored by the shard-side method. All groups in the batch must 
hash to the same
+     * __consumer_offsets partition (the caller guarantees this — the 
eligibility scan is per
+     * partition). Runtime write failures (NOT_COORDINATOR etc.) are logged 
here and swallowed
+     * so a single failed write does not poison the cycle's allOf — the next 
cycle will retry
+     * naturally because the persisted storedEpoch is still non-default.
      */
-    private CompletableFuture<Void> 
clearStoredDescriptionTopologyEpochAsync(String groupId, int 
expectedStoredEpoch) {
-        return runtime.<Void>scheduleWriteOperation(
+    private CompletableFuture<Void> 
clearStoredDescriptionTopologyEpochBatchAsync(
+        Map<String, Integer> expectedStoredEpochByGroupId
+    ) {
+        TopicPartition tp = 
topicPartitionFor(expectedStoredEpochByGroupId.keySet().iterator().next());

Review Comment:
   The batch's target partition is derived from `keySet().iterator().next()`, 
which throws `NoSuchElementException` on an empty map. It's safe today because 
the only caller guards with `if (toClear.isEmpty())`, but that coupling is 
implicit. Consider an early `if (expectedStoredEpochByGroupId.isEmpty()) return 
CompletableFuture.completedFuture(null);` here so the method is self-protecting 
and doesn't depend on the caller's guard staying in place.



##########
group-coordinator/src/main/java/org/apache/kafka/coordinator/group/GroupCoordinatorService.java:
##########
@@ -911,22 +915,27 @@ private void recordPluginDeleteOutcome(int attempted, int 
errors) {
     }
 
     /**
-     * Conditional metadata write that clears {@code 
StoredDescriptionTopologyEpoch} for
-     * {@code groupId} only when the persisted value still equals {@code 
expectedStoredEpoch}.
-     * Mismatches and missing groups are silently ignored by the shard-side 
method. Runtime
-     * write failures (NOT_COORDINATOR etc.) are logged here and swallowed so 
a single failed
-     * write does not poison the cycle's allOf — the next cycle will retry 
naturally because
-     * the persisted storedEpoch is still non-default.
+     * Batched conditional metadata write that clears {@code 
StoredDescriptionTopologyEpoch}
+     * for every entry in {@code expectedStoredEpochByGroupId}, but only for 
the entries whose
+     * persisted value still equals the supplied epoch. Mismatches and missing 
groups are
+     * silently ignored by the shard-side method. All groups in the batch must 
hash to the same
+     * __consumer_offsets partition (the caller guarantees this — the 
eligibility scan is per
+     * partition). Runtime write failures (NOT_COORDINATOR etc.) are logged 
here and swallowed
+     * so a single failed write does not poison the cycle's allOf — the next 
cycle will retry
+     * naturally because the persisted storedEpoch is still non-default.
      */
-    private CompletableFuture<Void> 
clearStoredDescriptionTopologyEpochAsync(String groupId, int 
expectedStoredEpoch) {
-        return runtime.<Void>scheduleWriteOperation(
+    private CompletableFuture<Void> 
clearStoredDescriptionTopologyEpochBatchAsync(
+        Map<String, Integer> expectedStoredEpochByGroupId
+    ) {
+        TopicPartition tp = 
topicPartitionFor(expectedStoredEpochByGroupId.keySet().iterator().next());
+        return runtime.scheduleWriteOperation(
             "clear-stored-topology-epoch",
-            topicPartitionFor(groupId),
-            coordinator -> 
coordinator.clearStoredDescriptionTopologyEpoch(groupId, expectedStoredEpoch)
+            tp,
+            coordinator -> 
coordinator.clearStoredDescriptionTopologyEpochBatch(expectedStoredEpochByGroupId)
         ).handle((__, throwable) -> {
             if (throwable != null) {
-                log.warn("Failed to clear StoredDescriptionTopologyEpoch for 
group {}; the next cleanup cycle will retry.",
-                    groupId, throwable);
+                log.warn("Failed to clear StoredDescriptionTopologyEpoch for 
{} group(s) on partition {}; the next cleanup cycle will retry.",

Review Comment:
   The batched failure log now reports `size()` + partition instead of the 
individual groupId, so a failed write no longer tells you which groups didn't 
get cleared. Given the next-cycle retry guarantee this is acceptable, but 
logging the group ids (here or at debug) would make a stuck group easier to 
diagnose.



-- 
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