Intro
This guide is for engineers who already ship to Kafka and want to operate it with confidence at scale. We go beyond basics to explain how Kafka actually moves bytes and metadata, why internal algorithms look the way they do, and how to make pragmatic trade-offs in production. You will learn the mechanics behind replication, leader election, KRaft, compaction, batching, idempotence, transactions, consumer rebalancing, and the ecosystem you need to run a dependable event streaming platform.
Producer -> Partitioner -> Leader Broker -> Followers (ISR) -> Consumer Group -> Downstream Services
Architecture and Log Internals
Kafka is a distributed, replicated, append-only log. Each partition has a single leader and zero or more followers. Followers in the In-Sync Replica set (ISR) fetch from the leader. Two offsets are essential:
- LEO (log end offset): next append position for each replica.
- High Watermark (HW): last offset replicated to all ISR members; consumers read up to HW for durability.
Why this design exists: append-only + page cache + sequential I/O yields predictable latency. Followers pull (not push) via the fetch protocol, decoupling write paths from replication backpressure.
Segments and retention: a partition is a sequence of segment files (log + index). Retention trims old segments by time or size. Compaction is orthogonal: compacted topics keep the latest record per key; tombstones (key with null value) delete keys after compaction.
Zero-copy: Linux sendfile transfers bytes from page cache to the socket without user-space copies, reducing CPU and GC.
Replication protocol basics:
- Leader appends batches, advances LEO.
- Followers fetch, write, fsync per policy, advance their LEO.
- Controller tracks ISR membership; HW advances to min(LEO over ISR).
ISR shrink/expand:
- Shrink: follower falls behind (replica.lag.time.max.ms) or errors out.
- Expand: follower catches up to HW and passes health checks.
Leader election (KRaft or ZooKeeper):
- Detect failure via heartbeats.
- Pick a leader from ISR; prefer the most up-to-date.
- Update metadata and publish to brokers.
- Clients refresh metadata and retry.
KRaft and Controller Quorum
Modern Kafka (KRaft) removes ZooKeeper. Brokers include a controller role and participate in a Raft-like consensus quorum for metadata.
[ Controller Quorum ] <-- Raft log of metadata
| commits
[ Brokers (data + metadata caches) ]
Why KRaft: simpler ops, tighter consistency model for metadata, faster metadata propagation, and easier scale. Migration: run mixed mode, snapshot metadata, switch the controller quorum, and decommission ZooKeeper. Use kafka-metadata-shell to inspect the metadata log.
Tiered storage (modern Kafka): cold segments move to object storage, keeping hot data local. This separates retention from disk sizing and enables long histories without oversized brokers.
Producer Semantics That Matter
Acks and durability:
- acks=0: fire-and-forget, high loss risk.
- acks=1: leader-only durability; can lose data on leader failover.
- acks=all: quorum durability; use with min.insync.replicas (mISR) >= 2 in prod.
Batching and latency:
- batch.size: target batch bytes per partition.
- linger.ms: wait time to form a batch; increases throughput, adds small latency.
- compression.type: lz4/zstd reduce bytes and improve throughput; monitor CPU.
Idempotent producer: enables sequence numbers and producer IDs (PIDs) to dedupe retries per partition, eliminating duplicates on retry. Turned on by enable.idempotence=true.
Transactions: group writes to multiple partitions + offset commits for Exactly-Once Semantics (EOS) across a topology. Requires transactional.id and idempotence.
Practical example:
# Durable topic with quorum writes
kafka-topics --create --topic orders --partitions 12 --replication-factor 3 \
--bootstrap-server :9092
kafka-configs --alter --topic orders --add-config min.insync.replicas=2 \
--bootstrap-server :9092
# Produce with batching and compression
kafka-console-producer --topic orders --bootstrap-server :9092 \
--producer-property acks=all \
--producer-property linger.ms=5 \
--producer-property compression.type=zstd
Consumers, Offsets, and Rebalancing
Consumer groups coordinate partition ownership. Offsets are stored in __consumer_offsets. Rebalancing algorithms:
- Range/round-robin: simple, can cause thrash.
- Sticky assignment: preserves prior ownership to reduce cache churn.
- Cooperative (incremental) rebalancing: members give up partitions gradually, avoiding stop-the-world pauses.
Static membership: assign group.instance.id to pin a consumer to its identity through restarts, cutting down on rebalances.
Heartbeat protocol: consumers poll and heartbeat; missed heartbeats mark a member dead and trigger rebalancing. Tune session.timeout.ms and heartbeat.interval.ms.
Fetch sessions: broker-side caching of fetch state reduces metadata overhead and CPU for large groups.
Diagnostic command:
kafka-consumer-groups --bootstrap-server :9092 --describe --group orders-app
Transactions and Exactly-Once, Step by Step
[ Txn Producer ] --beginTxn--> [ Txn Coordinator ] --PID/epoch--> [ Brokers ]
|--produce--> temp state
|--sendOffsetsToTxn--> atomically write output + offsets
|--commitTxn--> visible to readers
Algorithm:
- Producer starts a transaction (beginTransaction), gets PID/epoch.
- Writes to target partitions; broker tracks pending state.
- Producer sends offsets to the transaction for consumed input.
- Commit marks records + offsets atomically visible; abort discards pending writes.
When to use: cross-partition processors that must avoid duplicates and gaps. Avoid for firehose ingestion where idempotent producer is sufficient.
Ecosystem Components You Will Actually Use
- Schema Registry: enforce compatibility (backward/forward). Prevents poison messages and enables safe evolution.
- Kafka Connect: pluggable source/sink framework. Use distributed mode, DLQ on sink errors, and config providers for secrets.
- Debezium: CDC on top of Connect for MySQL/Postgres/etc. Feed event sourcing or search indexes.
- Kafka Streams: JVM library for stateful processing with EOS. Great for embeddable microservice topologies.
- ksqlDB: SQL-on-streams for rapid pipelines; good for ops teams and quick joins/aggregations.
- MirrorMaker 2 / Cluster Linking: inter-cluster replication and DR. Use Cluster Linking for metadata-aware replication and simpler failover.
- Flink / Spark: large-scale, multi-language stream processing; integrate via Kafka source/sink connectors.
- Kubernetes: run with StatefulSets, PodDisruptionBudget, hostPath or fast block volumes, and a LoadBalancer/NodePort + advertised.listeners aligned with client networks.
Production Patterns (Concise Blueprints)
- E-commerce: orders topic (compacted) + payments (append). Saga orchestrator emits state transitions; outbox ensures atomic write to DB and Kafka. Consumers update inventory and shipping. DLQs per service.
- Banking: transactions topic with mISR=2, rack-aware RF=3. Fraud detection in Flink; EOS-enabled enrichment stream writes to alerts.
- IoT: device-telemetry with tiered storage for 180 days. Edge batching (linger.ms=20, zstd) reduces bandwidth; ksqlDB aggregates per-minute.
Troubleshooting: Fast Paths
- High consumer lag
- Symptoms: growing lag, stable broker CPU.
- Diagnosis: max.poll.interval.ms too low, processing slower than poll; or fetch.max.wait.ms too high.
- Commands: kafka-consumer-groups --describe; check app logs for rebalance.
- Metrics: records-lag-max, fetch-latency-avg, processing latency.
- Fix: increase max.poll.interval.ms, scale out consumers, reduce per-record work; use cooperative rebalancing.
- ISR shrinking
- Symptoms: replicas frequently leave ISR.
- Cause: slow disks/network, GC pauses.
- Metrics: under-replicated-partitions, request-queue-time, flush latency.
- Fix: tune GC, move to NVMe, increase replica.lag.time.max.ms cautiously, ensure network buffers.
- Leader imbalance
- Symptoms: some brokers hotter.
- Diagnosis: partition leadership skew.
- Commands: kafka-preferred-replica-election (or KRaft auto-balancer), kafka-topics --describe.
- Fix: enable self-balancing or run leader reassignments regularly.
- Rebalance storms
- Symptoms: frequent pauses, throughput dips.
- Cause: short session.timeout.ms, container restarts, autoscaling churn.
- Fix: static membership, cooperative rebalancing, stabilize autoscalers, increase timeouts.
- Large messages
- Symptoms: RecordTooLarge, high memory/GC.
- Fix: raise max.message.bytes and fetch limits only if necessary; prefer chunking or external blob store + reference.
- Schema incompatibility
- Symptoms: deserialization failures in consumers.
- Fix: enforce compatibility in Schema Registry; use subject-per-topic with backward compatibility for consumers.
Performance and Operations
Tuning quick wins:
- Producers: acks=all, linger.ms=5-20, batch.size=32-128KB, compression=zstd/lz4.
- Consumers: fetch.min.bytes=1KB-64KB, fetch.max.wait.ms=20-50ms, max.partition.fetch.bytes sized to record.
- Brokers: num.partitions per topic sized for parallelism but avoid > few thousands per broker. Use page cache (avoid swapping), send/receive socket buffers (e.g., 1-4MB), and GC tuned for throughput (G1/ZGC).
- Filesystem: XFS/ext4, noatime, NVMe for log.dirs. Separate disks for data vs OS if possible.
Operational practices:
- Capacity planning: model ingress/egress, replication factor, retention, compression ratio; leave 30-40% free disk.
- Partition sizing: start 2-3x expected consumer parallelism; allow headroom for growth.
- Retention: time + size; enable compaction for key-based state; monitor cleaner IO.
- Replication & DR: RF=3, mISR=2. Cross-region via Cluster Linking or MM2 with per-topic ACLs and lag monitoring.
- Upgrades: rolling with protocol compatibility; read release notes for inter-broker protocol and message format.
- Security: TLS everywhere, SASL (OIDC/SCRAM), ACLs by principle of least privilege; audit logs.
- Observability: JMX + logs + tracing; alert on under-replicated-partitions > 0, offline-partitions > 0, request queue time, network/disk saturation, controller health.
Comparisons (When to Pick What)
- Kafka vs RabbitMQ/NATS/ActiveMQ: Kafka wins for durable, replayable, high-throughput logs; MQs are better for low-latency RPC-like work queues.
- Kafka vs Pulsar: Pulsar has segment-tier separation and built-in geo-replication; Kafka KRaft + tiered storage closes gaps. Kafka has broader ecosystem.
- Kafka vs Kinesis/Event Hubs: managed, fast start; Kafka offers portability, richer semantics, and no vendor limits.
- Kafka Connect vs custom: prefer Connect for maintained connectors, DLQ, scaling; custom only for niche protocols.
- Kafka Streams vs Flink/Spark: Streams for embedded microservices and EOS simplicity; Flink for complex stateful analytics, exactly-once at scale, multi-tenant clusters; Spark for batch-first shops.
FAQs (Advanced)
- How many partitions should I create? Start with 2-3x consumer concurrency; watch p99 latency and controller load; avoid thousands per broker.
- When should I use log compaction? When you need latest-by-key state or CDC upserts; not for large binary payloads.
- Can Kafka guarantee ordering? Per partition yes; use correct keys and keep partitions per key small.
- How does Kafka achieve durability? Replicated append-only logs + HW read-bound + fsync policies + acks=all + mISR.
- Should every topic use the same replication factor? No; critical topics RF=3, ephemeral RF=2, dev RF=1.
- How does KRaft differ from ZooKeeper? Integrated Raft quorum for metadata, simpler ops, faster failover; no external ZK.
- How do transactions actually work? PID/epoch + pending writes + atomic commit of records and offsets via Transaction Coordinator.
- How does idempotence work? Per-partition sequence numbers dedupe retries; guarantees exactly-once per partition on a single producer session.
- What causes consumer lag? Slow processing, small fetch, GC pauses, rebalances; diagnose with consumer group and broker fetch metrics.
- How do I handle poison messages? Use DLQs with headers, limit retries, alert on DLQ volume, and fix upstream schema.
Conclusion
Running Kafka well is about aligning internals with your requirements: acks with mISR for durability, batching for throughput, compaction for state, cooperative rebalancing for stability, and KRaft for operational simplicity. Start with clear SLAs, model throughput and retention, pick patterns that match your domain, and automate the boring parts: schemas, observability, DR, and upgrades. Then verify every assumption with commands, metrics, and failure drills before traffic grows.