Thread
-
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-11-25T11:28:49Z
Hi all, I applied the v11 patchset on my 19devel source code without any modifications and ran some rounds of pgbench for testing. I ran multiple workloads with different client counts and it executed successfully without any backend crashes. I am including the observations herewith for further review. 1. pgbench -c 4 -j 4 -T 10 -p 55432 postgres pgbench (19devel) starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 50 query mode: simple number of clients: 4 number of threads: 4 maximum number of tries: 1 duration: 10 s number of transactions actually processed: 10103 number of failed transactions: 0 (0.000%) latency average = 3.958 ms initial connection time = 4.970 ms tps = 1010.552537 (without initial connection time) 2. pgbench -c 8 -j 8 -T 120 -p 55432 postgres pgbench (19devel) starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 50 query mode: simple number of clients: 8 number of threads: 8 maximum number of tries: 1 duration: 120 s number of transactions actually processed: 193468 number of failed transactions: 0 (0.000%) latency average = 4.962 ms initial connection time = 8.135 ms tps = 1612.271773 (without initial connection time) 3. pgbench -c 16 -j 8 -T 180 -p 55432 postgres pgbench (19devel) starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 50 query mode: simple number of clients: 16 number of threads: 8 maximum number of tries: 1 duration: 180 s number of transactions actually processed: 598737 number of failed transactions: 0 (0.000%) latency average = 4.810 ms initial connection time = 13.793 ms tps = 3326.398071 (without initial connection time) I will continue testing with more workloads including forced checkpoints to see if it can trigger any edge cases or WAL flush decisions. But for now, the v11 series appears stable on my system. Looking forward to more feedback. Regards, Soumya
-
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-12-08T07:14:33Z
Hi all, Based on the ongoing discussions on Checkpointer Write Combining [1] and the last patch v11 discussed in forum, I have re-implemented the logic for CleanVictimBuffer() and BufferNeedsWALFlush() with a few changes: static void CleanVictimBuffer(BufferDesc *bufdesc, bool from_ring, IOContext io_context) { XLogRecPtr max_lsn = InvalidXLogRecPtr; LWLock *content_lock; content_lock = BufHdrGetContentLock(bufdesc); LWLockAcquire(content_lock, LW_SHARED); if (!PrepareFlushBuffer(bufdesc, &max_lsn)) { LWLockRelease(content_lock); return; } LWLockRelease(content_lock); LWLockAcquire(content_lock, LW_EXCLUSIVE); if (!PrepareFlushBuffer(bufdesc, &max_lsn)) { LWLockRelease(content_lock); return; } BufferSetStateWriteInProgress(bufdesc); LWLockRelease(content_lock); RecordBufferForBatch(bufdesc, max_lsn); } Here I added some extra safety checks and early-exit handling, making sure the function returns cleanly when a buffer no longer needs writing and ensuring locks are handled correctly. static bool BufferNeedsWALFlush(BufferDesc *bufdesc, XLogRecPtr *lsn) { uint32 buf_state; buf_state = LockBufHdr(bufdesc); if (buf_state & BM_PERMANENT) *lsn = BufferGetLSN(bufdesc); else *lsn = InvalidXLogRecPtr; UnlockBufHdr(bufdesc); if (!(*lsn != InvalidXLogRecPtr)) return false; return XLogNeedsFlush(*lsn); } Here I made some changes in the logic of the function that only returns a real LSN when the buffer actually needs a WAL flush. If the buffer is not permanent or doesn’t need WAL, it returns false and set the LSN to InvalidXLogRecPtr, by which it can avoid confusions or unsafe downstream behaviors. I am now focusing on the PageSetBatchChecksumInplace() as mentioned in [1] and the write combining logic. I will update my implementations soon once I complete it. I hope these corrected versions of functions will be helpful in further implementation. Looking forward to more feedback. Regards, Soumya Reference: Discussion [1] https://www.postgresql.org/message-id/CAAKRu_ZiEpE_EHww3S3-E3iznybdnX8mXSO7Wsuru7=P9Y=czQ@mail.gmail.com -
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-12-15T09:37:00Z
Hi all, With reference to the last patches (v11) I received [1] and while reviewing Melanie’s latest feedback, I understood that PageSetBatchChecksumInplace() is currently WIP and depends on upcoming changes to hint-bit locking. It will be contrary to the flow if I propose new functional changes to checksum batching at this time. So for now I will focus on preparatory or documentation improvements until I get the updates on dependencies. Regarding my patch attached, the patch introduces write-combining during checkpoints by batching contiguous buffers and allowing them to be written using vectorized I/O. My patch includes write-combining for checkpoint buffer flushes, contiguous buffer batching, Preserved WAL ordering, locking, and buffer state invariants. The change is currently limited to the checkpointer path (BufferSync()). So far I tested my implementation and found that all the regression (233 tests) and isolation tests (121 tests) got passed, the manual pgbench validation completed successfully and also verified pg_stat_bgwriter counters before and after checkpoints. So far the implementation is stable in my system. And currently, I am focussing on the implementation of check pointer write combining for bgwriter behavior and will update it soon after validating my implementation. I hope my findings and approach will be helpful in further implementations and I would appreciate feedback on my approach. Regards, Soumya Reference: [1] https://www.postgresql.org/message-id/flat/CAAKRu_ZiEpE_EHww3S3-E3iznybdnX8mXSO7Wsuru7%3DP9Y%3DczQ%40mail.gmail.com#52e4f67645f26609427d5b1fc31fdf08
-
Re: Checkpointer write combining
Melanie Plageman <melanieplageman@gmail.com> — 2025-12-15T21:48:40Z
On Mon, Dec 15, 2025 at 4:36 AM Soumya S Murali <soumyamurali.work@gmail.com> wrote: > > With reference to the last patches (v11) I received [1] and while reviewing Melanie’s latest feedback, I understood that PageSetBatchChecksumInplace() is currently WIP and depends on upcoming changes to hint-bit locking. It will be contrary to the flow if I propose new functional changes to checksum batching at this time. So for now I will focus on preparatory or documentation improvements until I get the updates on dependencies. > Regarding my patch attached, the patch introduces write-combining during checkpoints by batching contiguous buffers and allowing them to be written using vectorized I/O. My patch includes write-combining for checkpoint buffer flushes, contiguous buffer batching, Preserved WAL ordering, locking, and buffer state invariants. The change is currently limited to the checkpointer path (BufferSync()). So far I tested my implementation and found that all the regression (233 tests) and isolation tests (121 tests) got passed, the manual pgbench validation completed successfully and also verified pg_stat_bgwriter counters before and after checkpoints. So far the implementation is stable in my system. Can you explain how your implementation differs from what was posted in v11 0006 [1]? That implements checkpointer write combining. I'm open to ideas for improving the code, but I don't understand how your patch is supposed to fit into the ongoing work on this thread. - Melanie [1] https://www.postgresql.org/message-id/CAAKRu_ZiEpE_EHww3S3-E3iznybdnX8mXSO7Wsuru7%3DP9Y%3DczQ%40mail.gmail.com
-
Re: Checkpointer write combining
Melanie Plageman <melanieplageman@gmail.com> — 2025-12-15T21:50:31Z
On Mon, Dec 8, 2025 at 2:14 AM Soumya S Murali <soumyamurali.work@gmail.com> wrote: > > Here I made some changes in the logic of the function that only returns a real LSN when the buffer actually needs a WAL flush. If the buffer is not permanent or doesn’t need WAL, it returns false and set the LSN to InvalidXLogRecPtr, by which it can avoid confusions or unsafe downstream behaviors. Thanks for the suggestions. It would be best if you attached a patch instead of pasting it in the email like this with no formatting (and no diff). It is very hard to tell what you've changed. - Melanie
-
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-12-17T04:46:48Z
Hi all, On Tue, Dec 16, 2025 at 3:20 AM Melanie Plageman <melanieplageman@gmail.com> wrote: > > On Mon, Dec 8, 2025 at 2:14 AM Soumya S Murali > <soumyamurali.work@gmail.com> wrote: > > > > Here I made some changes in the logic of the function that only returns a real LSN when the buffer actually needs a WAL flush. If the buffer is not permanent or doesn’t need WAL, it returns false and set the LSN to InvalidXLogRecPtr, by which it can avoid confusions or unsafe downstream behaviors. > > Thanks for the suggestions. It would be best if you attached a patch > instead of pasting it in the email like this with no formatting (and > no diff). It is very hard to tell what you've changed. > > - Melanie Thank you for the clarification. I understand the concern, and apologies for the earlier confusion. My intention is to stay aligned with the direction of your v11 series, so this patch contains only small, incremental changes intended to be easy to review and not conflict with ongoing work. My previous message was only meant to summarize logic I was experimenting with locally, not to propose it as a final patch. Based on the feedback, I revisited the implementation, made the necessary modifications, and verified the updated logic. I am attaching the patch for review. It has been tested with make check and make -C src/test/isolation check. Thank you again for the guidance. I hope this is helpful for the ongoing work, and I am looking forward to further feedback. Regards Soumya
-
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-12-17T05:53:08Z
Hi all, On Tue, Dec 16, 2025 at 3:18 AM Melanie Plageman <melanieplageman@gmail.com> wrote: > > On Mon, Dec 15, 2025 at 4:36 AM Soumya S Murali > <soumyamurali.work@gmail.com> wrote: > > > > With reference to the last patches (v11) I received [1] and while reviewing Melanie’s latest feedback, I understood that PageSetBatchChecksumInplace() is currently WIP and depends on upcoming changes to hint-bit locking. It will be contrary to the flow if I propose new functional changes to checksum batching at this time. So for now I will focus on preparatory or documentation improvements until I get the updates on dependencies. > > Regarding my patch attached, the patch introduces write-combining during checkpoints by batching contiguous buffers and allowing them to be written using vectorized I/O. My patch includes write-combining for checkpoint buffer flushes, contiguous buffer batching, Preserved WAL ordering, locking, and buffer state invariants. The change is currently limited to the checkpointer path (BufferSync()). So far I tested my implementation and found that all the regression (233 tests) and isolation tests (121 tests) got passed, the manual pgbench validation completed successfully and also verified pg_stat_bgwriter counters before and after checkpoints. So far the implementation is stable in my system. > > Can you explain how your implementation differs from what was posted > in v11 0006 [1]? That implements checkpointer write combining. I'm > open to ideas for improving the code, but I don't understand how your > patch is supposed to fit into the ongoing work on this thread. > > - Melanie > > [1] https://www.postgresql.org/message-id/CAAKRu_ZiEpE_EHww3S3-E3iznybdnX8mXSO7Wsuru7%3DP9Y%3DczQ%40mail.gmail.com Thank you for the question. My patch is not intended to replace or redesign v11-0006. I am fully aligned with that patch and treated it as the baseline for my work. The work I sent is intentionally incremental, rather than introducing a new batching logic. v11-0006 already implements the core checkpointer write-combining logic (batch formation, contiguity checks, WAL ordering, pin limits, and IO issuance). I did not change that structure. My changes focus on correctness around existing CleanVictimBuffer() ensuring content locks are always released on early exit paths and making the shared exclusive lock transitions explicit. This directly addresses the lock-handling issue you pointed out earlier in the thread. And the BufferNeedsWALFlush() clarifying semantics so that an LSN is only returned when the buffer is logged (BM_PERMANENT), otherwise explicitly setting it to InvalidXLogRecPtr. This matches the direction you mentioned about avoiding confusing or unsafe LSN propagation. I intentionally did not modify PageSetBatchChecksumInplace(), since it is clearly marked WIP and depended on the hint-bit locking work as you mentioned. I just validated the v11-0006 design on a fresh tree, done a few small correctness cleanups that do not alter behavior and done the testing like make check, isolation tests and manual checkpoint validation for confirmation. I hope you find this useful. Thank you for the guidance and patience. Looking forward to more feedback. Regards, Soumya
-
Re: Checkpointer write combining
Melanie Plageman <melanieplageman@gmail.com> — 2025-12-17T15:39:05Z
On Tue, Dec 16, 2025 at 11:46 PM Soumya S Murali <soumyamurali.work@gmail.com> wrote: > > Thank you for the clarification. > I understand the concern, and apologies for the earlier confusion. My > intention is to stay aligned with the direction of your v11 series, so > this patch contains only small, incremental changes intended to be > easy to review and not conflict with ongoing work. > My previous message was only meant to summarize logic I was > experimenting with locally, not to propose it as a final patch. Based > on the feedback, I revisited the implementation, made the necessary > modifications, and verified the updated logic. > I am attaching the patch for review. It has been tested with make > check and make -C src/test/isolation check. > Thank you again for the guidance. I hope this is helpful for the > ongoing work, and I am looking forward to further feedback. I took a look at your patch and the first two parts were already fixed in a previous version --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -4405,7 +4405,7 @@ BufferNeedsWALFlush(BufferDesc *bufdesc, XLogRecPtr *lsn) UnlockBufHdr(bufdesc); /* Skip all WAL flush logic if relation is not logged */ - if (!(*lsn != InvalidXLogRecPtr)) + if (!XLogRecPtrIsValid(*lsn)) return false; @@ -4433,6 +4433,12 @@ CleanVictimBuffer(BufferDesc *bufdesc, bool from_ring, IOContext io_context) content_lock = BufHdrGetContentLock(bufdesc); LWLockAcquire(content_lock, LW_SHARED); + if (!PrepareFlushBuffer(bufdesc, &max_lsn)) + { + LWLockRelease(content_lock); + return; + } + And I don't understand the last parts of the diff. @@ -4440,19 +4446,14 @@ CleanVictimBuffer(BufferDesc *bufdesc, bool from_ring, IOContext io_context) LWLockRelease(content_lock); LWLockAcquire(content_lock, LW_EXCLUSIVE); - /* - * Mark the buffer ready for checksum and write. - */ - PrepareBufferForCheckpoint(bufdesc, &max_lsn); + if (!PrepareFlushBuffer(bufdesc, &max_lsn)) + { + LWLockRelease(content_lock); + return; + } /* Release exclusive lock; the batch will write the page later */ LWLockRelease(content_lock); - - /* - * Add LSN to caller's batch tracking. - * Caller handles XLogFlush() using highest LSN. - */ - PrepareBufferForCheckpoint(bufdesc, max_lsn); } AFAIK, there has never been a function called PrepareBufferForCheckpoint() and my current patchset doesn't have it either. - Melanie -
Re: Checkpointer write combining
Soumya S Murali <soumyamurali.work@gmail.com> — 2025-12-29T10:07:40Z
Hi all, On Wed, Dec 17, 2025 at 9:09 PM Melanie Plageman <melanieplageman@gmail.com> wrote: > > On Tue, Dec 16, 2025 at 11:46 PM Soumya S Murali > <soumyamurali.work@gmail.com> wrote: > > > > Thank you for the clarification. > > I understand the concern, and apologies for the earlier confusion. My > > intention is to stay aligned with the direction of your v11 series, so > > this patch contains only small, incremental changes intended to be > > easy to review and not conflict with ongoing work. > > My previous message was only meant to summarize logic I was > > experimenting with locally, not to propose it as a final patch. Based > > on the feedback, I revisited the implementation, made the necessary > > modifications, and verified the updated logic. > > I am attaching the patch for review. It has been tested with make > > check and make -C src/test/isolation check. > > Thank you again for the guidance. I hope this is helpful for the > > ongoing work, and I am looking forward to further feedback. > > I took a look at your patch and the first two parts were already fixed > in a previous version > > --- a/src/backend/storage/buffer/bufmgr.c > +++ b/src/backend/storage/buffer/bufmgr.c > @@ -4405,7 +4405,7 @@ BufferNeedsWALFlush(BufferDesc *bufdesc, XLogRecPtr *lsn) > UnlockBufHdr(bufdesc); > > /* Skip all WAL flush logic if relation is not logged */ > - if (!(*lsn != InvalidXLogRecPtr)) > + if (!XLogRecPtrIsValid(*lsn)) > return false; > > > @@ -4433,6 +4433,12 @@ CleanVictimBuffer(BufferDesc *bufdesc, bool > from_ring, IOContext io_context) > content_lock = BufHdrGetContentLock(bufdesc); > LWLockAcquire(content_lock, LW_SHARED); > > + if (!PrepareFlushBuffer(bufdesc, &max_lsn)) > + { > + LWLockRelease(content_lock); > + return; > + } > + > > And I don't understand the last parts of the diff. > > @@ -4440,19 +4446,14 @@ CleanVictimBuffer(BufferDesc *bufdesc, bool > from_ring, IOContext io_context) > LWLockRelease(content_lock); > LWLockAcquire(content_lock, LW_EXCLUSIVE); > > - /* > - * Mark the buffer ready for checksum and write. > - */ > - PrepareBufferForCheckpoint(bufdesc, &max_lsn); > + if (!PrepareFlushBuffer(bufdesc, &max_lsn)) > + { > + LWLockRelease(content_lock); > + return; > + } > > /* Release exclusive lock; the batch will write the page later */ > LWLockRelease(content_lock); > - > - /* > - * Add LSN to caller's batch tracking. > - * Caller handles XLogFlush() using highest LSN. > - */ > - PrepareBufferForCheckpoint(bufdesc, max_lsn); > } > > AFAIK, there has never been a function called > PrepareBufferForCheckpoint() and my current patchset doesn't have it > either. > > - Melanie Thank you for reviewing the patch and for the clarification. It seems I was working on the v11 patch version that I had received from November, and I did not have the newer fixes that already addressed the XLogRecPtrIsValid() check and the shared-lock early return case in CleanVictimBuffer(). That's why the first part of my changes duplicated the work that has already been fixed. Regarding the latter part of the diff, the reason I attempted that change was that in my local tree the code was still performing both WAL–LSN read + dirty check inside the exclusive lock followed by marking/queuing the buffer for write. And I initially interpreted that section as functionally similar to the description of PrepareBufferForCheckpoint() from the early discussion in the thread. Because I didn't have the newer version of the patchset where this function was removed or renamed, I refactored it into a second PrepareFlushBuffer() call to ensure correctness on re-checking after lock upgrade and to keep failure paths symmetrical (especially unlock and early return). I now understand that this was based on an outdated context and why it conflicts with the current patch flow. So I will rebase my work on the latest version of the patch and will drop that section to avoid misalignment. If there are any specific areas where I can help align with the ongoing work, please let me know I’m happy to continue contributing in the direction you are taking the patchset. Regards, Soumya