Skip to content

KAFKA-20897: Add broker pods and canary partition placement isolation (KIP-1095) - #23095

Open
ericzhifengchen wants to merge 9 commits into
apache:trunkfrom
ericzhifengchen:pod-isolation
Open

KAFKA-20897: Add broker pods and canary partition placement isolation (KIP-1095)#23095
ericzhifengchen wants to merge 9 commits into
apache:trunkfrom
ericzhifengchen:pod-isolation

Conversation

@ericzhifengchen

Copy link
Copy Markdown
Contributor

Implements the broker-side placement portion of KIP-1095. Adds an optional
broker attribute, pod, and a replica placement layer that can confine a
configurable subset of partitions to a single pod.

https://cwiki.apache.org/confluence/display/KAFKA/KIP-1095%3A+Kafka+Canary+Isolation

Motivation

A bad broker deployment can affect any partition hosted on the brokers being
upgraded, and operators have no way to bound that set in advance. Confining a
known fraction of partitions to a designated pod makes the blast radius of a
regression a deployment-time decision rather than an after-the-fact
discovery.

Rack-awareness cannot express this: rack means "spread replicas across these
groups", whereas this requires "confine these partitions to this group".

Changes

Broker pods. New broker config broker.pod (string, default null),
exposed as AbstractKafkaConfig.pod(). The value travels through broker
registration into the metadata log and out through Metadata and
DescribeCluster, where it becomes Node.pod(). A Pod field is added as
a tagged field to four messages, so no message version is bumped:

Message Field versions Tag
BrokerRegistrationRequest 0+ 0
RegisterBrokerRecord 0+ 2
DescribeClusterResponse 0+ 0
MetadataResponse 11+ 0

Pod isolation. PodReplicaPlacer decorates the configured
ReplicaPlacer with a map of pod name to a predicate over partition index.
A partition matching exactly one rule is placed only on brokers in that pod;
a partition matching none is placed across the pods carrying no rule.
Consecutive partitions resolving to the same broker set are coalesced into a
single PlacementSpec, so the delegate still sees contiguous ranges and its
striping and rack-awareness are unchanged.

A broker with no pod belongs to every pod, which allows incremental
adoption. A rule naming a pod with no live brokers is ignored and falls
through to default placement. A partition matching more than one rule raises
IllegalStateException naming the conflicting pods. A pod smaller than the
requested replication factor fails with InvalidReplicationFactorException
rather than spilling replicas outside the pod.

Canary isolation. Two controller configs install the single rule this
KIP configures: canary.pod.name (default canary-broker) and
canary.partition.interval (int, default 0). With interval N, partition i
is a canary partition when i % N == N - 1, so a topic with fewer than N
partitions has none. 0 disables the feature.

Tooling. kafka-reassign-partitions --generate accepts --canary-name
and --canary-interval so that offline plans match controller placement.
Without them a reassignment would silently undo isolation on the topics it
moves. This also fixes broker pod being dropped from generated plans when
rack-awareness was disabled.

Compatibility

Off by default. With canary.partition.interval at 0 the rule map is empty
and placement is byte-identical to StripedReplicaPlacer.

All protocol additions are tagged, nullable and default to null, so no
message version is bumped and no API version negotiation changes. Older
clients skip the unknown tag. A broker that does not set broker.pod
behaves exactly as it does today.

Note that isolation is only complete once every broker declares a pod, since
an unlabelled broker is eligible for every pod. A partially labelled cluster
is a migration state, not a supported configuration.

Testing

  • PodReplicaPlacerTest (13 cases): placement with zero, one and multiple
    pods; pod-less brokers; empty clusters; partition addition to existing
    topics; and the overlapping-rule error.
  • CanarySpecTest (8 cases): interval-to-predicate conversion, including 0,
    negative, interval 1, and the property that a topic smaller than the
    interval has no canary partition.
  • BrokerRegistrationTest, MetadataCacheTest, ClusterImageBrokersNodeTest
    and BrokerLifecycleManagerTest: propagation through registration, the
    metadata log, the metadata cache and Node.
  • KafkaConfigTest: the new broker config and validation of the new
    controller config.
  • ReassignPartitionsUnitTest: generated plans with and without canary
    placement.

shonali-ks and others added 9 commits August 4, 2026 16:37
…pec (1/n)

Summary:
Porting: https://code.uberinternal.com/D16273381

[[ https://docs.google.com/document/d/1UCkCMFyomM0pxVw6iC7MqNtJJHHmPfdQUPhGGtk5MyA/edit?tab=t.0#heading=h.9h4ggmvsh0g5 | Kafka Canary Isolation ERD ]]. The pod concept was introduced in the cluster to group a set of brokers for a specific purpose.

In this diff, we are introducing pod as a tagged field in the Kafka proto to unify the response. The custom-uber client can still access the Pod information. With the open-source client, the Pod information won't be propagated.

Port: https://code.uberinternal.com/D16273381

Test Plan: UTs

Reviewers: #ldap_kafka_admins, #ldap_kafka_core_reviewers, kchandraprakash, yulan

Reviewed By: #ldap_kafka_admins, #ldap_kafka_core_reviewers, kchandraprakash, yulan

Subscribers: kchandraprakash

Tags: #has_java

JIRA Issues: DKAFC-5422

Differential Revision: https://code.uberinternal.com/D16483059
Summary:
Introduce pod into kafka configuration and broker metadata to represent a kafka sub cluster.

Porting: https://code.uberinternal.com/D13108811

Test Plan: UTs

Reviewers: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Reviewed By: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Subscribers: kchandraprakash

Tags: #has_java

JIRA Issues: DKAFC-5178

Differential Revision: https://code.uberinternal.com/D16484465
Summary:
Implement the flow to pass through pod information during broker registration

Porting: https://code.uberinternal.com/D13444957

Reviewers: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Reviewed By: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Tags: #has_java

JIRA Issues: DKAFC-5422

Differential Revision: https://code.uberinternal.com/D16487217
Summary:
Implementation to support pod value to be propagated to node for the client to be able to see the sub cluster

Porting: https://code.uberinternal.com/D13495727

Test Plan: UTs

Reviewers: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Reviewed By: #ldap_kafka_admins, #ldap_kafka_core_reviewers, yulan, kchandraprakash

Subscribers: kchandraprakash

Tags: #has_java

JIRA Issues: DKAFC-5422

Differential Revision: https://code.uberinternal.com/D16489599
Reviewers: #ldap_kafka_admins, #ldap_kafka_core_reviewers, kchandraprakash

Reviewed By: #ldap_kafka_admins, #ldap_kafka_core_reviewers, kchandraprakash

Subscribers: kchandraprakash

Tags: #has_java

JIRA Issues: DKAFC-6702

Differential Revision: https://code.uberinternal.com/D20643021
The canary fraction was configured as a percentage and converted to a
partition stride with floor(1/percentage). That conversion is lossy and
silently misleading: 0.15 yields an effective 16.7%, and any value above
0.5 collapses to a stride of 1, designating every partition as a canary
partition rather than the requested fraction. The reassignment command
option had no range validation at all, so a value above 1.0 silently
disabled canary placement instead of failing.

The percentage was never continuous, since floor(1/p) quantizes every
input to some 1/N. Configuring the stride directly expresses exactly the
same set of behaviours without the lossy conversion or the unreachable
inputs.

canary.partition.percentage (double, 0.0-1.0) becomes
canary.partition.interval (int, >= 0), and --canary-percentage becomes
--canary-interval. Selection semantics are unchanged: partition index i
is a canary partition when i % interval == interval - 1, so a topic with
fewer partitions than the interval has none.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants