minmax-revmap-redesign-over-v15-1.patch
text/x-diff
Filename: minmax-revmap-redesign-over-v15-1.patch
Type: text/x-diff
Part: 0
Message:
Re: Minmax indexes
Patch
Format: unified
Series: patch v15
| File | + | − |
|---|---|---|
| contrib/pageinspect/mmfuncs.c | 5 | 52 |
| contrib/pageinspect/pageinspect--1.2.sql | 2 | 9 |
| src/backend/access/minmax/minmax.c | 192 | 36 |
| src/backend/access/minmax/mmrevmap.c | 127 | 395 |
| src/backend/access/minmax/mmtuple.c | 1 | 1 |
| src/backend/access/minmax/mmxlog.c | 35 | 71 |
| src/include/access/minmax_internal.h | 2 | 0 |
| src/include/access/minmax_page.h | 15 | 35 |
| src/include/access/minmax_revmap.h | 0 | 5 |
| src/include/access/minmax_tuple.h | 3 | 2 |
| src/include/access/minmax_xlog.h | 7 | 33 |
commit ce4df0e9dbd43f7e3d4fdf3f7920301f81f17d63
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Fri Aug 15 18:32:19 2014 +0300
Get rid of array pages. Instead, move all tuples out of the way
diff --git a/contrib/pageinspect/mmfuncs.c b/contrib/pageinspect/mmfuncs.c
index 6cd559a..51cc9e2 100644
--- a/contrib/pageinspect/mmfuncs.c
+++ b/contrib/pageinspect/mmfuncs.c
@@ -74,9 +74,6 @@ minmax_page_type(PG_FUNCTION_ARGS)
case MINMAX_PAGETYPE_META:
type = "meta";
break;
- case MINMAX_PAGETYPE_REVMAP_ARRAY:
- type = "revmap array";
- break;
case MINMAX_PAGETYPE_REVMAP:
type = "revmap";
break;
@@ -343,11 +340,9 @@ minmax_metapage_info(PG_FUNCTION_ARGS)
Page page;
MinmaxMetaPageData *meta;
TupleDesc tupdesc;
- Datum values[3];
- bool nulls[3];
- ArrayBuildState *astate = NULL;
+ Datum values[4];
+ bool nulls[4];
HeapTuple htup;
- int i;
page = verify_minmax_page(raw_page, MINMAX_PAGETYPE_META, "metapage");
@@ -361,22 +356,8 @@ minmax_metapage_info(PG_FUNCTION_ARGS)
MemSet(nulls, 0, sizeof(nulls));
values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->minmaxMagic));
values[1] = Int32GetDatum(meta->minmaxVersion);
-
- /* Extract (possibly empty) list of revmap array page numbers. */
- for (i = 0; i < MAX_REVMAP_ARRAYPAGES; i++)
- {
- BlockNumber blkno;
-
- blkno = meta->revmapArrayPages[i];
- if (blkno == InvalidBlockNumber)
- break; /* XXX or continue? */
- astate = accumArrayResult(astate, Int64GetDatum((int64) blkno),
- false, INT8OID, CurrentMemoryContext);
- }
- if (astate == NULL)
- nulls[2] = true;
- else
- values[2] = makeArrayResult(astate, CurrentMemoryContext);
+ values[2] = Int32GetDatum(meta->pagesPerRange);
+ values[3] = Int64GetDatum(meta->lastRevmapPage);
htup = heap_form_tuple(tupdesc, values, nulls);
@@ -384,34 +365,6 @@ minmax_metapage_info(PG_FUNCTION_ARGS)
}
/*
- * Return the BlockNumber array stored in a revmap array page
- */
-Datum
-minmax_revmap_array_data(PG_FUNCTION_ARGS)
-{
- bytea *raw_page = PG_GETARG_BYTEA_P(0);
- Page page;
- ArrayBuildState *astate = NULL;
- RevmapArrayContents *contents;
- Datum blkarr;
- int i;
-
- page = verify_minmax_page(raw_page, MINMAX_PAGETYPE_REVMAP_ARRAY,
- "revmap array");
-
- contents = (RevmapArrayContents *) PageGetContents(page);
-
- for (i = 0; i < contents->rma_nblocks; i++)
- astate = accumArrayResult(astate,
- Int64GetDatum((int64) contents->rma_blocks[i]),
- false, INT8OID, CurrentMemoryContext);
- Assert(astate != NULL);
-
- blkarr = makeArrayResult(astate, CurrentMemoryContext);
- PG_RETURN_DATUM(blkarr);
-}
-
-/*
* Return the TID array stored in a minmax revmap page
*/
Datum
@@ -437,7 +390,7 @@ minmax_revmap_data(PG_FUNCTION_ARGS)
/* Extract values from the revmap page */
contents = (RevmapContents *) PageGetContents(page);
MemSet(nulls, 0, sizeof(nulls));
- values[0] = Int64GetDatum((uint64) contents->rmr_logblk);
+ values[0] = Int64GetDatum((uint64) 0);
/* Extract (possibly empty) list of TIDs in this page. */
for (i = 0; i < REGULAR_REVMAP_PAGE_MAXITEMS; i++)
diff --git a/contrib/pageinspect/pageinspect--1.2.sql b/contrib/pageinspect/pageinspect--1.2.sql
index 56c9ba8..cba90ca 100644
--- a/contrib/pageinspect/pageinspect--1.2.sql
+++ b/contrib/pageinspect/pageinspect--1.2.sql
@@ -110,7 +110,7 @@ LANGUAGE C STRICT;
-- minmax_metapage_info()
--
CREATE FUNCTION minmax_metapage_info(IN page bytea, OUT magic text,
- OUT version integer, OUT revmap_array_pages BIGINT[])
+ OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigint)
AS 'MODULE_PATHNAME', 'minmax_metapage_info'
LANGUAGE C STRICT;
@@ -128,16 +128,9 @@ AS 'MODULE_PATHNAME', 'minmax_page_items'
LANGUAGE C STRICT;
--
--- minmax_revmap_array_data()
-CREATE FUNCTION minmax_revmap_array_data(IN page bytea,
- OUT revmap_pages BIGINT[])
-AS 'MODULE_PATHNAME', 'minmax_revmap_array_data'
-LANGUAGE C STRICT;
-
---
-- minmax_revmap_data()
CREATE FUNCTION minmax_revmap_data(IN page bytea,
- OUT logblk BIGINT, OUT pages tid[])
+ OUT dummy bigint, OUT pages tid[])
AS 'MODULE_PATHNAME', 'minmax_revmap_data'
LANGUAGE C STRICT;
diff --git a/src/backend/access/minmax/minmax.c b/src/backend/access/minmax/minmax.c
index addb3a0..18f85d7 100644
--- a/src/backend/access/minmax/minmax.c
+++ b/src/backend/access/minmax/minmax.c
@@ -34,9 +34,11 @@
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
+#include "storage/smgr.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/rel.h"
#include "utils/syscache.h"
@@ -76,8 +78,8 @@ static void summarize_range(MMBuildState *mmstate, Relation heapRel,
static bool mm_doupdate(Relation idxrel, BlockNumber pagesPerRange,
mmRevmapAccess *rmAccess, BlockNumber heapBlk,
Buffer oldbuf, OffsetNumber oldoff,
- MMTuple *origtup, Size origsz,
- MMTuple *newtup, Size newsz,
+ const MMTuple *origtup, Size origsz,
+ const MMTuple *newtup, Size newsz,
bool samepage, bool *extended);
static void mm_doinsert(Relation idxrel, BlockNumber pagesPerRange,
mmRevmapAccess *rmAccess, Buffer *buffer, BlockNumber heapblkno,
@@ -85,6 +87,7 @@ static void mm_doinsert(Relation idxrel, BlockNumber pagesPerRange,
static Buffer mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
bool *extended);
static void form_and_insert_tuple(MMBuildState *mmstate);
+static Size mm_page_get_freespace(Page page);
/*
@@ -536,11 +539,15 @@ mmbuild(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("expression indexes not supported")));
+ /*
+ * Critical section not required, because on error the creation of the
+ * whole relation will be rolled back.
+ */
+
meta = ReadBuffer(index, P_NEW);
Assert(BufferGetBlockNumber(meta) == MINMAX_METAPAGE_BLKNO);
LockBuffer(meta, BUFFER_LOCK_EXCLUSIVE);
- START_CRIT_SECTION();
mm_metapage_init(BufferGetPage(meta), MinmaxGetPagesPerRange(index),
MINMAX_CURRENT_VERSION);
MarkBufferDirty(meta);
@@ -568,17 +575,11 @@ mmbuild(PG_FUNCTION_ARGS)
}
UnlockReleaseBuffer(meta);
- END_CRIT_SECTION();
-
- /*
- * Set up an empty revmap, and get access to it
- */
- mmRevmapCreate(index);
- rmAccess = mmRevmapAccessInit(index, &pagesPerRange);
/*
* Initialize our state, including the deformed tuple state.
*/
+ rmAccess = mmRevmapAccessInit(index, &pagesPerRange);
mmstate = initialize_mm_buildstate(index, rmAccess, pagesPerRange);
/*
@@ -664,10 +665,11 @@ mmvacuumcleanup(PG_FUNCTION_ARGS)
heapRel = heap_open(IndexGetRelation(RelationGetRelid(info->index), false),
AccessShareLock);
+ rmAccess = mmRevmapAccessInit(info->index, &pagesPerRange);
+
/*
* Scan the revmap to find unsummarized items.
*/
- rmAccess = mmRevmapAccessInit(info->index, &pagesPerRange);
buf = InvalidBuffer;
heapNumBlocks = RelationGetNumberOfBlocks(heapRel);
for (heapBlk = 0; heapBlk < heapNumBlocks; heapBlk += pagesPerRange)
@@ -751,13 +753,32 @@ mm_page_init(Page page, uint16 type)
}
/*
+ * Return the amount of free space on a regular minmax index page.
+ *
+ * If the page is not a regular page, or has been marked with the
+ * MINMAX_EVACUATE_PAGE flag, returns 0.
+ */
+static Size
+mm_page_get_freespace(Page page)
+{
+ MinmaxSpecialSpace *special;
+
+ special = (MinmaxSpecialSpace *) PageGetSpecialPointer(page);
+ if (!MINMAX_IS_REGULAR_PAGE(page) ||
+ (special->flags & MINMAX_EVACUATE_PAGE) != 0)
+ return 0;
+ else
+ return PageGetFreeSpace(page);
+
+}
+
+/*
* Initialize a new minmax index' metapage.
*/
void
mm_metapage_init(Page page, BlockNumber pagesPerRange, uint16 version)
{
MinmaxMetaPageData *metadata;
- int i;
mm_page_init(page, MINMAX_PAGETYPE_META);
@@ -766,8 +787,7 @@ mm_metapage_init(Page page, BlockNumber pagesPerRange, uint16 version)
metadata->minmaxMagic = MINMAX_META_MAGIC;
metadata->pagesPerRange = pagesPerRange;
metadata->minmaxVersion = version;
- for (i = 0; i < MAX_REVMAP_ARRAYPAGES; i++)
- metadata->revmapArrayPages[i] = InvalidBlockNumber;
+ metadata->lastRevmapPage = 0;
}
/*
@@ -875,7 +895,7 @@ terminate_mm_buildstate(MMBuildState *mmstate)
page = BufferGetPage(mmstate->currentInsertBuf);
RecordPageWithFreeSpace(mmstate->irel,
BufferGetBlockNumber(mmstate->currentInsertBuf),
- PageGetFreeSpace(page));
+ mm_page_get_freespace(page));
ReleaseBuffer(mmstate->currentInsertBuf);
}
vacuumfsm = mmstate->extended;
@@ -938,8 +958,8 @@ static bool
mm_doupdate(Relation idxrel, BlockNumber pagesPerRange,
mmRevmapAccess *rmAccess, BlockNumber heapBlk,
Buffer oldbuf, OffsetNumber oldoff,
- MMTuple *origtup, Size origsz,
- MMTuple *newtup, Size newsz,
+ const MMTuple *origtup, Size origsz,
+ const MMTuple *newtup, Size newsz,
bool samepage, bool *extended)
{
Page oldpage;
@@ -947,11 +967,15 @@ mm_doupdate(Relation idxrel, BlockNumber pagesPerRange,
MMTuple *oldtup;
Size oldsz;
Buffer newbuf;
+ MinmaxSpecialSpace *special;
if (!samepage)
{
/* need a page on which to put the item */
newbuf = mm_getinsertbuffer(idxrel, oldbuf, newsz, extended);
+ if (!BufferIsValid(newbuf))
+ return false;
+
/*
* Note: it's possible (though unlikely) that the returned newbuf is
* the same as oldbuf, if mm_getinsertbuffer determined that the old
@@ -985,6 +1009,8 @@ mm_doupdate(Relation idxrel, BlockNumber pagesPerRange,
return false;
}
+ special = (MinmaxSpecialSpace *) PageGetSpecialPointer(oldpage);
+
/*
* Great, the old tuple is intact. We can proceed with the update.
*
@@ -994,7 +1020,8 @@ mm_doupdate(Relation idxrel, BlockNumber pagesPerRange,
* the caller told us there isn't, if a concurrent updated moved a tuple
* elsewhere or replaced a tuple with a smaller one.
*/
- if (newsz <= origsz || PageGetExactFreeSpace(oldpage) >= (origsz - newsz))
+ if ((special->flags & MINMAX_EVACUATE_PAGE) == 0 &&
+ (newsz <= origsz || PageGetExactFreeSpace(oldpage) >= (origsz - newsz)))
{
if (BufferIsValid(newbuf))
UnlockReleaseBuffer(newbuf);
@@ -1151,34 +1178,44 @@ mm_doinsert(Relation idxrel, BlockNumber pagesPerRange,
itemsz = MAXALIGN(itemsz);
+ /*
+ * Lock the revmap page for the update. Note that this may require
+ * extending the revmap, which in turn may require moving the currently
+ * pinned index block out of the way.
+ */
+ revmapbuf = mmLockRevmapPageForUpdate(rmAccess, heapBlk);
+
+ /*
+ * Obtain a locked buffer to insert the new tuple. Note mm_getinsertbuffer
+ * ensures there's enough space in the returned buffer.
+ */
if (BufferIsValid(*buffer))
{
page = BufferGetPage(*buffer);
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (PageGetFreeSpace(page) < itemsz)
+
+ /*
+ * It's possible that another backend (or ourselves!) extended the
+ * revmap over the page we held a pin on, so we cannot assume that
+ * it's still a regular page.
+ */
+ if (mm_page_get_freespace(page) < itemsz)
{
UnlockReleaseBuffer(*buffer);
*buffer = InvalidBuffer;
}
}
-
- /*
- * Obtain a locked buffer to insert the new tuple. Note mm_getinsertbuffer
- * ensures there's enough space in the returned buffer.
- */
if (!BufferIsValid(*buffer))
{
*buffer = mm_getinsertbuffer(idxrel, InvalidBuffer, itemsz, extended);
+ Assert(BufferIsValid(*buffer));
page = BufferGetPage(*buffer);
- Assert(PageGetFreeSpace(page) >= itemsz);
+ Assert(mm_page_get_freespace(page) >= itemsz);
}
page = BufferGetPage(*buffer);
blk = BufferGetBlockNumber(*buffer);
- /* lock the revmap for the update */
- revmapbuf = mmLockRevmapPageForUpdate(rmAccess, heapBlk);
-
START_CRIT_SECTION();
off = PageAddItem(page, (Item) tup, itemsz, InvalidOffsetNumber,
false, false);
@@ -1233,12 +1270,116 @@ mm_doinsert(Relation idxrel, BlockNumber pagesPerRange,
}
/*
+ * Checks if a regular minmax index page is empty.
+ *
+ * If it's not, it's marked for "evacuation", meaning that no new tuples will
+ * be added to it.
+ */
+bool
+mm_start_evacuating_page(Relation idxRel, Buffer buf)
+{
+ OffsetNumber off;
+ OffsetNumber maxoff;
+ MinmaxSpecialSpace *special;
+ Page page;
+
+ page = BufferGetPage(buf);
+
+ if (PageIsNew(page))
+ return false;
+
+ special = (MinmaxSpecialSpace *) PageGetSpecialPointer(page);
+
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (off = FirstOffsetNumber; off <= maxoff; off++)
+ {
+ ItemId lp;
+
+ lp = PageGetItemId(page, off);
+ if (ItemIdIsUsed(lp))
+ {
+ /* prevent other backends from adding more stuff to this page. */
+ special->flags |= MINMAX_EVACUATE_PAGE;
+ MarkBufferDirtyHint(buf, true);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Move all tuples out of a page.
+ *
+ * The caller must hold an exclusive lock on the page. The lock and pin are
+ * released.
+ */
+void
+mm_evacuate_page(Relation idxRel, Buffer buf)
+{
+ OffsetNumber off;
+ OffsetNumber maxoff;
+ MinmaxSpecialSpace *special;
+ Page page;
+ mmRevmapAccess *rmAccess;
+ BlockNumber pagesPerRange;
+
+ rmAccess = mmRevmapAccessInit(idxRel, &pagesPerRange);
+
+ page = BufferGetPage(buf);
+ special = (MinmaxSpecialSpace *) PageGetSpecialPointer(page);
+
+ Assert(special->flags & MINMAX_EVACUATE_PAGE);
+
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (off = FirstOffsetNumber; off <= maxoff; off++)
+ {
+ MMTuple *tup;
+ Size sz;
+ ItemId lp;
+ bool extended = false;
+
+ lp = PageGetItemId(page, off);
+ if (ItemIdIsUsed(lp))
+ {
+ tup = (MMTuple *) PageGetItem(page, lp);
+ sz = ItemIdGetLength(lp);
+
+ tup = minmax_copy_tuple(tup, sz);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ if (!mm_doupdate(idxRel, pagesPerRange, rmAccess, tup->mt_blkno, buf,
+ off, tup, sz, tup, sz, false, &extended))
+ off--; /* retry */
+
+ LockBuffer(buf, BUFFER_LOCK_SHARE);
+
+ if (extended)
+ IndexFreeSpaceMapVacuum(idxRel);
+
+ /* It's possible that someone extended the revmap over this page */
+ if (!MINMAX_IS_REGULAR_PAGE(page))
+ break;
+ }
+ }
+
+ mmRevmapAccessTerminate(rmAccess);
+
+ UnlockReleaseBuffer(buf);
+}
+
+/*
* Return a pinned and locked buffer which can be used to insert an index item
* of size itemsz. If oldbuf is a valid buffer, it is also locked (in a order
* determined to avoid deadlocks.)
*
* If there's no existing page with enough free space to accomodate the new
* item, the relation is extended. If this happens, *extended is set to true.
+ *
+ * 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
+ * InvalidBuffer.
*/
static Buffer
mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
@@ -1261,7 +1402,9 @@ mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
* if we have to restart here, neither buffer is locked and buf is not
* a pinned buffer.
*/
- newblk = GetPageWithFreeSpace(irel, itemsz);
+ newblk = RelationGetTargetBlock(irel);
+ if (newblk == InvalidBlockNumber)
+ newblk = GetPageWithFreeSpace(irel, itemsz);
for (;;)
{
Buffer buf;
@@ -1298,14 +1441,19 @@ mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
buf = ReadBuffer(irel, newblk);
}
- if (BufferIsValid(oldbuf) && newblk < oldblk)
+ if (BufferIsValid(oldbuf) && oldblk < newblk)
+ {
LockBuffer(oldbuf, BUFFER_LOCK_EXCLUSIVE);
+ if (!MINMAX_IS_REGULAR_PAGE(BufferGetPage(oldbuf)))
+ {
+ LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buf);
+ return InvalidBuffer;
+ }
+ }
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- if (BufferIsValid(oldbuf) && newblk > oldblk)
- LockBuffer(oldbuf, BUFFER_LOCK_EXCLUSIVE);
-
if (extensionLockHeld)
UnlockRelationForExtension(irel, ExclusiveLock);
@@ -1319,13 +1467,21 @@ mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
* 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.
+ *
+ * (mm_page_get_freespace also checks that the FSM didn't hand us a
+ * page that has since been repurposed for the revmap.)
*/
- freespace = PageGetFreeSpace(page);
-
+ freespace = mm_page_get_freespace(page);
if (freespace >= itemsz)
{
if (extended)
*was_extended = true;
+ RelationSetTargetBlock(irel, BufferGetBlockNumber(buf));
+
+ /* Lock the old buffer if not locked already */
+ if (BufferIsValid(oldbuf) && newblk < oldblk)
+ LockBuffer(oldbuf, BUFFER_LOCK_EXCLUSIVE);
+
return buf;
}
@@ -1352,7 +1508,7 @@ mm_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
if (newblk != oldblk)
UnlockReleaseBuffer(buf);
- if (BufferIsValid(oldbuf))
+ if (BufferIsValid(oldbuf) && oldblk < newblk)
LockBuffer(oldbuf, BUFFER_LOCK_UNLOCK);
newblk = RecordAndGetPageWithFreeSpace(irel, newblk, freespace, itemsz);
diff --git a/src/backend/access/minmax/mmrevmap.c b/src/backend/access/minmax/mmrevmap.c
index 923490e..48df2cd 100644
--- a/src/backend/access/minmax/mmrevmap.c
+++ b/src/backend/access/minmax/mmrevmap.c
@@ -8,14 +8,10 @@
* into a table that violates the previously recorded min/max values, a new
* tuple is inserted into the index and the revmap is updated to point to it.
*
- * The pages of the revmap are interspersed in the index's main fork. The
- * first revmap page is always the index's page number one (that is,
- * immediately after the metapage). Subsequent revmap pages are allocated as
- * they are needed; their locations are tracked by "array pages". The metapage
- * contains a large BlockNumber array, which correspond to array pages. Thus,
- * to find the second revmap page, we read the metapage and obtain the block
- * number of the first array page; we then read that page, and the first
- * element in it is the revmap page we're looking for.
+ * The pages of the revmap are in the beginning of the index, starting at
+ * immediately after the metapage at block 1. When the revmap needs to be
+ * expanded, all tuples on the regular minmax page at that block (if any) are
+ * moved out of the way.
*
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -41,7 +37,7 @@
/*
- * In regular revmap pages, each item stores an ItemPointerData. These defines
+ * In revmap pages, each item stores an ItemPointerData. These defines
* let one find the logical revmap page number and index number of the revmap
* item for the given heap block number.
*/
@@ -50,29 +46,19 @@
#define HEAPBLK_TO_REVMAP_INDEX(pagesPerRange, heapBlk) \
((heapBlk / pagesPerRange) % REGULAR_REVMAP_PAGE_MAXITEMS)
-/*
- * In array revmap pages, each item stores a BlockNumber. These defines let
- * one find the page and index number of a given revmap block number. Note
- * that the first revmap page (revmap logical page number 0) is always stored
- * in physical block number 1, so array pages do not store that one.
- */
-#define MAPBLK_TO_RMARRAY_BLK(rmBlk) ((rmBlk - 1) / ARRAY_REVMAP_PAGE_MAXITEMS)
-#define MAPBLK_TO_RMARRAY_INDEX(rmBlk) ((rmBlk - 1) % ARRAY_REVMAP_PAGE_MAXITEMS)
-
struct mmRevmapAccess
{
Relation idxrel;
BlockNumber pagesPerRange;
+ BlockNumber lastRevmapPage; /* cached from the metapage */
Buffer metaBuf;
Buffer currBuf;
- Buffer currArrayBuf;
- BlockNumber *revmapArrayPages;
};
/* typedef appears in minmax_revmap.h */
-static Buffer mm_getnewbuffer(Relation irel);
+static void rm_extend(mmRevmapAccess *rmAccess);
/*
* Initialize an access object for a reverse range map, which can be used to
@@ -94,8 +80,7 @@ mmRevmapAccessInit(Relation idxrel, BlockNumber *pagesPerRange)
rmAccess->idxrel = idxrel;
rmAccess->pagesPerRange = metadata->pagesPerRange;
rmAccess->currBuf = InvalidBuffer;
- rmAccess->currArrayBuf = InvalidBuffer;
- rmAccess->revmapArrayPages = NULL;
+ rmAccess->lastRevmapPage = InvalidBlockNumber;
if (pagesPerRange)
*pagesPerRange = metadata->pagesPerRange;
@@ -109,30 +94,24 @@ mmRevmapAccessInit(Relation idxrel, BlockNumber *pagesPerRange)
void
mmRevmapAccessTerminate(mmRevmapAccess *rmAccess)
{
- if (rmAccess->revmapArrayPages != NULL)
- pfree(rmAccess->revmapArrayPages);
if (rmAccess->metaBuf != InvalidBuffer)
ReleaseBuffer(rmAccess->metaBuf);
if (rmAccess->currBuf != InvalidBuffer)
ReleaseBuffer(rmAccess->currBuf);
- if (rmAccess->currArrayBuf != InvalidBuffer)
- ReleaseBuffer(rmAccess->currArrayBuf);
pfree(rmAccess);
}
/*
- * Lock the metapage as specified by called, and update the given rmAccess with
- * the metapage data. The metapage buffer is locked when this function
- * returns; it's the caller's responsibility to unlock it.
+ * Read the metapage and update the given rmAccess with the metapage data.
*/
static void
-rmaccess_get_metapage(mmRevmapAccess *rmAccess, int lockmode)
+rmaccess_read_metapage(mmRevmapAccess *rmAccess)
{
MinmaxMetaPageData *metadata;
MinmaxSpecialSpace *special PG_USED_FOR_ASSERTS_ONLY;
Page metapage;
- LockBuffer(rmAccess->metaBuf, lockmode);
+ LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_SHARE);
metapage = BufferGetPage(rmAccess->metaBuf);
#ifdef USE_ASSERT_CHECKING
@@ -141,51 +120,11 @@ rmaccess_get_metapage(mmRevmapAccess *rmAccess, int lockmode)
Assert(special->type == MINMAX_PAGETYPE_META);
#endif
- /* first time through? allocate the array */
- if (rmAccess->revmapArrayPages == NULL)
- rmAccess->revmapArrayPages =
- palloc(sizeof(BlockNumber) * MAX_REVMAP_ARRAYPAGES);
-
metadata = (MinmaxMetaPageData *) PageGetContents(metapage);
- memcpy(rmAccess->revmapArrayPages, metadata->revmapArrayPages,
- sizeof(BlockNumber) * MAX_REVMAP_ARRAYPAGES);
-}
-
-/*
- * Update the metapage, so that item arrayBlkIdx in the array of revmap array
- * pages points to block number newPgBlkno.
- */
-static void
-update_minmax_metapg(Relation idxrel, Buffer meta, uint32 arrayBlkIdx,
- BlockNumber newPgBlkno)
-{
- MinmaxMetaPageData *metadata;
-
- metadata = (MinmaxMetaPageData *) PageGetContents(BufferGetPage(meta));
-
- START_CRIT_SECTION();
- metadata->revmapArrayPages[arrayBlkIdx] = newPgBlkno;
- MarkBufferDirty(meta);
- if (RelationNeedsWAL(idxrel))
- {
- xl_minmax_metapg_set xlrec;
- XLogRecPtr recptr;
- XLogRecData rdata;
- xlrec.node = idxrel->rd_node;
- xlrec.blkidx = arrayBlkIdx;
- xlrec.newpg = newPgBlkno;
+ rmAccess->lastRevmapPage = metadata->lastRevmapPage;
- rdata.data = (char *) &xlrec;
- rdata.len = SizeOfMinmaxMetapgSet;
- rdata.buffer = InvalidBuffer;
- rdata.buffer_std = false;
- rdata.next = NULL;
-
- recptr = XLogInsert(RM_MINMAX_ID, XLOG_MINMAX_METAPG_SET, &rdata);
- PageSetLSN(BufferGetPage(meta), recptr);
- }
- END_CRIT_SECTION();
+ LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_UNLOCK);
}
/*
@@ -200,250 +139,140 @@ update_minmax_metapg(Relation idxrel, Buffer meta, uint32 arrayBlkIdx,
static BlockNumber
rm_get_phys_blkno(mmRevmapAccess *rmAccess, BlockNumber mapBlk, bool extend)
{
- int arrayBlkIdx;
- BlockNumber arrayBlk;
- RevmapArrayContents *contents;
- int revmapIdx;
BlockNumber targetblk;
+ if (rmAccess->lastRevmapPage == InvalidBlockNumber)
+ rmaccess_read_metapage(rmAccess);
+
/* the first revmap page is always block number 1 */
- if (mapBlk == 0)
- return (BlockNumber) 1;
+ targetblk = mapBlk + 1;
- /*
- * For all other cases, take the long route of checking the metapage and
- * revmap array pages.
- */
+ if (targetblk <= rmAccess->lastRevmapPage)
+ return targetblk;
- /*
- * Copy the revmap array from the metapage into private storage, if not
- * done already in this scan.
- */
- if (rmAccess->revmapArrayPages == NULL)
- {
- rmaccess_get_metapage(rmAccess, BUFFER_LOCK_SHARE);
- LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_UNLOCK);
- }
+ if (!extend)
+ return InvalidBlockNumber;
- /*
- * Consult the metapage array; if the array page we need is not set there,
- * we need to extend the index to allocate the array page, and update the
- * metapage array.
- */
- arrayBlkIdx = MAPBLK_TO_RMARRAY_BLK(mapBlk);
- if (arrayBlkIdx > MAX_REVMAP_ARRAYPAGES)
- elog(ERROR, "non-existant revmap array page requested");
+ /* Extend the revmap */
+ while (targetblk > rmAccess->lastRevmapPage)
+ rm_extend(rmAccess);
- arrayBlk = rmAccess->revmapArrayPages[arrayBlkIdx];
- if (arrayBlk == InvalidBlockNumber)
- {
- /* if not asked to extend, there's no further work to do here */
- if (!extend)
- return InvalidBlockNumber;
-
- /*
- * If we need to create a new array page, check the metapage again;
- * someone might have created it after the last time we read the
- * metapage. This time we acquire an exclusive lock, since we may need
- * to extend. Lock before doing the physical relation extension, to
- * avoid leaving an unused page around in case someone does this
- * concurrently. Note that, unfortunately, we will be keeping the lock
- * on the metapage alongside the relation extension lock, while doing a
- * syscall involving disk I/O. Extending to add a new revmap array page
- * is fairly infrequent, so it shouldn't be too bad.
- *
- * XXX it is possible to extend the relation unconditionally before
- * locking the metapage, and later if we find that someone else had
- * already added this page, save the page in FSM as MaxFSMRequestSize.
- * That would be better for concurrency. Explore someday.
- */
- rmaccess_get_metapage(rmAccess, BUFFER_LOCK_EXCLUSIVE);
+ return targetblk;
+}
- if (rmAccess->revmapArrayPages[arrayBlkIdx] == InvalidBlockNumber)
- {
- BlockNumber newPgBlkno;
-
- /*
- * Ok, definitely need to allocate a new revmap array page;
- * initialize a new page to the initial (empty) array revmap state
- * and register it in metapage.
- */
- rmAccess->currArrayBuf = mm_getnewbuffer(rmAccess->idxrel);
- START_CRIT_SECTION();
- initialize_rma_page(rmAccess->currArrayBuf);
- MarkBufferDirty(rmAccess->currArrayBuf);
- if (RelationNeedsWAL(rmAccess->idxrel))
- {
- xl_minmax_init_rmpg xlrec;
- XLogRecPtr recptr;
- XLogRecData rdata;
-
- xlrec.node = rmAccess->idxrel->rd_node;
- xlrec.blkno = BufferGetBlockNumber(rmAccess->currArrayBuf);
- xlrec.array = true;
- xlrec.logblk = InvalidBlockNumber;
-
- rdata.data = (char *) &xlrec;
- rdata.len = SizeOfMinmaxInitRmpg;
- rdata.buffer = InvalidBuffer; /* FIXME */
- rdata.buffer_std = false;
- rdata.next = NULL;
-
- recptr = XLogInsert(RM_MINMAX_ID, XLOG_MINMAX_INIT_RMPG, &rdata);
- PageSetLSN(BufferGetPage(rmAccess->currArrayBuf), recptr);
- }
- END_CRIT_SECTION();
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_UNLOCK);
- newPgBlkno = BufferGetBlockNumber(rmAccess->currArrayBuf);
- rmAccess->revmapArrayPages[arrayBlkIdx] = newPgBlkno;
+/*
+ * Extend the revmap by one page.
+ *
+ * If there is an existing minmax page at that block, it is atomically moved
+ * out of the way, and the redirect pointer on the new revmap page is set
+ * to point to its new location.
+ *
+ * If rmAccess->lastRevmapPage is out-of-date, it's updated and nothing else
+ * is done.
+ */
+static void
+rm_extend(mmRevmapAccess *rmAccess)
+{
+ Buffer buf;
+ Page page;
+ Page metapage;
+ MinmaxMetaPageData *metadata;
+ BlockNumber mapBlk;
+ BlockNumber nblocks;
+ Relation irel = rmAccess->idxrel;
+ bool needLock = !RELATION_IS_LOCAL(irel);
- MINMAX_elog(DEBUG2, "allocated block for revmap array page: %u",
- BufferGetBlockNumber(rmAccess->currArrayBuf));
+ /*
+ * Lock the metapage. This locks out concurrent extensions of the revmap,
+ * but note that we still need to grab the relation extension lock because
+ * another backend can still extend the index with regular minmax pages.
+ */
+ LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_EXCLUSIVE);
+ metapage = BufferGetPage(rmAccess->metaBuf);
+ metadata = (MinmaxMetaPageData *) PageGetContents(metapage);
- /* Update the metapage to point to the new array page. */
- update_minmax_metapg(rmAccess->idxrel, rmAccess->metaBuf, arrayBlkIdx,
- newPgBlkno);
- }
+ /* Check that our cached lastRevmapPage value was up-to-date */
+ if (metadata->lastRevmapPage != rmAccess->lastRevmapPage)
+ {
+ rmAccess->lastRevmapPage = metadata->lastRevmapPage;
LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_UNLOCK);
- arrayBlk = rmAccess->revmapArrayPages[arrayBlkIdx];
+ return;
}
+ mapBlk = metadata->lastRevmapPage + 1;
- /*
- * By here, we know the array page is set in the metapage array. Read that
- * page; except that if we just allocated it, or we already hold pin on it,
- * we don't need to read it again.
- */
- Assert(arrayBlk != InvalidBlockNumber);
-
- if (rmAccess->currArrayBuf == InvalidBuffer ||
- BufferGetBlockNumber(rmAccess->currArrayBuf) != arrayBlk)
+ nblocks = RelationGetNumberOfBlocks(irel);
+ if (mapBlk < nblocks)
{
- if (rmAccess->currArrayBuf != InvalidBuffer)
- ReleaseBuffer(rmAccess->currArrayBuf);
+ /* Check that the existing index block is sane. */
+ buf = ReadBuffer(rmAccess->idxrel, mapBlk);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ page = BufferGetPage(buf);
+ }
+ else
+ {
+ if (needLock)
+ LockRelationForExtension(irel, ExclusiveLock);
+
+ buf = ReadBuffer(irel, P_NEW);
+ Assert(BufferGetBlockNumber(buf) == mapBlk);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ page = BufferGetPage(buf);
- rmAccess->currArrayBuf =
- ReadBuffer(rmAccess->idxrel, arrayBlk);
+ if (needLock)
+ UnlockRelationForExtension(irel, ExclusiveLock);
}
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_SHARE);
+ /* Check that it's a regular block (or an empty page) */
+ if (!PageIsNew(page) && !MINMAX_IS_REGULAR_PAGE(page))
+ elog(ERROR, "unexpected minmax page type: 0x%04X",
+ MINMAX_PAGE_TYPE(page));
- /*
- * And now we can inspect its contents; if the target page is set, we can
- * just return. Even if not set, we can also return if caller asked us not
- * to extend the revmap.
- */
- contents = (RevmapArrayContents *)
- PageGetContents(BufferGetPage(rmAccess->currArrayBuf));
- revmapIdx = MAPBLK_TO_RMARRAY_INDEX(mapBlk);
- if (!extend || revmapIdx <= contents->rma_nblocks - 1)
+ /* If the page is in use, evacuate it and restart */
+ if (mm_start_evacuating_page(rmAccess->idxrel, buf))
{
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_UNLOCK);
-
- return contents->rma_blocks[revmapIdx];
+ LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_UNLOCK);
+ mm_evacuate_page(rmAccess->idxrel, buf);
+ return;
}
/*
- * Trade our shared lock in the array page for exclusive, because we now
- * need to allocate one more revmap page and modify the array page.
+ * Ok, we have now locked the metapage and the target block. Re-initialize
+ * it as a revmap page.
*/
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_UNLOCK);
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_EXCLUSIVE);
-
- contents = (RevmapArrayContents *)
- PageGetContents(BufferGetPage(rmAccess->currArrayBuf));
+ START_CRIT_SECTION();
- /*
- * If someone else already set the value while we were waiting for the
- * exclusive lock, we're done; otherwise, allocate a new block as the
- * new revmap page, and update the array page to point to it.
- */
- if (contents->rma_blocks[revmapIdx] != InvalidBlockNumber)
- {
- targetblk = contents->rma_blocks[revmapIdx];
- }
- else
- {
- Buffer newbuf;
-
- /* not possible to get here if we weren't asked to extend */
- Assert(extend);
- newbuf = mm_getnewbuffer(rmAccess->idxrel);
- START_CRIT_SECTION();
- targetblk = initialize_rmr_page(newbuf, mapBlk);
- MarkBufferDirty(newbuf);
- if (RelationNeedsWAL(rmAccess->idxrel))
- {
- xl_minmax_init_rmpg xlrec;
- XLogRecPtr recptr;
- XLogRecData rdata;
-
- xlrec.node = rmAccess->idxrel->rd_node;
- xlrec.blkno = BufferGetBlockNumber(newbuf);
- xlrec.array = false;
- xlrec.logblk = mapBlk;
-
- rdata.data = (char *) &xlrec;
- rdata.len = SizeOfMinmaxInitRmpg;
- rdata.buffer = InvalidBuffer;
- rdata.buffer_std = false;
- rdata.next = NULL;
-
- recptr = XLogInsert(RM_MINMAX_ID, XLOG_MINMAX_INIT_RMPG, &rdata);
- PageSetLSN(BufferGetPage(newbuf), recptr);
- }
- END_CRIT_SECTION();
+ /* the rmr_tids array is initialized to all invalid by PageInit */
+ mm_page_init(page, MINMAX_PAGETYPE_REVMAP);
+ MarkBufferDirty(buf);
- UnlockReleaseBuffer(newbuf);
+ metadata->lastRevmapPage = mapBlk;
+ MarkBufferDirty(rmAccess->metaBuf);
- /*
- * Now make the revmap array page point to the newly allocated page.
- * If necessary, also update the total number of items in it.
- */
- START_CRIT_SECTION();
+ if (RelationNeedsWAL(rmAccess->idxrel))
+ {
+ xl_minmax_revmap_extend xlrec;
+ XLogRecPtr recptr;
+ XLogRecData rdata;
- contents->rma_blocks[revmapIdx] = targetblk;
- if (contents->rma_nblocks < revmapIdx + 1)
- contents->rma_nblocks = revmapIdx + 1;
- MarkBufferDirty(rmAccess->currArrayBuf);
+ xlrec.node = rmAccess->idxrel->rd_node;
+ xlrec.targetBlk = mapBlk;
- /* XLOG stuff */
- if (RelationNeedsWAL(rmAccess->idxrel))
- {
- xl_minmax_rmarray_set xlrec;
- XLogRecPtr recptr;
- XLogRecData rdata[2];
- uint8 info;
-
- info = XLOG_MINMAX_RMARRAY_SET;
-
- xlrec.node = rmAccess->idxrel->rd_node;
- xlrec.rmarray = BufferGetBlockNumber(rmAccess->currArrayBuf);
- xlrec.blkidx = revmapIdx;
- xlrec.newpg = targetblk;
-
- rdata[0].data = (char *) &xlrec;
- rdata[0].len = SizeOfMinmaxRmarraySet;
- rdata[0].buffer = InvalidBuffer;
- rdata[0].buffer_std = false;
- rdata[0].next = &rdata[1];
-
- rdata[1].data = NULL;
- rdata[1].len = 0;
- rdata[1].buffer = rmAccess->currArrayBuf;
- rdata[1].buffer_std = false;
- rdata[1].next = NULL;
-
- recptr = XLogInsert(RM_MINMAX_ID, info, rdata);
- PageSetLSN(BufferGetPage(rmAccess->currArrayBuf), recptr);
- }
+ rdata.data = (char *) &xlrec;
+ rdata.len = SizeOfMinmaxRevmapExtend;
+ rdata.buffer = InvalidBuffer;
+ rdata.buffer_std = false;
+ rdata.next = NULL;
- END_CRIT_SECTION();
+ recptr = XLogInsert(RM_MINMAX_ID, XLOG_MINMAX_REVMAP_EXTEND, &rdata);
+ PageSetLSN(metapage, recptr);
+ PageSetLSN(page, recptr);
}
- LockBuffer(rmAccess->currArrayBuf, BUFFER_LOCK_UNLOCK);
+ END_CRIT_SECTION();
- return targetblk;
+ LockBuffer(rmAccess->metaBuf, BUFFER_LOCK_UNLOCK);
+ UnlockReleaseBuffer(buf);
}
/*
@@ -604,17 +433,23 @@ mmGetMMTupleForHeapBlock(mmRevmapAccess *rmAccess, BlockNumber heapBlk,
}
LockBuffer(*buf, mode);
page = BufferGetPage(*buf);
- lp = PageGetItemId(page, *off);
- if (ItemIdIsUsed(lp))
- {
- mmtup = (MMTuple *) PageGetItem(page, lp);
- if (mmtup->mt_blkno == heapBlk)
+ /* If we land on a revmap page, start over */
+ if (MINMAX_IS_REGULAR_PAGE(page))
+ {
+ lp = PageGetItemId(page, *off);
+ if (ItemIdIsUsed(lp))
{
- /* found it! */
- return mmtup;
+ mmtup = (MMTuple *) PageGetItem(page, lp);
+
+ if (mmtup->mt_blkno == heapBlk)
+ {
+ /* found it! */
+ return mmtup;
+ }
}
}
+
/*
* No luck. Assume that the revmap was updated concurrently.
*
@@ -627,106 +462,3 @@ mmGetMMTupleForHeapBlock(mmRevmapAccess *rmAccess, BlockNumber heapBlk,
/* not reached, but keep compiler quiet */
return NULL;
}
-
-/*
- * Initialize the revmap of a new minmax index.
- *
- * NB -- caller is assumed to WAL-log this operation
- */
-void
-mmRevmapCreate(Relation idxrel)
-{
- Buffer buf;
-
- /*
- * The first page of the revmap is always stored in block number 1 of the
- * main fork. Because of this, the only thing we need to do is request
- * a new page; we assume we are called immediately after the metapage has
- * been initialized.
- */
- buf = mm_getnewbuffer(idxrel);
- Assert(BufferGetBlockNumber(buf) == 1);
-
- mm_page_init(BufferGetPage(buf), MINMAX_PAGETYPE_REVMAP);
- MarkBufferDirty(buf);
-
- UnlockReleaseBuffer(buf);
-}
-
-/*
- * Initialize a new regular revmap page, which stores the given revmap logical
- * page number. The newly allocated physical block number is returned.
- *
- * Used both by regular code path as well as during xlog replay.
- */
-BlockNumber
-initialize_rmr_page(Buffer newbuf, BlockNumber mapBlk)
-{
- BlockNumber blkno;
- Page page;
- RevmapContents *contents;
-
- page = BufferGetPage(newbuf);
-
- mm_page_init(page, MINMAX_PAGETYPE_REVMAP);
- contents = (RevmapContents *) PageGetContents(page);
- contents->rmr_logblk = mapBlk;
- /* the rmr_tids array is initialized to all invalid by PageInit */
-
- blkno = BufferGetBlockNumber(newbuf);
-
- return blkno;
-}
-
-/*
- * Given a buffer (hopefully containing a blank page), set it up as a revmap
- * array page.
- *
- * Used both by regular code path as well as during xlog replay.
- */
-void
-initialize_rma_page(Buffer buf)
-{
- Page arrayPg;
- RevmapArrayContents *contents;
-
- arrayPg = BufferGetPage(buf);
- mm_page_init(arrayPg, MINMAX_PAGETYPE_REVMAP_ARRAY);
- contents = (RevmapArrayContents *) PageGetContents(arrayPg);
- contents->rma_nblocks = 0;
- /* set the whole array to InvalidBlockNumber */
- memset(contents->rma_blocks, 0xFF,
- sizeof(BlockNumber) * ARRAY_REVMAP_PAGE_MAXITEMS);
-}
-
-/*
- * Return an exclusively-locked buffer resulting from extending the relation.
- */
-static Buffer
-mm_getnewbuffer(Relation irel)
-{
- Buffer buffer;
- bool needLock = !RELATION_IS_LOCAL(irel);
-
- /*
- * XXX As a possible improvement, we could request a blank page to the FSM
- * here. Such pages could get inserted into the FSM if, for instance, two
- * processes extend the relation concurrently to add one more page to the
- * revmap and the second one discovers it doesn't actually need the page it
- * got.
- */
-
- if (needLock)
- LockRelationForExtension(irel, ExclusiveLock);
-
- buffer = ReadBuffer(irel, P_NEW);
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- MINMAX_elog(DEBUG2, "mm_getnewbuffer: extending to page %u",
- BufferGetBlockNumber(buffer));
-
- if (needLock)
- UnlockRelationForExtension(irel, ExclusiveLock);
-
- return buffer;
-}
diff --git a/src/backend/access/minmax/mmtuple.c b/src/backend/access/minmax/mmtuple.c
index 2e5aac5..b203b3a 100644
--- a/src/backend/access/minmax/mmtuple.c
+++ b/src/backend/access/minmax/mmtuple.c
@@ -256,7 +256,7 @@ minmax_copy_tuple(MMTuple *tuple, Size len)
}
bool
-minmax_tuples_equal(MMTuple *a, Size alen, MMTuple *b, Size blen)
+minmax_tuples_equal(const MMTuple *a, Size alen, const MMTuple *b, Size blen)
{
if (alen != blen)
return false;
diff --git a/src/backend/access/minmax/mmxlog.c b/src/backend/access/minmax/mmxlog.c
index ab3f9fe..5690ceb 100644
--- a/src/backend/access/minmax/mmxlog.c
+++ b/src/backend/access/minmax/mmxlog.c
@@ -246,84 +246,54 @@ minmax_xlog_samepage_update(XLogRecPtr lsn, XLogRecord *record)
static void
-minmax_xlog_metapg_set(XLogRecPtr lsn, XLogRecord *record)
+minmax_xlog_revmap_extend(XLogRecPtr lsn, XLogRecord *record)
{
- xl_minmax_metapg_set *xlrec = (xl_minmax_metapg_set *) XLogRecGetData(record);
- Buffer meta;
+ xl_minmax_revmap_extend *xlrec = (xl_minmax_revmap_extend *) XLogRecGetData(record);
+ Buffer metabuf;
Page metapg;
MinmaxMetaPageData *metadata;
+ Buffer buf;
+ Page page;
- /* If we have a full-page image, restore it and we're done */
- if (record->xl_info & XLR_BKP_BLOCK(0))
- {
- (void) RestoreBackupBlock(lsn, record, 0, false, false);
- return;
- }
-
- meta = XLogReadBuffer(xlrec->node, MINMAX_METAPAGE_BLKNO, false);
- Assert(BufferIsValid(meta));
-
- metapg = BufferGetPage(meta);
- metadata = (MinmaxMetaPageData *) PageGetContents(metapg);
- metadata->revmapArrayPages[xlrec->blkidx] = xlrec->newpg;
-
- PageSetLSN(metapg, lsn);
- MarkBufferDirty(meta);
- UnlockReleaseBuffer(meta);
-}
-
-static void
-minmax_xlog_init_rmpg(XLogRecPtr lsn, XLogRecord *record)
-{
- xl_minmax_init_rmpg *xlrec = (xl_minmax_init_rmpg *) XLogRecGetData(record);
- Buffer buffer;
-
+ /* Update the metapage */
if (record->xl_info & XLR_BKP_BLOCK(0))
{
- (void) RestoreBackupBlock(lsn, record, 0, false, false);
- return;
+ metabuf = RestoreBackupBlock(lsn, record, 0, false, true);
}
-
- buffer = XLogReadBuffer(xlrec->node, xlrec->blkno, true);
- Assert(BufferIsValid(buffer));
-
- if (xlrec->array)
- initialize_rma_page(buffer);
else
- initialize_rmr_page(buffer, xlrec->logblk);
-
- PageSetLSN(BufferGetPage(buffer), lsn);
- MarkBufferDirty(buffer);
- UnlockReleaseBuffer(buffer);
-}
+ {
+ metabuf = XLogReadBuffer(xlrec->node, MINMAX_METAPAGE_BLKNO, false);
+ if (BufferIsValid(metabuf))
+ {
+ metapg = BufferGetPage(metabuf);
+ if (lsn > PageGetLSN(metapg))
+ {
+ metadata = (MinmaxMetaPageData *) PageGetContents(metapg);
-static void
-minmax_xlog_rmarray_set(XLogRecPtr lsn, XLogRecord *record)
-{
- xl_minmax_rmarray_set *xlrec = (xl_minmax_rmarray_set *) XLogRecGetData(record);
- Buffer buffer;
- Page page;
- RevmapArrayContents *contents;
+ Assert(metadata->lastRevmapPage == xlrec->targetBlk - 1);
+ metadata->lastRevmapPage = xlrec->targetBlk;
- /* If we have a full-page image, restore it and we're done */
- if (record->xl_info & XLR_BKP_BLOCK(0))
- {
- (void) RestoreBackupBlock(lsn, record, 0, false, false);
- return;
+ PageSetLSN(metapg, lsn);
+ MarkBufferDirty(metabuf);
+ }
+ }
}
- buffer = XLogReadBuffer(xlrec->node, xlrec->rmarray, false);
- Assert(BufferIsValid(buffer));
+ /* Re-init the target block as a revmap page */
- page = BufferGetPage(buffer);
-
- contents = (RevmapArrayContents *) PageGetContents(page);
- contents->rma_blocks[xlrec->blkidx] = xlrec->newpg;
- contents->rma_nblocks = xlrec->blkidx + 1; /* XXX is this okay? */
+ buf = XLogReadBuffer(xlrec->node, xlrec->targetBlk, true);
+ page = (Page) BufferGetPage(buf);
+ mm_page_init(page, MINMAX_PAGETYPE_REVMAP);
PageSetLSN(page, lsn);
- MarkBufferDirty(buffer);
- UnlockReleaseBuffer(buffer);
+ MarkBufferDirty(buf);
+
+ metadata->lastRevmapPage = xlrec->targetBlk;
+ PageSetLSN(metapg, lsn);
+ MarkBufferDirty(metabuf);
+
+ UnlockReleaseBuffer(buf);
+ UnlockReleaseBuffer(metabuf);
}
void
@@ -345,14 +315,8 @@ minmax_redo(XLogRecPtr lsn, XLogRecord *record)
case XLOG_MINMAX_SAMEPAGE_UPDATE:
minmax_xlog_samepage_update(lsn, record);
break;
- case XLOG_MINMAX_METAPG_SET:
- minmax_xlog_metapg_set(lsn, record);
- break;
- case XLOG_MINMAX_RMARRAY_SET:
- minmax_xlog_rmarray_set(lsn, record);
- break;
- case XLOG_MINMAX_INIT_RMPG:
- minmax_xlog_init_rmpg(lsn, record);
+ case XLOG_MINMAX_REVMAP_EXTEND:
+ minmax_xlog_revmap_extend(lsn, record);
break;
default:
elog(PANIC, "minmax_redo: unknown op code %u", info);
diff --git a/src/include/access/minmax_internal.h b/src/include/access/minmax_internal.h
index 47ed279..c206168 100644
--- a/src/include/access/minmax_internal.h
+++ b/src/include/access/minmax_internal.h
@@ -87,5 +87,7 @@ extern void minmax_free_mmdesc(MinmaxDesc *mmdesc);
extern void mm_page_init(Page page, uint16 type);
extern void mm_metapage_init(Page page, BlockNumber pagesPerRange,
uint16 version);
+extern bool mm_start_evacuating_page(Relation idxRel, Buffer buf);
+extern void mm_evacuate_page(Relation idxRel, Buffer buf);
#endif /* MINMAX_INTERNAL_H */
diff --git a/src/include/access/minmax_page.h b/src/include/access/minmax_page.h
index 04f40d8..df7f940 100644
--- a/src/include/access/minmax_page.h
+++ b/src/include/access/minmax_page.h
@@ -19,13 +19,21 @@
/* special space on all minmax pages stores a "type" identifier */
#define MINMAX_PAGETYPE_META 0xF091
-#define MINMAX_PAGETYPE_REVMAP_ARRAY 0xF092
-#define MINMAX_PAGETYPE_REVMAP 0xF093
-#define MINMAX_PAGETYPE_REGULAR 0xF094
+#define MINMAX_PAGETYPE_REVMAP 0xF092
+#define MINMAX_PAGETYPE_REGULAR 0xF093
+
+#define MINMAX_PAGE_TYPE(page) \
+ (((MinmaxSpecialSpace *) PageGetSpecialPointer(page))->type)
+#define MINMAX_IS_REVMAP_PAGE(page) (MINMAX_PAGE_TYPE(page) == MINMAX_PAGETYPE_REVMAP)
+#define MINMAX_IS_REGULAR_PAGE(page) (MINMAX_PAGE_TYPE(page) == MINMAX_PAGETYPE_REGULAR)
+
+/* flags */
+#define MINMAX_EVACUATE_PAGE 1
typedef struct MinmaxSpecialSpace
{
- uint16 type;
+ uint16 flags;
+ uint16 type;
} MinmaxSpecialSpace;
/* Metapage definitions */
@@ -34,30 +42,18 @@ typedef struct MinmaxMetaPageData
uint32 minmaxMagic;
uint32 minmaxVersion;
BlockNumber pagesPerRange;
- BlockNumber revmapArrayPages[1]; /* actually MAX_REVMAP_ARRAYPAGES */
+ BlockNumber lastRevmapPage;
} MinmaxMetaPageData;
-/*
- * Number of array pages listed in metapage. Need to consider leaving enough
- * space for the page header, the metapage struct, and the minmax special
- * space.
- */
-#define MAX_REVMAP_ARRAYPAGES \
- ((BLCKSZ - \
- MAXALIGN(SizeOfPageHeaderData) - \
- offsetof(MinmaxMetaPageData, revmapArrayPages) - \
- MAXALIGN(sizeof(MinmaxSpecialSpace)) ) / \
- sizeof(BlockNumber))
-
#define MINMAX_CURRENT_VERSION 1
#define MINMAX_META_MAGIC 0xA8109CFA
-#define MINMAX_METAPAGE_BLKNO 0
+#define MINMAX_METAPAGE_BLKNO 0
+#define MINMAX_REVMAP_FIRST_BLKNO 1
/* Definitions for regular revmap pages */
typedef struct RevmapContents
{
- int32 rmr_logblk; /* logical blkno of this revmap page */
ItemPointerData rmr_tids[1]; /* really REGULAR_REVMAP_PAGE_MAXITEMS */
} RevmapContents;
@@ -69,20 +65,4 @@ typedef struct RevmapContents
#define REGULAR_REVMAP_PAGE_MAXITEMS \
(REGULAR_REVMAP_CONTENT_SIZE / sizeof(ItemPointerData))
-/* Definitions for array revmap pages */
-typedef struct RevmapArrayContents
-{
- int32 rma_nblocks;
- BlockNumber rma_blocks[1]; /* really ARRAY_REVMAP_PAGE_MAXITEMS */
-} RevmapArrayContents;
-
-#define REVMAP_ARRAY_CONTENT_SIZE \
- (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
- offsetof(RevmapArrayContents, rma_blocks) - \
- MAXALIGN(sizeof(MinmaxSpecialSpace)))
-/* max num of items in the array */
-#define ARRAY_REVMAP_PAGE_MAXITEMS \
- (REVMAP_ARRAY_CONTENT_SIZE / sizeof(BlockNumber))
-
-
#endif /* MINMAX_PAGE_H */
diff --git a/src/include/access/minmax_revmap.h b/src/include/access/minmax_revmap.h
index 68729d8..73c6cd4 100644
--- a/src/include/access/minmax_revmap.h
+++ b/src/include/access/minmax_revmap.h
@@ -33,9 +33,4 @@ extern MMTuple *mmGetMMTupleForHeapBlock(mmRevmapAccess *rmAccess,
BlockNumber heapBlk, Buffer *buf, OffsetNumber *off,
int mode);
-/* internal stuff also used by xlog replay */
-extern BlockNumber initialize_rmr_page(Buffer newbuf, BlockNumber mapBlk);
-extern void initialize_rma_page(Buffer buf);
-
-
#endif /* MINMAX_REVMAP_H */
diff --git a/src/include/access/minmax_tuple.h b/src/include/access/minmax_tuple.h
index 989a179..eff4d52 100644
--- a/src/include/access/minmax_tuple.h
+++ b/src/include/access/minmax_tuple.h
@@ -77,8 +77,9 @@ typedef struct MMTuple
extern MMTuple *minmax_form_tuple(MinmaxDesc *mmdesc, BlockNumber blkno,
DeformedMMTuple *tuple, Size *size);
extern void minmax_free_tuple(MMTuple *tuple);
-MMTuple *minmax_copy_tuple(MMTuple *tuple, Size len);
-extern bool minmax_tuples_equal(MMTuple *a, Size alen, MMTuple *b, Size blen);
+extern MMTuple *minmax_copy_tuple(MMTuple *tuple, Size len);
+extern bool minmax_tuples_equal(const MMTuple *a, Size alen,
+ const MMTuple *b, Size blen);
extern DeformedMMTuple *minmax_new_dtuple(MinmaxDesc *mmdesc);
extern void minmax_dtuple_initialize(DeformedMMTuple *dtuple,
diff --git a/src/include/access/minmax_xlog.h b/src/include/access/minmax_xlog.h
index 00d3425..01bb065 100644
--- a/src/include/access/minmax_xlog.h
+++ b/src/include/access/minmax_xlog.h
@@ -31,9 +31,8 @@
#define XLOG_MINMAX_INSERT 0x10
#define XLOG_MINMAX_UPDATE 0x20
#define XLOG_MINMAX_SAMEPAGE_UPDATE 0x30
-#define XLOG_MINMAX_METAPG_SET 0x40
-#define XLOG_MINMAX_RMARRAY_SET 0x50
-#define XLOG_MINMAX_INIT_RMPG 0x60
+#define XLOG_MINMAX_REVMAP_EXTEND 0x40
+#define XLOG_MINMAX_REVMAP_VACUUM 0x50
#define XLOG_MINMAX_OPMASK 0x70
/*
@@ -90,39 +89,14 @@ typedef struct xl_minmax_samepage_update
#define SizeOfMinmaxSamepageUpdate (offsetof(xl_minmax_samepage_update, tid) + sizeof(ItemPointerData))
-/* This is what we need to know about a "metapage set" operation */
-typedef struct xl_minmax_metapg_set
+/* This is what we need to know about a revmap extension */
+typedef struct xl_minmax_revmap_extend
{
RelFileNode node;
- uint32 blkidx;
- BlockNumber newpg;
-} xl_minmax_metapg_set;
+ BlockNumber targetBlk;
+} xl_minmax_revmap_extend;
-#define SizeOfMinmaxMetapgSet (offsetof(xl_minmax_metapg_set, newpg) + \
- sizeof(BlockNumber))
-
-/* This is what we need to know about a "revmap array set" operation */
-typedef struct xl_minmax_rmarray_set
-{
- RelFileNode node;
- BlockNumber rmarray;
- uint32 blkidx;
- BlockNumber newpg;
-} xl_minmax_rmarray_set;
-
-#define SizeOfMinmaxRmarraySet (offsetof(xl_minmax_rmarray_set, newpg) + \
- sizeof(BlockNumber))
-
-/* This is what we need to know when we initialize a new revmap page */
-typedef struct xl_minmax_init_rmpg
-{
- RelFileNode node;
- bool array; /* array revmap page or regular revmap page */
- BlockNumber blkno;
- BlockNumber logblk; /* only used by regular revmap pages */
-} xl_minmax_init_rmpg;
-
-#define SizeOfMinmaxInitRmpg (offsetof(xl_minmax_init_rmpg, blkno) + \
+#define SizeOfMinmaxRevmapExtend (offsetof(xl_minmax_revmap_extend, targetBlk) + \
sizeof(BlockNumber))