Scenario 04.2.1 · Transactional write: commit vs abort
Kafka transaction commit vs abort with read_committed
One partition, two transactions: commit makes the batch visible to read_committed; abort marks the range aborted and a read_committed consumer skips it entirely.
Run this scenario in the playgroundCluster ready · read_committed. t+0.000s
One partition (RF 3, min.ISR 2), a transactional producer, and a read_committed consumer. read_committed only delivers records up to the Last Stable Offset (LSO) - never records inside an open or aborted transaction.
Open at this step: step 1, Cluster ready · read_committedbegin txn 1. t+0.100s
begin_transaction moves the producer to `ongoing`. This is client-side only: the coordinator learns of the transaction, and starts its timeout, when the first partition is added.
Open at this step: step 2, begin txn 1Produced · uncommitted. t+1.100s
Two records were appended and replicated - the high-watermark advanced to 2, so they are durable. But the LSO stays pinned at 0: the records are physically in the log yet uncommitted, so a read_committed consumer cannot see them.
Open at this step: step 3, Produced · uncommittedcommit · LSO advances. t+1.900s
commit_transaction writes a commit marker and the LSO advances to the high watermark (2), so the read_committed consumer advances to 2. In Kafka the marker is a control record with its own offset (2 here), so the log end, LSO and position would be 3; the simulator gives markers no offset.
Open at this step: step 4, commit · LSO advancesbegin txn 2. t+2.400s
A second transaction opens on the same partition. Same starting point: records will append but stay invisible until this txn resolves.
Open at this step: step 5, begin txn 2Produced · uncommitted. t+3.100s
Two more records append (HW = 4), again held below the LSO (still 2). So far this is identical to txn 1 - the raw log cannot yet tell a doomed batch from a good one.
Open at this step: step 6, Produced · uncommittedabort · range skipped. t+4.000s
abort_transaction records the range [2,4) in abortedRanges and releases the LSO PAST it to 4. The read_committed consumer advances its position over the range but delivers NONE of those records - the aborted batch is discarded. The HW advanced for both batches; only commit-vs-abort decided what is visible.
Open at this step: step 7, abort · range skippedWhat you start with
The atom of a Kafka transaction shown twice on one partition while a read_committed consumer watches: one transaction that commits and one that aborts. Records are physically appended (the high-watermark advances) in both cases - only the Last Stable Offset (LSO) and read_committed delivery tell them apart.
Step by step
- Cluster ready · read_committed t+0.000s One partition (RF 3, min.ISR 2), a transactional producer, and a read_committed consumer. read_committed only delivers records up to the Last Stable Offset (LSO) - never records inside an open or aborted transaction. Open at this step: step 1, Cluster ready · read_committed
- begin txn 1 t+0.100s begin_transaction moves the producer to `ongoing`. This is client-side only: the coordinator learns of the transaction, and starts its timeout, when the first partition is added. Open at this step: step 2, begin txn 1
- Produced · uncommitted t+1.100s Two records were appended and replicated - the high-watermark advanced to 2, so they are durable. But the LSO stays pinned at 0: the records are physically in the log yet uncommitted, so a read_committed consumer cannot see them. Open at this step: step 3, Produced · uncommitted
- commit · LSO advances t+1.900s commit_transaction writes a commit marker and the LSO advances to the high watermark (2), so the read_committed consumer advances to 2. In Kafka the marker is a control record with its own offset (2 here), so the log end, LSO and position would be 3; the simulator gives markers no offset. Open at this step: step 4, commit · LSO advances
- begin txn 2 t+2.400s A second transaction opens on the same partition. Same starting point: records will append but stay invisible until this txn resolves. Open at this step: step 5, begin txn 2
- Produced · uncommitted t+3.100s Two more records append (HW = 4), again held below the LSO (still 2). So far this is identical to txn 1 - the raw log cannot yet tell a doomed batch from a good one. Open at this step: step 6, Produced · uncommitted
- abort · range skipped t+4.000s abort_transaction records the range [2,4) in abortedRanges and releases the LSO PAST it to 4. The read_committed consumer advances its position over the range but delivers NONE of those records - the aborted batch is discarded. The HW advanced for both batches; only commit-vs-abort decided what is visible. Open at this step: step 7, abort · range skipped
The takeaway
Committing advances the LSO so read_committed can read the batch; aborting writes an abort marker, records the aborted offset range and releases the LSO past it, so a read_committed consumer steps over the aborted records and delivers none of them.
Configs in this scenario
-
min.The fewest in-sync replicas, leader included, that must have a write for an acks=all produce to succeed (default 1); with fewer in the ISR the leader rejects the write with NOT_ENOUGH_REPLICAS. How min.insync.replicas worksinsync. replicas
Embed this scenario
Paste this into a blog post, a course page or your team's docs. The scenario plays inside your page, and the link under it points readers to this walkthrough.
<iframe src="https://monedula.dev/kafka-simulator/embed/?scenario=04.2.1" title="Kafka Simulator: Transactional write: commit vs abort" width="100%" height="520" style="border:1px solid #d0d0d0;border-radius:8px" loading="lazy"></iframe>
<p><a href="https://monedula.dev/kafka-simulator/scenarios/txn-commit-vs-abort/">Kafka transaction commit vs abort with read_committed</a></p> Further reading
More in Transactions
- Kafka at-most-once vs at-least-once vs exactly-once
- At-least-once delivery in Kafka: where duplicates come from
- Kafka isolation.level: read_committed vs read_uncommitted
- Kafka last stable offset (LSO) with concurrent transactions
- Kafka transaction.timeout.ms: the coordinator aborts
- Kafka read_committed during a leader election
- Kafka consume-process-produce loop with exactly-once
- Kafka zombie producer fencing with transactional.id
- Kafka processing.guarantee=exactly_once_v2 end to end