Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6799d5

Browse files
committed
Move page initialization from RelationAddExtraBlocks() to use.
Previously we initialized pages when bulk extending in RelationAddExtraBlocks(). That has a major disadvantage: It ties RelationAddExtraBlocks() to heap, as other types of storage are likely to need different amounts of special space, have different amount of free space (previously determined by PageGetHeapFreeSpace()). That we're relying on initializing pages, but not WAL logging the initialization, also means the risk for getting "WARNING: relation \"%s\" page %u is uninitialized --- fixing" style warnings in vacuums after crashes/immediate shutdowns, is considerably higher. The warning sounds much more serious than what they are. Fix those two issues together by not initializing pages in RelationAddExtraPages() (but continue to do so in RelationGetBufferForTuple(), which is linked much more closely to heap), and accepting uninitialized pages as normal in vacuumlazy.c. When vacuumlazy encounters an empty page it now adds it to the FSM, but does nothing else. We chose to not issue a debug message, much less a warning in that case - it seems rarely useful, and quite likely to scare people unnecessarily. For now empty pages aren't added to the VM, because standbys would not re-discover such pages after a promotion. In contrast to other sources for empty pages, there's no corresponding WAL records triggering FSM updates during replay. Author: Andres Freund Reviewed-By: Tom Lane Discussion: https://postgr.es/m/[email protected]
1 parent d4316b8 commit e6799d5

File tree

2 files changed

+59
-49
lines changed

2 files changed

+59
-49
lines changed

src/backend/access/heap/hio.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
204204
/*
205205
* Extend by one page. This should generally match the main-line
206206
* extension code in RelationGetBufferForTuple, except that we hold
207-
* the relation extension lock throughout.
207+
* the relation extension lock throughout, and we don't immediately
208+
* initialize the page (see below).
208209
*/
209210
buffer = ReadBufferBI(relation, P_NEW, bistate);
210211

@@ -216,18 +217,16 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
216217
BufferGetBlockNumber(buffer),
217218
RelationGetRelationName(relation));
218219

219-
PageInit(page, BufferGetPageSize(buffer), 0);
220-
221220
/*
222-
* We mark all the new buffers dirty, but do nothing to write them
223-
* out; they'll probably get used soon, and even if they are not, a
224-
* crash will leave an okay all-zeroes page on disk.
221+
* Add the page to the FSM without initializing. If we were to
222+
* initialize here the page would potentially get flushed out to disk
223+
* before we add any useful content. There's no guarantee that that'd
224+
* happen before a potential crash, so we need to deal with
225+
* uninitialized pages anyway, thus avoid the potential for
226+
* unnecessary writes.
225227
*/
226-
MarkBufferDirty(buffer);
227-
228-
/* we'll need this info below */
229228
blockNum = BufferGetBlockNumber(buffer);
230-
freespace = PageGetHeapFreeSpace(page);
229+
freespace = BufferGetPageSize(buffer) - SizeOfPageHeaderData;
231230

232231
UnlockReleaseBuffer(buffer);
233232

