support_unlogged_gist_indexes_v3.patch
application/octet-stream
Filename: support_unlogged_gist_indexes_v3.patch
Type: application/octet-stream
Part: 0
Message:
Re: unlogged tables vs. GIST
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/create_table.sgml | 1 | 2 |
| src/backend/access/gist/gistbuild.c | 1 | 11 |
| src/backend/access/gist/gist.c | 18 | 5 |
| src/backend/access/gist/gistutil.c | 0 | 15 |
| src/backend/access/gist/gistvacuum.c | 1 | 1 |
| src/backend/access/transam/xlog.c | 47 | 0 |
| src/backend/storage/buffer/bufmgr.c | 17 | 2 |
| src/include/access/gist_private.h | 1 | 2 |
| src/include/access/xlog.h | 1 | 0 |
| src/include/catalog/pg_control.h | 1 | 0 |
| src/include/utils/rel.h | 7 | 0 |
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 8872920..af11eb0 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -182,8 +182,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
automatically truncated after a crash or unclean shutdown. The contents
of an unlogged table are also not replicated to standby servers.
Any indexes created on an unlogged table are automatically unlogged as
- well; however, unlogged <link linkend="GiST">GiST indexes</link> are
- currently not supported and cannot be created on an unlogged table.
+ well.
</para>
</listitem>
</varlistentry>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 95d33c8..cf1f499 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -71,9 +71,22 @@ createTempGistContext(void)
Datum
gistbuildempty(PG_FUNCTION_ARGS)
{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("unlogged GiST indexes are not supported")));
+ Relation index = (Relation) PG_GETARG_POINTER(0);
+ Buffer buffer;
+
+ /* Initialize the root page */
+ buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /* Initialize and xlog buffer */
+ START_CRIT_SECTION();
+ GISTInitBuffer(buffer, F_LEAF);
+ MarkBufferDirty(buffer);
+ log_newpage_buffer(buffer);
+ END_CRIT_SECTION();
+
+ /* Unlock and release the buffer */
+ UnlockReleaseBuffer(buffer);
PG_RETURN_VOID();
}
@@ -391,7 +404,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
dist, oldrlink, oldnsn, leftchildbuf,
markfollowright);
else
- recptr = GetXLogRecPtrForTemp();
+ recptr = GetXLogRecPtrForUnloggedRel(RelationIsUnlogged(rel));
for (ptr = dist; ptr; ptr = ptr->next)
{
@@ -448,7 +461,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
}
else
{
- recptr = GetXLogRecPtrForTemp();
+ recptr = GetXLogRecPtrForUnloggedRel(RelationIsUnlogged(rel));
PageSetLSN(page, recptr);
}
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index aec5b52..be7078e 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -158,16 +158,6 @@ gistbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index));
- /*
- * We can't yet handle unlogged GiST indexes, because we depend on LSNs.
- * This is duplicative of an error in gistbuildempty, but we want to check
- * here so as to throw error before doing all the index-build work.
- */
- if (heap->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("unlogged GiST indexes are not supported")));
-
/* no locking is needed */
buildstate.giststate = initGISTstate(index);
@@ -204,7 +194,7 @@ gistbuild(PG_FUNCTION_ARGS)
PageSetTLI(page, ThisTimeLineID);
}
else
- PageSetLSN(page, GetXLogRecPtrForTemp());
+ PageSetLSN(page, GetXLogRecPtrForUnloggedRel(RelationIsUnlogged(heap)));
UnlockReleaseBuffer(buffer);
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 8b60774..1dce570 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -743,18 +743,3 @@ gistoptions(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(rdopts);
}
-
-/*
- * 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 = 1;
- counter++;
- return counter;
-}
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index dcad36e..a00e1db 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -238,7 +238,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
PageSetTLI(page, ThisTimeLineID);
}
else
- PageSetLSN(page, GetXLogRecPtrForTemp());
+ PageSetLSN(page, GetXLogRecPtrForUnloggedRel(RelationIsUnlogged(rel)));
END_CRIT_SECTION();
}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 51a515a..d477bf9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -386,6 +386,9 @@ typedef struct XLogCtlData
XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */
XLogSegNo lastRemovedSegNo; /* latest removed/recycled XLOG segment */
+ /* Protected by ulsn_lck: */
+ XLogRecPtr unloggedLSN; /* Fake LSN for unlogged GiST index */
+
/* Protected by WALWriteLock: */
XLogCtlWrite Write;
@@ -476,6 +479,7 @@ typedef struct XLogCtlData
XLogRecPtr lastFpwDisableRecPtr;
slock_t info_lck; /* locks shared variables shown above */
+ slock_t ulsn_lck; /* locks shared variable unloggedLSN */
} XLogCtlData;
static XLogCtlData *XLogCtl = NULL;
@@ -4208,6 +4212,40 @@ GetSystemIdentifier(void)
}
/*
+ * Temporary GiST indexes are not WAL-logged, but we need LSNs to detect
+ * concurrent page splits anyway. GetXLogRecPtrForUnloggedRel() 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 using static counter
+ * Similarily unlogged GiST indexes are also not WAL-logged. But we need a
+ * persistent counter across clean shutdown. Use counter from ControlFile which
+ * is copied in XLogCtl.unloggedLSN to accomplish that
+ * If isUnlogged is true, return persistent counter from XLogCtl else return
+ * session wide temporary counter
+ */
+XLogRecPtr
+GetXLogRecPtrForUnloggedRel(bool isUnlogged)
+{
+ XLogRecPtr nextUnloggedLSN;
+ static XLogRecPtr counter = 1;
+
+ if (isUnlogged)
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ /* increment the unloggedLSN counter, need SpinLock */
+ SpinLockAcquire(&xlogctl->ulsn_lck);
+ nextUnloggedLSN = xlogctl->unloggedLSN++;
+ SpinLockRelease(&xlogctl->ulsn_lck);
+ }
+ else
+ nextUnloggedLSN = counter++; /* TEMP relation, use static counter */
+
+ return nextUnloggedLSN;
+}
+
+/*
* Auto-tune the number of XLOG buffers.
*
* The preferred setting for wal_buffers is about 3% of shared_buffers, with
@@ -4355,6 +4393,7 @@ XLOGShmemInit(void)
XLogCtl->WalWriterSleeping = false;
XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages);
SpinLockInit(&XLogCtl->info_lck);
+ SpinLockInit(&XLogCtl->ulsn_lck);
InitSharedLatch(&XLogCtl->recoveryWakeupLatch);
/*
@@ -4496,6 +4535,7 @@ BootStrapXLOG(void)
ControlFile->time = checkPoint.time;
ControlFile->checkPoint = checkPoint.redo;
ControlFile->checkPointCopy = checkPoint;
+ ControlFile->unloggedLSN = 1;
/* Set important parameter values for use when replaying WAL */
ControlFile->MaxConnections = MaxConnections;
@@ -5505,6 +5545,9 @@ StartupXLOG(void)
XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch;
XLogCtl->ckptXid = checkPoint.nextXid;
+ /* initaiize unlogged LSN from Control File */
+ XLogCtl->unloggedLSN = ControlFile->unloggedLSN;
+
/*
* We must replay WAL entries using the same TimeLineID they were created
* under, so temporarily adopt the TLI indicated by the checkpoint (see
@@ -7034,6 +7077,10 @@ CreateCheckPoint(int flags)
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
ControlFile->state = DB_SHUTDOWNING;
ControlFile->time = (pg_time_t) time(NULL);
+ /* Store unloggedLSN value as we want it persistent across shutdown */
+ SpinLockAcquire(&XLogCtl->ulsn_lck);
+ ControlFile->unloggedLSN = XLogCtl->unloggedLSN;
+ SpinLockRelease(&XLogCtl->ulsn_lck);
UpdateControlFile();
LWLockRelease(ControlFileLock);
}
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 03ed41d..092dafd 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -1920,9 +1920,24 @@ FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln)
* Force XLOG flush up to buffer's LSN. This implements the basic WAL
* rule that log updates must hit disk before any of the data-file changes
* they describe do.
+ * However, this rule does not apply for unlogged relations, which will be
+ * lost after a crash anyway. Most unlogged relation pages do not bear LSNs
+ * since we never emit WAL records for them, and therefore flushing up
+ * through the buffer LSN would be useless, but harmless. However, GiST
+ * indexes use LSNs internally to track page-splits, and therefore
+ * temporary and unlogged GiST pages bear "fake" LSNs generated by
+ * GetXLogRecPtrForUnloggedRel. It is unlikely but possible that the fake
+ * LSN counter used for unlogged relations could advance past the WAL
+ * insertion point; and if it did happen, attempting to flush WAL through
+ * that location would fail, with disastrous system-wide consequences. To
+ * make sure that can't happen, skip the flush if the buffer isn't
+ * permanent.
*/
- recptr = BufferGetLSN(buf);
- XLogFlush(recptr);
+ if (buf->flags & BM_PERMANENT)
+ {
+ recptr = BufferGetLSN(buf);
+ XLogFlush(recptr);
+ }
/*
* Now it's safe to write buffer to disk. Note that no one else should
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 357b8c9..8d4fa9d 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -16,6 +16,7 @@
#include "access/gist.h"
#include "access/itup.h"
+#include "access/xlog.h"
#include "fmgr.h"
#include "storage/bufmgr.h"
#include "storage/buffile.h"
@@ -511,8 +512,6 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
GISTENTRY *entry2, bool isnull2,
Datum *dst, bool *dstisnull);
-extern XLogRecPtr GetXLogRecPtrForTemp(void);
-
/* gistvacuum.c */
extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 885b5fc..81ee1b3 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -294,6 +294,7 @@ extern char *XLogFileNameP(TimeLineID tli, XLogSegNo segno);
extern void UpdateControlFile(void);
extern uint64 GetSystemIdentifier(void);
+extern XLogRecPtr GetXLogRecPtrForUnloggedRel(bool isUnlogged);
extern Size XLOGShmemSize(void);
extern void XLOGShmemInit(void);
extern void BootStrapXLOG(void);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ead3a6e..17522d0 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -157,6 +157,7 @@ typedef struct ControlFileData
XLogRecPtr backupStartPoint;
XLogRecPtr backupEndPoint;
bool backupEndRequired;
+ XLogRecPtr unloggedLSN; /* fake LSN for unlogged GiST index */
/*
* Parameter settings that determine if the WAL can be used for archival
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index bde5f17..2acb34e 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -366,6 +366,13 @@ typedef struct StdRdOptions
((relation)->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
/*
+ * RelationIsUnlogged
+ * True if relation is unlogged.
+ */
+#define RelationIsUnlogged(relation) \
+ ((relation)->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+
+/*
* RelationUsesLocalBuffers
* True if relation's pages are stored in local buffers.
*/