BrinTuple *oldtup;
Size oldsz;
Buffer newbuf;
+ BlockNumber newblk = InvalidBlockNumber;
bool extended;
Assert(newsz == MAXALIGN(newsz));
Assert(!extended);
newbuf = InvalidBuffer;
}
+ else
+ newblk = BufferGetBlockNumber(newbuf);
}
else
{
brin_initialize_empty_new_buffer(idxrel, newbuf);
UnlockReleaseBuffer(newbuf);
if (extended)
- FreeSpaceMapVacuum(idxrel);
+ FreeSpaceMapVacuumRange(idxrel, newblk, newblk + 1);
}
return false;
}
LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK);
if (BufferIsValid(newbuf))
{
+ /* As above, initialize and record new page if we got one */
if (extended)
brin_initialize_empty_new_buffer(idxrel, newbuf);
UnlockReleaseBuffer(newbuf);
if (extended)
- FreeSpaceMapVacuum(idxrel);
+ FreeSpaceMapVacuumRange(idxrel, newblk, newblk + 1);
}
return false;
}
if (((BrinPageFlags(oldpage) & BRIN_EVACUATE_PAGE) == 0) &&
brin_can_do_samepage_update(oldbuf, origsz, newsz))
{
- if (BufferIsValid(newbuf))
- {
- /* as above */
- if (extended)
- brin_initialize_empty_new_buffer(idxrel, newbuf);
- UnlockReleaseBuffer(newbuf);
- }
-
START_CRIT_SECTION();
if (!PageIndexTupleOverwrite(oldpage, oldoff, (Item) newtup, newsz))
elog(ERROR, "failed to replace BRIN tuple");
LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK);
- if (extended)
- FreeSpaceMapVacuum(idxrel);
+ if (BufferIsValid(newbuf))
+ {
+ /* As above, initialize and record new page if we got one */
+ if (extended)
+ brin_initialize_empty_new_buffer(idxrel, newbuf);
+ UnlockReleaseBuffer(newbuf);
+ if (extended)
+ FreeSpaceMapVacuumRange(idxrel, newblk, newblk + 1);
+ }
return true;
}
Buffer revmapbuf;
ItemPointerData newtid;
OffsetNumber newoff;
- BlockNumber newblk = InvalidBlockNumber;
Size freespace = 0;
revmapbuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
* need to do that here.
*/
if (extended)
- brin_page_init(BufferGetPage(newbuf), BRIN_PAGETYPE_REGULAR);
+ brin_page_init(newpage, BRIN_PAGETYPE_REGULAR);
PageIndexTupleDeleteNoCompact(oldpage, oldoff);
newoff = PageAddItem(newpage, (Item) newtup, newsz,
/* needed to update FSM below */
if (extended)
- {
- newblk = BufferGetBlockNumber(newbuf);
freespace = br_page_get_freespace(newpage);
- }
- ItemPointerSet(&newtid, BufferGetBlockNumber(newbuf), newoff);
+ ItemPointerSet(&newtid, newblk, newoff);
brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, newtid);
MarkBufferDirty(revmapbuf);
if (extended)
{
- Assert(BlockNumberIsValid(newblk));
RecordPageWithFreeSpace(idxrel, newblk, freespace);
- FreeSpaceMapVacuum(idxrel);
+ FreeSpaceMapVacuumRange(idxrel, newblk, newblk + 1);
}
return true;
Page page;
BlockNumber blk;
OffsetNumber off;
+ Size freespace = 0;
Buffer revmapbuf;
ItemPointerData tid;
bool extended;
/* Execute the actual insertion */
START_CRIT_SECTION();
if (extended)
- brin_page_init(BufferGetPage(*buffer), BRIN_PAGETYPE_REGULAR);
+ brin_page_init(page, BRIN_PAGETYPE_REGULAR);
off = PageAddItem(page, (Item) tup, itemsz, InvalidOffsetNumber,
false, false);
if (off == InvalidOffsetNumber)
- elog(ERROR, "could not insert new index tuple to page");
+ elog(ERROR, "failed to add BRIN tuple to new page");
MarkBufferDirty(*buffer);
- BRIN_elog((DEBUG2, "inserted tuple (%u,%u) for range starting at %u",
- blk, off, heapBlk));
+ /* needed to update FSM below */
+ if (extended)
+ freespace = br_page_get_freespace(page);
ItemPointerSet(&tid, blk, off);
brinSetHeapBlockItemptr(revmapbuf, pagesPerRange, heapBlk, tid);
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
LockBuffer(revmapbuf, BUFFER_LOCK_UNLOCK);
+ BRIN_elog((DEBUG2, "inserted tuple (%u,%u) for range starting at %u",
+ blk, off, heapBlk));
+
if (extended)
- FreeSpaceMapVacuum(idxrel);
+ {
+ RecordPageWithFreeSpace(idxrel, blk, freespace);
+ FreeSpaceMapVacuumRange(idxrel, blk, blk + 1);
+ }
return off;
}
}
/*
- * Given a BRIN index page, initialize it if necessary, and record it into the
- * FSM if necessary. Return value is true if the FSM itself needs "vacuuming".
+ * Given a BRIN index page, initialize it if necessary, and record its
+ * current free space in the FSM.
+ *
* The main use for this is when, during vacuuming, an uninitialized page is
* found, which could be the result of relation extension followed by a crash
* before the page can be used.
+ *
+ * Here, we don't bother to update upper FSM pages, instead expecting that our
+ * caller (brin_vacuum_scan) will fix them at the end of the scan. Elsewhere
+ * in this file, it's generally a good idea to propagate additions of free
+ * space into the upper FSM pages immediately.
*/
-bool
+void
brin_page_cleanup(Relation idxrel, Buffer buf)
{
Page page = BufferGetPage(buf);
- Size freespace;
/*
* If a page was left uninitialized, initialize it now; also record it in
{
brin_initialize_empty_new_buffer(idxrel, buf);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- return true;
+ return;
}
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
}
/* Nothing to be done for non-regular index pages */
if (BRIN_IS_META_PAGE(BufferGetPage(buf)) ||
BRIN_IS_REVMAP_PAGE(BufferGetPage(buf)))
- return false;
+ return;
/* Measure free space and record it */
- freespace = br_page_get_freespace(page);
- if (freespace > GetRecordedFreeSpace(idxrel, BufferGetBlockNumber(buf)))
- {
- RecordPageWithFreeSpace(idxrel, BufferGetBlockNumber(buf), freespace);
- return true;
- }
-
- return false;
+ RecordPageWithFreeSpace(idxrel, BufferGetBlockNumber(buf),
+ br_page_get_freespace(page));
}
/*
* Return a pinned and exclusively locked buffer which can be used to insert an
* index item of size itemsz (caller must ensure not to request sizes
* impossible to fulfill). If oldbuf is a valid buffer, it is also locked (in
- * an order determined to avoid deadlocks.)
+ * an order determined to avoid deadlocks).
*
* If we find that the old page is no longer a regular index page (because
* of a revmap extension), the old buffer is unlocked and we return
* If there's no existing page with enough free space to accommodate the new
* item, the relation is extended. If this happens, *extended is set to true,
* and it is the caller's responsibility to initialize the page (and WAL-log
- * that fact) prior to use.
+ * that fact) prior to use. The caller should also update the FSM with the
+ * page's remaining free space after the insertion.
*
- * Note that in some corner cases it is possible for this routine to extend the
- * relation and then not return the buffer. It is this routine's
+ * Note that the caller is not expected to update FSM unless *extended is set
+ * true. This policy means that we'll update FSM when a page is created, and
+ * when it's found to have too little space for a desired tuple insertion,
+ * but not every single time we add a tuple to the page.
+ *
+ * Note that in some corner cases it is possible for this routine to extend
+ * the relation and then not return the new page. It is this routine's
* responsibility to WAL-log the page initialization and to record the page in
- * FSM if that happens. Such a buffer may later be reused by this routine.
+ * FSM if that happens, since the caller certainly can't do it.
*/
static Buffer
brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
/* callers must have checked */
Assert(itemsz <= BrinMaxItemSize);
- *extended = false;
-
if (BufferIsValid(oldbuf))
oldblk = BufferGetBlockNumber(oldbuf);
else
oldblk = InvalidBlockNumber;
+ /* Choose initial target page, re-using existing target if known */
+ newblk = RelationGetTargetBlock(irel);
+ if (newblk == InvalidBlockNumber)
+ newblk = GetPageWithFreeSpace(irel, itemsz);
+
/*
* Loop until we find a page with sufficient free space. By the time we
* return to caller out of this loop, both buffers are valid and locked;
- * if we have to restart here, neither buffer is locked and buf is not a
- * pinned buffer.
+ * if we have to restart here, neither page is locked and newblk isn't
+ * pinned (if it's even valid).
*/
- newblk = RelationGetTargetBlock(irel);
- if (newblk == InvalidBlockNumber)
- newblk = GetPageWithFreeSpace(irel, itemsz);
for (;;)
{
Buffer buf;
CHECK_FOR_INTERRUPTS();
+ *extended = false;
+
if (newblk == InvalidBlockNumber)
{
/*
/*
* We lock the old buffer first, if it's earlier than the new one; but
- * before we do, we need to check that it hasn't been turned into a
- * revmap page concurrently; if we detect that it happened, give up
- * and tell caller to start over.
+ * then we need to check that it hasn't been turned into a revmap page
+ * concurrently. If we detect that that happened, give up and tell
+ * caller to start over.
*/
if (BufferIsValid(oldbuf) && oldblk < newblk)
{
* it first.
*/
if (*extended)
- {
brin_initialize_empty_new_buffer(irel, buf);
- /* shouldn't matter, but don't confuse caller */
- *extended = false;
- }
if (extensionLockHeld)
UnlockRelationForExtension(irel, ExclusiveLock);
ReleaseBuffer(buf);
+
+ if (*extended)
+ {
+ FreeSpaceMapVacuumRange(irel, newblk, newblk + 1);
+ /* shouldn't matter, but don't confuse caller */
+ *extended = false;
+ }
+
return InvalidBuffer;
}
}
/*
* We have a new buffer to insert into. Check that the new page has
* enough free space, and return it if it does; otherwise start over.
- * Note that we allow for the FSM to be out of date here, and in that
- * case we update it and move on.
- *
* (br_page_get_freespace also checks that the FSM didn't hand us a
* page that has since been repurposed for the revmap.)
*/
BrinMaxItemSize : br_page_get_freespace(page);
if (freespace >= itemsz)
{
- RelationSetTargetBlock(irel, BufferGetBlockNumber(buf));
-
- /*
- * Since the target block specification can get lost on cache
- * invalidations, make sure we update the more permanent FSM with
- * data about it before going away.
- */
- if (*extended)
- RecordPageWithFreeSpace(irel, BufferGetBlockNumber(buf),
- freespace);
+ RelationSetTargetBlock(irel, newblk);
/*
* Lock the old buffer if not locked already. Note that in this
if (*extended)
{
brin_initialize_empty_new_buffer(irel, buf);
+ /* since this should not happen, skip FreeSpaceMapVacuum */
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
if (BufferIsValid(oldbuf) && oldblk <= newblk)
LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK);
+ /*
+ * Update the FSM with the new, presumably smaller, freespace value
+ * for this page, then search for a new target page.
+ */
newblk = RecordAndGetPageWithFreeSpace(irel, newblk, freespace, itemsz);
}
}
* there is no mechanism to get the space back and the index would bloat.
* Also, because we would not WAL-log the action that would initialize the
* page, the page would go uninitialized in a standby (or after recovery).
+ *
+ * While we record the page in FSM here, caller is responsible for doing FSM
+ * upper-page update if that seems appropriate.
*/
static void
brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer)