At a glance
Key concepts and capabilities
fsync before any data page moves; replicas and CDC both read that logVACUUM reclaims the dead onesSELECT … FOR UPDATE holds a row for the transaction; SKIP LOCKED turns a table into a work queueUNIQUE index on an idempotency key is the cheapest exactly-once there issynchronous_commit trades write latency for no acknowledged lossUse cases
Orders and money, in one transaction
The reason to reach for Postgres first. An order row and its payment attempt are
written in the same transaction, so a crash between them is impossible, and the
UNIQUE (order_id, idempotency_key) index means a retried checkout returns the
original result instead of charging twice.
Read scaling, and the read-after-write trap
Reads scale out on replicas; writes do not. The catch is the user who posts and immediately reloads: their own write may not have arrived on the replica yet. Route that one read to the primary, or return the write's own result.
A work queue, without a queue
SELECT … FOR UPDATE SKIP LOCKED lets several workers drain one table safely:
each claims rows nobody else holds, and a worker that dies rolls back and the
rows become claimable again. Good enough for outbox rows, scheduled emails and
retries — and one fewer system than Kafka.
Feeding a search index from the WAL
Logical decoding turns the same write-ahead log into a stream of row changes, so a search index, a cache or a warehouse is fed from the database's own log rather than by a second write from the application. That is what makes the index rebuildable, and what keeps it from silently diverging.
Growing past one primary
Partitioning splits one table by range or hash inside the same database, so queries and vacuum touch less data. Sharding splits it across independent clusters keyed by tenant or user id — at which point transactions become sagas and joins become application-side fan-out, so it is the step you justify.