From 5c7b117aa3572f1c8566f033b99d242baf1e7190 Mon Sep 17 00:00:00 2001 From: Craig Ringer Date: Mon, 5 Sep 2016 16:13:35 +0800 Subject: [PATCH 09/21] Allow GetOldestXmin(...) to optionally disregard the catalog_xmin Add a new catalog_xmin out-parameter to GetOldestXmin(...), for use when calculating hot standby feedback xmins. When passed, any needed catalog_xmin is returned separately instead of being merged with the return value. Adjust existing call sites. --- contrib/pg_visibility/pg_visibility.c | 4 +-- contrib/pgstattuple/pgstatapprox.c | 2 +- src/backend/access/transam/xlog.c | 4 +-- src/backend/catalog/index.c | 2 +- src/backend/commands/analyze.c | 2 +- src/backend/commands/vacuum.c | 4 +-- src/backend/replication/walreceiver.c | 2 +- src/backend/storage/ipc/procarray.c | 51 +++++++++++++++++++++++------------ src/include/storage/procarray.h | 2 +- 9 files changed, 45 insertions(+), 28 deletions(-) diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index 9985e3e..4fa3ad4 100644 --- a/contrib/pg_visibility/pg_visibility.c +++ b/contrib/pg_visibility/pg_visibility.c @@ -538,7 +538,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) if (all_visible) { /* Don't pass rel; that will fail in recovery. */ - OldestXmin = GetOldestXmin(NULL, true); + OldestXmin = GetOldestXmin(NULL, true, false); } rel = relation_open(relid, AccessShareLock); @@ -660,7 +660,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) * a buffer lock. And this shouldn't happen often, so it's * worth being careful so as to avoid false positives. */ - RecomputedOldestXmin = GetOldestXmin(NULL, true); + RecomputedOldestXmin = GetOldestXmin(NULL, true, false); if (!TransactionIdPrecedes(OldestXmin, RecomputedOldestXmin)) record_corrupt_item(items, &tuple.t_self); diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c index f524fc4..5b33c97 100644 --- a/contrib/pgstattuple/pgstatapprox.c +++ b/contrib/pgstattuple/pgstatapprox.c @@ -70,7 +70,7 @@ statapprox_heap(Relation rel, output_type *stat) TransactionId OldestXmin; uint64 misc_count = 0; - OldestXmin = GetOldestXmin(rel, true); + OldestXmin = GetOldestXmin(rel, true, false); bstrategy = GetAccessStrategy(BAS_BULKREAD); nblocks = RelationGetNumberOfBlocks(rel); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6cec027..56c672c 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8653,7 +8653,7 @@ CreateCheckPoint(int flags) * StartupSUBTRANS hasn't been called yet. */ if (!RecoveryInProgress()) - TruncateSUBTRANS(GetOldestXmin(NULL, false)); + TruncateSUBTRANS(GetOldestXmin(NULL, false, NULL)); /* Real work is done, but log and update stats before releasing lock. */ LogCheckpointEnd(false); @@ -9016,7 +9016,7 @@ CreateRestartPoint(int flags) * this because StartupSUBTRANS hasn't been called yet. */ if (EnableHotStandby) - TruncateSUBTRANS(GetOldestXmin(NULL, false)); + TruncateSUBTRANS(GetOldestXmin(NULL, false, NULL)); /* Real work is done, but log and update before releasing lock. */ LogCheckpointEnd(true); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 08b646d..b673c06 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2272,7 +2272,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, { snapshot = SnapshotAny; /* okay to ignore lazy VACUUMs here */ - OldestXmin = GetOldestXmin(heapRelation, true); + OldestXmin = GetOldestXmin(heapRelation, true, NULL); } scan = heap_beginscan_strat(heapRelation, /* relation */ diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c617abb..9b0cc3a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -992,7 +992,7 @@ acquire_sample_rows(Relation onerel, int elevel, totalblocks = RelationGetNumberOfBlocks(onerel); /* Need a cutoff xmin for HeapTupleSatisfiesVacuum */ - OldestXmin = GetOldestXmin(onerel, true); + OldestXmin = GetOldestXmin(onerel, true, NULL); /* Prepare for sampling block numbers */ BlockSampler_Init(&bs, totalblocks, targrows, random()); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 58bbf55..aaee9a6 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -497,7 +497,7 @@ vacuum_set_xid_limits(Relation rel, * always an independent transaction. */ *oldestXmin = - TransactionIdLimitedForOldSnapshots(GetOldestXmin(rel, true), rel); + TransactionIdLimitedForOldSnapshots(GetOldestXmin(rel, true, NULL), rel); Assert(TransactionIdIsNormal(*oldestXmin)); @@ -909,7 +909,7 @@ vac_update_datfrozenxid(void) * committed pg_class entries for new tables; see AddNewRelationTuple(). * So we cannot produce a wrong minimum by starting with this. */ - newFrozenXid = GetOldestXmin(NULL, true); + newFrozenXid = GetOldestXmin(NULL, true, NULL); /* * Similarly, initialize the MultiXact "min" with the value that would be diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index 06ca9e4..80cc482 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -1206,7 +1206,7 @@ XLogWalRcvSendHSFeedback(bool immed) * everything else has been checked. */ if (hot_standby_feedback) - xmin = GetOldestXmin(NULL, false); + xmin = GetOldestXmin(NULL, false, NULL); else xmin = InvalidTransactionId; diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index e5d487d..a4e3549 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -1298,17 +1298,22 @@ TransactionIdIsActive(TransactionId xid) * process can set its xmin based on transactions that are no longer running * in the master but are still being replayed on the standby, thus possibly * making the GetOldestXmin reading go backwards. In this case there is a - * possibility that we lose data that the standby would like to have, but - * there is little we can do about that --- data is only protected if the - * walsender runs continuously while queries are executed on the standby. - * (The Hot Standby code deals with such cases by failing standby queries - * that needed to access already-removed data, so there's no integrity bug.) + * possibility that we lose data that the standby would like to have + * unless the standby uses a replication slot to make its xmin persistent + * even when it isn't connected. The Hot Standby code deals with such cases by + * failing standby queries that needed to access already-removed data, so + * there's no integrity bug. + * * The return value is also adjusted with vacuum_defer_cleanup_age, so * increasing that setting on the fly is another easy way to make * GetOldestXmin() move backwards, with no consequences for data integrity. + * + * The caller may request that replication slots' catalog_xmin values be + * disregarded when calculating the global xmin. The caller must account + * for catalog_xmin separately. */ TransactionId -GetOldestXmin(Relation rel, bool ignoreVacuum) +GetOldestXmin(Relation rel, bool ignoreVacuum, TransactionId *catalog_xmin) { ProcArrayStruct *arrayP = procArray; TransactionId result; @@ -1433,17 +1438,29 @@ GetOldestXmin(Relation rel, bool ignoreVacuum) NormalTransactionIdPrecedes(replication_slot_xmin, result)) result = replication_slot_xmin; - /* - * After locks have been released and defer_cleanup_age has been applied, - * check whether we need to back up further to make logical decoding - * possible. We need to do so if we're computing the global limit (rel = - * NULL) or if the passed relation is a catalog relation of some kind. - */ - if ((rel == NULL || - RelationIsAccessibleInLogicalDecoding(rel)) && - TransactionIdIsValid(replication_slot_catalog_xmin) && - NormalTransactionIdPrecedes(replication_slot_catalog_xmin, result)) - result = replication_slot_catalog_xmin; + if (!(rel == NULL || RelationIsAccessibleInLogicalDecoding(rel))) + replication_slot_catalog_xmin = InvalidXLogRecPtr; + + if (catalog_xmin != NULL) + { + /* + * The caller wants any logical decoding specific xmin reported + * separately, so don't merge it with the xmin we'll return. + */ + *catalog_xmin = replication_slot_catalog_xmin; + } + else + { + /* + * After locks have been released and defer_cleanup_age has been applied, + * check whether we need to back up further to make logical decoding + * possible. We need to do so if we're computing the global limit (rel = + * NULL) or if the passed relation is a catalog relation of some kind. + */ + if (TransactionIdIsValid(replication_slot_catalog_xmin) && + NormalTransactionIdPrecedes(replication_slot_catalog_xmin, result)) + result = replication_slot_catalog_xmin; + } return result; } diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index dd37c0c..f7d1d96 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -53,7 +53,7 @@ extern RunningTransactions GetRunningTransactionData(void); extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); -extern TransactionId GetOldestXmin(Relation rel, bool ignoreVacuum); +extern TransactionId GetOldestXmin(Relation rel, bool ignoreVacuum, TransactionId *catalog_xmin); extern TransactionId GetOldestActiveTransactionId(void); extern TransactionId GetOldestSafeDecodingTransactionId(void); -- 2.5.5