@@ -479,6 +478,18 @@ RelationGetBufferForTuple(Relation relation, Size len,
479478
* we're done.
480479
*/
481480
page = BufferGetPage(buffer);
481+
482+
/*
483+
* Initialize page, it'll be used soon. We could avoid dirtying the
484+
* buffer here, and rely on the caller to do so whenever it puts a
485+
* tuple onto the page, but there seems not much benefit in doing so.
486+
*/
487+
if (PageIsNew(page))
488+
{
489+
PageInit(page, BufferGetPageSize(buffer), 0);
490+
MarkBufferDirty(buffer);
491+
}
492+
482493
pageFreeSpace = PageGetHeapFreeSpace(page);
483494
if (len + saveFreeSpace <= pageFreeSpace)
484495
{

src/backend/access/heap/vacuumlazy.c

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -861,42 +861,38 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
861861
if (PageIsNew(page))
862862
{
863863
/*
864-
* An all-zeroes page could be left over if a backend extends the
865-
* relation but crashes before initializing the page. Reclaim such
866-
* pages for use.
864+
* All-zeroes pages can be left over if either a backend extends
865+
* the relation by a single page, but crashes before the newly
866+
* initialized page has been written out, or when bulk-extending
867+
* the relation (which creates a number of empty pages at the tail
868+
* end of the relation, but enters them into the FSM).
867869
*
868-
* We have to be careful here because we could be looking at a
869-
* page that someone has just added to the relation and not yet
870-
* been able to initialize (see RelationGetBufferForTuple). To
871-
* protect against that, release the buffer lock, grab the
872-
* relation extension lock momentarily, and re-lock the buffer. If
873-
* the page is still uninitialized by then, it must be left over
874-
* from a crashed backend, and we can initialize it.
870+
* Make sure these pages are in the FSM, to ensure they can be
871+
* reused. Do that by testing if there's any space recorded for
872+
* the page. If not, enter it.
875873
*
876-
* We don't really need the relation lock when this is a new or
877-
* temp relation, but it's probably not worth the code space to
878-
* check that, since this surely isn't a critical path.
879-
*
880-
* Note: the comparable code in vacuum.c need not worry because
881-
* it's got exclusive lock on the whole relation.
874+
* Note we do not enter the page into the visibilitymap. That has
875+
* the downside that we repeatedly visit this page in subsequent
876+
* vacuums, but otherwise we'll never not discover the space on a
877+
* promoted standby. The harm of repeated checking ought to
878+
* normally not be too bad - the space usually should be used at
879+
* some point, otherwise there wouldn't be any regular vacuums.
880+
*/
881+
empty_pages++;
882+
883+
/*
884+
* Perform checking of FSM after releasing lock, the fsm is
885+
* approximate, after all.
882886
*/
883-
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
884-
LockRelationForExtension(onerel, ExclusiveLock);
885-
UnlockRelationForExtension(onerel, ExclusiveLock);
886-
LockBufferForCleanup(buf);
887-
if (PageIsNew(page))
888-
{
889-
ereport(WARNING,
890-
(errmsg("relation \"%s\" page %u is uninitialized --- fixing",
891-
relname, blkno)));
892-
PageInit(page, BufferGetPageSize(buf), 0);
893-
empty_pages++;
894-
}
895-
freespace = PageGetHeapFreeSpace(page);
896-
MarkBufferDirty(buf);
897887
UnlockReleaseBuffer(buf);
898888

899-
RecordPageWithFreeSpace(onerel, blkno, freespace);
889+
if (GetRecordedFreeSpace(onerel, blkno) == 0)
890+
{
891+
Size freespace;
892+
893+
freespace = BufferGetPageSize(buf) - SizeOfPageHeaderData;
894+
RecordPageWithFreeSpace(onerel, blkno, freespace);
895+
}
900896
continue;
901897
}
902898

@@ -905,7 +901,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
905901
empty_pages++;
906902
freespace = PageGetHeapFreeSpace(page);
907903

908-
/* empty pages are always all-visible and all-frozen */
904+
/*
905+
* Empty pages are always all-visible and all-frozen (note that
906+
* the same is currently not true for new pages, see above).
907+
*/
909908
if (!PageIsAllVisible(page))
910909
{
911910
START_CRIT_SECTION();
@@ -1639,12 +1638,13 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup)
16391638

16401639
*hastup = false;
16411640

1642-
/* If we hit an uninitialized page, we want to force vacuuming it. */
1643-
if (PageIsNew(page))
1644-
return true;
1645-
1646-
/* Quick out for ordinary empty page. */
1647-
if (PageIsEmpty(page))
1641+
/*
1642+
* New and empty pages, obviously, don't contain tuples. We could make
1643+
* sure that the page is registered in the FSM, but it doesn't seem worth
1644+
* waiting for a cleanup lock just for that, especially because it's
1645+
* likely that the pin holder will do so.
1646+
*/
1647+
if (PageIsNew(page) || PageIsEmpty(page))
16481648
return false;
16491649

16501650
maxoff = PageGetMaxOffsetNumber(page);
@@ -2029,7 +2029,6 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
20292029

20302030
if (PageIsNew(page) || PageIsEmpty(page))
20312031
{
2032-
/* PageIsNew probably shouldn't happen... */
20332032
UnlockReleaseBuffer(buf);
20342033
continue;
20352034
}

0 commit comments

Comments
 (0)