aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2015-12-10 16:25:12 +0100
committerAndres Freund <andres@anarazel.de>2015-12-10 16:29:27 +0100
commit3199c13fcc34ee79bc26cac17204e93cdc4a756f (patch)
treed0be941795e801d6f58dc8c9576e312a77a409c4 /src
parentf9fc8e79cd44a755a397886e791eefffacf02a2a (diff)
downloadpostgresql-3199c13fcc34ee79bc26cac17204e93cdc4a756f.tar.gz
postgresql-3199c13fcc34ee79bc26cac17204e93cdc4a756f.zip
Fix bug leading to restoring unlogged relations from empty files.
At the end of crash recovery, unlogged relations are reset to the empty state, using their init fork as the template. The init fork is copied to the main fork without going through shared buffers. Unfortunately WAL replay so far has not necessarily flushed writes from shared buffers to disk at that point. In normal crash recovery, and before the introduction of 'fast promotions' in fd4ced523 / 9.3, the END_OF_RECOVERY checkpoint flushes the buffers out in time. But with fast promotions that's not the case anymore. To fix, force WAL writes targeting the init fork to be flushed immediately (using the new FlushOneBuffer() function). In 9.5+ that flush can centrally be triggered from the code dealing with restoring full page writes (XLogReadBufferForRedoExtended), in earlier releases that responsibility is in the hands of XLOG_HEAP_NEWPAGE's replay function. Backpatch to 9.1, even if this currently is only known to trigger in 9.3+. Flushing earlier is more robust, and it is advantageous to keep the branches similar. Typical symptoms of this bug are errors like 'ERROR: index "..." contains unexpected zero page at block 0' shortly after promoting a node. Reported-By: Thom Brown Author: Andres Freund and Michael Paquier Discussion: 20150326175024.GJ451@alap3.anarazel.de Backpatch: 9.1-
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/heapam.c10
-rw-r--r--src/backend/storage/buffer/bufmgr.c21
-rw-r--r--src/include/storage/bufmgr.h1
3 files changed, 32 insertions, 0 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 1273d1201ff..4f52f05c290 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -4395,6 +4395,16 @@ heap_xlog_newpage(XLogRecPtr lsn, XLogRecord *record)
}
MarkBufferDirty(buffer);
+
+ /*
+ * At the end of crash recovery the init forks of unlogged relations are
+ * copied, without going through shared buffers. So we need to force the
+ * on-disk state of init forks to always be in sync with the state in
+ * shared buffers.
+ */
+ if (xlrec->forknum == INIT_FORKNUM)
+ FlushOneBuffer(buffer);
+
UnlockReleaseBuffer(buffer);
}
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 6ef8c247547..c06b51c9b41 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -2238,6 +2238,27 @@ FlushDatabaseBuffers(Oid dbid)
}
/*
+ * Flush a previously, shared or exclusively, locked and pinned buffer to the
+ * OS.
+ */
+void
+FlushOneBuffer(Buffer buffer)
+{
+ volatile BufferDesc *bufHdr;
+
+ /* currently not needed, but no fundamental reason not to support */
+ Assert(!BufferIsLocal(buffer));
+
+ Assert(BufferIsPinned(buffer));
+
+ bufHdr = &BufferDescriptors[buffer - 1];
+
+ LWLockHeldByMe(bufHdr->content_lock);
+
+ FlushBuffer(bufHdr, NULL);
+}
+
+/*
* ReleaseBuffer -- release the pin on a buffer
*/
void
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index eb85d6f208d..362daf471e8 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -186,6 +186,7 @@ extern void CheckPointBuffers(int flags);
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
ForkNumber forkNum);
+extern void FlushOneBuffer(Buffer buffer);
extern void FlushRelationBuffers(Relation rel);
extern void FlushDatabaseBuffers(Oid dbid);
extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,