Replication & HASource-grounded

Replication Slots: Guaranteeing WAL Retention

A replica (physical standby or logical subscriber) consumes WAL from the primary. If the primary recycles WAL before the replica has read it, the replica falls irrecoverably behind and must be rebuilt. The old wal_keep_size approach — keep a fixed amount of WAL and hope it's enough — is fragile. Replication slots solve this precisely by tracking exactly how far each consumer has progressed and ret

Grounded in the source

Where this comes from in postgres/postgres

Each citation was checked against the REL_17_STABLE branch: real file path, symbol present, exact line number. Follow any link to read the code.

src/backend/replication/slot.cVerified · REL_17_STABLE

Primary symbol: ReplicationSlotCreate · line 338

The one thing to understand first

A replica (physical standby or logical subscriber) consumes WAL from the primary. If the primary recycles WAL before the replica has read it, the replica falls irrecoverably behind and must be rebuilt. The old wal_keep_size approach — keep a fixed amount of WAL and hope it's enough — is fragile. Replication slots solve this precisely by tracking exactly how far each consumer has progressed and retaining WAL until then.

A slot is a promise the primary makes to a consumer: "I will keep this WAL until you read it." The catch is that the promise has no expiry — make it to a consumer that never returns and it becomes a promise to fill your own disk.

How a slot works

A slot (src/backend/replication/slot.c) is a named, persistent marker recording the oldest WAL position (restart_lsn) a consumer still needs. The primary will not remove WAL beyond that position. Slots survive restarts, so a standby can disconnect, come back hours later, and resume from exactly where it stopped — no rebuild required.

Reference
SQL
-- Physical slot for a streaming standby
SELECT pg_create_physical_replication_slot('standby1');

-- Logical slot (created automatically by a subscription, or manually)
SELECT pg_create_logical_replication_slot('cdc1', 'pgoutput');

-- Inspect all slots
SELECT slot_name, slot_type, active, restart_lsn,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM   pg_replication_slots;

Logical slots also pin the catalog horizon

A logical slot does more than retain WAL: it also holds back the catalog xmin — preventing vacuum from removing catalog rows the decoder might still need to interpret older WAL. This means an abandoned logical slot can cause catalog bloat in addition to WAL accumulation. The same mechanism that guarantees correctness creates the retention risk.

Layer 3 — Watch it happen on your own database

A slot keeps WAL for as long as it exists, whether or not the consumer is alive. If a standby is decommissioned but its slot is left behind, or a subscriber stays down, the primary retains WAL forever and eventually fills its disk — a leading cause of production outages. The slot guarantees delivery at the cost of unbounded retention if the consumer never returns. Catch it before it bites:

Reference
SQL
-- Find inactive slots that are accumulating WAL
SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM   pg_replication_slots
WHERE  NOT active
ORDER  BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;

-- Drop a slot that is no longer needed
SELECT pg_drop_replication_slot('old_standby');

An active = false row with a large retained_wal is the disk-fill footgun in progress. Dropping the dead slot immediately frees the primary to recycle that WAL.

Layer 4 — The levers this hands you

The slot gives you one safety valve and a short discipline checklist:

  • **Set max_slot_wal_keep_size on every primary. It caps how much WAL a slot may retain; if a slot falls further behind than the cap, PostgreSQL invalidates the slot** and recycles the WAL — sacrificing one replica to protect the primary's disk. A lost replica is recoverable (rebuild it); a full disk on the primary is an outage.
  • Always monitor pg_replication_slots for inactive slots and large retained WAL.
  • Drop slots immediately when decommissioning a replica.
  • Alert on retained WAL crossing a threshold well before disk pressure.
  • For logical slots, also watch catalog bloat from a held-back catalog_xmin — a logical slot pins the catalog horizon so vacuum cannot remove catalog rows the decoder may still need.

Layer 5 — What an Oracle DBA should expect vs what they get

Slots are the Postgres equivalent of Data Guard's log retention guarantees, but with a sharper edge an Oracle DBA must respect:

  • A slot ≈ Data Guard's guaranteed log retention / a mandatory archive destination, ensuring the primary keeps redo until the standby consumes it — but there is no RMAN-managed archive area quietly cleaning up behind you.
  • No automatic cap by default. Oracle's FRA (Fast Recovery Area) has a size limit and deletion policies; a Postgres slot retains WAL without bound unless you explicitly set max_slot_wal_keep_size. Forgetting it is how primaries run out of disk.
  • Logical slots pin the catalog horizon, which has no clean Oracle analogue — an abandoned logical slot bloats the catalog as well as the WAL, something LogMiner/GoldenGate setups don't expose the same way.
  • Invalidation is the deliberate trade-off: Postgres will drop a too-far-behind slot to save the primary, whereas an Oracle DBA might expect the primary to instead stall — here, protecting the primary wins.

Key takeaway

A replication slot records the oldest WAL position a consumer still needs and forces the primary to retain WAL (and, for logical slots, catalog rows) until then — turning "hope there's enough WAL" into "guaranteed delivery." That guarantee is also the danger: an inactive slot retains WAL forever and fills the disk, so monitor pg_replication_slots, drop dead slots, and always set max_slot_wal_keep_size as a backstop.

Interview lens

How this shows up in interviews

What an interviewer is actually testing, the same mechanism seen from three roles, and the gap between a shallow answer and one that shows real depth.

What the interviewer is testing

Whether you understand that slots precisely track a consumer's progress to retain WAL — and the disk-fill risk an abandoned slot creates.

Questions you should be able to answer

  • What problem do replication slots solve over wal_keep_size?
  • What's the danger of a slot whose consumer has gone away?
  • Physical vs logical slot?

Database engineer

You monitor pg_replication_slots.restart_lsn and set max_slot_wal_keep_size so an abandoned slot can't fill pg_wal.

Software engineer

You know an orphaned slot (a dropped replica or stalled CDC consumer) silently accumulates WAL until the primary's disk fills — a classic outage.

Feature builder

You ensure any CDC consumer you build advances and cleans up its slot, or you cap its retention.

Shallow answer

'Slots keep WAL for replicas.' Right idea, but misses restart_lsn precision and the disk-fill failure mode.

Answer that shows depth

Without slots you keep a fixed amount of WAL (wal_keep_size) and hope the replica reads it before recycling — fragile. A replication slot records exactly how far a specific consumer has progressed (restart_lsn) and guarantees the primary won't recycle WAL past that point, so a lagging replica never falls irrecoverably behind. The flip side: a slot with no active consumer pins WAL forever and fills pg_wal until the disk is full — an outage — which is why max_slot_wal_keep_size (a cap that sacrifices the slot instead of the primary) and monitoring restart_lsn matter. Physical slots serve standbys; logical slots serve decoding and subscriptions.

How this was verified

This explanation is anchored to PostgreSQL's source tree — the files linked above, on the REL_17_STABLE branch — and cross-checked against the official documentation for versions 15 through 18. Every SQL query shown was executed and its output captured in a throwaway PostgreSQL 17.10 lab; where a query needed correcting to run, that correction is noted inline.

Official documentation: §27.2.6 Replication Slots