← Back to list

Reasons Increasing Kafka Partitions Is Easy on Paper but Messy in Production

Anh Trần Tuấn · 2026-07-19 09:00 · 0 claps · 7.6 min read paywalled
#message-broker #performance #scalability #availability #deployment
Open on Medium ↗

Reasons Increasing Kafka Partitions Is Easy on Paper but Messy in Production

Source: Reasons Increasing Kafka Partitions Is Easy on Paper but Messy in Production

Nobody ever planned for the quiet moment when a traffic spike forces a team to “just add a few partitions.” The request sounds harmless: more partitions equals more parallelism. On whiteboards and architecture diagrams that is a tidy, almost magical lever. In the field, however, that same lever is attached to a system with state, replicas, rebalances and human operators — and those attachments make the change feel like open-heart surgery.

1. Why the idea looks simple

At a glance the reasoning is straightforward: a partition is the unit of parallelism and ordering in Kafka. More partitions can spread produce/consume load across more brokers and more consumer threads, raising throughput and reducing per-partition hotspotting. The broker API even exposes a single operation to increase partition count. That is the “paper” simplicity.

1.1 The operation you run (conceptually)

Conceptually the command is a one-liner: change metadata for a topic so it has N partitions. Kafka’s metadata and controller will accept that request and create partition metadata and initial replicas. On modern clusters you can run the change via CLI or AdminClient and Kafka will create partition directories and assign replicas automatically (or via reassignment).

1.2 Why that hides complexity

Increasing partitions touches three moving systems at once: brokers (metadata + disk + replication), producers (partitioner decisions), and consumers (assignment + ordering expectations). Each system has its own constraints, resources, and failure modes. These cross-cutting effects are what make an otherwise trivial metadata mutation messy in production.

2. The operational pitfalls that bite teams

2.1 Consumer rebalances and processing gaps

When partitions are added, consumer groups subscribing to that topic rejoin the group and experience a rebalance. For stateful consumers or stream processors this can result in duplicated processing, lost in-flight work, or long processing pauses. If your consumer logic commits offsets incorrectly during a rebalance, you can replay or skip messages.

2.2 Ordering and key distribution changes

Applications that rely on per-key ordering assume the same partitioner and same number of partitions. Increasing partitions doesn’t move existing messages, but it changes the mapping for new messages. A key that hashed to partition 5 with 8 partitions might hash to a different partition with 16 partitions, breaking ordering guarantees across the point of change for that key’s subsequent messages. This is especially visible with hash-based partitioners.

2.3 Uneven load / hot-spotting

More partitions do not always mean more evenly distributed load. If partitioning is based on a small keyspace, added partitions can remain cold while a few partitions stay hot. Also, creating many small partitions increases controller and replica overhead — CPU, memory and file-descriptor pressure rise roughly with partition count.

2.4 Replication and ISR lag

Each new partition adds replicas that must be replicated across brokers. Large-scale increases trigger many replication streams, possible network saturation, and increased disk I/O. Under-replicated partitions (URP) spike if brokers can’t keep up, and that creates availability risk. During reassignment you can hit the controller with a spike in tasks that increases controller CPU and delays other metadata updates.

2.5 Broker resource and OS-level limits

Partitions mean log segments and open file descriptors. On Linux the number of files per broker increases and causes increased memory pressure for page cache and metadata structures. If you don’t model per-partition overhead (heap usage for in-memory partition structures, file descriptors, disk throughput), a partition bump can push brokers past practical limits.

3. Concrete Java example: increasing partitions with AdminClient

Here is a compact Java example showing how to request more partitions programmatically. After the snippet you’ll find a line-by-line explanation and operational caveats.

import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.CreatePartitionsResult;
import org.apache.kafka.clients.admin.NewPartitions;

import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "broker1:9092,broker2:9092");
try (AdminClient admin = AdminClient.create(props)) {
    Map<String, NewPartitions> newParts = Collections.singletonMap("my-topic", NewPartitions.increaseTo(20));
    CreatePartitionsResult result = admin.createPartitions(newParts);
    // Block until the controller acknowledges or timeout
    result.all().get(30, TimeUnit.SECONDS);
}

