Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 93d8882

Browse files
committed
Don't throw a warning if vacuum sees PD_ALL_VISIBLE flag set on a page that
contains newly-inserted tuples that according to our OldestXmin are not yet visible to everyone. The value returned by GetOldestXmin() is conservative, and it can move backwards on repeated calls, so if we see that contradiction between the PD_ALL_VISIBLE flag and status of tuples on the page, we have to assume it's because an earlier vacuum calculated a higher OldestXmin value, and all the tuples really are visible to everyone. We have received several reports of this bug, with the "PD_ALL_VISIBLE flag was incorrectly set in relation ..." warning appearing in logs. We were finally able to hunt it down with David Gould's help to run extra diagnostics in an environment where this happened frequently. Also reword the warning, per Robert Haas' suggestion, to not imply that the PD_ALL_VISIBLE flag is necessarily at fault, as it might also be a symptom of corruption on a tuple header. Backpatch to 8.4, where the PD_ALL_VISIBLE flag was introduced.
1 parent 915cd10 commit 93d8882

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

src/backend/commands/vacuumlazy.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
349349
Size freespace;
350350
bool all_visible_according_to_vm = false;
351351
bool all_visible;
352+
bool has_dead_tuples;
352353

353354
/*
354355
* Skip pages that don't require vacuuming according to the visibility
@@ -500,6 +501,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
500501
* requiring freezing.
501502
*/
502503
all_visible = true;
504+
has_dead_tuples = false;
503505
nfrozen = 0;
504506
hastup = false;
505507
prev_dead_count = vacrelstats->num_dead_tuples;
@@ -640,6 +642,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
640642
HeapTupleHeaderAdvanceLatestRemovedXid(tuple.t_data,
641643
&vacrelstats->latestRemovedXid);
642644
tups_vacuumed += 1;
645+
has_dead_tuples = true;
643646
}
644647
else
645648
{
@@ -702,9 +705,22 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
702705
PageSetAllVisible(page);
703706
SetBufferCommitInfoNeedsSave(buf);
704707
}
705-
else if (PageIsAllVisible(page) && !all_visible)
708+
/*
709+
* It's possible for the value returned by GetOldestXmin() to move
710+
* backwards, so it's not wrong for us to see tuples that appear to
711+
* not be visible to everyone yet, while PD_ALL_VISIBLE is already
712+
* set. The real safe xmin value never moves backwards, but
713+
* GetOldestXmin() is conservative and sometimes returns a value
714+
* that's unnecessarily small, so if we see that contradiction it
715+
* just means that the tuples that we think are not visible to
716+
* everyone yet actually are, and the PD_ALL_VISIBLE flag is correct.
717+
*
718+
* There should never be dead tuples on a page with PD_ALL_VISIBLE
719+
* set, however.
720+
*/
721+
else if (PageIsAllVisible(page) && has_dead_tuples)
706722
{
707-
elog(WARNING, "PD_ALL_VISIBLE flag was incorrectly set in relation \"%s\" page %u",
723+
elog(WARNING, "page containing dead tuples is marked as all-visible in relation \"%s\" page %u",
708724
relname, blkno);
709725
PageClearAllVisible(page);
710726
SetBufferCommitInfoNeedsSave(buf);

src/backend/storage/ipc/procarray.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,18 @@ TransactionIdIsActive(TransactionId xid)
10001000
* This is also used to determine where to truncate pg_subtrans. allDbs
10011001
* must be TRUE for that case, and ignoreVacuum FALSE.
10021002
*
1003+
* Note: it's possible for the calculated value to move backwards on repeated
1004+
* calls. The calculated value is conservative, so that anything older is
1005+
* definitely not considered as running by anyone anymore, but the exact
1006+
* value calculated depends on a number of things. For example, if allDbs is
1007+
* TRUE and there are no transactions running in the current database,
1008+
* GetOldestXmin() returns latestCompletedXid. If a transaction begins after
1009+
* that, its xmin will include in-progress transactions in other databases
1010+
* that started earlier, so another call will return an lower value. The
1011+
* return value is also adjusted with vacuum_defer_cleanup_age, so increasing
1012+
* that setting on the fly is an easy way to have GetOldestXmin() move
1013+
* backwards.
1014+
*
10031015
* Note: we include all currently running xids in the set of considered xids.
10041016
* This ensures that if a just-started xact has not yet set its snapshot,
10051017
* when it does set the snapshot it cannot set xmin less than what we compute.

0 commit comments

Comments
 (0)