Tied to Mission: Backend Depth — the physical layer under the transaction story you already have: it reuses the log (Lesson 17), the page cache (Lesson 11), and replication (Lesson 15), and it answers the durability half of ACID that Lesson 2 only touched on the read side.
3am. The master goes down. Failover promotes the standby. By 3:10 the tickets are rolling in: "My payment was confirmed, and now it's gone." Postgres reports no corruption — it started fine, the data files are intact. Yet hundreds of transactions that clients watched return COMMITTED simply do not exist anymore. This is the physical version of Lesson 15's async durability window, and it has one root: a commit reply is a claim about a log, not about your data — and you chose how durable that claim really was.
When a client sends COMMIT, Postgres does not write the transaction's data anywhere. It performs three steps:
pg_wal, currently held in shared memory.synchronous_commit = on).COMMITTED to the client.The data pages — the actual changed rows — are left sitting in shared_buffers, Postgres's own page cache, and are written to the data files later, by the background writer and at checkpoints. So at the instant the client hears "committed," the new row may not be in the table file at all. It exists in exactly one place: a record in the WAL. This is the physical form of an idea you've already met twice — the log is the truth, and the table is a cache of the log (Lesson 17); a standby is a second copy of that same log (Lesson 15). "Commit" is the moment the log, not the table, is made safe.
write() hands bytes to the OS page cache — RAM (Lesson 11). If power dies, RAM dies. fsync() is the call that waits until the storage device itself confirms the bytes survive power loss. The window between the two is the durability window: everything inside it is a promise that a crash will break.
Two consequences. First, Postgres defaults to fsync'ing the WAL at commit because that is the entire meaning of "durable" — a committed transaction is one whose commit record has crossed the durability window on this machine. Second, the contract runs both ways: a disk with a write-back cache can report "persisted" while holding data in its own volatile RAM, and Postgres's docs are blunt that if hardware lies about fsync, the database can be silently corrupted on power loss. Durability is a deal with your storage device, not a Postgres feature.
So read the client-visible reply exactly: "COMMITTED" means a record saying the transaction committed is in the WAL on this machine's storage. Not "your data is safe." Not "a standby has it." Not "it's on disk anywhere but here." Each qualifier is a place where the 3am incident leaked.
The WAL is just a sequence of records, each with a position (LSN). "Committed" is the client being told: the log is now durable up to position X. Every durability question downstream — standby lag, failover loss, synchronous_commit=off — is a question about where position X has and hasn't landed. Keep that mental model and the rest of this lesson is just naming the landing spots.
The WAL holds redo records for every change (heap tuples, index entries) plus the commit record itself. On restart, crash recovery replays the WAL forward from the last checkpoint's redo location, redoing committed changes that never made it to the data files; anything not covered by a durable commit record is simply discarded — Postgres rolls forward, never backward, which is why there is no such thing as "rolled back to the last checkpoint."
The one genuinely subtle crash case is the torn page: a crash mid-write can leave an 8KB data page half-written across a sector boundary, and replaying a WAL record onto a torn page would corrupt the database. The fix is full_page_writes (on by default): the first time a page changes after a checkpoint, the whole page image is written to the WAL, so recovery can restore it wholesale before applying changes. This is Lesson 11's page-size machinery wearing a crash-safety hat — and it's why one big UPDATE can generate far more WAL than the bytes it changed. (Lesson 8's bloat and this WAL amplification are both "the write path costs more than the row.")
An HDD fsync costs on the order of 5–10ms — a rotation, plus a write, plus a confirmation. If every commit needed its own fsync, a commit-heavy workload would collapse to tens of transactions per second. Group commit is the amortization: backends committing at nearly the same time share one WAL flush — the first flush to reach a given region of the log carries everyone waiting behind it, and they all return together. Commit latency stays per-transaction; commit throughput is shared.
This is also what the big knob actually trades. synchronous_commit = off replies COMMITTED before the fsync, buying latency at the cost of a durability window. The docs' warning is precise: with it off, recent commits can be lost on an OS or power crash — and even on failover to a standby, because the WAL may not have reached it. Off is a latency knob with a data-loss price; on is the default because the default promise is "durable."
Streaming replication is the standby reading the same WAL the primary fsyncs. So the commit ack's scope is configurable, and each setting is a landing spot for position X:
synchronous_standby_names set, commit on): the primary waits for the standby's ack before replying. Commit now costs local fsync plus one network round-trip. The ack has three granularities — remote_write (the standby's OS cache: durable against the standby crashing, not against its power dying), remote_flush (the standby's disk: durable), and remote_apply (the standby's database has applied it: what the app can actually read there)."COMMITTED" is therefore a graded promise with a scope you choose: this machine's disk, that machine's RAM, that machine's disk, or that machine's database. The 3am incident is what happens when the config's scope ("this machine's disk") is narrower than the operation's assumption ("two machines"). The failure wasn't corruption — it was a scope mismatch, the same shape as Lesson 15's consistency scopes, one layer down.
Durability is a position in a log on a particular storage device, not a moment in your conversation with the client. Every durability incident is a scope mismatch: someone heard "safe" where the configuration meant "safe on this one machine, for now." When you next see a commit-related incident — lost acked writes after failover, a mysterious gap after synchronous_commit=off, a standby promoted with missing tail — the first question is never "is the DB corrupt?" It's always "where had the WAL landed before anyone was told anything?"
synchronous_commit = off. The machine loses power. What does recovery look like?0) Get a session. Your Postgres (Fedora-style install, pg 18) doesn't accept passwordless connections for your OS user — but the OS postgres user connects via peer auth, so run everything through sudo -u postgres: sudo -u postgres createdb scratch, then sudo -u postgres psql -d scratch for the SQL below and sudo -u postgres pgbench ... for the benchmark. 1) Feel the fsync cost. sudo -u postgres pgbench -i scratch to initialize, then sudo -u postgres pgbench -c 20 -j 4 -T 10 scratch (write-heavy, one commit per transaction). Note the tps. Then sudo -u postgres psql -d scratch -c "ALTER SYSTEM SET synchronous_commit = off;" + SELECT pg_reload_conf();, rerun, compare — the gap is the fsync-per-commit cost, and its size (far smaller than txn-count ÷ fsync-latency) is group commit working. Restore on afterwards (same two commands). 2) Read the knobs. SHOW wal_sync_method; SHOW full_page_writes; SHOW synchronous_commit; — name what each one controls from the lesson. 3) See the log move. SELECT pg_current_wal_lsn();, run a big UPDATE on a small table, and read the LSN again: the log advanced even though the table file may not have been touched yet. 4) The durability window in miniature: a 10-line Python script that writes a file 10,000 times with and without os.fsync() — run it on /tmp (often tmpfs = RAM, fsync nearly free) and on real disk. The difference is the device's persistence contract, made measurable.
PostgreSQL's own "Reliability and the Write-Ahead Log" chapter is the authoritative treatment of the commit path and crash recovery; its WAL configuration reference documents fsync, synchronous_commit, and full_page_writes precisely. Hironobu Suzuki's free "The Internals of PostgreSQL," Ch. 9 — Write Ahead Logging, goes one level down to the source code (WAL buffers, group commit, recovery). The synchronous replication section of the docs is the reference for what "durable" is allowed to mean in a cluster. For the log-as-truth framing, Kreps' "The Log" (already in RESOURCES.md) again applies — the WAL is its canonical example.
That was the first of the third pass's optional deepenings (the plan listed it second, after TLS/HTTP·2·3 — I took the transaction-physical-layer one first because the mission's transaction criterion is the deepest unused seam, and the local Postgres makes it hands-on). The other two — TLS + HTTP/2/3, and time & clocks — are still open, and a recap across all 21+1 lessons is available on request. Ask anything that didn't land.