Replication — operational orientation
For the conceptual model (slot lifetimes, decoding pipeline, conflict semantics)
read knowledge/architecture/replication.md.
Which kind of replication?
| If the user wants… |
Flavor |
Key GUCs |
| Hot standby, byte-identical copy, optional read queries |
Physical streaming |
wal_level=replica, max_wal_senders, primary_conninfo, primary_slot_name |
| Point-in-time recovery from a base backup + WAL archive |
Physical archive (PITR) |
wal_level=replica, archive_mode, archive_command/archive_library, restore_command |
| Stream row-level changes to a non-PG consumer (CDC, audit, custom sink) |
Logical decoding via SQL or replication-protocol API + an output plugin |
wal_level=logical, max_replication_slots, plugin (e.g. test_decoding, pgoutput) |
| Selective table replication between PG clusters, schema-agnostic upgrade, cross-version |
Logical replication (CREATE PUBLICATION / CREATE SUBSCRIPTION) |
wal_level=logical, max_replication_slots, max_logical_replication_workers, max_sync_workers_per_subscription, max_parallel_apply_workers_per_subscription |
| Wait-for-ACK durability on commit |
Synchronous replication (overlay on physical or logical) |
synchronous_standby_names, synchronous_commit |
| Standbys keep logical slots in sync for failover |
Slot sync |
sync_replication_slots, synchronized_standby_slots, slot failover=true |
[from-comment source/src/backend/replication/walsender.c:5-18;
from-comment source/src/backend/replication/walreceiver.c:5-17;
from-comment source/src/backend/replication/syncrep.c:5-58;
from-comment source/src/backend/replication/logical/launcher.c:10-15,53-56]
wal_level — what it gates
minimal — only crash recovery; no walsenders, no archive, no replication of
any kind.
replica — adds enough info for physical streaming and PITR archive.
logical — additionally logs the data needed by reorderbuffer to reconstruct
row changes (replica identity, etc.). Required for logical decoding and
logical replication.
[from-code source/src/backend/utils/misc/guc_tables.c:525,649 — wal_level_options, effective_wal_level]
Where to look in source
Physical streaming
- Sender:
source/src/backend/replication/walsender.c (134 KB). Handles
replication-protocol commands IDENTIFY_SYSTEM, BASE_BACKUP,
CREATE_REPLICATION_SLOT, START_REPLICATION. Grammar:
source/src/backend/replication/repl_gram.y:62-300.
Batch size: MAX_SEND_SIZE = XLOG_BLCKSZ * 16 (128 KB with default 8K
blocks) — walsender.c:110-118.
- Receiver:
source/src/backend/replication/walreceiver.c + the dynamically
loaded libpqwalreceiver/ (keeps libpq out of the main server binary).
[from-README source/src/backend/replication/README:3-16]
- Postmaster signaling for walsender lifecycle:
source/src/backend/replication/README:39-70.
Replication slots (physical and logical)
source/src/backend/replication/slot.c — allocation, on-disk format,
invalidation causes (RS_INVAL_WAL_REMOVED, RS_INVAL_HORIZON,
RS_INVAL_WAL_LEVEL, RS_INVAL_IDLE_TIMEOUT).
[from-code source/src/include/replication/slot.h:58-72]
- SQL surface:
slotfuncs.c.
- Persistency states
RS_PERSISTENT | RS_EPHEMERAL | RS_TEMPORARY —
source/src/include/replication/slot.h:43-48.
- On-disk fields the user actually monitors:
restart_lsn, catalog_xmin,
confirmed_flush, invalidated, two_phase, failover, synced
(slot.h:95-162).
Logical decoding pipeline
- Coordinator:
source/src/backend/replication/logical/logical.c — sets up
LogicalDecodingContext, wraps output-plugin callbacks
(source/src/backend/replication/logical/logical.c:57-80).
- Decoder:
source/src/backend/replication/logical/decode.c — turns xlog
records into reorderbuffer calls.
- Reassembly + spill-to-disk:
source/src/backend/replication/logical/reorderbuffer.c
(≈162 KB; ships in-progress transactions to disk under memory pressure —
reorderbuffer.c:33-83).
- Catalog snapshots for decoding:
logical/snapbuild.c.
- Output plugins: contract in
source/src/include/replication/output_plugin.h:36,216-243
(_PG_output_plugin_init, OutputPluginCallbacks). Built-in plugin used by
logical replication: source/src/backend/replication/pgoutput/.
Logical replication (PUB/SUB)
- Launcher (bgworker spawning per-subscription):
logical/launcher.c.
- Apply worker:
logical/worker.c (~194 KB). Streaming and two-phase notes at
worker.c:22-80.
- Initial table sync:
logical/tablesync.c — state machine
INIT → DATASYNC → FINISHEDCOPY → SYNCWAIT → CATCHUP → SYNCDONE → READY
(tablesync.c:29-55).
- Parallel apply:
logical/applyparallelworker.c.
- Conflict logging (v18):
logical/conflict.c — types in
ConflictTypeNames[] (conflict.c:27-36).
- Sequence sync:
logical/sequencesync.c.
- Slot sync to standby:
logical/slotsync.c. The three sync-ready
preconditions (standby WAL past remote confirmed_flush_lsn; catalog xmin
not behind; consistent snapshot buildable at restart_lsn before
confirmed_flush_lsn) — slotsync.c:11-40. Mirror slots stay
RS_TEMPORARY until all three hold, then flip to RS_PERSISTENT with
synced=true. Full discussion in knowledge/architecture/replication.md §2.
- Replication origins:
logical/origin.c.
Synchronous replication
source/src/backend/replication/syncrep.c — wait queue on the primary; FIRST
(priority) vs ANY (quorum) parsed by syncrep_gram.y
(syncrep.c:32-58).
Knob cheatsheet
# Primary (any flavor)
wal_level = replica | logical
max_wal_senders = 10
max_replication_slots = 10
# Standby (physical)
primary_conninfo = 'host=... user=replication'
primary_slot_name = 'standby_1' # ties to a physical slot
hot_standby = on
# Logical replication subscriber
max_logical_replication_workers = 4 # launcher.c:54
max_sync_workers_per_subscription = 2 # launcher.c:55
max_parallel_apply_workers_per_subscription = 2 # launcher.c:56
# Logical decoding tuning
logical_decoding_work_mem = 64MB # per-reorderbuffer memory ceiling
# before the LARGEST in-progress txn
# spills to disk (reorderbuffer.c)
# Synchronous
synchronous_standby_names = 'FIRST 2 (s1, s2, s3)' # or 'ANY 2 (...)'
synchronous_commit = on | remote_apply | remote_write | local | off
# Failover slots (v17+)
sync_replication_slots = on # on standby
synchronized_standby_slots = 's1,s2' # on primary
Common diagnostic views
pg_stat_replication — one row per active walsender (on primary).
pg_stat_wal_receiver — on standby.
pg_replication_slots — slot state, restart_lsn, confirmed_flush_lsn,
wal_status, invalidation_reason.
pg_stat_subscription, pg_stat_subscription_stats — subscriber side.
pg_replication_origin_status — apply progress per origin.
Replication-protocol commands (psql replication=true)
IDENTIFY_SYSTEM, CREATE_REPLICATION_SLOT slot {PHYSICAL|LOGICAL plugin} ...,
START_REPLICATION [SLOT s] {PHYSICAL %X/%X [TIMELINE n] | SLOT s LOGICAL %X/%X options},
BASE_BACKUP (...), READ_REPLICATION_SLOT, DROP_REPLICATION_SLOT,
TIMELINE_HISTORY.
[from-code source/src/backend/replication/repl_gram.y:62-300]
When to send the user deeper
- "Why is my slot blocking WAL recycling?" → slot's
restart_lsn /
catalog_xmin; see knowledge/architecture/replication.md §slots.
- "How do I write an output plugin?" →
output_plugin.h callbacks +
contrib/test_decoding as canonical example. Docs:
https://www.postgresql.org/docs/current/logicaldecoding.html.
- "Subscriber missed an UPDATE" → conflict types in
logical/conflict.c; in
v18 logging is automatic.
- "Failover broke my logical subscription" → slot sync chapter +
failover
flag on slot.
- "Why is my logical slot stuck behind a physical standby?" →
synchronized_standby_slots gate + the walsender wakeup chain
(WalSndWaitForWal → PhysicalWakeupLogicalWalSnd →
wal_confirm_rcv_cv). See knowledge/architecture/replication.md §2.
Files examined for this skill
| file |
lines |
depth |
source/src/backend/replication/README |
1-76 |
full |
source/src/backend/replication/walsender.c |
1-120 |
header |
source/src/backend/replication/walreceiver.c |
1-80 |
header |
source/src/backend/replication/slot.c |
1-100 |
header |
source/src/include/replication/slot.h |
1-200 |
partial |
source/src/backend/replication/syncrep.c |
1-80 |
header |
source/src/backend/replication/logical/decode.c |
1-80 |
header |
source/src/backend/replication/logical/reorderbuffer.c |
1-120 |
header |
source/src/backend/replication/logical/logical.c |
1-80 |
header |
source/src/backend/replication/logical/worker.c |
1-80 |
header |
source/src/backend/replication/logical/tablesync.c |
1-80 |
header |
source/src/backend/replication/logical/launcher.c |
1-60 |
header |
source/src/backend/replication/logical/conflict.c |
1-60 |
header |
source/src/backend/replication/repl_gram.y |
grep |
command surface |
source/src/include/replication/output_plugin.h |
grep |
callback contract |
Cross-references
.claude/skills/wal-and-xlog/SKILL.md — WAL records consumed by walsenders; rm_decode for logical decoding visibility.
.claude/skills/locking/SKILL.md — ProcArrayLock, ReplicationSlotControlLock, SyncRepLock ordering; standby-conflict-detection.
.claude/skills/bgworker-and-extensions/SKILL.md — logical-replication apply workers are bgworkers; subscription launcher manages them.
.claude/skills/error-handling/SKILL.md — apply-worker error escalation; conflict detection in logical replication.
.claude/skills/testing/SKILL.md — TAP tests in src/test/recovery/t/, src/test/subscription/t/.
knowledge/architecture/replication.md — long-form architecture.
knowledge/subsystems/replication.md — deep-dive corpus doc covering both physical and logical replication.
1---2name: replication-overview3description: Hack or review PostgreSQL replication internals — covers physical streaming, archive shipping, logical decoding, logical replication PUB/SUB, and synchronous-commit machinery. Names walsender / walreceiver / replication slot / output plugin callbacks plus the relevant GUCs (wal_level, max_wal_senders, max_replication_slots, max_logical_replication_workers, primary_conninfo, primary_slot_name, synchronous_standby_names) and the source files behind each flavor. Use whenever a PG patch touches walsender, walreceiver, replication slots, logical decoding, output plugins, publications/subscriptions, conflict detection, sync replication, failover slots, pg_basebackup, or pg_receivewal — or when picking which mechanism fits a feature design. Skip for MySQL row-based / GTID replication, MongoDB replica sets, Cassandra hinted handoff, Kafka MirrorMaker / Confluent Replicator, Debezium / Flink CDC pipelines, AWS DMS, and Galera / Group Replication.4---56# Replication — operational orientation78For the conceptual model (slot lifetimes, decoding pipeline, conflict semantics)9read `knowledge/architecture/replication.md`.1011## Which kind of replication?1213| If the user wants… | Flavor | Key GUCs |14|---|---|---|15| Hot standby, byte-identical copy, optional read queries | **Physical streaming** | `wal_level=replica`, `max_wal_senders`, `primary_conninfo`, `primary_slot_name` |16| Point-in-time recovery from a base backup + WAL archive | **Physical archive (PITR)** | `wal_level=replica`, `archive_mode`, `archive_command`/`archive_library`, `restore_command` |17| Stream row-level changes to a non-PG consumer (CDC, audit, custom sink) | **Logical decoding** via SQL or replication-protocol API + an output plugin | `wal_level=logical`, `max_replication_slots`, plugin (e.g. `test_decoding`, `pgoutput`) |18| Selective table replication between PG clusters, schema-agnostic upgrade, cross-version | **Logical replication** (`CREATE PUBLICATION` / `CREATE SUBSCRIPTION`) | `wal_level=logical`, `max_replication_slots`, `max_logical_replication_workers`, `max_sync_workers_per_subscription`, `max_parallel_apply_workers_per_subscription` |19| Wait-for-ACK durability on commit | **Synchronous replication** (overlay on physical or logical) | `synchronous_standby_names`, `synchronous_commit` |20| Standbys keep logical slots in sync for failover | **Slot sync** | `sync_replication_slots`, `synchronized_standby_slots`, slot `failover=true` |2122[from-comment `source/src/backend/replication/walsender.c:5-18`;23from-comment `source/src/backend/replication/walreceiver.c:5-17`;24from-comment `source/src/backend/replication/syncrep.c:5-58`;25from-comment `source/src/backend/replication/logical/launcher.c:10-15,53-56`]2627## wal_level — what it gates2829- `minimal` — only crash recovery; no walsenders, no archive, no replication of30 any kind.31- `replica` — adds enough info for physical streaming and PITR archive.32- `logical` — additionally logs the data needed by `reorderbuffer` to reconstruct33 row changes (replica identity, etc.). Required for logical decoding and34 logical replication.3536[from-code `source/src/backend/utils/misc/guc_tables.c:525,649` — `wal_level_options`, `effective_wal_level`]3738## Where to look in source3940### Physical streaming41- Sender: `source/src/backend/replication/walsender.c` (~134 KB). Handles42 replication-protocol commands `IDENTIFY_SYSTEM`, `BASE_BACKUP`,43 `CREATE_REPLICATION_SLOT`, `START_REPLICATION`. Grammar:44 `source/src/backend/replication/repl_gram.y:62-300`.45 Batch size: `MAX_SEND_SIZE = XLOG_BLCKSZ * 16` (~128 KB with default 8K46 blocks) — `walsender.c:110-118`.47- Receiver: `source/src/backend/replication/walreceiver.c` + the dynamically48 loaded `libpqwalreceiver/` (keeps libpq out of the main server binary).49 [from-README `source/src/backend/replication/README:3-16`]50- Postmaster signaling for walsender lifecycle:51 `source/src/backend/replication/README:39-70`.5253### Replication slots (physical and logical)54- `source/src/backend/replication/slot.c` — allocation, on-disk format,55 invalidation causes (`RS_INVAL_WAL_REMOVED`, `RS_INVAL_HORIZON`,56 `RS_INVAL_WAL_LEVEL`, `RS_INVAL_IDLE_TIMEOUT`).57 [from-code `source/src/include/replication/slot.h:58-72`]58- SQL surface: `slotfuncs.c`.59- Persistency states `RS_PERSISTENT | RS_EPHEMERAL | RS_TEMPORARY` —60 `source/src/include/replication/slot.h:43-48`.61- On-disk fields the user actually monitors: `restart_lsn`, `catalog_xmin`,62 `confirmed_flush`, `invalidated`, `two_phase`, `failover`, `synced`63 (`slot.h:95-162`).6465### Logical decoding pipeline66- Coordinator: `source/src/backend/replication/logical/logical.c` — sets up67 `LogicalDecodingContext`, wraps output-plugin callbacks68 (`source/src/backend/replication/logical/logical.c:57-80`).69- Decoder: `source/src/backend/replication/logical/decode.c` — turns xlog70 records into `reorderbuffer` calls.71- Reassembly + spill-to-disk: `source/src/backend/replication/logical/reorderbuffer.c`72 (≈162 KB; ships in-progress transactions to disk under memory pressure —73 `reorderbuffer.c:33-83`).74- Catalog snapshots for decoding: `logical/snapbuild.c`.75- Output plugins: contract in `source/src/include/replication/output_plugin.h:36,216-243`76 (`_PG_output_plugin_init`, `OutputPluginCallbacks`). Built-in plugin used by77 logical replication: `source/src/backend/replication/pgoutput/`.7879### Logical replication (PUB/SUB)80- Launcher (bgworker spawning per-subscription): `logical/launcher.c`.81- Apply worker: `logical/worker.c` (~194 KB). Streaming and two-phase notes at82 `worker.c:22-80`.83- Initial table sync: `logical/tablesync.c` — state machine84 `INIT → DATASYNC → FINISHEDCOPY → SYNCWAIT → CATCHUP → SYNCDONE → READY`85 (`tablesync.c:29-55`).86- Parallel apply: `logical/applyparallelworker.c`.87- Conflict logging (v18): `logical/conflict.c` — types in88 `ConflictTypeNames[]` (`conflict.c:27-36`).89- Sequence sync: `logical/sequencesync.c`.90- Slot sync to standby: `logical/slotsync.c`. The three sync-ready91 preconditions (standby WAL past remote `confirmed_flush_lsn`; catalog xmin92 not behind; consistent snapshot buildable at `restart_lsn` before93 `confirmed_flush_lsn`) — `slotsync.c:11-40`. Mirror slots stay94 `RS_TEMPORARY` until all three hold, then flip to `RS_PERSISTENT` with95 `synced=true`. Full discussion in `knowledge/architecture/replication.md` §2.96- Replication origins: `logical/origin.c`.9798### Synchronous replication99- `source/src/backend/replication/syncrep.c` — wait queue on the primary; FIRST100 (priority) vs ANY (quorum) parsed by `syncrep_gram.y`101 (`syncrep.c:32-58`).102103## Knob cheatsheet104105```106# Primary (any flavor)107wal_level = replica | logical108max_wal_senders = 10109max_replication_slots = 10110111# Standby (physical)112primary_conninfo = 'host=... user=replication'113primary_slot_name = 'standby_1' # ties to a physical slot114hot_standby = on115116# Logical replication subscriber117max_logical_replication_workers = 4 # launcher.c:54118max_sync_workers_per_subscription = 2 # launcher.c:55119max_parallel_apply_workers_per_subscription = 2 # launcher.c:56120121# Logical decoding tuning122logical_decoding_work_mem = 64MB # per-reorderbuffer memory ceiling123 # before the LARGEST in-progress txn124 # spills to disk (reorderbuffer.c)125126# Synchronous127synchronous_standby_names = 'FIRST 2 (s1, s2, s3)' # or 'ANY 2 (...)'128synchronous_commit = on | remote_apply | remote_write | local | off129130# Failover slots (v17+)131sync_replication_slots = on # on standby132synchronized_standby_slots = 's1,s2' # on primary133```134135## Common diagnostic views136137- `pg_stat_replication` — one row per active walsender (on primary).138- `pg_stat_wal_receiver` — on standby.139- `pg_replication_slots` — slot state, `restart_lsn`, `confirmed_flush_lsn`,140 `wal_status`, `invalidation_reason`.141- `pg_stat_subscription`, `pg_stat_subscription_stats` — subscriber side.142- `pg_replication_origin_status` — apply progress per origin.143144## Replication-protocol commands (psql `replication=true`)145146`IDENTIFY_SYSTEM`, `CREATE_REPLICATION_SLOT slot {PHYSICAL|LOGICAL plugin} ...`,147`START_REPLICATION [SLOT s] {PHYSICAL %X/%X [TIMELINE n] | SLOT s LOGICAL %X/%X options}`,148`BASE_BACKUP (...)`, `READ_REPLICATION_SLOT`, `DROP_REPLICATION_SLOT`,149`TIMELINE_HISTORY`.150[from-code `source/src/backend/replication/repl_gram.y:62-300`]151152## When to send the user deeper153154- "Why is my slot blocking WAL recycling?" → slot's `restart_lsn` /155 `catalog_xmin`; see `knowledge/architecture/replication.md` §slots.156- "How do I write an output plugin?" → `output_plugin.h` callbacks +157 `contrib/test_decoding` as canonical example. Docs:158 https://www.postgresql.org/docs/current/logicaldecoding.html.159- "Subscriber missed an UPDATE" → conflict types in `logical/conflict.c`; in160 v18 logging is automatic.161- "Failover broke my logical subscription" → slot sync chapter + `failover`162 flag on slot.163- "Why is my logical slot stuck behind a physical standby?" →164 `synchronized_standby_slots` gate + the walsender wakeup chain165 (`WalSndWaitForWal` → `PhysicalWakeupLogicalWalSnd` →166 `wal_confirm_rcv_cv`). See `knowledge/architecture/replication.md` §2.167168## Files examined for this skill169170| file | lines | depth |171|---|---|---|172| `source/src/backend/replication/README` | 1-76 | full |173| `source/src/backend/replication/walsender.c` | 1-120 | header |174| `source/src/backend/replication/walreceiver.c` | 1-80 | header |175| `source/src/backend/replication/slot.c` | 1-100 | header |176| `source/src/include/replication/slot.h` | 1-200 | partial |177| `source/src/backend/replication/syncrep.c` | 1-80 | header |178| `source/src/backend/replication/logical/decode.c` | 1-80 | header |179| `source/src/backend/replication/logical/reorderbuffer.c` | 1-120 | header |180| `source/src/backend/replication/logical/logical.c` | 1-80 | header |181| `source/src/backend/replication/logical/worker.c` | 1-80 | header |182| `source/src/backend/replication/logical/tablesync.c` | 1-80 | header |183| `source/src/backend/replication/logical/launcher.c` | 1-60 | header |184| `source/src/backend/replication/logical/conflict.c` | 1-60 | header |185| `source/src/backend/replication/repl_gram.y` | grep | command surface |186| `source/src/include/replication/output_plugin.h` | grep | callback contract |187188## Cross-references189190- `.claude/skills/wal-and-xlog/SKILL.md` — WAL records consumed by walsenders; `rm_decode` for logical decoding visibility.191- `.claude/skills/locking/SKILL.md` — `ProcArrayLock`, `ReplicationSlotControlLock`, `SyncRepLock` ordering; standby-conflict-detection.192- `.claude/skills/bgworker-and-extensions/SKILL.md` — logical-replication apply workers are bgworkers; subscription launcher manages them.193- `.claude/skills/error-handling/SKILL.md` — apply-worker error escalation; conflict detection in logical replication.194- `.claude/skills/testing/SKILL.md` — TAP tests in `src/test/recovery/t/`, `src/test/subscription/t/`.195- `knowledge/architecture/replication.md` — long-form architecture.196- `knowledge/subsystems/replication.md` — deep-dive corpus doc covering both physical and logical replication.