-
Notifications
You must be signed in to change notification settings - Fork 14.8k
KAFKA-19662: Reset share group offsets for unsubscribed topics #20708
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 1 commit
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |||||
| import org.apache.kafka.common.KafkaException; | ||||||
| import org.apache.kafka.common.KafkaFuture; | ||||||
| import org.apache.kafka.common.TopicPartition; | ||||||
| import org.apache.kafka.common.errors.GroupIdNotFoundException; | ||||||
| import org.apache.kafka.common.errors.GroupNotEmptyException; | ||||||
| import org.apache.kafka.common.protocol.Errors; | ||||||
| import org.apache.kafka.common.utils.Utils; | ||||||
|
|
@@ -385,10 +386,25 @@ void resetOffsets() { | |||||
| if (!(GroupState.EMPTY.equals(shareGroupDescription.groupState()) || GroupState.DEAD.equals(shareGroupDescription.groupState()))) { | ||||||
| CommandLineUtils.printErrorAndExit(String.format("Share group '%s' is not empty.", groupId)); | ||||||
| } | ||||||
| Map<TopicPartition, OffsetAndMetadata> offsetsToReset = prepareOffsetsToReset(groupId); | ||||||
| if (offsetsToReset == null) { | ||||||
| return; | ||||||
| resetOffsetsForInactiveGroup(groupId); | ||||||
| } catch (InterruptedException ie) { | ||||||
| throw new RuntimeException(ie); | ||||||
| } catch (ExecutionException ee) { | ||||||
| Throwable cause = ee.getCause(); | ||||||
| if (cause instanceof GroupIdNotFoundException) { | ||||||
| resetOffsetsForInactiveGroup(groupId); | ||||||
|
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. Just wondering, why is there a retry mechanism when 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 think this PR aims to align the behavior between consumer groups and shared consumer groups. For regular consumer groups, we already have similar logic — if the group doesn’t exist, it will be automatically created. see kafka/group-coordinator/src/main/java/org/apache/kafka/coordinator/group/OffsetMetadataManager.java Lines 465 to 466 in fd9b551
In this PR, the shared consumer group now follows the same behavior: https://github.com/apache/kafka/pull/20708/files#diff-00f0f81cf13e66781777d94f7d2e68a581663385c37e98792507f2294c91bb09R8309-R8310 Back to your question, I think your understanding is correct. This is something like a retry mechanism: if describeShareGroups throws a Perhaps Andrew can elaborate more 😃 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. It's because if you do the operation on a non-existent group, it creates the group (which is by definition empty) and then resets the offsets. I know this is a bit weird, but if you want to initialise the group in a particular state before starting any consumers, this is how you would do it. |
||||||
| } else if (cause instanceof KafkaException) { | ||||||
| CommandLineUtils.printErrorAndExit(cause.getMessage()); | ||||||
| } else { | ||||||
| throw new RuntimeException(cause); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private void resetOffsetsForInactiveGroup(String groupId) { | ||||||
| try { | ||||||
| Collection<TopicPartition> partitionsToReset = getPartitionsToReset(groupId); | ||||||
| Map<TopicPartition, OffsetAndMetadata> offsetsToReset = prepareOffsetsToReset(groupId, partitionsToReset); | ||||||
| boolean dryRun = opts.options.has(opts.dryRunOpt) || !opts.options.has(opts.executeOpt); | ||||||
| if (!dryRun) { | ||||||
| adminClient.alterShareGroupOffsets(groupId, | ||||||
|
|
@@ -404,24 +420,28 @@ void resetOffsets() { | |||||
| } catch (ExecutionException ee) { | ||||||
| Throwable cause = ee.getCause(); | ||||||
| if (cause instanceof KafkaException) { | ||||||
| CommandLineUtils.printErrorAndExit(cause.getMessage()); | ||||||
| throw (KafkaException) cause; | ||||||
| } else { | ||||||
| throw new RuntimeException(cause); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| protected Map<TopicPartition, OffsetAndMetadata> prepareOffsetsToReset(String groupId) throws ExecutionException, InterruptedException { | ||||||
| Map<String, ListShareGroupOffsetsSpec> groupSpecs = Map.of(groupId, new ListShareGroupOffsetsSpec()); | ||||||
| Map<TopicPartition, OffsetAndMetadata> offsetsByTopicPartitions = adminClient.listShareGroupOffsets(groupSpecs).all().get().get(groupId); | ||||||
| private Collection<TopicPartition> getPartitionsToReset(String groupId) throws ExecutionException, InterruptedException { | ||||||
| Collection<TopicPartition> partitionsToReset; | ||||||
|
|
||||||
| if (opts.options.has(opts.topicOpt)) { | ||||||
| partitionsToReset = offsetsUtils.parseTopicPartitionsToReset(opts.options.valuesOf(opts.topicOpt)); | ||||||
| } else { | ||||||
| Map<String, ListShareGroupOffsetsSpec> groupSpecs = Map.of(groupId, new ListShareGroupOffsetsSpec()); | ||||||
| Map<TopicPartition, OffsetAndMetadata> offsetsByTopicPartitions = adminClient.listShareGroupOffsets(groupSpecs).all().get().get(groupId); | ||||||
| partitionsToReset = offsetsByTopicPartitions.keySet(); | ||||||
| } | ||||||
|
|
||||||
| return partitionsToReset; | ||||||
| } | ||||||
|
|
||||||
| private Map<TopicPartition, OffsetAndMetadata> prepareOffsetsToReset(String groupId, Collection<TopicPartition> partitionsToReset) { | ||||||
|
||||||
| offsetsUtils.checkAllTopicPartitionsValid(partitionsToReset); | ||||||
| if (opts.options.has(opts.resetToEarliestOpt)) { | ||||||
| return offsetsUtils.resetToEarliest(partitionsToReset); | ||||||
|
|
||||||
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.
I think the method
validateOffsetsAlterable()could be removed.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.
I did :)