Cracking ML Interviews

Open in the interactive study hub

Apache Kafka

Apache Kafka is the leading distributed event streaming platform. It handles trillions of events per day at companies like LinkedIn, Uber, and Netflix, powering real-time data pipelines, event-driven architectures, and streaming analytics.


What is Kafka?

Kafka is a distributed commit log that allows producers to publish events and consumers to read them, at any scale, in real-time.

Microservice A  →
                  → Kafka (durable log) → Analytics Pipeline
Microservice B  →                       → Another Microservice
                                        → Data Warehouse (Snowflake/Redshift)

Kafka vs Message Queues (RabbitMQ, SQS)

Feature Kafka Traditional MQ
Message retention Days/weeks (configurable) Deleted after consumption
Consumer model Multiple consumers read independently Typically point-to-point
Throughput Millions of msgs/sec Thousands
Ordering Guaranteed per partition Varies
Replay Yes: rewind consumer offset No
Use case Streaming, event sourcing, large scale Task queues, RPC

Core Concepts

Producer → Topic → Partition → Consumer Group

┌──────────────── Topic: "orders" ───────────────────┐
│  Partition 0:  [msg0] [msg3] [msg6] ...            │
│  Partition 1:  [msg1] [msg4] [msg7] ...            │
│  Partition 2:  [msg2] [msg5] [msg8] ...            │
└────────────────────────────────────────────────────┘
Concept Description
Event/Message A record with key, value, timestamp, headers
Topic Named stream of events. Like a table or folder.
Partition Ordered, immutable log. Topics are split into partitions for parallelism.
Offset Position of a message within a partition (monotonically increasing integer)
Producer Writes events to topics
Consumer Reads events from topics
Consumer Group Group of consumers sharing load. Each partition → one consumer per group.
Broker A Kafka server. Cluster = multiple brokers.
KRaft (formerly ZooKeeper) Cluster metadata management. Kafka 4.0+ runs only in KRaft mode; ZooKeeper support was removed.
Replication Factor How many copies of each partition (for fault tolerance)

Consumer Groups

Topic: "orders" (3 partitions)

Consumer Group "analytics":
  Consumer A → Partition 0
  Consumer B → Partition 1
  Consumer C → Partition 2

Consumer Group "warehouse":
  Consumer X → Partition 0, 1
  Consumer Y → Partition 2

Both groups read independently: no coordination.

Running Kafka

Docker (local development)

# docker-compose.yml  (single-node KRaft: no ZooKeeper container needed)
services:
  kafka:
    image: apache/kafka:4.0.0
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
docker compose up -d

Managed Kafka in 2026

Service Provider Notes
Confluent Cloud Confluent Feature-rich, expensive
Amazon MSK AWS Managed Kafka on AWS
Aiven for Kafka Aiven Multi-cloud managed
Redpanda Cloud Redpanda Kafka-compatible, lower latency

Kafka CLI

# Topic management
kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic orders --partitions 3 --replication-factor 1

kafka-topics.sh --bootstrap-server localhost:9092 --list

kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --topic orders

kafka-topics.sh --bootstrap-server localhost:9092 \
  --delete --topic orders

# Produce messages (console producer)
kafka-console-producer.sh --bootstrap-server localhost:9092 \
  --topic orders
> {"order_id": "1", "amount": 99.99}
> {"order_id": "2", "amount": 149.00}

# Consume messages
kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic orders --from-beginning

# Consumer group inspection
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group my-group --describe

# Reset offsets
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group my-group --topic orders --reset-offsets --to-earliest --execute

Python Producer

from confluent_kafka import Producer
import json

producer = Producer({
    "bootstrap.servers": "localhost:9092",
    "acks": "all",                    # wait for all replicas to confirm
    "enable.idempotence": True,       # no duplicates from retries (full exactly-once needs transactions)
    "compression.type": "snappy",     # compress messages
    "linger.ms": 5,                   # batch messages for 5ms
    "batch.size": 65536,              # 64KB batch size
})

def delivery_callback(err, msg):
    if err:
        print(f"Delivery failed: {err}")
    else:
        print(f"Delivered to {msg.topic()} partition {msg.partition()} offset {msg.offset()}")

