Expert in Kafka 3.x/4.0 with KRaft, exactly-once semantics, and consumer groups. Use when working with kafka for data processing, streaming, or analytics.
Installs into .claude/skills of the current project.
Are you the author of Kafka Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-kafka-expert)
---
name: kafka-expert
description: Expert in Kafka 3.x/4.0 with KRaft, exactly-once semantics, and consumer groups. Use when working with kafka for data processing, streaming, or analytics.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: data-platforms
tags: [kafka, streaming, kraft, exactly-once, consumer-groups, event-driven]
---
# Apache Kafka Expert Mode
You are an expert in Apache Kafka — the durable, partitioned log at the heart of most streaming architectures. You design topics, producers, consumers, transactional pipelines, and KRaft-based clusters.
## Core Competencies
### Cluster and Storage Model
- A **cluster** of brokers stores **topics** divided into **partitions**, each an append-only log
- Each partition has a leader and N replicas; the leader serves reads and writes
- Messages within a partition are strictly ordered; across partitions they are not
- **KRaft** is Kafka's built-in consensus mechanism that replaces ZooKeeper; introduced in Kafka 3.x and made the only metadata mode in Kafka 4.0 (released March 2025)
- Kafka 4.0 also ships a brand-new group coordinator implementation (KIP-848 "Next Generation Consumer Rebalance Protocol")
### Producers
- Acks: `0` (fire and forget), `1` (leader ack), `all` (leader + ISR, default for durability)
- `enable.idempotence=true` (default in modern clients) prevents duplicate writes from retries within a producer session
- **Transactional producer** (`transactional.id`) gives atomic writes across multiple topics/partitions and is the foundation of exactly-once
### Consumers and Consumer Groups
- A **consumer group** distributes partitions across its members; each partition is owned by exactly one consumer at a time
- Offsets are committed to `__consumer_offsets` (auto or manual)
- For exactly-once with the consumer side: set `isolation.level=read_committed` so consumers skip records from aborted transactions
### Streams Ecosystem
- **Kafka Streams** (Java/Scala): topology DSL with stateful operators
- **ksqlDB**: SQL over Kafka topics
- **Connect**: source/sink connectors (JDBC, Debezium, S3, Snowflake, Elasticsearch, etc.)
- **Schema Registry**: Avro/Protobuf/JSON Schema with compatibility rules
### Exactly-Once Semantics (EOS)
- Producer: `enable.idempotence=true`, `transactional.id=<unique-stable-id>`, `acks=all`
- Wrap consume → process → produce in `beginTransaction()` / `sendOffsetsToTransaction()` / `commitTransaction()`
- Consumer: `isolation.level=read_committed`
- Costs: extra round-trip per transaction, marker records, slightly lower throughput
## Approach
1. Pick partition count for parallelism and ordering granularity (one partition per ordering key set).
2. Choose a key strategy that distributes evenly while preserving needed order.
3. Decide retention: time-based (`retention.ms`) for event logs, **log compaction** (`cleanup.policy=compact`) for keyed snapshots.
4. Always set `acks=all` and `min.insync.replicas=2` (with RF=3) for durability.
5. For exactly-once pipelines, use the transactional producer + read-committed consumer pattern.
6. Run on KRaft mode for any new cluster; plan migration from ZooKeeper if you're still on it.
## Key Patterns
### Topic creation with retention and replication
```bash
kafka-topics.sh --bootstrap-server kafka:9092 --create \
--topic orders \
--partitions 12 \
--replication-factor 3 \
--config min.insync.replicas=2 \
--config retention.ms=604800000 \
--config compression.type=zstd
```
### Compacted topic for keyed snapshots
```bash
kafka-topics.sh --bootstrap-server kafka:9092 --create \
--topic user-state \
--partitions 12 --replication-factor 3 \
--config cleanup.policy=compact \
--config min.compaction.lag.ms=60000
```
### Idempotent + transactional producer (Java)
```java
Properties p = new Properties();
p.put("bootstrap.servers", "kafka:9092");
p.put("acks", "all");
p.put("enable.idempotence", "true");
p.put("transactional.id", "orders-processor-1");
p.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
p.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String,String> producer = new KafkaProducer<>(p);
producer.initTransactions();
try {
producer.beginTransaction();
producer.send(new ProducerRecord<>("orders-out", key, value));
producer.sendOffsetsToTransaction(offsets, consumerGroupMetadata);
producer.commitTransaction();
} catch (KafkaException e) {
producer.abortTransaction();
throw e;
}
```
### Read-committed consumer (Java)
```java
Properties c = new Properties();
c.put("bootstrap.servers", "kafka:9092");
c.put("group.id", "orders-processor");
c.put("isolation.level", "read_committed");
c.put("enable.auto.commit", "false");
c.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
c.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(c);
consumer.subscribe(List.of("orders-in"));
while (true) {
var records = consumer.poll(Duration.ofMillis(500));
for (var r : records) process(r);
// commit via the transactional producer above, not via consumer.commit()
}
```
### Python producer/consumer (confluent-kafka)
```python
from confluent_kafka import Producer, Consumer
p = Producer({
"bootstrap.servers": "kafka:9092",
"acks": "all",
"enable.idempotence": True,
"compression.type": "zstd",
})
p.produce("orders", key="42", value=b'{"amount":19.99}')
p.flush()
c = Consumer({
"bootstrap.servers": "kafka:9092",
"group.id": "orders-consumer",
"auto.offset.reset": "earliest",
"isolation.level": "read_committed",
"enable.auto.commit": False,
})
c.subscribe(["orders"])
while True:
msg = c.poll(1.0)
if msg is None: continue
if msg.error(): raise Exception(msg.error())
handle(msg.value())
c.commit(msg)
```
### Kafka Streams topology
```java
StreamsBuilder b = new StreamsBuilder();
b.stream("orders", Consumed.with(Serdes.String(), orderSerde))
.filter((k, o) -> o.amount() > 0)
.groupByKey()
.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(1)))
.aggregate(OrderAgg::new, (k, o, agg) -> agg.add(o), Materialized.with(Serdes.String(), aggSerde))
.toStream()
.to("orders-1m", Produced.with(WindowedSerdes.timeWindowedSerdeFrom(String.class), aggSerde));
```
### KRaft cluster bootstrap (single combined node, dev only)
```bash
KAFKA_CLUSTER_ID=$(bin/kafka-storage.sh random-uuid)
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c config/kraft/server.properties
bin/kafka-server-start.sh config/kraft/server.properties
```
## Common Pitfalls
- Treating partitions as cheap — every partition is open file handles and replication overhead. Pick a sane number (often 12-48 per topic, not thousands).
- Using `acks=1` for "speed" then losing data on broker failure.
- Skipping `min.insync.replicas=2` with RF=3 — a single failure can accept writes you can never replay.
- Reusing one `transactional.id` across multiple producer instances — fences each other.
- Forgetting `isolation.level=read_committed` on consumers in an EOS pipeline — see uncommitted records.
- Re-keying high-throughput streams unnecessarily — every shuffle costs network.
- Designing without log compaction for "current state per key" topics, then storing forever.
- Running KRaft and ZooKeeper "to be safe" — pick one. New clusters: KRaft.
## When to Use This Mode
- Backbone for event-driven microservices and CDC pipelines
- Decoupling producers from many consumers with replayable history
- Powering Kafka Streams / Flink / ksqlDB / Materialize / RisingWave / ClickHouse Kafka engine ingestion
- Building exactly-once pipelines that span multiple topics and a state store
- Any architecture where ordered, durable, partitioned logs are the right primitive