Skip to content

KAFKA-19570: Implement offline migration for streams groups#20288

Merged
lucasbru merged 3 commits into
apache:trunkfrom
lucasbru:offline_migration
Aug 26, 2025
Merged

KAFKA-19570: Implement offline migration for streams groups#20288
lucasbru merged 3 commits into
apache:trunkfrom
lucasbru:offline_migration

Conversation

@lucasbru

@lucasbru lucasbru commented Aug 1, 2025

Copy link
Copy Markdown
Member

Offline migration essentially preserves offsets and nothing else. So
effectively write tombstones for classic group type when a streams
heartbeat is sent to with the group ID of an empty classic group, and
write tombstones for the streams group type when a classic consumer
attempts to join with a group ID of an empty streams group.

Reviewers: Bill Bejeck bbejeck@apache.org, Sean Quah
squah@confluent.io, Dongnuo Lyu dlyu@confluent.io

Offline migration essentially preserves offsets and nothing else. So effectively write tombstones for classic group type when a streams heartbeat is sent to with the group ID of an empty classic group, and write tombstones for the streams group type when a classic consumer attempts to join with a group ID of an empty streams group.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements offline migration for Kafka Streams groups, allowing seamless conversion between classic consumer groups and streams groups when the target group is empty. The migration preserves offsets while writing tombstones for the previous group type.

Key changes:

  • Added migration logic for empty classic groups to streams groups when a streams heartbeat is received
  • Added migration logic for empty streams groups to classic groups when a classic consumer attempts to join
  • Enhanced group creation methods to handle cross-group-type migrations

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
GroupMetadataManager.java Implements core migration logic with helper methods and updates group creation/join flows
GroupMetadataManagerTest.java Adds comprehensive test coverage for both migration scenarios and edge cases

* The group will be materialized during the replay.
*
* If there is an empty classic consumer group of the same name, it will be deleted and a new streams
* group.

Copilot AI Aug 1, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is incomplete - it ends abruptly with 'a new streams' without completing the sentence. Should be 'a new streams group will be created.'

Suggested change
* group.
* group will be created.

Copilot uses AI. Check for mistakes.

@squah-confluent squah-confluent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise lgtm

Comment on lines +836 to +837
* If there is an empty classic consumer group of the same name, it will be deleted and a new streams
* group.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's first comment looks correct, we're missing a word here.

Suggested change
* If there is an empty classic consumer group of the same name, it will be deleted and a new streams
* group.
* If there is an empty classic consumer group of the same name, it will be deleted and a new streams
* group wil be created.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return classicGroupJoinToConsumerGroup((ConsumerGroup) group, context, request, responseFuture);
} else if (group.type() == CONSUMER || group.type() == CLASSIC) {
} else if (group.type() == CONSUMER || group.type() == CLASSIC || group.type() == STREAMS && group.isEmpty()) {
return classicGroupJoinToClassicGroup(context, request, responseFuture);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a comment to state the groups accepted by classicGroupJoinToClassicGroup here?

  • existing classic group
  • existing empty consumer group (will be converted to classic)
  • existing empty streams group (will be converted to classic)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@lucasbru

Copy link
Copy Markdown
Member Author

@squah-confluent @dongnuo123 Thanks for the comments. I addressed the them and merged trunk

@lucasbru
lucasbru requested review from bbejeck and mjsax August 25, 2025 09:59

@bbejeck bbejeck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @lucasbru - LGTM

@lucasbru
lucasbru merged commit f621a63 into apache:trunk Aug 26, 2025
36 of 38 checks passed
@lucasbru lucasbru added the KIP-1071 PRs related to KIP-1071 label Oct 23, 2025
eduwercamacaro pushed a commit to littlehorse-enterprises/kafka that referenced this pull request Nov 12, 2025
…0288)

Offline migration essentially preserves offsets and nothing else. So
effectively write tombstones for classic group type when a streams
heartbeat is sent to with the group ID of an empty classic group, and
write tombstones for the streams group type when a classic consumer
attempts to join with a group ID of an empty streams group.

Reviewers: Bill Bejeck <bbejeck@apache.org>, Sean Quah
 <squah@confluent.io>, Dongnuo Lyu <dlyu@confluent.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group-coordinator KIP-1071 PRs related to KIP-1071

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants