PostgreSQL Streaming vs Logical Replication
Fast recall
Quick revision
Core answer
pg_wal fills.Whether you can distinguish physical from logical replication, pick the right model for HA vs migration vs CDC, configure replication slots and synchronous_commit correctly, and read pg_stat_replication for lag.
Trying to use streaming replication for a major-version upgrade (it cannot cross major versions), or forgetting to drop orphaned replication slots and discovering the primary has stopped accepting writes because pg_wal filled the disk.
Explain replication slots and why they are required, how `synchronous_commit = remote_apply` trades latency for zero-data-loss failover, and how Debezium attaches as a logical consumer via the `pgoutput` plugin for CDC pipelines.
Interview-ready guide
Interview answer
PostgreSQL has two replication models built on top of the write-ahead log. Choosing between them is usually half the answer.
Streaming replication, for HA and read scaling
- Ships raw WAL bytes from the primary to standbys that replay them block for block
- The standby is an exact physical copy and can serve read-only queries
- Configure
wal_level=replica, create a replication slot so the primary keeps WAL until the standby consumes it, take a base backup withpg_basebackup -R - Monitor with
pg_stat_replication— the LSN diff betweensent_lsnandreplay_lsnis the lag metric - Cannot cross major versions and cannot replicate a subset of tables
Logical replication, for upgrades and partial copies
- Decodes WAL into row-level change events and ships them per publication to subscribers that apply through normal SQL
- Cross-version and cross-database replication work
- Used for zero-downtime major-version upgrades, selective table replication, and CDC into Kafka via Debezium's
pgoutputplugin - Requires primary keys or
REPLICA IDENTITY FULL, and DDL must be replicated manually via Flyway on both sides
Synchronous modes
off,local,on,remote_write,remote_apply— each trades latency for a stronger durability guaranteeremote_applyis the only mode that guarantees the sync standby has replayed the change before commit returns
The operational pitfall that shows up in interviews
- Orphaned replication slots fill
pg_waland stop the primary from accepting writes - Always drop slots on decommission; alert on
pg_replication_slots.wal_bytes_kept
Rule of thumb
- Streaming for HA inside a cluster; logical for anything that crosses a boundary — version, database, or system
PostgreSQL Streaming vs Logical Replication
Direct Answer
PostgreSQL has two replication families built on the write-ahead log (WAL). Streaming (physical) replication ships raw WAL bytes to standbys, which replay them block-for-block — used for HA, read scaling, and fast failover; cannot cross major versions or replicate a subset of tables. Logical replication decodes WAL into row-level change events per publication and applies them on subscribers — enabling zero-downtime major upgrades, partial replication, and CDC into Kafka via Debezium. Replication slots guarantee WAL retention but must be dropped on decommission or the primary's pg_wal fills.
Code Example
-- ============== PRIMARY ==============
-- postgresql.conf
-- wal_level = replica
-- max_wal_senders = 10
-- max_replication_slots = 10
-- synchronous_commit = on
-- synchronous_standby_names = 'standby_a'
-- pg_hba.conf
-- host replication replicator 10.0.0.0/8 scram-sha-256
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD '...';
-- Physical replication slot — ensures the primary keeps WAL until the standby replays it
SELECT pg_create_physical_replication_slot('standby_a');
-- ============== STANDBY ==============
-- pg_basebackup copies the entire data directory and writes standby.signal
-- pg_basebackup -h primary -D /var/lib/pgsql/17/data -U replicator \
-- -X stream -S standby_a -R --checkpoint=fast --progress
-- Start the standby; it connects, streams WAL, and enters hot-standby read-only mode.
-- ============== MONITORING ==============
-- On primary
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS apply_lag_bytes,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS total_lag_bytes
FROM pg_stat_replication;
-- On standby
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(),
now() - pg_last_xact_replay_timestamp() AS replay_delay;
-- ============== SYNCHRONOUS MODES ==============
-- off — fire and forget
-- local — primary fsync only
-- remote_write — standby received (in memory)
-- on — standby fsynced WAL
-- remote_apply — standby replayed (zero-lag reads on standby; costly latency)
-- ============== THE SLOT HAZARD ==============
-- If a standby is removed without dropping its slot, pg_wal grows without bound.
SELECT slot_name, active, restart_lsn,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS wal_bytes_kept
FROM pg_replication_slots;
-- Always drop orphaned slots
SELECT pg_drop_replication_slot('standby_a');
Common Mistakes & Pitfalls
Trying to use streaming replication for a major-version upgrade (it cannot cross major versions), or forgetting to drop orphaned replication slots and discovering the primary has stopped accepting writes because pg_wal filled the disk.
Check your understanding
Answer these without looking back at the article.
- What is a replication slot and why is it required for reliable standbys?
- How would you perform a major-version upgrade with near-zero downtime?
- What does `synchronous_commit = remote_apply` guarantee, and what does it cost?
- How does Debezium hook into PostgreSQL's WAL stream and what slot does it leave behind?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.