# Produce with key (key determines partition)
order = {"order_id": "ORD-001", "customer_id": "C-123", "amount": 99.99}

producer.produce(
    topic="orders",
    key="C-123",              # same key → same partition (ordering per customer)
    value=json.dumps(order),
    callback=delivery_callback
)

# Flush (wait for all messages to be delivered)
producer.flush()

# Context manager pattern
from contextlib import contextmanager

@contextmanager
def kafka_producer(config):
    p = Producer(config)
    try:
        yield p
    finally:
        p.flush(timeout=30)

with kafka_producer({"bootstrap.servers": "localhost:9092"}) as p:
    for i in range(1000):
        p.produce("events", value=json.dumps({"id": i}))
        p.poll(0)  # serve delivery callbacks

Python Consumer

from confluent_kafka import Consumer, KafkaError
import json

consumer = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "order-processor",
    "auto.offset.reset": "earliest",         # start from beginning if no committed offset
    "enable.auto.commit": False,             # manual commit for at-least-once
    "max.poll.interval.ms": 300000,          # 5 min processing time before rebalance
})

consumer.subscribe(["orders", "returns"])

def process_order(order: dict):
    # Your business logic here
    pass

try:
    while True:
        msg = consumer.poll(timeout=1.0)

        if msg is None:
            continue

        if msg.error():
            if msg.error().code() == KafkaError._PARTITION_EOF:
                print(f"Reached end of partition {msg.partition()}")
            else:
                raise Exception(msg.error())
            continue

        # Process message
        order = json.loads(msg.value().decode("utf-8"))
        print(f"Processing order {order['order_id']} from partition {msg.partition()} offset {msg.offset()}")

        try:
            process_order(order)
            # Manual commit after successful processing
            consumer.commit(asynchronous=False)
        except Exception as e:
            print(f"Processing failed: {e}")
            # Don't commit: message will be re-delivered

finally:
    consumer.close()

Batch Consumer

# Process in batches for efficiency
import json
from confluent_kafka import Consumer

consumer = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "batch-processor",
    "auto.offset.reset": "earliest",
    "enable.auto.commit": False,
})
consumer.subscribe(["events"])

BATCH_SIZE = 1000
BATCH_TIMEOUT = 5.0  # seconds

def process_batch(batch: list[dict]):
    ...  # your bulk write / transformation

while True:
    # consume() returns up to BATCH_SIZE messages, or fewer once the timeout expires
    msgs = consumer.consume(num_messages=BATCH_SIZE, timeout=BATCH_TIMEOUT)
    batch = [json.loads(m.value()) for m in msgs if not m.error()]

    if batch:
        process_batch(batch)
        consumer.commit(asynchronous=False)   # commit only after the batch succeeded

Kafka Schemas with Schema Registry

In production, always use a Schema Registry to enforce message schemas.

from confluent_kafka import Producer
from confluent_kafka.schema_registry import SchemaRegistryClient
from confluent_kafka.schema_registry.avro import AvroSerializer
from confluent_kafka.serialization import SerializationContext, MessageField

# Define Avro schema
order_schema = """
{
  "type": "record",
  "name": "Order",
  "fields": [
    {"name": "order_id", "type": "string"},
    {"name": "customer_id", "type": "string"},
    {"name": "amount", "type": "double"},
    {"name": "created_at", "type": {"type": "long", "logicalType": "timestamp-millis"}}
  ]
}
"""

schema_registry_client = SchemaRegistryClient({"url": "http://localhost:8081"})
avro_serializer = AvroSerializer(schema_registry_client, order_schema)

producer = Producer({"bootstrap.servers": "localhost:9092"})

order = {"order_id": "ORD-001", "customer_id": "C-123", "amount": 99.99, "created_at": 1700000000000}

producer.produce(
    topic="orders",
    value=avro_serializer(order, SerializationContext("orders", MessageField.VALUE))
)

Kafka Streams & Kafka Connect

Kafka Connect (no-code integrations)

