Skip to content

Commit fc02e67

Browse files
committed
Fix race condition between relation extension and vacuum.
In e6799d5 I removed vacuumlazy.c trickery around re-checking whether a page is actually empty after acquiring an extension lock on the relation, because the page is not PageInit()ed anymore, and entries in the FSM ought not to lead to user-visible errors. As reported by various buildfarm animals that is not correct, given the way to code currently stands: If vacuum processes a page that's just been newly added by either RelationGetBufferForTuple() or RelationAddExtraBlocks(), it could add that page to the FSM and it could be reused by other backends, before those two functions check whether the newly added page is actually new. That's a relatively narrow race, but several buildfarm machines appear to be able to hit it. While it seems wrong that the FSM, given it's lack of durability and approximative nature, can trigger errors like this, that seems better fixed in a separate commit. Especially given that a good portion of the buildfarm is red, and this is just re-introducing logic that existed a few hours ago. Author: Andres Freund Discussion: https://postgr.es/m/20190128222259.zhi7ovzgtkft6em6@alap3.anarazel.de
1 parent 36a1281 commit fc02e67

File tree

1 file changed

+31
-5
lines changed

1 file changed

+31
-5
lines changed

src/backend/access/heap/vacuumlazy.c

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
860860

861861
if (PageIsNew(page))
862862
{
863+
bool still_new;
864+
863865
/*
864866
* All-zeroes pages can be left over if either a backend extends
865867
* the relation by a single page, but crashes before the newly
@@ -877,21 +879,45 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
877879
* promoted standby. The harm of repeated checking ought to
878880
* normally not be too bad - the space usually should be used at
879881
* some point, otherwise there wouldn't be any regular vacuums.
882+
*
883+
* We have to be careful here because we could be looking at a
884+
* page that someone has just added to the relation and the
885+
* extending backend might not yet have been able to lock the page
886+
* (see RelationGetBufferForTuple), which is problematic because
887+
* of cross-checks that new pages are actually new. If we add this
888+
* page to the FSM, this page could be reused, and such
889+
* crosschecks could fail. To protect against that, release the
890+
* buffer lock, grab the relation extension lock momentarily, and
891+
* re-lock the buffer. If the page is still empty and not in the
892+
* FSM by then, it must be left over from a from a crashed
893+
* backend, and we can record the free space.
894+
*
895+
* Note: the comparable code in vacuum.c need not worry because
896+
* it's got an exclusive lock on the whole relation.
880897
*/
881-
empty_pages++;
898+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
899+
LockRelationForExtension(onerel, ExclusiveLock);
900+
UnlockRelationForExtension(onerel, ExclusiveLock);
901+
LockBufferForCleanup(buf);
882902

883903
/*
884904
* Perform checking of FSM after releasing lock, the fsm is
885905
* approximate, after all.
886906
*/
907+
still_new = PageIsNew(page);
887908
UnlockReleaseBuffer(buf);
888909

889-
if (GetRecordedFreeSpace(onerel, blkno) == 0)
910+
if (still_new)
890911
{
891-
Size freespace;
912+
empty_pages++;
913+
914+
if (GetRecordedFreeSpace(onerel, blkno) == 0)
915+
{
916+
Size freespace;
892917

893-
freespace = BufferGetPageSize(buf) - SizeOfPageHeaderData;
894-
RecordPageWithFreeSpace(onerel, blkno, freespace);
918+
freespace = BufferGetPageSize(buf) - SizeOfPageHeaderData;
919+
RecordPageWithFreeSpace(onerel, blkno, freespace);
920+
}
895921
}
896922
continue;
897923
}

0 commit comments

Comments
 (0)