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