
Status
Current state: Under discussion
Discussion thread: here
JIRA: here
Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).
Summary
Today, kafka-reassign-partitions.sh --execute submits all partition reassignments from the JSON in one AdminClient#alterPartitionReassignments call (unless the user manually splits work). For large clusters this can create large bursts of replication traffic and controller work.
This KIP proposes tool-only pacing controls:
- --reassignment-batch-size — caps how many topic partitions are submitted per step (semantics differ slightly depending on mode; see below). 0 preserves legacy behavior (single request for the entire plan).
- --incremental — optional mode used with --reassignment-batch-size > 0 to keep at most N partition reassignments in flight for this execution, submitting the next partition from a deterministic queue when a slot frees up.
Ordering for both modes is (topic name, partition index), not the order of entries in the JSON file.
No broker protocol, controller, or metadata changes are required; pacing is implemented entirely in the reassignment tool using existing Admin APIs (alterPartitionReassignments, listPartitionReassignments, metadata reads).
Motivation
Problem
- Operational risk: Submitting hundreds or thousands of partition reassignments in one RPC can stress replication links, disk, and the controller in ways that are hard to predict during maintenance windows.
- Limited operator control: Throttling (--throttle) limits bandwidth but does not limit how many partitions are simultaneously moving; operators often want serialised waves or bounded concurrency without hand-splitting JSON files.
- Manual workarounds: Teams split reassignment JSON by hand or wrap the tool in scripts that call alterPartitionReassignments in chunks — error-prone and inconsistent across deployments.
Goals
- Provide first-class, documented pacing in the supported reassignment tool.
- Preserve full backward compatibility when defaults are unchanged (--reassignment-batch-size 0, no --incremental).
- Keep behavior deterministic and explainable (stable partition ordering).
Non-goals
- Replacing cluster-wide replication quotas or broker-side limits.
- Changing how the controller executes reassignments (same server-side semantics).
- Global coordination across multiple concurrent tool processes (each execute remains independent).
Public interfaces
CLI (kafka-reassign-partitions.sh)
Option | Applies to | Semantics |
--reassignment-batch-size <int> | --execute only | - Default 0: legacy — entire plan in one alterPartitionReassignments request; no extra waits in the tool.
- > 0 without --incremental: split the plan into contiguous batches of at most N partitions (sorted by topic, then partition id). After each batch except the last, the tool blocks until every partition in that batch reports complete (current ISR matches target and reassignment not active) before submitting the next batch.
- > 0 with --incremental: N is the maximum number of partition reassignments from this JSON that may be active at once; when one completes, the tool submits the next partition from the sorted queue.
|
--incremental | --execute only | Requires --reassignment-batch-size > 0. Mutually exclusive interpretation of batch size as in-flight cap (see above). |
Validation and compatibility
- --reassignment-batch-size must be ≥ 0. Negative values are rejected.
- --incremental without --reassignment-batch-size > 0 is rejected at argument validation time.
- --reassignment-batch-size and --incremental are not permitted with --list, --generate, --verify, or --cancel (same pattern as other execute-only options).
Programmatic API
ReassignPartitionsCommand.executeAssignment gains parameters:
- int reassignmentBatchSize
- boolean incremental
Existing callers that omit pacing pass 0 and false to preserve legacy behaviour.
Detailed design
Partition counting
One row in the JSON (one TopicPartition) = one partition reassignment for pacing purposes, regardless of how many brokers join or leave the replica set in that single move.
Batch construction (non-incremental, reassignmentBatchSize > 0)
- Build the map of TopicPartition → target replicas from the JSON.
- Split into batches: sort keys with compareTopicPartitions, then chunk in groups of N partitions.
- For each batch except the last:
alterPartitionReassignments(batch) → wait until all partitions in that batch are complete → next batch. - For the last batch:
alterPartitionReassignments only — the tool does not wait for completion before printing success (same family as legacy “started” messaging). Operators should run --verify for full completion.
Incremental mode (--incremental)
- Sort partitions deterministically (compareTopicPartitions).
- Maintain a pending deque and an in-flight map (submissions for this execute only).
- Loop until pending is empty: remove completed partitions from in-flight (using the same completion predicate as non-incremental wait paths), then submit new partitions up to the N in-flight cap.
- Poll interval between iterations when work remains: INCREMENTAL_REASSIGNMENT_POLL_INTERVAL_MS (500 ms in the default implementation).
Semantics: The tool returns after all partitions in this JSON have been successfully submitted to alterPartitionReassignments, not necessarily after all replication has finished (consistent with legacy execute for “completion” of submission).
Non-incremental wait between batches
- Poll interval: BATCH_REASSIGNMENT_POLL_INTERVAL_MS (500 ms in the reference implementation).
- Completion uses existing findPartitionReassignmentStates / PartitionReassignmentState logic; inconsistent terminal states produce TerseException (same class of errors as today’s verify path).
Interaction with --additional
--additional only bypasses the “existing reassignment on cluster” guard. It does not merge pacing across concurrent executes: --reassignment-batch-size applies per process invocation to that JSON. Multiple overlapping executes can each contribute up to N in-flight partition reassignments from their respective plans.
Interaction with throttles
Existing --throttle / --replica-alter-log-dirs-throttle behaviour is unchanged; pacing is orthogonal and can be combined.
Compatibility
- Default CLI: unchanged legacy path (reassignment-batch-size defaults to 0; incremental absent).
- Brokers / ZK / KRaft: no change.
- Wire protocol: unchanged (same Admin APIs).
Limitations and future work
- No timeout on batch-completion waits in the reference implementation; stuck reassignments can poll indefinitely until operator intervention (same class of risk as long-running admin operations without deadlines).
- Incremental + --additional: total cluster in-flight work can exceed N when multiple tool processes run overlapping plans; documentation / optional warnings are recommended.
Rejected alternatives
- Only documentation: “Split your JSON manually” — does not scale and yields inconsistent operations.
- New broker-side “max concurrent reassignments” quota: much larger scope; tool-side pacing addresses the common case without protocol work.
- Order = JSON file order only: rejected in favour of deterministic sorted order so behaviour is reproducible and independent of file editing.
Test plan
- Unit tests: batch splitting; incremental ordering; failure on second batch; completion predicate shared between wait and incremental removal.
- Args tests: invalid combinations; execute-only restriction for new flags.
- Integration / cluster tests: execute with small batch sizes (including batch size 1) in KRaft and ZK modes where the project already runs ClusterTest.
- Manual: large plan with --reassignment-batch-size and --list / --verify to observe waves and completion.
Documentation
- Extend the kafka-reassign-partitions section of the Kafka documentation / ops guides: semantics of 0, non-incremental batching, incremental, --additional, and verification with --verify.