From 5cbb8e5806a444df04ed2f54d1c45f4aa1ea8f7b Mon Sep 17 00:00:00 2001 From: Mingwei Jia Date: Sat, 12 Apr 2025 21:10:44 +0800 Subject: [PATCH] If the checkpoint's oldestActiveXid at standby startup is less than nextXid, then the visibility of transactions within that range should be determined using the CLOG. This is because there may be transactions in that range that have already committed on the primary, and these committed transactions will not be replayed again on the standby. --- src/backend/access/transam/xlog.c | 2 ++ src/backend/storage/ipc/procarray.c | 4 +++ src/backend/utils/time/snapmgr.c | 12 ++++++++ src/include/access/transam.h | 2 ++ src/include/storage/procarray.h | 2 ++ .../modules/test_misc/t/008_csnstandby.pl | 30 +++++++++++++++++++ 6 files changed, 52 insertions(+) create mode 100644 src/test/modules/test_misc/t/008_csnstandby.pl diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index deb2cd1883c..2796458491d 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5565,6 +5565,8 @@ StartupXLOG(void) TransamVariables->nextXid = checkPoint.nextXid; TransamVariables->nextOid = checkPoint.nextOid; TransamVariables->oidCount = 0; + TransamVariables->nextXidStandbyStart = + XidFromFullTransactionId(checkPoint.nextXid); MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset); AdvanceOldestClogXid(checkPoint.oldestXid); SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB); diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index c82e8d8c438..d4c67512c56 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -962,6 +962,10 @@ ProcArrayUpdateOldestRunningXid(TransactionId oldestRunningXID) procArray->oldest_running_primary_xid = oldestRunningXID; LWLockRelease(ProcArrayLock); } +TransactionId GetOldestRunningXid(void) +{ + return procArray->oldest_running_primary_xid; +} /* diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index df9e8ba37f4..0ebcb0af835 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -1969,6 +1969,18 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) uint32 cache_idx = snapshot->xmax - xid; uint64 wordno = cache_idx / INPROGRESS_CACHE_XIDS_PER_WORD; uint64 slotno = (cache_idx % INPROGRESS_CACHE_XIDS_PER_WORD) * INPROGRESS_CACHE_BITS; + TransactionId nextXidStart = TransamVariables->nextXidStandbyStart; + TransactionId oldestRunning = GetOldestRunningXid(); + + if (TransactionIdPrecedes(oldestRunning, nextXidStart) + && TransactionIdPrecedes(xid, nextXidStart)) + { + if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid)) + { + return false; + } + return true; + } if (snapshot->inprogress_cache) { diff --git a/src/include/access/transam.h b/src/include/access/transam.h index a7054fe11cd..a75d66f4d40 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -240,6 +240,8 @@ typedef struct TransamVariablesData /* During recovery, LSN of latest replayed commit record */ XLogRecPtr latestCommitLSN; + /* checkpoint`s next xid when hot-standby start */ + TransactionId nextXidStandbyStart; /* * Number of top-level transactions with xids (i.e. which may have diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index de74fce24e4..a4129de8101 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -29,6 +29,8 @@ extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid); extern void ProcArrayClearTransaction(PGPROC *proc); extern void ProcArrayUpdateOldestRunningXid(TransactionId oldestRunningXID); + +extern TransactionId GetOldestRunningXid(void); extern void ProcArrayInitRecovery(TransactionId initializedUptoXID); extern void RecordKnownAssignedTransactionIds(TransactionId xid); diff --git a/src/test/modules/test_misc/t/008_csnstandby.pl b/src/test/modules/test_misc/t/008_csnstandby.pl new file mode 100644 index 00000000000..98708490d91 --- /dev/null +++ b/src/test/modules/test_misc/t/008_csnstandby.pl @@ -0,0 +1,30 @@ + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(); +$primary->append_conf('postgresql.conf', 'max_wal_senders = 5'); +$primary->append_conf('postgresql.conf', 'wal_level=replica'); +$primary->start; +my $count = ''; +$primary->safe_psql('postgres', 'create table t1(i int, j int)'); + +my $primary_a = $primary->background_psql('postgres', on_error_die => 1); +$primary_a->query_safe("begin"); +$primary_a->query_safe("insert into t1 values(1,1)"); +$primary->safe_psql('postgres', 'insert into t1 values(2,1)'); +$primary->backup('bkp'); + +my $replica = PostgreSQL::Test::Cluster->new('replica'); +$replica->init_from_backup($primary, 'bkp', has_streaming => 1); +$replica->start; + +$count = $replica->safe_psql('postgres', "select count(*) from t1"); +is($count, '1', "get right visiablity before primary checkpoint in hot standby"); + +done_testing(); + -- 2.45.0