Commit d1996ed5 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Change the way parent pages are tracked during buffered GiST build.

We used to mimic the way a stack is constructed when descending the tree
during normal GiST inserts, but that was quite complicated during a buffered
build. It was also wrong: in GiST, the left-to-right relationships on
different levels might not match each other, so that when you know the
parent of a child page, you won't necessarily find the parent of the page to
the right of the child page by following the rightlinks at the parent level.
This sometimes led to "could not re-find parent" errors while building a
GiST index.

We now use a simple hash table to track the parent of every internal page.
Whenever a page is split, and downlinks are moved from one page to another,
we update the hash table accordingly. This is also better for performance
than the old method, as we never need to move right to re-find the parent
page, which could take a significant amount of time for buffers that were
created much earlier in the index build.
parent be02b168
This diff is collapsed.
...@@ -107,16 +107,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel) ...@@ -107,16 +107,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel)
sizeof(GISTNodeBuffer *)); sizeof(GISTNodeBuffer *));
gfbb->loadedBuffersCount = 0; gfbb->loadedBuffersCount = 0;
/* gfbb->rootlevel = maxLevel;
* Root path item of the tree. Updated on each root node split.
*/
gfbb->rootitem = (GISTBufferingInsertStack *) MemoryContextAlloc(
gfbb->context, sizeof(GISTBufferingInsertStack));
gfbb->rootitem->parent = NULL;
gfbb->rootitem->blkno = GIST_ROOT_BLKNO;
gfbb->rootitem->downlinkoffnum = InvalidOffsetNumber;
gfbb->rootitem->level = maxLevel;
gfbb->rootitem->refCount = 1;
return gfbb; return gfbb;
} }
...@@ -127,9 +118,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel) ...@@ -127,9 +118,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel)
*/ */
GISTNodeBuffer * GISTNodeBuffer *
gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
BlockNumber nodeBlocknum, BlockNumber nodeBlocknum, int level)
OffsetNumber downlinkoffnum,
GISTBufferingInsertStack *parent)
{ {
GISTNodeBuffer *nodeBuffer; GISTNodeBuffer *nodeBuffer;
bool found; bool found;
...@@ -144,8 +133,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -144,8 +133,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
/* /*
* Node buffer wasn't found. Initialize the new buffer as empty. * Node buffer wasn't found. Initialize the new buffer as empty.
*/ */
GISTBufferingInsertStack *path;
int level;
MemoryContext oldcxt = MemoryContextSwitchTo(gfbb->context); MemoryContext oldcxt = MemoryContextSwitchTo(gfbb->context);
/* nodeBuffer->nodeBlocknum is the hash key and was filled in already */ /* nodeBuffer->nodeBlocknum is the hash key and was filled in already */
...@@ -153,33 +140,12 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -153,33 +140,12 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
nodeBuffer->pageBlocknum = InvalidBlockNumber; nodeBuffer->pageBlocknum = InvalidBlockNumber;
nodeBuffer->pageBuffer = NULL; nodeBuffer->pageBuffer = NULL;
nodeBuffer->queuedForEmptying = false; nodeBuffer->queuedForEmptying = false;
nodeBuffer->level = level;
/*
* Create a path stack for the page.
*/
if (nodeBlocknum != GIST_ROOT_BLKNO)
{
path = (GISTBufferingInsertStack *) palloc(
sizeof(GISTBufferingInsertStack));
path->parent = parent;
path->blkno = nodeBlocknum;
path->downlinkoffnum = downlinkoffnum;
path->level = parent->level - 1;
path->refCount = 0; /* initially unreferenced */
parent->refCount++; /* this path references its parent */
Assert(path->level > 0);
}
else
path = gfbb->rootitem;
nodeBuffer->path = path;
path->refCount++;
/* /*
* Add this buffer to the list of buffers on this level. Enlarge * Add this buffer to the list of buffers on this level. Enlarge
* buffersOnLevels array if needed. * buffersOnLevels array if needed.
*/ */
level = path->level;
if (level >= gfbb->buffersOnLevelsLen) if (level >= gfbb->buffersOnLevelsLen)
{ {
int i; int i;
...@@ -210,20 +176,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -210,20 +176,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
} }
else
{
if (parent != nodeBuffer->path->parent)
{
/*
* A different parent path item was provided than we've
* remembered. We trust caller to provide more correct parent than
* we have. Previous parent may be outdated by page split.
*/
gistDecreasePathRefcount(nodeBuffer->path->parent);
nodeBuffer->path->parent = parent;
parent->refCount++;
}
}
return nodeBuffer; return nodeBuffer;
} }
...@@ -585,7 +537,7 @@ typedef struct ...@@ -585,7 +537,7 @@ typedef struct
*/ */
void void
gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
Relation r, GISTBufferingInsertStack *path, Relation r, int level,
Buffer buffer, List *splitinfo) Buffer buffer, List *splitinfo)
{ {
RelocationBufferInfo *relocationBuffersInfos; RelocationBufferInfo *relocationBuffersInfos;
...@@ -601,7 +553,7 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -601,7 +553,7 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
ListCell *lc; ListCell *lc;
/* If the splitted page doesn't have buffers, we have nothing to do. */ /* If the splitted page doesn't have buffers, we have nothing to do. */
if (!LEVEL_HAS_BUFFERS(path->level, gfbb)) if (!LEVEL_HAS_BUFFERS(level, gfbb))
return; return;
/* /*
...@@ -660,14 +612,11 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -660,14 +612,11 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
/* /*
* Create a node buffer for the page. The leftmost half is on the same * Create a node buffer for the page. The leftmost half is on the same
* block as the old page before split, so for the leftmost half this * block as the old page before split, so for the leftmost half this
* will return the original buffer, which was emptied earlier in this * will return the original buffer. The tuples on the original buffer
* function. * were relinked to the temporary buffer, so the original one is now
* empty.
*/ */
newNodeBuffer = gistGetNodeBuffer(gfbb, newNodeBuffer = gistGetNodeBuffer(gfbb, giststate, BufferGetBlockNumber(si->buf), level);
giststate,
BufferGetBlockNumber(si->buf),
path->downlinkoffnum,
path->parent);
relocationBuffersInfos[i].nodeBuffer = newNodeBuffer; relocationBuffersInfos[i].nodeBuffer = newNodeBuffer;
relocationBuffersInfos[i].splitinfo = si; relocationBuffersInfos[i].splitinfo = si;
......
...@@ -329,7 +329,7 @@ typedef struct ...@@ -329,7 +329,7 @@ typedef struct
/* is this a temporary copy, not in the hash table? */ /* is this a temporary copy, not in the hash table? */
bool isTemp; bool isTemp;
struct GISTBufferingInsertStack *path; int level; /* 0 == leaf */
} GISTNodeBuffer; } GISTNodeBuffer;
/* /*
...@@ -338,7 +338,7 @@ typedef struct ...@@ -338,7 +338,7 @@ typedef struct
*/ */
#define LEVEL_HAS_BUFFERS(nlevel, gfbb) \ #define LEVEL_HAS_BUFFERS(nlevel, gfbb) \
((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \ ((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
(nlevel) != (gfbb)->rootitem->level) (nlevel) != (gfbb)->rootlevel)
/* Is specified buffer at least half-filled (should be queued for emptying)? */ /* Is specified buffer at least half-filled (should be queued for emptying)? */
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb) \ #define BUFFER_HALF_FILLED(nodeBuffer, gfbb) \
...@@ -352,26 +352,6 @@ typedef struct ...@@ -352,26 +352,6 @@ typedef struct
#define BUFFER_OVERFLOWED(nodeBuffer, gfbb) \ #define BUFFER_OVERFLOWED(nodeBuffer, gfbb) \
((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer) ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
/*
* Extended GISTInsertStack for buffering GiST index build.
*/
typedef struct GISTBufferingInsertStack
{
/* current page */
BlockNumber blkno;
/* offset of the downlink in the parent page, that points to this page */
OffsetNumber downlinkoffnum;
/* pointer to parent */
struct GISTBufferingInsertStack *parent;
int refCount;
/* level number */
int level;
} GISTBufferingInsertStack;
/* /*
* Data structure with general information about build buffers. * Data structure with general information about build buffers.
*/ */
...@@ -416,8 +396,8 @@ typedef struct GISTBuildBuffers ...@@ -416,8 +396,8 @@ typedef struct GISTBuildBuffers
int loadedBuffersCount; /* # of entries in loadedBuffers */ int loadedBuffersCount; /* # of entries in loadedBuffers */
int loadedBuffersLen; /* allocated size of loadedBuffers */ int loadedBuffersLen; /* allocated size of loadedBuffers */
/* A path item that points to the current root node */ /* Level of the current root node (= height of the index tree - 1) */
GISTBufferingInsertStack *rootitem; int rootlevel;
} GISTBuildBuffers; } GISTBuildBuffers;
/* /*
...@@ -551,15 +531,13 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup, ...@@ -551,15 +531,13 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */ /* gistbuild.c */
extern Datum gistbuild(PG_FUNCTION_ARGS); extern Datum gistbuild(PG_FUNCTION_ARGS);
extern void gistValidateBufferingOption(char *value); extern void gistValidateBufferingOption(char *value);
extern void gistDecreasePathRefcount(GISTBufferingInsertStack *path);
/* gistbuildbuffers.c */ /* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep, extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
int maxLevel); int maxLevel);
extern GISTNodeBuffer *gistGetNodeBuffer(GISTBuildBuffers *gfbb, extern GISTNodeBuffer *gistGetNodeBuffer(GISTBuildBuffers *gfbb,
GISTSTATE *giststate, GISTSTATE *giststate,
BlockNumber blkno, OffsetNumber downlinkoffnum, BlockNumber blkno, int level);
GISTBufferingInsertStack *parent);
extern void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, extern void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb,
GISTNodeBuffer *nodeBuffer, IndexTuple item); GISTNodeBuffer *nodeBuffer, IndexTuple item);
extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb, extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
...@@ -567,7 +545,7 @@ extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb, ...@@ -567,7 +545,7 @@ extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
extern void gistFreeBuildBuffers(GISTBuildBuffers *gfbb); extern void gistFreeBuildBuffers(GISTBuildBuffers *gfbb);
extern void gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, extern void gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb,
GISTSTATE *giststate, Relation r, GISTSTATE *giststate, Relation r,
GISTBufferingInsertStack *path, Buffer buffer, int level, Buffer buffer,
List *splitinfo); List *splitinfo);
extern void gistUnloadNodeBuffers(GISTBuildBuffers *gfbb); extern void gistUnloadNodeBuffers(GISTBuildBuffers *gfbb);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment