8000 Fix bug leading to restoring unlogged relations from empty files. · home201448/postgres@b19405a · GitHub
[go: up one dir, main page]

Skip to content

Commit b19405a

Browse files
committed
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 fd4ced5 / 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-
1 parent b3e377a commit b19405a

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

src/backend/access/heap/heapam.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7175,6 +7175,16 @@ heap_xlog_newpage(XLogRecPtr lsn, XLogRecord *record)
71757175
}
71767176

71777177
MarkBufferDirty(buffer);
7178+
7179+
/*
7180+
* At the end of crash recovery the init forks of unlogged relations are
7181+
* copied, without going through shared buffers. So we need to force the
7182+
* on-disk state of init forks to always be in sync with the state in
7183+
* shared buffers.
7184+
*/
7185+
if (xlrec->forknum == INIT_FORKNUM)
7186+
FlushOneBuffer(buffer);
7187+
71787188
UnlockReleaseBuffer(buffer);
71797189
}
71807190

src/backend/storage/buffer/bufmgr.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,6 +2549,27 @@ FlushDatabaseBuffers(Oid dbid)
25492549
}
25502550
}
25512551

2552+
/*
2553+
* Flush a previously, shared or exclusively, locked and pinned buffer to the
2554+
* OS.
2555+
*/
2556+
void
2557+
FlushOneBuffer(Buffer buffer)
2558+
{
2559+
volatile BufferDesc *bufHdr;
2560+
2561+
/* currently not needed, but no fundamental reason not to support */
2562+
Assert(!BufferIsLocal(buffer));
2563+
2564+
Assert(BufferIsPinned(buffer));
2565+
2566+
bufHdr = &BufferDescriptors[buffer - 1];
2567+
2568+
LWLockHeldByMe(bufHdr->content_lock);
2569+
2570+
FlushBuffer(bufHdr, NULL);
2571+
}
2572+
25522573
/*
25532574
* ReleaseBuffer -- release the pin on a buffer
25542575
*/

src/include/storage/bufmgr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ extern void CheckPointBuffers(int flags);
191191
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
192192
extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
193193
ForkNumber forkNum);
194+
extern void FlushOneBuffer(Buffer buffer);
194195
extern void FlushRelationBuffers(Relation rel);
195196
extern void FlushDatabaseBuffers(Oid dbid);
196197
extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,

0 commit comments

Comments
 (0)
0