From 33d6b7f2559dcb312de4cdcc3d75c93dad7dfe47 Mon Sep 17 00:00:00 2001 From: Rui Zhao Date: Sun, 13 Sep 2026 00:00:27 +0800 Subject: [PATCH v5 1/3] Wait for the transactions of an initial decoding snapshot to finish SnapBuildInitialSnapshot() converts the snapshot builder's list of committed transactions into a regular MVCC snapshot, which is then used with HeapTupleSatisfiesMVCC(). That function consults CLOG about the transactions the snapshot takes as not running, so each of them has to have finished committing before the snapshot is handed out: the commit record is written first, CLOG is updated afterwards, and the transaction stays in the procarray until after that. Read the set of running transactions once, and wait on the transaction lock of those that are in the snapshot's list, as SnapBuildWaitSnapshot() does in the same code path; the others have left the procarray and so have updated CLOG. Historic snapshots built by SnapBuildBuildSnapshot() need no such wait: they rely on the xip array for transactions between xmin and xmax, and consult CLOG only for transactions below xmin, which had left the procarray when the xl_running_xacts record that set xmin was written. --- src/backend/replication/logical/snapbuild.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index de491ea0c4b..261f25a5cd7 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -517,8 +517,22 @@ SnapBuildInitialSnapshot(SnapBuild *builder) (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("initial slot snapshot too large"))); - newxip[newxcnt++] = xid; + newxip[newxcnt] = xid; } + else + { + /* + * The commit record of this transaction has been decoded, but the + * commit itself may not have finished, if it's still in the process + * of removing itself from the procarray or waiting for a synchronous + * standby. To avoid producing a snapshot that inconsistently shows + * this transaction as committed, wait until it actually is. + */ + if (!RecoveryInProgress()) + XactLockTableWait(xid, NULL, NULL, XLTW_None); + } + + newxcnt++; TransactionIdAdvance(xid); } -- 2.47.3