Kafka ACK (Acknowledgment) visualization β€” interactive simulation showing how acks=0, acks=1, and acks=-1 behave with producers, brokers, and replication.

kafka producer acks

producer.acks configuration β€” reliability vs throughput
LIVE SIMULATION
Speed 2x
Producer
app-01
ISR
Leader
broker-0
kafka:9092
offset 0
ISR
Follower
broker-1
kafka:9093
offset 0
ISR
Follower
broker-2
kafka:9094
offset 0
00:00Ready. Select a mode and click "Send msg" to begin.
Sent
0
Acked
0
Lost
0
Avg latency
β€”

How the three modes compare

Property
acks=0
acks=1
acks=all (βˆ’1)
Who acks?
Nobody
Leader only
All ISR brokers
Throughput
Highest
Medium
Lower
Durability
None
Partial
Strong
Data loss risk
High (always possible)
Medium (leader crash)
Minimal (with min.insync.replicas)
Latency
Near zero
One RTT
Multiple RTTs
Retries safe?
β€”
Usually (check idempotent)
Yes (enable.idempotence)

Key concepts

ISR β€” In-Sync Replicas

A dynamic set of brokers that have replicated all messages within replica.lag.time.max.ms. For acks=all, every broker in this set must write and confirm. If a follower falls behind, it's removed from ISR.

min.insync.replicas

Works together with acks=all. Sets the minimum ISR size that must be available. E.g. with 3 replicas and min.insync.replicas=2, the cluster can tolerate 1 broker failure without blocking the producer.

The acks=1 silent loss

Leader acks the producer, then crashes before followers replicate. Kafka elects a new leader from the followers β€” this new leader does not have the message. The producer got an ACK but the message is gone.

Idempotent producer

Set enable.idempotence=true (default in Kafka 3+). Each message gets a sequence ID. Retries won't cause duplicates. Required for exactly-once semantics. Automatically sets acks=all.

acks=all flow

1. Producer β†’ Leader (write to log). 2. Leader β†’ Follower-1 (replicate). 3. Leader β†’ Follower-2 (replicate). 4. Followers ACK leader. 5. Leader ACKs producer. Only then does the producer continue.

Unclean leader election

If unclean.leader.election.enable=true, a lagging broker can become leader β€” accepting possible data loss. For financial/transactional topics set it to false (default since Kafka 0.11).

ISR membership

broker-0
Leader Β· ISR βœ“
β†’
broker-1
Follower Β· ISR βœ“
β†’
broker-2
Lagging Β· out of ISR βœ—
With acks=all and min.insync.replicas=2, the above cluster can still produce β€” broker-2 is out of ISR but the 2 remaining ISR brokers satisfy the minimum.

Metrics / click-stream ingestion USE acks=0

You're sending millions of analytics events per second. Losing a few events is acceptable. Every millisecond of latency matters. Fire-and-forget gives you the highest possible throughput with minimal broker overhead.

acks=0 compression.type=lz4 linger.ms=5 batch.size=65536

Activity feeds / notification events USE acks=1

A reasonable middle ground. You want reasonable reliability without paying the full cost of ISR acknowledgment on every message. Acceptable for workloads where an occasional message loss on leader crash is tolerable.

acks=1 retries=3 retry.backoff.ms=100

Financial transactions / order events USE acks=all

You must not lose a single message. Payments, inventory updates, booking confirmations β€” any message loss causes real-world problems. Use acks=all with idempotent producer for exactly-once semantics.

acks=all (or acks=-1) min.insync.replicas=2 enable.idempotence=true retries=Integer.MAX_VALUE max.in.flight.requests.per.connection=5

Using acks=1 for financial events AVOID

Leader acks your payment, then crashes before replication. A new leader is elected β€” it never got the message. The payment is silently lost but the producer returned success. Your system believes the payment was recorded.

# Dangerous pattern for critical data: acks=1 ← leader acks, then crashes # Follower elected as new leader # Message is gone β€” no error returned to producer

High-throughput with strong guarantees USE acks=all + tuning

You need both throughput and durability. Tune batching to amortize the ISR ack cost across many messages. linger.ms delays sending slightly to fill batches. Combine with async callbacks instead of blocking .get().

acks=all linger.ms=20 batch.size=131072 compression.type=snappy enable.idempotence=true