Please consider this scenario (race conditions):
1. FlushBuffer() has written the buffer but hasn't yet managed to clear the
BM_DIRTY flag (however BM_JUST_DIRTIED could be cleared by now).
2. Another backend modified a hint bit and called MarkBufferDirtyHint().
3. In MarkBufferDirtyHint(), if XLogHintBitIsNeeded() evaluates to true
(e.g. due to checksums enabled), new LSN is computed, however it's not
assigned to the page because the buffer is still dirty:
if (!(buf_state & BM_DIRTY))
{
...
if (!XLogRecPtrIsInvalid(lsn))
PageSetLSN(page, lsn);
}
4. MarkBufferDirtyHint() completes.
5. In the first session, FlushBuffer()->TerminateBufferIO() will not clear
BM_DIRTY because MarkBufferDirtyHint() has eventually set
BM_JUST_DIRTIED. Thus the hint bit change itself will be written by the next
call of FlushBuffer(). However page LSN is hasn't been updated so the
requirement that WAL must be flushed first is not met.
I think that PageSetLSN() should be called regardless BM_DIRTY. Do I miss any
subtle detail?
--
Antonin Houska
Web: https://www.cybertec-postgresql.com