Transactions

A transaction groups several writes so that all of them happen or none of them do. One commit waits for one write to disk, about 0.4 ms here. With an outbox, an order and its event commit together, so no event goes missing.

Play a level

See it on the board

The same traffic runs on two boards. The simulator measured both when this page was built.

Board to show
SQLHTTP/2gRPCgRPCUsersUsersPostgres2 vCPU · 2 GBSearch index2 vCPU · 2 GBChange capt…2 vCPU · 2 GB
SQLHTTP/2gRPCgRPCUsersUsersPostgresSearch in…Change ca…
Order and event written apart: misses a goal
SQLHTTP/2gRPCgRPCUsersUsersPostgres2 vCPU · 2 GBSearch index2 vCPU · 2 GBChange capt…2 vCPU · 2 GB
SQLHTTP/2gRPCgRPCUsersUsersPostgresSearch in…Change ca…
Transactional outbox on the orders database: meets the goals
See it on the board
Measure Goal Order and event written apart Transactional outbox on the orders database
Data mismatches (Reads) under 1,000 39,155 Fail 599 Pass
Data age under 8 s 79.9 s Fail 5.2 s Pass
Monthly cost 24 USD/mo 24 USD/mo
p99 latency 23 ms 23 ms
Error rate 6.3% 6.3%

Key numbers

Default database and change capture settings from the part catalog. The simulator uses the same values.
NumberValue
Write one commit to disk0.4 ms
Database operation (Postgres), p992 ms
Writes per second for each vCPU (Postgres)2,500 writes/s
Time between saved publisher positions, default30 s
An order and its event commit togetherTap to turn the outbox on or off

Outbox · 10 orders · 10 events · 0 lost

How it works

Many actions change more than one thing. An order saves a row and must also tell other services. If the service writes the row and then sends a message, a crash between the two steps leaves a row without its message. If it sends first, a failed write leaves a message about an order that does not exist.

A transaction closes this gap. All its writes commit together, or none of them do. Other readers never see half of the change. The database records the commit on disk before it confirms, so a confirmed commit survives a crash.

The network is outside the transaction: a database cannot commit a message in another system. The outbox pattern solves this. The service writes the order and an event row to an outbox table in the same transaction. A separate publisher reads the outbox and sends each event. It saves its position, so after a stop it continues from that point. It can send an event twice, so consumers must ignore repeats.

Isolation is the other half. Strong isolation stops two transactions from seeing each other's partial work, but more transactions must then wait or retry.

In the levels you add an outbox to the orders database so that each commit leads to its event, also after a failure.