Skip to content
Apache Kafka kf consumers 4 min read

Partition Assignment Strategies

When several consumers share a group.id, something has to decide which member owns which partition. That decision is made by a pluggable partition assignor, configured through partition.assignment.strategy, and run on the group leader during every rebalance. The assignor you pick directly shapes how evenly load is spread, how much partition ownership churns when membership changes, and — critically in production — whether a rebalance stops the entire group or lets most members keep processing. This page compares the four built-in strategies and explains why CooperativeStickyAssignor is the right default for modern applications.

How assignment fits into a rebalance

A rebalance is triggered whenever group membership or the subscribed topic-partition set changes: a consumer joins, leaves, crashes, or a partition is added. During the rebalance the group coordinator elects one member as leader and hands it the list of members and their subscriptions. The leader runs the configured assignor to compute the full assignment, then ships each member its share through the coordinator.

The assignor is purely client-side logic, so all members of a group must agree on a compatible strategy. You can list more than one assignor for rolling upgrades; the group negotiates the most preferred strategy supported by every member.

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.CooperativeStickyAssignor;

Properties props = new Properties();
props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,
        CooperativeStickyAssignor.class.getName());

RangeAssignor

RangeAssignor is the historical default. It works per topic: it sorts that topic’s partitions numerically and the subscribing consumers lexicographically, then lays the partitions out in contiguous ranges. With three partitions and two consumers, the first consumer gets partitions 0 and 1 and the second gets partition 2.

Range is simple and keeps co-partitioned topics aligned — useful when you join two topics that share a key and want the same partition number of each on the same consumer. Its weakness is imbalance: because it divides each topic independently, the lexicographically-first consumer tends to collect the leftover partition of every topic, so a consumer subscribed to many topics can end up overloaded.

RoundRobinAssignor

RoundRobinAssignor flattens all subscribed topic-partitions into a single list and deals them out one at a time across consumers, like dealing cards. This produces a much more even spread than Range, especially across many topics, and differs by at most one partition per consumer.

The cost is stickiness: round-robin recomputes the whole layout from scratch on every rebalance, so a single member joining or leaving can reshuffle nearly every partition to a new owner. That churn forces consumers to discard cached state and re-seek positions for partitions they did not actually lose.

StickyAssignor

StickyAssignor aims for two goals at once: distribute partitions as evenly as round-robin, but preserve existing assignments as much as possible across rebalances. When a member leaves, only its orphaned partitions are redistributed; everyone else keeps what they had. This minimizes wasted work — fewer offset re-seeks, less cache invalidation, fewer in-flight records dropped.

StickyAssignor is still an eager assignor, though. Eager protocol means every member revokes all of its partitions at the start of the rebalance, the new assignment is computed, and only then does processing resume. That global pause is the classic “stop-the-world” rebalance.

CooperativeStickyAssignor

CooperativeStickyAssignor keeps the balanced, sticky assignment of StickyAssignor but switches to the incremental cooperative rebalancing protocol. Instead of revoking everything up front, the rebalance runs in two passes: members keep processing the partitions they will retain, and only the partitions that genuinely change owners are revoked and reassigned. Most of the group never stops.

This is the strategy to reach for in modern deployments. It dramatically reduces rebalance impact during scaling, deploys, and brief network blips — exactly the events that happen constantly in container orchestrators.

Switching an existing group to CooperativeStickyAssignor requires a two-phase rolling upgrade, not a single restart. First deploy every member with both assignors listed — [CooperativeStickyAssignor, StickyAssignor] (eager-compatible) — then in a second deploy remove the eager assignor. Jumping straight to cooperative-only across a mixed group corrupts assignment.

Strategy comparison

StrategyProtocolBalanceStickinessStop-the-world?Best for
RangeAssignorEagerPoor across many topicsLowYesCo-partitioned joins; legacy defaults
RoundRobinAssignorEagerEvenLowYesEven spread when churn is rare
StickyAssignorEagerEvenHighYesEven spread with less re-seek cost
CooperativeStickyAssignorCooperativeEvenHighNo (incremental)The default for modern apps

Configuring it in Spring Boot

In Spring for Apache Kafka the assignor is just another consumer property, set under spring.kafka.consumer.properties.

# application.yml
spring:
  kafka:
    bootstrap-servers: broker1:9092,broker2:9092
    consumer:
      group-id: order-processor
      properties:
        partition.assignment.strategy: org.apache.kafka.clients.consumer.CooperativeStickyAssignor

You can confirm the negotiated strategy from the consumer’s startup logs.

Output:

INFO  o.a.k.clients.consumer.ConsumerConfig - ConsumerConfig values:
        partition.assignment.strategy = [class org.apache.kafka.clients.consumer.CooperativeStickyAssignor]
INFO  o.a.k.c.c.internals.ConsumerCoordinator - [Consumer clientId=consumer-order-processor-1, groupId=order-processor] (Re-)joining group
INFO  o.a.k.c.c.internals.ConsumerCoordinator - [Consumer clientId=consumer-order-processor-1, groupId=order-processor] Adding newly assigned partitions: orders-0, orders-1

Best Practices

  • Default to CooperativeStickyAssignor for new groups; it gives even, sticky assignment without stop-the-world pauses.
  • Migrate to cooperative in two phases — deploy with [CooperativeStickyAssignor, StickyAssignor] first, then remove the eager assignor — never in a single mixed-protocol restart.
  • Keep partition.assignment.strategy identical (or compatibly ordered) across every member of a group; mismatches cause failed or lopsided joins.
  • Pair cooperative rebalancing with rebalance listeners that commit offsets only for revoked partitions, so retained partitions keep flowing.
  • Prefer StickyAssignor over RoundRobinAssignor if you must stay on the eager protocol, to cut needless re-seeks.
  • Reserve RangeAssignor for cases where co-partitioned topics must align partition-for-partition on the same consumer.
Last updated June 1, 2026
Was this helpful?