Replication slot — the bookmark that fills disks

A replication slot is a server-side cursor identified by an LSN that pins WAL recycling at restart_lsn — so a dead or stalled connector means WAL accumulates until pg_wal/ fills the disk and the database goes read-only.

Previously

Debezium tails the log and emits one change event per row change — but if it crashes, the database needs to know where it left off so the WAL can be safely recycled. Postgres provides exactly this bookmark; it's called a replication slot, and the position it remembers is an LSN.

Scene 05

Replication slot — the bookmark that fills disks

  1. Watch
  2. Try it
  3. Predict
  4. Capture
WAL retention · replication slotslot active · write rate 5 MB/srestart_lsnwrite head · 4 MBslot · active = truepg_wal/ · 4 / 100 MB (4%)red zoneSlot advancing — WAL recycled normally.slot active · LSN 0/00400000 → LSN 0/00400000
What to watch for

Debezium needs to remember how far it's read so a restart doesn't lose its place. Postgres provides exactly this bookmark — a replication slot — pinned to an LSN. Watch restart_lsn slide forward in step with the write head while the connector is running. The disk stays green because Postgres can recycle WAL bytes behind the slot.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Postgres.advanceRestartLsn(slot, newLsn)
Slot's restart_lsn moves only when the consumer acks.
def advance_restart_lsn(slot, new_lsn):
if not slot.active:
# consumer gone -> restart_lsn frozen
return
if new_lsn <= slot.restart_lsn:
return # never moves backward
slot.restart_lsn = new_lsn
# only NOW may pg_wal/ GC bytes < restart_lsn
wake_checkpointer()
Postgres.maybeRecycleWal(currentLsn)
GC refuses to recycle bytes any slot still claims.
def maybe_recycle_wal(current_lsn):
pin = min(s.restart_lsn for s in pg_replication_slots)
for seg in pg_wal_segments():
if seg.end_lsn < pin:
recycle(seg) # safe: no slot needs it
else:
keep(seg) # slot pins this WAL
if disk_used(pg_wal) >= READONLY_THRESHOLD:
# cannot durably commit without WAL space
set_database_read_only()
refuse_further_writes()
Connector.confirmFlushedLsn()
Consumer tells Postgres how far it has durably read.
def confirm_flushed_lsn():
while connector.running:
flushed = kafka_producer.last_acked_lsn()
# standby status update message
replication_stream.send_status(
write_lsn=flushed,
flush_lsn=flushed,
apply_lsn=flushed,
)
sleep(status_interval_ms)

Where this sits in Build a CDC pipeline (Debezium + outbox)

Scene 05 of 12. A replication slot is a server-side cursor identified by an LSN that stops Postgres from recycling WAL the connector hasn't read — and an inactive slot is the #1 Debezium production failure.

Up next. The slot tracks where the connector left off in the WAL — but the WAL only goes back so far, so a connector attaching to a database with a billion existing rows cannot replay history from the log alone.

All 12 scenes in Build a CDC pipeline (Debezium + outbox) · Every curriculum

Built with Arqly
Every scene in Build a CDC pipeline (Debezium + outbox) builds on the one before it.All 12 Build a CDC pipeline (Debezium + outbox) scenes