DE Kafka Producers and Consumers

Understanding Kafka at a conceptual level is a starting point. Building production systems requires knowing how producers and consumers actually work — how producers guarantee delivery, how consumers track progress, how both handle failures, and how to tune them for throughput or latency. These details separate a working proof-of-concept from a reliable production pipeline.

Kafka Producers in Depth

A Kafka producer is any application that writes events to a Kafka topic. The producer connects to the Kafka cluster, serializes the event data into bytes, selects the target partition, and sends the event. Kafka's producer API handles batching, compression, retries, and acknowledgment internally.

Partitioning Logic

When a producer sends a message with a key, Kafka hashes the key and routes the message to a consistent partition. All messages with the same key always land in the same partition — maintaining order for that key across all messages. When no key is specified, Kafka distributes messages across partitions in round-robin fashion.

Partitioning by Key:

Producer sends orders with customer_id as key:
  customer_id="C001" --> hash("C001") % 3 = 1 --> Partition 1
  customer_id="C001" --> hash("C001") % 3 = 1 --> Partition 1
  customer_id="C002" --> hash("C002") % 3 = 0 --> Partition 0
  customer_id="C003" --> hash("C003") % 3 = 2 --> Partition 2

Result: All orders for C001 land in Partition 1, always in order.
A consumer reading Partition 1 sees C001 events in arrival sequence.

Acknowledgment (acks) Settings

The acks setting controls how many broker acknowledgments the producer waits for before considering a message successfully sent. This setting directly trades durability against throughput.

acks=0  : Producer sends and forgets. No acknowledgment waited.
          Highest throughput. Risk: messages can be lost if broker fails.
          Use for: Non-critical logs, metrics where some loss is acceptable.

acks=1  : Producer waits for acknowledgment from the leader broker only.
          Moderate throughput. Risk: data loss if leader fails before replication.
          Use for: General purpose; balanced trade-off.

acks=all: Producer waits for acknowledgment from all in-sync replicas.
          Lowest throughput. No data loss as long as one replica survives.
          Use for: Financial data, order events, critical pipelines.

Batching and Compression

Kafka producers batch multiple messages together before sending. Sending 1,000 messages as one batch is far more efficient than 1,000 individual network calls. The linger.ms setting controls how long the producer waits to fill a batch before sending. Higher values increase latency slightly but improve throughput significantly for high-volume systems.

Producer Tuning for Throughput:
  batch.size=1048576     # 1MB batch size
  linger.ms=20           # Wait 20ms to fill batch
  compression.type=snappy # Compress batches (reduces network I/O)

Producer Tuning for Low Latency:
  batch.size=16384       # Small batch size
  linger.ms=0            # Send immediately (no waiting)
  compression.type=none  # Skip compression overhead

Idempotent Producer

Without idempotency, network retries can cause duplicate messages. If the broker received a message but its acknowledgment was lost in transit, the producer retries and the broker stores it twice. Enabling the idempotent producer gives each message a unique sequence number; the broker deduplicates automatically.

Enable idempotent producer:
  enable.idempotence=true

Effect: Each message gets a producer ID + sequence number.
Broker rejects duplicate sequence numbers from the same producer.
Result: Exactly-once delivery from producer to broker.

Kafka Consumers in Depth

A Kafka consumer reads events from one or more partitions. It tracks its position using offsets and commits those offsets to Kafka so it can resume after a restart without reprocessing or losing events.

Poll Loop

Consumer applications run a continuous poll loop. The consumer calls poll() to fetch a batch of records from Kafka, processes them, commits the offset, and calls poll() again. The poll interval must stay frequent enough to prevent Kafka from considering the consumer dead and reassigning its partitions.

Consumer Poll Loop (Python using confluent-kafka):

from confluent_kafka import Consumer

consumer = Consumer({
    'bootstrap.servers': 'kafka-broker:9092',
    'group.id': 'warehouse-loader-group',
    'auto.offset.reset': 'earliest'
})
consumer.subscribe(['orders.created'])

