Skip to content

Commit 43b0c91

Browse files
committed
Fix aboriginal mistake in lazy VACUUM's code for truncating away
no-longer-needed pages at the end of a table. We thought we could throw away pages containing HEAPTUPLE_DEAD tuples; but this is not so, because such tuples very likely have index entries pointing at them, and we wouldn't have removed the index entries. The problem only emerges in a somewhat unlikely race condition: the dead tuples have to have been inserted by a transaction that later aborted, and this has to have happened between VACUUM's initial scan of the page and then rechecking it for empty in count_nondeletable_pages. But that timespan will include an index-cleaning pass, so it's not all that hard to hit. This seems to explain a couple of previously unsolved bug reports.
1 parent 9a36a09 commit 43b0c91

File tree

1 file changed

+14
-45
lines changed

1 file changed

+14
-45
lines changed

src/backend/commands/vacuumlazy.c

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*
3737
*
3838
* IDENTIFICATION
39-
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.95 2007/09/12 22:10:26 tgl Exp $
39+
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.96 2007/09/16 02:37:46 tgl Exp $
4040
*
4141
*-------------------------------------------------------------------------
4242
*/
@@ -784,9 +784,9 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
784784

785785
/*
786786
* Scan backwards from the end to verify that the end pages actually
787-
* contain nothing we need to keep. This is *necessary*, not optional,
788-
* because other backends could have added tuples to these pages whilst we
789-
* were vacuuming.
787+
* contain no tuples. This is *necessary*, not optional, because other
788+
* backends could have added tuples to these pages whilst we were
789+
* vacuuming.
790790
*/
791791
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats);
792792

@@ -846,15 +846,14 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
846846
}
847847

848848
/*
849-
* Rescan end pages to verify that they are (still) empty of needed tuples.
849+
* Rescan end pages to verify that they are (still) empty of tuples.
850850
*
851851
* Returns number of nondeletable pages (last nonempty page + 1).
852852
*/
853853
static BlockNumber
854854
count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
855855
{
856856
BlockNumber blkno;
857-
HeapTupleData tuple;
858857

859858
/* Strange coding of loop control is needed because blkno is unsigned */
860859
blkno = vacrelstats->rel_pages;
@@ -864,8 +863,7 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
864863
Page page;
865864
OffsetNumber offnum,
866865
maxoff;
867-
bool tupgone,
868-
hastup;
866+
bool hastup;
869867

870868
/*
871869
* We don't insert a vacuum delay point here, because we have an
@@ -901,42 +899,13 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
901899

902900
itemid = PageGetItemId(page, offnum);
903901

904-
if (!ItemIdIsUsed(itemid))
905-
continue;
906-
907-
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
908-
tuple.t_len = ItemIdGetLength(itemid);
909-
ItemPointerSet(&(tuple.t_self), blkno, offnum);
910-
911-
tupgone = false;
912-
913-
switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
914-
{
915-
case HEAPTUPLE_DEAD:
916-
tupgone = true; /* we can delete the tuple */
917-
break;
918-
case HEAPTUPLE_LIVE:
919-
/* Shouldn't be necessary to re-freeze anything */
920-
break;
921-
case HEAPTUPLE_RECENTLY_DEAD:
922-
923-
/*
924-
* If tuple is recently deleted then we must not remove it
925-
* from relation.
926-
*/
927-
break;
928-
case HEAPTUPLE_INSERT_IN_PROGRESS:
929-
/* This is an expected case during concurrent vacuum */
930-
break;
931-
case HEAPTUPLE_DELETE_IN_PROGRESS:
932-
/* This is an expected case during concurrent vacuum */
933-
break;
934-
default:
935-
elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
936-
break;
937-
}
938-
939-
if (!tupgone)
902+
/*
903+
* Note: any non-unused item should be taken as a reason to keep
904+
* this page. We formerly thought that DEAD tuples could be
905+
* thrown away, but that's not so, because we'd not have cleaned
906+
* out their index entries.
907+
*/
908+
if (ItemIdIsUsed(itemid))
940909
{
941910
hastup = true;
942911
break; /* can stop scanning */
@@ -952,7 +921,7 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
952921

953922
/*
954923
* If we fall out of the loop, all the previously-thought-to-be-empty
955-
* pages really are; we need not bother to look at the last known-nonempty
924+
* pages still are; we need not bother to look at the last known-nonempty
956925
* page.
957926
*/
958927
return vacrelstats->nonempty_pages;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy