Background processes
WAL receiver
On a streaming standby, the walreceiver connects to the primary, receives WAL, writes and flushes it to the local pg_wal, and reports its progress back.
In plain words
A standby server keeps itself up to date by replaying the primary's WAL. The walreceiver is the process that fetches it: it logs in to the primary like a client (using primary_conninfo), receives the stream of WAL, and saves it to the standby's own pg_wal directory.
It does not apply the changes itself. That is the job of the startup process, which the walreceiver wakes up whenever new WAL is safely on disk. In the other direction it regularly tells the primary how far it got, which is what the primary shows in pg_stat_replication.
If the connection breaks, it simply exits; the startup process starts a new one a few seconds later. Toggle hot_standby_feedback to see what extra information it sends.
For DBAs and operators
Who starts it, and when
Only the startup process asks for a walreceiver: when the standby (with standby.signal) has replayed all WAL it can find in pg_wal and the archive, and primary_conninfo is set. The postmaster then forks it. It connects with replication=true, runs IDENTIFY_SYSTEM (the system identifier must match), fetches timeline history files if needed, and sends START_REPLICATION from the start of the WAL segment it needs, using primary_slot_name if set (or a temporary slot with wal_receiver_create_temp_slot).
The loop
- Receive
XLogDataand write it into the current segment file inpg_wal(written_lsn). - When the socket is drained,
fsyncit (flushed_lsn) and wake the startup process. - Send a status reply (write/flush/apply positions) after each flush and at least every
wal_receiver_status_interval(10s). Ifhot_standby_feedback = on, also send the oldestxminof queries on the standby, which appears asbackend_xminin the primary'spg_stat_replication(and holds back VACUUM there). - Answer keepalives. If nothing arrives for
wal_receiver_timeout(60s), give up.
How to see it
SELECT pid, status, receive_start_lsn, written_lsn, flushed_lsn,
latest_end_lsn, last_msg_receipt_time, slot_name,
sender_host, sender_port
FROM pg_stat_wal_receiver;
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(),
now() - pg_last_xact_replay_timestamp() AS replay_delay;backend_type is walreceiver; ps shows postgres: walreceiver streaming 0/13000000. pg_stat_wal_receiver returns no row when no walreceiver is running: that is your first clue that streaming is down.
If it dies
Errors make it exit with FATAL; the startup process falls back to the archive and pg_wal, and asks for a new walreceiver after wal_retrieve_retry_interval (5s). Real lines from a test standby:
The "invalid record length" line is normal: the startup process reached the end of the WAL available locally. Other typical messages:
FATAL: terminating walreceiver due to timeoutFATAL: streaming replication receiver "walreceiver" could not connect to the primary server: connection to server at "10.0.0.1", port 5432 failed: Connection refusedFATAL: could not receive data from WAL stream: ERROR: requested WAL segment 000000010000000000000012 has already been removedThe last one means the primary recycled WAL the standby still needed: use a replication slot or wal_keep_size, or let restore_command fetch it from the archive. A crash by signal (SIGKILL) restarts the whole standby like any child crash.
Support notes
primary_conninfoandprimary_slot_nameare reloadable since PostgreSQL 13: a reload restarts the walreceiver.written_lsnahead of replay is normal; a growing gap is a slow or blocked startup process (see recovery conflicts), not the network.hot_standby_feedback = onprevents most snapshot conflicts but can bloat the primary if standby queries run for hours.
For developers: inside the source
Where it lives
walreceiver.c: the process (WalReceiverMain()).walreceiverfuncs.c: functions the startup process uses to control it (RequestXLogStreaming(),ShutdownWalRcv(),WalRcvStreaming(),GetWalRcvFlushRecPtr()).libpqwalreceiver.c: the libpq part, loaded at run time withload_file("libpqwalreceiver")so the backend binary does not link libpq. It fills theWalReceiverFunctionsTypetable used through macros such aswalrcv_connect(),walrcv_startstreaming(),walrcv_receive(),walrcv_send(); logical replication apply workers use the same table.walreceiver.h: shared state and the function table.
Key data structures
WalRcvData (one in shared memory, WalRcv) holds walRcvState (WALRCV_STOPPED, STARTING, STREAMING, WAITING, RESTARTING, STOPPING), the requested receiveStart/receiveStartTLI, flushedUpto, latestWalEnd, conninfo, slotname and sender host/port, protected by a mutex spinlock. writtenUpto is a pg_atomic_uint64 updated without the lock. pg_stat_get_wal_receiver() reads it for pg_stat_wal_receiver.
Call flow
- The startup process, in
WaitForWALToBecomeAvailable()(xlogrecovery.c), callsRequestXLogStreaming(), which setsWALRCV_STARTINGand sendsPMSIGNAL_START_WALRECEIVER; the postmaster forks the process. WalReceiverMain():walrcv_connect(),walrcv_identify_system(),WalRcvFetchTimeLineHistoryFiles(),walrcv_startstreaming().- Inner loop:
walrcv_receive()→XLogWalRcvProcessMsg()→XLogWalRcvWrite()for'w'messages,ProcessWalSndrMessage()for keepalives. When the socket is drained,XLogWalRcvFlush()doesissue_xlog_fsync(), advancesflushedUpto, callsWakeupRecovery()(andWalSndWakeup()for cascading standbys), thenXLogWalRcvSendReply()andXLogWalRcvSendHSFeedback(). - Sleeping uses
WaitLatchOrSocket()(wait eventWalReceiverMain) with a timeout fromWalRcvComputeNextWakeup(), which keeps one deadline perWalRcvWakeupReason:WALRCV_WAKEUP_TERMINATE(wal_receiver_timeout),PING,REPLY,HSFEEDBACK. - When the primary ends streaming (e.g. a timeline switch), the process does not exit:
WalRcvWaitForStartPosition()parks it inWALRCV_WAITINGuntil the startup process asks for a new start point.
Experimenting
- TAP:
001_stream_rep.pl,004_timeline_switch.pl,055_cascade_reconnect.pl. - Protocol messages are documented in
doc/src/sgml/protocol.sgml(Streaming Replication Protocol).
Parameters that shape this
primary_conninfo primary_slot_name wal_receiver_status_interval hot_standby_feedback wal_receiver_timeout wal_retrieve_retry_interval wal_receiver_create_temp_slot recovery_min_apply_delay
Related topics
WAL sender Startup process Streaming replication Hot standby and recovery conflicts Replication slots Synchronous replication Archiving and point-in-time recovery