// Source connector: PostgreSQL → Kafka
{
  "name": "postgres-source",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "localhost",
    "database.port": "5432",
    "database.user": "postgres",
    "database.password": "secret",
    "database.dbname": "mydb",
    "table.include.list": "public.orders,public.customers",
    "plugin.name": "pgoutput"
  }
}

// Sink connector: Kafka → Snowflake
{
  "name": "snowflake-sink",
  "config": {
    "connector.class": "com.snowflake.kafka.connector.SnowflakeSinkConnector",
    "topics": "orders",
    "snowflake.url.name": "myaccount.snowflakecomputing.com",
    "snowflake.database.name": "MYDB",
    "snowflake.schema.name": "PUBLIC",
    "key.converter": "org.apache.kafka.connect.json.JsonConverter",
    "value.converter": "com.snowflake.kafka.connector.records.SnowflakeAvroConverter"
  }
}

Debezium (CDC: Change Data Capture)

Debezium captures every database change and publishes it to Kafka:

PostgreSQL WAL → Debezium Connector → Kafka Topic → Consumers
                                      ┌─────────────────────────────┐
                                      │ Event: {                    │
                                      │   "op": "u",  // update     │
                                      │   "before": {"amount": 100},│
                                      │   "after":  {"amount": 150} │
                                      │ }                           │
                                      └─────────────────────────────┘

Kafka in Data Engineering Pipelines

PostgreSQL (OLTP)
      ↓ Debezium (CDC)
Kafka Topics
      ↓ Kafka Connect Sink / Spark Streaming / Flink
Snowflake / Delta Lake / S3
      ↓ dbt
Analytics / BI
# Spark Structured Streaming reading from Kafka
from pyspark.sql import SparkSession
from pyspark.sql import functions as F

# Match the connector to your Spark and Scala versions (Spark 4.x builds use Scala 2.13)
spark = SparkSession.builder \
    .appName("KafkaStreaming") \
    .config("spark.jars.packages", "org.apache.spark:spark-sql-kafka-0-10_2.12:3.5.0") \
    .getOrCreate()

df = spark.readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "localhost:9092") \
    .option("subscribe", "orders") \
    .option("startingOffsets", "latest") \
    .load()

# Parse JSON
orders = df.select(
    F.from_json(
        F.col("value").cast("string"),
        "order_id STRING, amount DOUBLE, customer_id STRING"
    ).alias("data")
).select("data.*")

# Write to Delta Lake
query = orders.writeStream \
    .format("delta") \
    .outputMode("append") \
    .option("checkpointLocation", "/checkpoints/orders") \
    .table("orders_streaming")

Key Configuration Reference

Producer

Config Recommended Purpose
acks=all Production Wait for all replicas
enable.idempotence=true Production No duplicates from producer retries
compression.type=snappy Production Compress messages
linger.ms=5-50 Production Batch messages
retries=INT_MAX Production Retry on transient errors

Consumer

Config Value Purpose
auto.offset.reset earliest/latest Where to start reading
enable.auto.commit false Manual commit for reliability
group.id Your app name Consumer group identity
max.poll.records 500 Records per poll
session.timeout.ms 30000 Heartbeat timeout

Topic

Config Value Purpose
retention.ms 604800000 (7 days) How long to keep messages
replication.factor 3 (production) Fault tolerance
num.partitions throughput / single_partition_throughput Parallelism
min.insync.replicas 2 Required replicas for acks=all

Kafka in 2026

Trend Description
KRaft mode ZooKeeper fully removed in Kafka 4.0
Kafka 4.0 KRaft-only, new consumer rebalance protocol (KIP-848) GA, early-access queues / share groups (KIP-932)
Tiered Storage Offload old segments to S3/GCS: cheaper long retention
Redpanda Kafka-compatible alternative in C++: no JVM, vendor claims lower tail latency
WarpStream Kafka-compatible, disaggregated architecture, zero inter-zone costs
Apache Flink Common stream processor alongside Kafka, often chosen over Kafka Streams for large stateful jobs
Confluent Tableflow Auto-sync Kafka topics to Iceberg tables

View or edit this guide on GitHub