while True:
    msg = consumer.poll(timeout=1.0)    # Wait up to 1s for a message
    if msg is None:
        continue
    if msg.error():
        handle_error(msg.error())
        continue

    event = json.loads(msg.value())      # Deserialize the event
    load_to_warehouse(event)             # Process the event
    consumer.commit()                    # Commit offset after success

Offset Management

Offsets track how far a consumer group has read in each partition. Committed offsets persist in Kafka's internal __consumer_offsets topic. On restart, the consumer fetches the last committed offset and resumes from there.

Offset Commit Strategies:

Auto-commit (enable.auto.commit=true):
  Kafka commits offsets automatically every 5 seconds.
  Risk: Consumer crashes after commit but before processing --
        those messages are skipped (at-most-once delivery).

Manual commit after processing:
  Consumer commits only after successfully processing each batch.
  Guarantees at-least-once delivery (may reprocess on crash).
  Code: consumer.commit() after processing each poll() result.

Transactional (exactly-once):
  Commit offset and write output atomically in one transaction.
  Guarantees each event processed exactly once.
  Requires Kafka transactions + idempotent consumer logic.

Consumer Group Rebalancing

When a consumer joins or leaves a group, Kafka reassigns partitions among the remaining members. During a rebalance, all consumers in the group pause, partitions redistribute, and processing resumes. Rebalances cause brief processing gaps. Data engineers minimize rebalances by keeping consumer instances stable and tuning heartbeat and session timeout settings.

Consumer Group Rebalance Scenario:

Before: 3 consumers, 6 partitions
  Consumer A: Partitions 0, 1
  Consumer B: Partitions 2, 3
  Consumer C: Partitions 4, 5

Consumer B crashes --> Rebalance triggers

After: 2 consumers, 6 partitions
  Consumer A: Partitions 0, 1, 2
  Consumer C: Partitions 3, 4, 5

Delivery Semantics

Semantic          | How Achieved               | Risk
------------------|----------------------------|---------------------------
At-most-once      | Auto-commit before process | Data loss on crash
At-least-once     | Manual commit after process| Duplicate processing on crash
Exactly-once      | Idempotent producer +      | No loss, no duplicates
                  | transactional API          | Higher complexity and cost

Dead Letter Queues

Some events cannot be processed — malformed JSON, unexpected schema, business logic violations. Rather than failing the entire consumer or silently dropping bad events, producers send failed events to a separate "dead letter" topic. An engineer inspects and replays or discards events from the dead letter queue after investigation.

Dead Letter Queue Pattern:

Normal flow: orders.created --> Consumer --> Data Warehouse
Failure flow: orders.created --> Consumer --> FAILS to parse
                            --> Publish to orders.created.dlq
                            --> Engineer investigates dlq
                            --> Fix and replay valid events

Monitoring Producers and Consumers

Consumer lag is the most critical metric to monitor — it measures how far behind consumers are from the latest events in the topic. Growing lag means the consumer cannot keep up with the production rate and real-time guarantees are breaking down.

Consumer Lag Monitoring:

Topic: orders.created, 6 partitions
Latest offset per partition: [1500, 1480, 1520, 1490, 1510, 1500]
Consumer committed offset:   [1498, 1478, 1520, 1487, 1508, 1499]
Lag per partition:           [  2,    2,    0,    3,    2,    1]
Total lag:                   10 messages behind

Alert: If total lag > 10,000 and growing -- consumer is falling behind

Summary

Kafka producers control delivery reliability through acknowledgment settings, batching, compression, and idempotency. Consumers maintain progress through offset management, choosing between at-most-once, at-least-once, or exactly-once delivery semantics. Consumer groups enable parallel processing by distributing partitions across multiple instances. Dead letter queues handle unprocessable events safely. Monitoring consumer lag is the primary signal of streaming pipeline health. Mastering producer and consumer configuration turns Kafka from a simple message bus into a reliable, high-throughput data backbone.

Leave a Comment

Your email address will not be published. Required fields are marked *