The GiST scan algorithm uses LSNs to detect concurrent pages splits, but

temporary indexes are not WAL-logged. We used a constant LSN for temporary
indexes, on the assumption that we don't need to worry about concurrent page
splits in temporary indexes because they're only visible to the current
session. But that assumption is wrong, it's possible to insert rows and
split pages in the same session, while a scan is in progress. For example,
by opening a cursor and fetching some rows, and INSERTing new rows before
fetching some more.

Fix by generating fake increasing LSNs, used in place of real LSNs in
temporary GiST indexes.
This commit is contained in:
Heikki Linnakangas 2010-11-16 11:02:11 +02:00
parent 0e27a73191
commit acbffaa77d
4 changed files with 30 additions and 10 deletions

View File

@ -23,8 +23,6 @@
#include "miscadmin.h"
#include "utils/memutils.h"
const XLogRecPtr XLogRecPtrForTemp = {1, 1};
/* Working state for gistbuild and its callback */
typedef struct
{
@ -127,7 +125,7 @@ gistbuild(PG_FUNCTION_ARGS)
END_CRIT_SECTION();
}
else
PageSetLSN(BufferGetPage(buffer), XLogRecPtrForTemp);
PageSetLSN(BufferGetPage(buffer), GetXLogRecPtrForTemp());
LockBuffer(buffer, GIST_UNLOCK);
WriteBuffer(buffer);
@ -356,7 +354,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
ptr = dist;
while (ptr)
{
PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
ptr = ptr->next;
}
}
@ -475,7 +473,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
END_CRIT_SECTION();
}
else
PageSetLSN(state->stack->page, XLogRecPtrForTemp);
PageSetLSN(state->stack->page, GetXLogRecPtrForTemp());
if (state->stack->blkno == GIST_ROOT_BLKNO)
state->needInsertComplete = false;
@ -1206,7 +1204,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
END_CRIT_SECTION();
}
else
PageSetLSN(page, XLogRecPtrForTemp);
PageSetLSN(page, GetXLogRecPtrForTemp());
}
void

View File

@ -977,3 +977,24 @@ gistNewBuffer(Relation r)
return buffer;
}
/*
* Temporary GiST indexes are not WAL-logged, but we need LSNs to detect
* concurrent page splits anyway. GetXLogRecPtrForTemp() provides a fake
* sequence of LSNs for that purpose. Each call generates an LSN that is
* greater than any previous value returned by this function in the same
* session.
*/
XLogRecPtr
GetXLogRecPtrForTemp(void)
{
static XLogRecPtr counter = {0, 1};
counter.xrecoff++;
if (counter.xrecoff == 0)
{
counter.xlogid++;
counter.xrecoff++;
}
return counter;
}

View File

@ -201,7 +201,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
ptr = dist;
while (ptr)
{
PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
ptr = ptr->next;
}
}
@ -306,7 +306,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
pfree(rdata);
}
else
PageSetLSN(page, XLogRecPtrForTemp);
PageSetLSN(page, GetXLogRecPtrForTemp());
WriteBuffer(buffer);
}
else
@ -589,7 +589,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
pfree(rdata);
}
else
PageSetLSN(page, XLogRecPtrForTemp);
PageSetLSN(page, GetXLogRecPtrForTemp());
WriteNoReleaseBuffer(buffer);
}
}

View File

@ -91,7 +91,6 @@ typedef struct GISTScanOpaqueData
typedef GISTScanOpaqueData *GISTScanOpaque;
/* XLog stuff */
extern const XLogRecPtr XLogRecPtrForTemp;
#define XLOG_GIST_ENTRY_UPDATE 0x00
#define XLOG_GIST_ENTRY_DELETE 0x10
@ -318,6 +317,8 @@ extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
void gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
IndexTuple *itup, int len, GISTSTATE *giststate);
extern XLogRecPtr GetXLogRecPtrForTemp(void);
/* gistvacuum.c */
extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);