Explanation and important caveats:

  • AdminClient bootstrap: you must connect to the cluster’s bootstrap brokers. If the controller is under load the call can take time or fail transiently.
  • NewPartitions.increaseTo(): this only increases the partition count; it does not rebalance existing partition replicas. New partitions are created with replica assignments generated by the controller’s default algorithm, which may place leaders on busy brokers.
  • result.all().get(): this waits for the controller to update metadata. In large clusters the operation can be slow; do not assume immediacy in production. Consider asynchronous handling and a retry/backoff strategy.
  • No data movement for old partitions: existing messages remain in their partitions; new partitions start empty. If you were expecting automated redistribution of historical data to balance disk usage, that does not happen automatically.
  • Permissions and broker config: your principal must be authorized to alter topics. Broker config settings like auto.create.topics.enable or controller throttles can affect behavior.

4. Performance behavior: what changes under the hood

4.1 Controller and metadata CPU cost

Controller work scales roughly with the number of partitions because it maintains in-memory partition metadata and coordinates replica state changes. Bulk partition increases cause a temporary spike in controller tasks (creating partition directories, initializing replicas, writing metadata to Zookeeper or the internal __cluster metadata topic in KRaft), which can spike CPU and delay other operations such as leader election.

4.2 Disk and network IO

Each new partition triggers log segment creation and initial replication traffic. If you increase many partitions concurrently, disk write throughput and replication network traffic can spike, potentially causing I/O contention and increased producer/consumer latencies. Also, fragmented small segments can be less efficient for compaction and IO patterns.

4.3 Memory and file descriptor pressure

Kafka tracks some per-partition structures in heap. While each partition may only consume a small amount individually, tens or hundreds of thousands of partitions multiply that into significant heap usage. Similarly, OS-level file descriptor limits per-broker can be reached if open files (segments, index files) increase quickly.

4.4 Client-side throughput changes

Producers that use a default hash partitioner will change partition selection pattern after adding partitions, which affects batching efficiency and throughput. Consumers using a fixed thread-per-partition model may see increased thread usage or need reconfiguration when the assigned partition count per consumer changes.

5. Java example: consumer rebalance handling after partition changes

This snippet shows a consumer subscribing with a rebalance listener that handles newly assigned partitions conservatively. The comments explain why each step matters when partitions are added.

import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;

import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;

Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092");
props.put("group.id", "my-group");
props.put("enable.auto.commit", "false");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

KafkaConsumer<String,String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("my-topic"), new ConsumerRebalanceListener() {
    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
        // commit the work we've done so we don't reprocess after rebalance
        consumer.commitSync();
    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
        for (TopicPartition tp : partitions) {
            OffsetAndMetadata committed = consumer.committed(tp);
            if (committed == null) {
                // New partition: choose safe starting point
                consumer.seekToEnd(Collections.singleton(tp));
            } else {
                // Resume from committed offset
                consumer.seek(tp, committed.offset());
            }
        }
    }
});

// consumer loop omitted for brevity

Detailed explanation:

  • enable.auto.commit=false: manual committing gives you control during rebalances; trusting auto-commit risks either duplicate or lost processing around the rebalance boundary.
  • onPartitionsRevoked: committing here flushes work before the group membership changes. If you have non-idempotent side-effects you must ensure proper transactional/at-least-once semantics here.
  • onPartitionsAssigned: newly assigned partitions may be ones created after you started the consumer. Committed offsets can be null for those partitions — you need an explicit decision: start at beginning, end, or a stored application-specific pointer. Choosing seekToEnd avoids reprocessing old messages (the new partitions are empty immediately after create), but in some workflows you want to start from beginning.
  • Edge case: consumer.committed(tp) can throw if broker unavailable; defensive code should handle transient errors and possibly retry with backoff.

6. Practical strategies and trade-offs

6.1 Plan the partition growth pattern

Avoid sudden, large jumps in partition count. Increase partitions incrementally and observe broker/consumer metrics at each step. Create partitions during low-traffic windows and throttle replica migration if you also run partition reassignment.

6.2 Consider creating a new topic and migrating

