Lesson 0010 · Replication

Replication: streaming vs logical

The WAL is a complete, ordered stream of every change. Ship it to another server and you have a replica — either by copying the bytes (streaming) or the meaning (logical). This closes the mission.

~11 minWarm-up recall + quiz + MySQL contrastCheat sheet: here

Warm-up: recall 0009 first

Closed book. Today is 0009 extended across the network.

At commit, the only thing Postgres must flush to disk is:

Only the sequential WAL is flushed at commit (0009). That same WAL stream is what a replica consumes — this lesson ships it elsewhere.

synchronous_commit = off risks losing recent commits but never corrupts. It's a dial that trades:

It trades a sliver of durability (a small window of recent commits) for write throughput. Synchronous replication is the same dial pointed at a standby, not local disk.

You ended 0009 with a key realization: the WAL is not just a crash-recovery tool, it's a complete, ordered record of every change the database makes. That's the whole secret of replication — feed that stream to a second server and it can reconstruct the same data. Postgres offers two ways to do it, and the difference is what travels: raw bytes, or logical row changes.

Streaming (physical) replication — copy the bytes

The default. A standby connects to the primary and continuously receives WAL records as they're produced, replaying them to stay nearly current. PostgreSQL Docs: "The standby connects to the primary, which streams WAL records to the standby as they're generated, without waiting for the WAL file to be filled." Because it replays the physical WAL, the standby is a byte-for-byte copy of the whole cluster — same version, same layout — and serves read-only queries (a hot standby). It's the backbone of Postgres high availability: if the primary dies, promote a standby.

Async by default → the failover data-loss window

Streaming is asynchronous unless you say otherwise: the primary commits and ships the WAL afterward, so a standby lags by a little. PostgreSQL Docs: "Streaming replication is asynchronous by default… there is a small delay between committing a transaction in the primary and the changes becoming visible in the standby." The consequence to burn in: if the primary suffers a catastrophic failure, anything committed but not yet shipped is lost on failover. PostgreSQL Docs: "there is a window for data loss should the primary server suffer a catastrophic failure; transactions not yet shipped will be lost." This is the exact async trade-off you learned for MySQL's binlog replication (0008).

Synchronous replication — the dial from 0009, aimed at a standby

Want zero loss? Make the primary wait for the standby. Set synchronous_standby_names and each commit blocks until the standby confirms the WAL is on its disk too. PostgreSQL Docs: "each commit of a write transaction will wait until confirmation is received that the commit has been written to the write-ahead log on disk of both the primary and standby server." This is literally synchronous_commit from 0009 extended over the network — instead of "wait for local disk," it's "wait for the standby's disk."

The availability price

Synchronous means the primary's writes are hostage to the standby. If the lone synchronous standby goes down, commits can hang. PostgreSQL Docs: "Such transaction commits may never be completed if any one of the synchronous standbys should crash." That's the CAP-flavored bind: async = fast, may lose a little on failover; sync = no loss, but a standby outage can stall the primary. The mirror image of MySQL's async vs semi-synchronous choice.

Logical replication — copy the meaning

Streaming copies bytes, so the replica must be an identical clone. Sometimes you want less, or different: just a few tables, into a newer major version, or a writable target. Logical replication does that by shipping row-level changes instead of physical blocks. PostgreSQL Docs: "Logical replication is a method of replicating data objects and their changes, based upon their replication identity (usually a primary key). We use the term logical in contrast to physical replication, which uses exact block addresses and byte-by-byte replication." It works publish/subscribe: a publisher exposes a set of tables; subscribers subscribe. PostgreSQL Docs: "Logical replication uses a publish and subscribe model…"

Because it moves meaning, not bytes, logical replication unlocks what physical can't: replicate a subset of tables, between different major versions, or across different platforms — and the target is a normal, writable database. PostgreSQL Docs — use cases: "Replicating between different major versions of PostgreSQL"; "Replicating between PostgreSQL instances on different platforms"; "Sending incremental changes in a single database or a subset of a database to subscribers as they occur." The cost is that it's higher-level: it tracks changes by replication identity (the primary key), not raw disk blocks, so it has more rules and some limitations a byte copy never worries about.

TWO WAYS TO SHIP THE WAL Physical / streaming │ replay the WAL bytes → identical whole-cluster clone (copy the bytes) │ same version, read-only standby, HA failover │ Logical (pub/sub) │ decode the WAL into row changes → apply logically (copy the meaning) │ subset of tables, cross-version, writable target

Postgres vs MySQL — what crosses the wire

QuestionMySQL / InnoDBPostgreSQL
Default replication ships…the binlog (logical events)the WAL (physical bytes)
Closest cousin of MySQL binlog replPostgres logical replication
Byte-level whole-cluster clone(not the binlog model)Streaming / physical standby
No-loss optionSemi-synchronousSynchronous replication
Async failover riskLose un-shipped binlogLose un-shipped WAL
Subset / cross-versionbinlog is fairly flexibleLogical replication

Check yourself

From memory. Two items reach back on purpose.

Streaming (physical) replication works by shipping and replaying:

The standby continuously receives WAL records and replays them — a byte-for-byte physical copy of the whole cluster. Logical replication is the one that ships row-level changes.

With default (async) streaming, a primary crash on failover can:

Async ships WAL after commit, so transactions committed but not yet sent are lost if the primary dies. Synchronous replication closes that window at an availability cost.

Synchronous replication's cost if the lone sync standby goes down is:

A synchronous commit waits for the standby's confirmation, so losing the standby can stall commits. That's the availability price of zero data loss.

You need to replicate just three tables into a newer major version. Use:

Logical replication ships row changes by replication identity, so it can do a subset of tables across major versions into a writable target — none of which byte-level physical replication allows.

Synchronous replication is essentially which earlier dial, aimed at a standby? (recall 0009)

synchronous_commit (0009) waits for the local WAL flush; synchronous replication extends that to wait for the standby's WAL flush too. Same durability-vs-throughput dial, one hop further.
Optional — when you have an instance

A lab to run later — inspect replication state and sketch logical pub/sub:

-- on a primary with a standby attached:
SELECT client_addr, state, sync_state, replay_lag FROM pg_stat_replication;
SHOW synchronous_standby_names;      -- empty = all async

-- logical replication shape (publisher, then subscriber):
CREATE PUBLICATION mypub FOR TABLE orders, customers;                 -- on publisher
CREATE SUBSCRIPTION mysub CONNECTION 'host=… dbname=…' PUBLICATION mypub;  -- on subscriber

Watch replay_lag on the primary — that lag is the async data-loss window. Bring a replication topology you're considering and we'll reason about the trade-offs.

Primary source — read this next

PostgreSQL Docs — 26.2 Log-Shipping Standby Servers (streaming & synchronous replication) and 31. Logical Replication. For the mechanism that turns WAL into logical change events, see 49. Logical Decoding.