kip-848-migration-guide.md
October 9, 2025 · View on GitHub
KIP-848 - Migration Guide
Overview
-
What changed:
The Group Leader role (consumer member) is removed. Assignments are calculated by the Group Coordinator (broker) and distributed via heartbeats.
-
Requirements:
- Broker version 4.0.0+
Confluent.Kafkaversion 2.12.0+: GA (production-ready)
-
Enablement (client-side):
GroupProtocol=ConsumerGroupRemoteAssignor=<assignor>(optional; broker-controlled ifnull; default broker assignor isuniform)
Available Features
All KIP-848 features are supported including:
- Subscription to one or more topics, including regular expression (regex) subscriptions
- Rebalance handlers (incremental only)
- Static group membership
- Configurable remote assignor
- Enforced max poll interval
- Upgrade from
classicprotocol or downgrade fromconsumerprotocol - AdminClient changes as per KIP
Contract Changes
Client Configuration changes
| Classic Protocol (Deprecated Configs in KIP-848) | KIP-848 / Next-Gen Replacement |
|---|---|
PartitionAssignmentStrategy | GroupRemoteAssignor |
SessionTimeoutMs | Broker config: group.consumer.session.timeout.ms (configurable per group) |
HeartbeatIntervalMs | Broker config: group.consumer.heartbeat.interval.ms (configurable per group) |
GroupProtocolType | Not used in the new protocol |
Rebalance Handler changes
- The protocol is fully incremental in KIP-848.
- ⚠️ The
partitionslist passed toPartitionsAssignedHandler()andPartitionsRevokedHandler()contains only the incremental changes — partitions being added or revoked — not the full assignment, as was the case withRangeorRoundRobinin the classic protocol. It's similar to theCooperativeStickyincremental handlers contract but number of calls can vary: there isn't a call for each rebalance. - All assignors under KIP-848 are now sticky, including
range, which was not sticky in the classic protocol.
Manual partition assignment
- You can still use
Assign()before being subscribed but after subscribing you can only useIncrementalAssign()andIncrementalUnassign().
Static Group Membership
- Duplicate
GroupInstanceIdhandling:- Newly joining member is fenced with UnreleasedInstanceId (fatal).
- (Classic protocol fenced the existing member instead.)
- Implications:
- Ensure only one active instance per
GroupInstanceId. - Consumers must shut down cleanly to avoid blocking replacements until session timeout expires.
- Ensure only one active instance per
Session Timeout & Fetching
- Session timeout is broker-controlled:
- If the Coordinator is unreachable, a consumer continues fetching messages but cannot commit offsets.
- Consumer is fenced once a heartbeat response is received from the Coordinator.
- In the classic protocol, the client stopped fetching when session timeout expired.
Closing / Auto-Commit
- On
Close()orUnsubscribe()with auto-commit enabled:- Member retries committing offsets until a timeout expires.
- Currently uses the default remote session timeout.
- Future KIP-1092 will allow custom commit timeouts.
Error Handling Changes
UnknownTopicOrPart(subscription case):- No longer returned if a topic is missing in the local cache when subscribing; the subscription proceeds.
TopicAuthorizationFailed:- Reported once per heartbeat or subscription change, even if only one topic is unauthorized.
Summary of Key Differences (Classic vs Next-Gen)
- Assignment: Classic protocol calculated by Group Leader (consumer); KIP-848 calculated by Group Coordinator (broker)
- Assignors: Classic range assignor was not sticky; KIP-848 assignors are sticky, including range
- Deprecated configs: Classic client configs are replaced by
GroupRemoteAssignorand broker-controlled session/heartbeat configs - Static membership fencing: KIP-848 fences new member on duplicate
GroupInstanceId - Session timeout: Classic enforced on client; KIP-848 enforced on broker
- Auto-commit on close: Classic stops at client session timeout; KIP-848 retries until remote timeout
- Unknown topics: KIP-848 does not return error on subscription if topic missing
- Upgrade/Downgrade: KIP-848 supports upgrade/downgrade from/to
classicandconsumerprotocols
Minimal Example Config
Classic Protocol
# Optional; default is 'classic'
GroupProtocol=Classic
PartitionAssignmentStrategy=<Range,RoundRobin,CooperativeSticky>
SessionTimeoutMs=45000
HeartbeatIntervalMs=15000
Next-Gen Protocol / KIP-848
GroupProtocol=Consumer
# Optional: select a remote assignor
# Valid options currently: 'uniform' or 'range'
# GroupRemoteAssignor=<uniform,range>
# If unset, broker chooses the assignor (default: 'uniform')
# Session & heartbeat now controlled by broker:
# group.consumer.session.timeout.ms
# group.consumer.heartbeat.interval.ms
Rebalance Callback Migration
Note: The partitions list contains only partitions being added or revoked, not the full partition list as in the eager protocol.
Ensure this is handled correctly if it updates data related to the assigned partitions.
Upgrade and Downgrade
- A group made up entirely of
classicconsumers runs under the classic protocol. - The group is upgraded to the consumer protocol as soon as at least one
consumerprotocol member joins. - The group is downgraded back to the classic protocol if the last
consumerprotocol member leaves whileclassicmembers remain. - Both rolling upgrade (classic → consumer) and rolling downgrade (consumer → classic) are supported.
Migration Checklist (Next-Gen Protocol / KIP-848)
- Upgrade to Confluent.Kafka ≥ 2.12.0 (GA release)
- Run against Kafka brokers ≥ 4.0.0
- Set
GroupProtocol=Consumer - Optionally set
GroupRemoteAssignor; leave unset fornullfor broker-controlled (default:uniform), valid options:uniformorrange - Replace deprecated configs with new ones
- Update rebalance callbacks to expect incremental changes only
- Review static membership handling (
GroupInstanceId) - Ensure proper shutdown to avoid fencing issues
- Adjust error handling for unknown topics and authorization failures