If ordering guarantees and replica alignment are critical, often the safer approach is to create a new topic with the desired partition count and move producers and consumers to it gradually. This avoids rehashing semantics for historic keys, lets you control data migration, and allows cutover testing.

6.3 Throttle replication and use reassignment planners

When performing large-scale partition/reassignment, use the partition reassignment tool to compute balanced replica placement and set replication throttles (producer/replica throttles) to limit I/O spikes. This reduces the risk of saturating broker disks or network links.

6.4 Update consumers and partitioners deliberately

Think through your partitioner (custom key -> partition logic). If you must change partition count, document and version partitioner behavior or use a consistent hashing technique that can minimize key movement (e.g., virtual nodes) if your system requires it — but note this has trade-offs in complexity and testability.

6.5 Monitor the right signals

Key metrics to track during partition increases: under-replicated partitions, leader election rate, controller CPU, replication bytes-in/out, produce/consume latency (99th percentile), broker network I/O, disk utilization, file descriptors, and consumer rebalance rates. Alert thresholds should be conservative during and after the change.

7. Edge cases and gotchas

7.1 Compacted topics and tombstones

Compacted topics rely on log compaction to deduplicate by key. Adding partitions doesn’t move keys between partitions retroactively, so compaction efficiency may change for new keys mapped differently. Careful thought is required when reassigning or migrating compacted data.

7.2 Transactions and exactly-once semantics

Kafka transactions and EOS semantics assume stable partition assignments between transactional produce and commit. A rebalance or sudden reassignment may interrupt transactional flows and cause aborts. If your producers use transactions, coordinate partition increases with your transaction-aware clients and test failover scenarios.

7.3 Streams applications and task mapping

Kafka Streams maps partitions to tasks. Changing partition counts changes task parallelism and can cause repartitioning and state stores to be reinitialized. The upgrade path for Streams apps should include state restoration strategies and controlled rebalances to avoid long application downtime.

8. Rule-of-thumb numbers and capacity planning

Every environment is different, but these practical heuristics help:

  • Estimate per-partition memory and file-descriptor overhead and multiply by max partitions/broker to ensure headroom.
  • Avoid more than thousands of partitions per broker unless you have tuned heap and OS limits; clusters with tens of thousands of partitions are achievable but require careful tuning and strong operational practices.
  • When increasing partitions by more than ~10–20% at once, treat it as a significant operational change requiring monitoring and rollback plans.

9. Checklist before you run an increase

  1. Run in staging with production-like load and consumer topology.
  2. Ensure consumers implement a robust rebalance listener and commit strategy.
  3. Plan for and throttle replica creation if necessary.
  4. Verify OS limits (ulimit, file descriptors) and broker heap configuration.
  5. Notify downstream teams and schedule during low-traffic windows.
  6. Monitor URP, controller CPU, produce/consume latency, and network/disk metrics.

10. Final thoughts

Increasing Kafka partitions is an easy sentence and a small API call, but the operation ripples across distributed state, clients, and infrastructure. Treat partition increases as a first-class operational change: plan, test, throttle, monitor, and if guarantees matter, prefer migration patterns that avoid rehash-induced ordering breakage. The whiteboard simplicity is a helpful guide, but production reality demands careful choreography.

If you have questions about a particular scenario or want help designing a safe partition-growth plan for your cluster, please leave a comment and I’ll respond.

If my articles have been valuable to you, I’d be deeply grateful for your support at here . Your encouragement fuels my passion for creating even more insightful and high-quality content!


메타데이터
post_id
a08fcb66b179
slug
reasons-increasing-kafka-partitions-is-easy-on-paper-but-messy-in-production-a08fcb66b179
url
https://medium.com/@tuananhbk1996/reasons-increasing-kafka-partitions-is-easy-on-paper-but-messy-in-production-a08fcb66b179
canonical_url
https://medium.com/@tuananhbk1996/reasons-increasing-kafka-partitions-is-easy-on-paper-but-messy-in-production-a08fcb66b179
author_url
https://medium.com/@tuananhbk1996
status
ok
fetched_at
2026-08-19 18:15:50