public inbox for [email protected]
help / color / mirror / Atom feedFrom: Kyotaro Horiguchi <[email protected]>
Subject: [PATCH 4/8] Add infrastructure to WAL-logging skip feature
Date: Tue, 26 Mar 2019 15:34:48 +0900
We used to optimize WAL-logging for truncation of in-transaction
crated tables in minimal mode by just signaling by
HEAP_INSERT_SKIP_WAL option on heap operations. This mechanism can
emit WAL records that results in corrupt state for certain series of
in-transaction operations. This patch provides infrastructure to track
pending at-commit fsyncs for a relation and in-transaction
truncations. heap_register_sync() should be used to start tracking
before batch operations like COPY and CLUSTER, and use
BufferNeedsWAL() instead of RelationNeedsWAL() at the places related
to WAL-logging about heap-modifying operations.
---
src/backend/access/heap/heapam.c | 31 +++
src/backend/access/transam/xact.c | 11 +
src/backend/catalog/storage.c | 418 ++++++++++++++++++++++++++++++++++--
src/backend/commands/tablecmds.c | 4 +-
src/backend/storage/buffer/bufmgr.c | 39 +++-
src/backend/utils/cache/relcache.c | 3 +
src/include/access/heapam.h | 1 +
src/include/catalog/storage.h | 8 +
src/include/storage/bufmgr.h | 2 +
src/include/utils/rel.h | 8 +
10 files changed, 493 insertions(+), 32 deletions(-)
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index c6e71dba6b..5a8627507f 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -51,6 +51,7 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
+#include "catalog/storage.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port/atomics.h"
@@ -8800,3 +8801,33 @@ heap_mask(char *pagedata, BlockNumber blkno)
}
}
}
+
+/*
+ * heap_register_sync - register a heap to be synced to disk at commit
+ *
+ * This can be used to skip WAL-logging changes on a relation file that has
+ * been created in the same transaction. This makes note of the current size of
+ * the relation, and ensures that when the relation is extended, any changes
+ * to the new blocks in the heap, in the same transaction, will not be
+ * WAL-logged. Instead, the heap contents are flushed to disk at commit,
+ * like heap_sync() does.
+ *
+ * This does the same for the TOAST heap, if any. Indexes are not affected.
+ */
+void
+heap_register_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ RecordWALSkipping(rel);
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ RecordWALSkipping(toastrel);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index c3214d4f4d..ad7cb3bcb9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -2022,6 +2022,9 @@ CommitTransaction(void)
/* close large objects before lower-level cleanup */
AtEOXact_LargeObject(true);
+ /* Flush updates to relations that we didn't WAL-logged */
+ smgrDoPendingSyncs(true);
+
/*
* Mark serializable transaction as complete for predicate locking
* purposes. This should be done as late as we can put it and still allow
@@ -2254,6 +2257,9 @@ PrepareTransaction(void)
/* close large objects before lower-level cleanup */
AtEOXact_LargeObject(true);
+ /* Flush updates to relations that we didn't WAL-logged */
+ smgrDoPendingSyncs(true);
+
/*
* Mark serializable transaction as complete for predicate locking
* purposes. This should be done as late as we can put it and still allow
@@ -2579,6 +2585,7 @@ AbortTransaction(void)
AtAbort_Notify();
AtEOXact_RelationMap(false, is_parallel_worker);
AtAbort_Twophase();
+ smgrDoPendingSyncs(false); /* abandon pending syncs */
/*
* Advertise the fact that we aborted in pg_xact (assuming that we got as
@@ -4097,6 +4104,8 @@ ReleaseSavepoint(const char *name)
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("savepoint \"%s\" does not exist within current savepoint level", name)));
+ smgrProcessWALRequirementInval(s->subTransactionId, true);
+
/*
* Mark "commit pending" all subtransactions up to the target
* subtransaction. The actual commits will happen when control gets to
@@ -4206,6 +4215,8 @@ RollbackToSavepoint(const char *name)
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("savepoint \"%s\" does not exist within current savepoint level", name)));
+ smgrProcessWALRequirementInval(s->subTransactionId, false);
+
/*
* Mark "abort pending" all subtransactions up to the target
* subtransaction. The actual aborts will happen when control gets to
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 0302507e6f..be37174ef2 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -27,7 +27,7 @@
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "storage/freespace.h"
-#include "storage/smgr.h"
+#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -62,6 +62,58 @@ typedef struct PendingRelDelete
static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
+/*
+ * We also track relation files (RelFileNode values) that have been created
+ * in the same transaction, and that have been modified without WAL-logging
+ * the action (an optimization possible with wal_level=minimal). When we are
+ * about to skip WAL-logging, a RelWalRequirement entry is created, and
+ * 'skip_wal_min_blk' is set to the current size of the relation. Any operations
+ * on blocks < skip_wal_min_blk need to be WAL-logged as usual, but for
+ * operations on higher blocks, WAL-logging is skipped.
+
+ *
+ * NB: after WAL-logging has been skipped for a block, we must not WAL-log
+ * any subsequent actions on the same block either. Replaying the WAL record
+ * of the subsequent action might fail otherwise, as the "before" state of
+ * the block might not match, as the earlier actions were not WAL-logged.
+ * Likewise, after we have WAL-logged an operation for a block, we must
+ * WAL-log any subsequent operations on the same page as well. Replaying
+ * a possible full-page-image from the earlier WAL record would otherwise
+ * revert the page to the old state, even if we sync the relation at end
+ * of transaction.
+ *
+ * If a relation is truncated (without creating a new relfilenode), and we
+ * emit a WAL record of the truncation, we can't skip WAL-logging for any
+ * of the truncated blocks anymore, as replaying the truncation record will
+ * destroy all the data inserted after that. But if we have already decided
+ * to skip WAL-logging changes to a relation, and the relation is truncated,
+ * we don't need to WAL-log the truncation either.
+ *
+ * This mechanism is currently only used by heaps. Indexes are always
+ * WAL-logged. Also, this only applies for wal_level=minimal; with higher
+ * WAL levels we need the WAL for PITR/replication anyway.
+ */
+typedef struct RelWalRequirement
+{
+ RelFileNode relnode; /* relation created in same xact */
+ bool forks[MAX_FORKNUM + 1]; /* target forknums */
+ BlockNumber skip_wal_min_blk; /* WAL-logging skipped for blocks >=
+ * skip_wal_min_blk */
+ BlockNumber wal_log_min_blk; /* The minimum blk number that requires
+ * WAL-logging even if skipped by the
+ * above*/
+ SubTransactionId create_sxid; /* subxid where this entry is created */
+ SubTransactionId invalidate_sxid; /* subxid where this entry is
+ * invalidated */
+} RelWalRequirement;
+
+/* Relations that need to be fsync'd at commit */
+static HTAB *walRequirements = NULL;
+
+static RelWalRequirement *getWalRequirementEntry(Relation rel, bool create);
+static RelWalRequirement *getWalRequirementEntryRNode(RelFileNode *node,
+ bool create);
+
/*
* RelationCreateStorage
* Create physical storage for a relation.
@@ -259,37 +311,290 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
*/
if (RelationNeedsWAL(rel))
{
- /*
- * Make an XLOG entry reporting the file truncation.
- */
- XLogRecPtr lsn;
- xl_smgr_truncate xlrec;
+ RelWalRequirement *walreq;
- xlrec.blkno = nblocks;
- xlrec.rnode = rel->rd_node;
- xlrec.flags = SMGR_TRUNCATE_ALL;
+ /* get pending sync entry, create if not yet */
+ walreq = getWalRequirementEntry(rel, true);
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+ if (walreq->skip_wal_min_blk == InvalidBlockNumber ||
+ walreq->skip_wal_min_blk < nblocks)
+ {
+ /*
+ * This is the first time truncation of this relation in this
+ * transaction or truncation that leaves pages that need at-commit
+ * fsync. Make an XLOG entry reporting the file truncation.
+ */
+ XLogRecPtr lsn;
+ xl_smgr_truncate xlrec;
- lsn = XLogInsert(RM_SMGR_ID,
- XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
+ xlrec.blkno = nblocks;
+ xlrec.rnode = rel->rd_node;
+ xlrec.flags = SMGR_TRUNCATE_ALL;
- /*
- * Flush, because otherwise the truncation of the main relation might
- * hit the disk before the WAL record, and the truncation of the FSM
- * or visibility map. If we crashed during that window, we'd be left
- * with a truncated heap, but the FSM or visibility map would still
- * contain entries for the non-existent heap pages.
- */
- if (fsm || vm)
- XLogFlush(lsn);
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+ lsn = XLogInsert(RM_SMGR_ID,
+ XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
+
+ /*
+ * Flush, because otherwise the truncation of the main relation
+ * might hit the disk before the WAL record, and the truncation of
+ * the FSM or visibility map. If we crashed during that window,
+ * we'd be left with a truncated heap, but the FSM or visibility
+ * map would still contain entries for the non-existent heap
+ * pages.
+ */
+ if (fsm || vm)
+ XLogFlush(lsn);
+
+ /* no longer skip WAL-logging for the blocks */
+ rel->rd_walrequirement->wal_log_min_blk = nblocks;
+ }
}
/* Do the real work */
smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks);
}
+/*
+ * Do changes to given heap page need to be WAL-logged?
+ *
+ * This takes into account any previous RecordPendingSync() requests.
+ *
+ * Note that it is required to check this before creating any WAL records for
+ * heap pages - it is not merely an optimization! WAL-logging a record, when
+ * we have already skipped a previous WAL record for the same page could lead
+ * to failure at WAL replay, as the "before" state expected by the record
+ * might not match what's on disk. Also, if the heap was truncated earlier, we
+ * must WAL-log any changes to the once-truncated blocks, because replaying
+ * the truncation record will destroy them.
+ */
+bool
+BufferNeedsWAL(Relation rel, Buffer buf)
+{
+ BlockNumber blkno = InvalidBlockNumber;
+ RelWalRequirement *walreq;
+
+ if (!RelationNeedsWAL(rel))
+ return false;
+
+ /* fetch existing pending sync entry */
+ walreq = getWalRequirementEntry(rel, false);
+
+ /*
+ * no point in doing further work if we know that we don't have special
+ * WAL requirement
+ */
+ if (!walreq)
+ return true;
+
+ Assert(BufferIsValid(buf));
+
+ blkno = BufferGetBlockNumber(buf);
+
+ /*
+ * We don't skip WAL-logging for pages that once done.
+ */
+ if (walreq->skip_wal_min_blk == InvalidBlockNumber ||
+ walreq->skip_wal_min_blk > blkno)
+ return true;
+
+ /*
+ * we don't skip WAL-logging for blocks that have got WAL-logged
+ * truncation
+ */
+ if (walreq->wal_log_min_blk != InvalidBlockNumber &&
+ walreq->wal_log_min_blk <= blkno)
+ return true;
+
+ return false;
+}
+
+bool
+BlockNeedsWAL(Relation rel, BlockNumber blkno)
+{
+ RelWalRequirement *walreq;
+
+ if (!RelationNeedsWAL(rel))
+ return false;
+
+ /* fetch exising pending sync entry */
+ walreq = getWalRequirementEntry(rel, false);
+
+ /*
+ * no point in doing further work if we know that we don't have special
+ * WAL requirement
+ */
+ if (!walreq)
+ return true;
+
+ /*
+ * We don't skip WAL-logging for pages that once done.
+ */
+ if (walreq->skip_wal_min_blk == InvalidBlockNumber ||
+ walreq->skip_wal_min_blk > blkno)
+ return true;
+
+ /*
+ * we don't skip WAL-logging for blocks that have got WAL-logged
+ * truncation
+ */
+ if (walreq->wal_log_min_blk != InvalidBlockNumber &&
+ walreq->wal_log_min_blk <= blkno)
+ return true;
+
+ return false;
+}
+
+/*
+ * Remember that the given relation doesn't need WAL-logging for the blocks
+ * after the current block size and fo the blocks that are going to be synced
+ * at commit.
+ */
+void
+RecordWALSkipping(Relation rel)
+{
+ RelWalRequirement *walreq;
+
+ Assert(RelationNeedsWAL(rel));
+
+ /* get pending sync entry, create if not yet */
+ walreq = getWalRequirementEntry(rel, true);
+
+ /*
+ * Record only the first registration.
+ */
+ if (walreq->skip_wal_min_blk != InvalidBlockNumber)
+ return;
+
+ walreq->skip_wal_min_blk = RelationGetNumberOfBlocks(rel);
+}
+
+/*
+ * Record commit-time file sync. This shouldn't be used mixing with
+ * RecordWALSkipping.
+ */
+void
+RecordPendingSync(SMgrRelation rel, ForkNumber forknum)
+{
+ RelWalRequirement *walreq;
+
+ walreq = getWalRequirementEntryRNode(&rel->smgr_rnode.node, true);
+ walreq->forks[forknum] = true;
+ walreq->skip_wal_min_blk = 0;
+}
+
+/*
+ * RelationInvalidateWALRequirements() -- invalidate wal requirement entry
+ */
+void
+RelationInvalidateWALRequirements(Relation rel)
+{
+ RelWalRequirement *walreq;
+
+ /* we know we don't have one */
+ if (rel->rd_nowalrequirement)
+ return;
+
+ walreq = getWalRequirementEntry(rel, false);
+
+ if (!walreq)
+ return;
+
+ /*
+ * The state is reset at subtransaction commit/abort. No invalidation
+ * request must not come for the same relation in the same subtransaction.
+ */
+ Assert(walreq->invalidate_sxid == InvalidSubTransactionId);
+
+ walreq->invalidate_sxid = GetCurrentSubTransactionId();
+}
+
+/*
+ * getWalRequirementEntry: get WAL requirement entry.
+ *
+ * Returns WAL requirement entry for the relation. The entry tracks
+ * WAL-skipping blocks for the relation. The WAL-skipped blocks need fsync at
+ * commit time. Creates one if needed when create is true.
+ */
+static RelWalRequirement *
+getWalRequirementEntry(Relation rel, bool create)
+{
+ RelWalRequirement *walreq_entry = NULL;
+
+ if (rel->rd_walrequirement)
+ return rel->rd_walrequirement;
+
+ /* we know we don't have pending sync entry */
+ if (!create && rel->rd_nowalrequirement)
+ return NULL;
+
+ walreq_entry = getWalRequirementEntryRNode(&rel->rd_node, create);
+
+ if (!walreq_entry)
+ {
+ /* prevent further hash lookup */
+ rel->rd_nowalrequirement = true;
+ return NULL;
+ }
+
+ walreq_entry->forks[MAIN_FORKNUM] = true;
+
+ /* hold shortcut in Relation */
+ rel->rd_nowalrequirement = false;
+ rel->rd_walrequirement = walreq_entry;
+
+ return walreq_entry;
+}
+
+/*
+ * getWalRequirementEntryRNode: get WAL requirement entry by rnode
+ *
+ * Returns WAL requirement entry for the RelFileNode.
+ */
+static RelWalRequirement *
+getWalRequirementEntryRNode(RelFileNode *rnode, bool create)
+{
+ RelWalRequirement *walreq_entry = NULL;
+ bool found;
+
+ if (!walRequirements)
+ {
+ /* First time through: initialize the hash table */
+ HASHCTL ctl;
+
+ if (!create)
+ return NULL;
+
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(RelFileNode);
+ ctl.entrysize = sizeof(RelWalRequirement);
+ ctl.hash = tag_hash;
+ walRequirements = hash_create("pending relation sync table", 5,
+ &ctl, HASH_ELEM | HASH_FUNCTION);
+ }
+
+ walreq_entry = (RelWalRequirement *)
+ hash_search(walRequirements, (void *) rnode,
+ create ? HASH_ENTER: HASH_FIND, &found);
+
+ if (!walreq_entry)
+ return NULL;
+
+ /* new entry created */
+ if (!found)
+ {
+ memset(&walreq_entry->forks, 0, sizeof(sizeof(walreq_entry->forks)));
+ walreq_entry->wal_log_min_blk = InvalidBlockNumber;
+ walreq_entry->skip_wal_min_blk = InvalidBlockNumber;
+ walreq_entry->create_sxid = GetCurrentSubTransactionId();
+ walreq_entry->invalidate_sxid = InvalidSubTransactionId;
+ }
+
+ return walreq_entry;
+}
+
/*
* smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
*
@@ -418,6 +723,75 @@ smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr)
return nrels;
}
+/*
+ * Sync to disk any relations that we have skipped WAL-logging earlier.
+ */
+void
+smgrDoPendingSyncs(bool isCommit)
+{
+ if (!walRequirements)
+ return;
+
+ if (isCommit)
+ {
+ HASH_SEQ_STATUS status;
+ RelWalRequirement *walreq;
+
+ hash_seq_init(&status, walRequirements);
+
+ while ((walreq = hash_seq_search(&status)) != NULL)
+ {
+ if (walreq->skip_wal_min_blk != InvalidBlockNumber &&
+ walreq->invalidate_sxid == InvalidSubTransactionId)
+ {
+ int f;
+
+ FlushRelationBuffersWithoutRelCache(walreq->relnode, false);
+
+ /* flush all requested forks */
+ for (f = MAIN_FORKNUM ; f <= MAX_FORKNUM ; f++)
+ {
+ if (walreq->forks[f])
+ smgrimmedsync(smgropen(walreq->relnode,
+ InvalidBackendId), f);
+ }
+ }
+ }
+ }
+
+ hash_destroy(walRequirements);
+ walRequirements = NULL;
+}
+
+/*
+ * Process pending invalidation of WAL requirements happened in the
+ * subtransaction
+ */
+void
+smgrProcessWALRequirementInval(SubTransactionId sxid, bool isCommit)
+{
+ HASH_SEQ_STATUS status;
+ RelWalRequirement *walreq;
+
+ if (!walRequirements)
+ return;
+
+ /* We expect that we don't have walRequirements in almost all cases */
+ hash_seq_init(&status, walRequirements);
+
+ while ((walreq = hash_seq_search(&status)) != NULL)
+ {
+ /* remove useless entry */
+ if (isCommit ?
+ walreq->invalidate_sxid == sxid :
+ walreq->create_sxid == sxid)
+ hash_search(walRequirements, &walreq->relnode, HASH_REMOVE, NULL);
+ /* or cancel invalidation */
+ else if (!isCommit && walreq->invalidate_sxid == sxid)
+ walreq->invalidate_sxid = InvalidSubTransactionId;
+ }
+}
+
/*
* PostPrepare_smgr -- Clean up after a successful PREPARE
*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3183b2aaa1..c9a0e02168 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -11587,11 +11587,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
/*
* Create and copy all forks of the relation, and schedule unlinking of
- * old physical files.
+ * old physical files. WAL requirements for the old node is no longer
+ * needed.
*
* NOTE: any conflict in relfilenode value will be caught in
* RelationCreateStorage().
*/
+ RelationInvalidateWALRequirements(rel);
RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
/* copy main fork */
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 273e2f385f..f00826712a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -451,6 +451,7 @@ static BufferDesc *BufferAlloc(SMgrRelation smgr,
BufferAccessStrategy strategy,
bool *foundPtr);
static void FlushBuffer(BufferDesc *buf, SMgrRelation reln);
+static void FlushRelationBuffers_common(SMgrRelation smgr, bool islocal);
static void AtProcExit_Buffers(int code, Datum arg);
static void CheckForBufferLeaks(void);
static int rnode_comparator(const void *p1, const void *p2);
@@ -3153,20 +3154,40 @@ PrintPinnedBufs(void)
void
FlushRelationBuffers(Relation rel)
{
- int i;
- BufferDesc *bufHdr;
-
/* Open rel at the smgr level if not already done */
RelationOpenSmgr(rel);
- if (RelationUsesLocalBuffers(rel))
+ FlushRelationBuffers_common(rel->rd_smgr, RelationUsesLocalBuffers(rel));
+}
+
+/*
+ * Like FlushRelationBuffers(), but the relation is specified by RelFileNode
+ */
+void
+FlushRelationBuffersWithoutRelCache(RelFileNode rnode, bool islocal)
+{
+ FlushRelationBuffers_common(smgropen(rnode, InvalidBackendId), islocal);
+}
+
+/*
+ * Code shared between functions FlushRelationBuffers() and
+ * FlushRelationBuffersWithoutRelCache().
+ */
+static void
+FlushRelationBuffers_common(SMgrRelation smgr, bool islocal)
+{
+ RelFileNode rnode = smgr->smgr_rnode.node;
+ int i;
+ BufferDesc *bufHdr;
+
+ if (islocal)
{
for (i = 0; i < NLocBuffer; i++)
{
uint32 buf_state;
bufHdr = GetLocalBufferDescriptor(i);
- if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) &&
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
((buf_state = pg_atomic_read_u32(&bufHdr->state)) &
(BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY))
{
@@ -3183,7 +3204,7 @@ FlushRelationBuffers(Relation rel)
PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
- smgrwrite(rel->rd_smgr,
+ smgrwrite(smgr,
bufHdr->tag.forkNum,
bufHdr->tag.blockNum,
localpage,
@@ -3213,18 +3234,18 @@ FlushRelationBuffers(Relation rel)
* As in DropRelFileNodeBuffers, an unlocked precheck should be safe
* and saves some cycles.
*/
- if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node))
+ if (!RelFileNodeEquals(bufHdr->tag.rnode, rnode))
continue;
ReservePrivateRefCountEntry();
buf_state = LockBufHdr(bufHdr);
- if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) &&
+ if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
(buf_state & (BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY))
{
PinBuffer_Locked(bufHdr);
LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED);
- FlushBuffer(bufHdr, rel->rd_smgr);
+ FlushBuffer(bufHdr, smgr);
LWLockRelease(BufferDescriptorGetContentLock(bufHdr));
UnpinBuffer(bufHdr, true);
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 84609e0725..95e834d45e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -75,6 +75,7 @@
#include "partitioning/partdesc.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rowsecurity.h"
+#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/array.h"
@@ -5625,6 +5626,8 @@ load_relcache_init_file(bool shared)
rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
rel->rd_amcache = NULL;
MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info));
+ rel->rd_nowalrequirement = false;
+ rel->rd_walrequirement = NULL;
/*
* Recompute lock and physical addressing info. This is needed in
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 3773a4df85..3d4fb7f3c3 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -172,6 +172,7 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
+extern void heap_register_sync(Relation relation);
extern void heap_sync(Relation relation);
/* in heap/pruneheap.c */
diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h
index 9f638be924..9034465001 100644
--- a/src/include/catalog/storage.h
+++ b/src/include/catalog/storage.h
@@ -16,12 +16,18 @@
#include "storage/block.h"
#include "storage/relfilenode.h"
+#include "storage/smgr.h"
#include "utils/relcache.h"
extern void RelationCreateStorage(RelFileNode rnode, char relpersistence);
extern void RelationDropStorage(Relation rel);
extern void RelationPreserveStorage(RelFileNode rnode, bool atCommit);
extern void RelationTruncate(Relation rel, BlockNumber nblocks);
+extern bool BufferNeedsWAL(Relation rel, Buffer buf);
+extern bool BlockNeedsWAL(Relation rel, BlockNumber blkno);
+extern void RecordWALSkipping(Relation rel);
+extern void RecordPendingSync(SMgrRelation rel, ForkNumber forknum);
+extern void RelationInvalidateWALRequirements(Relation rel);
/*
* These functions used to be in storage/smgr/smgr.c, which explains the
@@ -29,6 +35,8 @@ extern void RelationTruncate(Relation rel, BlockNumber nblocks);
*/
extern void smgrDoPendingDeletes(bool isCommit);
extern int smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr);
+extern void smgrDoPendingSyncs(bool isCommit);
+extern void smgrProcessWALRequirementInval(SubTransactionId sxid, bool isCommit);
extern void AtSubCommit_smgr(void);
extern void AtSubAbort_smgr(void);
extern void PostPrepare_smgr(void);
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index c5826f691d..8a9ea041dd 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -189,6 +189,8 @@ extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
ForkNumber forkNum);
extern void FlushOneBuffer(Buffer buffer);
extern void FlushRelationBuffers(Relation rel);
+extern void FlushRelationBuffersWithoutRelCache(RelFileNode rnode,
+ bool islocal);
extern void FlushDatabaseBuffers(Oid dbid);
extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,
ForkNumber forkNum, BlockNumber firstDelBlock);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 54028515a7..30f0d5bd83 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -198,6 +198,14 @@ typedef struct RelationData
/* use "struct" here to avoid needing to include pgstat.h: */
struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
+
+ /*
+ * rd_nowalrequirement is true if this relation is known not to have
+ * special WAL requirements. Otherwise we need to ask smgr for an entry
+ * if rd_walrequirement is NULL.
+ */
+ bool rd_nowalrequirement;
+ struct RelWalRequirement *rd_walrequirement;
} RelationData;
--
2.16.3
----Next_Part(Tue_Mar_26_16_35_07_2019_128)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v9-0005-Fix-WAL-skipping-feature.patch"
view thread (7+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH 4/8] Add infrastructure to WAL-logging skip feature
In-Reply-To: <no-message-id-1883702@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox