因此,备库也会产生死元组 (update、delete、事务回滚等),也需要相应进行 VACUUM,移除死元组,从而导致流复制场景中一个很常见的冲突——snapshot conflict,即快照冲突,备库查询需要的行版本被 VACUUM 移除了,从而报错:User query might have needed to see row versions that must be removed.
(1) The backend process writes and flushes WAL data to a WAL segment file by executing the functions XLogInsert() and XLogFlush().
(2) The walsender process sends the WAL data written into the WAL segment to the walreceiver process.
(3) After sending the WAL data, the backend process continues to wait for an ACK response from the standby server. More precisely, the backend process gets a latch by executing the internal function SyncRepWaitForLSN(), and waits for it to be released.
(4) The walreceiver on the standby server writes the received WAL data into the standby's WAL segment using the write() system call, and returns an ACK response to the walsender.
(5) The walreceiver flushes the WAL data to the WAL segment using the system call such as fsync(), returns another ACK response to the walsender, and informs the startup process about WAL data updated.
(6) The startup process replays the WAL data, which has been written to the WAL segment.
(7) The walsender releases the latch of the backend process on receiving the ACK response from the walreceiver, and then, the backend process's commit or abort action will be completed. The timing for latch-release depends on the parameter synchronous_commit.
It is 'on' (default), the latch is released when the ACK of step (5) received, whereas it is 'remote_write', the latch is released when the ACK of step (4) is received.
Read-only transactions and transaction rollbacks need not wait for replies from standby servers. Subtransaction commits do not wait for responses from standby servers, only top-level commits. Long running actions such as data loading or index building do not wait until the very final commit message. All two-phase commit actions require commit waits, including both prepare and commit.