>From 9907880952f40dfd11795625d659ea723352c376 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Tue, 26 Nov 2013 23:57:55 +0100 Subject: [PATCH] Don't sometimes incorrectly increase relfrozenxid in vacuums that truncate. (auto-)vacuum recognizes that it can increase relfrozenxid by checking whether it has processed all pages of a relation and frozen all that are older than the new relfrozenxid. Unfortunately it performed that check after truncating the dead pages of the end of the relation and used the new number of pages to decide whether all pages have been scanned. This can lead to relfrozenxid being increased above the actual oldest xid which in turn can lead to data loss due to xid wraparounds with some rows suddently missing. This likely has escaped notice so far because it takes a large number (~2^31) of xids being used to see the effect, while a full-table vacuum before that would fix the issue. --- src/backend/commands/vacuumlazy.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 6778c7d..5d9d32f 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -226,6 +226,24 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, vac_close_indexes(nindexes, Irel, NoLock); /* + * Compute whether we actually scanned the whole relation, if we did, we + * can adjust relfrozenxid. + * + * NB: We need to check before truncating the relation, because that will + * change ->rel_pages. + */ + new_frozen_xid = FreezeLimit; + if (vacrelstats->scanned_pages < vacrelstats->rel_pages) + { + Assert(!scan_all); + new_frozen_xid = InvalidTransactionId; + } + + new_min_multi = MultiXactCutoff; + if (scan_all || vacrelstats->scanned_pages < vacrelstats->rel_pages) + new_min_multi = InvalidMultiXactId; + + /* * Optionally truncate the relation. * * Don't even think about it unless we have a shot at releasing a goodly @@ -269,14 +287,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, if (new_rel_allvisible > new_rel_pages) new_rel_allvisible = new_rel_pages; - new_frozen_xid = FreezeLimit; - if (vacrelstats->scanned_pages < vacrelstats->rel_pages) - new_frozen_xid = InvalidTransactionId; - - new_min_multi = MultiXactCutoff; - if (vacrelstats->scanned_pages < vacrelstats->rel_pages) - new_min_multi = InvalidMultiXactId; - vac_update_relstats(onerel, new_rel_pages, new_rel_tuples, -- 1.8.3.251.g1462b67