On Mon, 2023-09-25 at 13:04 +0300, Heikki Linnakangas wrote:
> Yes, that's a problem.
Patch attached. I rearranged the code a bit to follow the expected
pattern of: write, mark dirty, WAL, set LSN. I think computing the
deltas could also be moved earlier, outside of the critical section,
but I'm not sure that would be useful.
Do you have a suggestion for any kind of test addition, or should we
just review carefully?
> I wish we had an assertion for that. XLogInsert() could assert that
> the
> page is already marked dirty, for example.
Unfortunately that's not always the case, e.g. log_newpage_range().
Regards,
Jeff Davis
From c54749a7939721f0a838115abce09967216cbc0f Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Tue, 26 Sep 2023 12:10:11 -0700
Subject: [PATCH v1] Fix GenericXLogFinish().
Mark the buffers dirty before writing WAL.
Discussion: https://postgr.es/m/[email protected]
---
src/backend/access/transam/generic_xlog.c | 66 ++++++++++++-----------
1 file changed, 34 insertions(+), 32 deletions(-)
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 6c68191ca6..2b7e054ebe 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -343,10 +343,12 @@ GenericXLogFinish(GenericXLogState *state)
if (state->isLogged)
{
/* Logged relation: make xlog record in critical section. */
- XLogBeginInsert();
-
START_CRIT_SECTION();
+ /*
+ * Compute deltas if necessary, write changes to buffers, and mark
+ * them dirty.
+ */
for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
{
PageData *pageData = &state->pages[i];
@@ -359,41 +361,42 @@ GenericXLogFinish(GenericXLogState *state)
page = BufferGetPage(pageData->buffer);
pageHeader = (PageHeader) pageData->image;
+ /* delta not needed for a full page image */
+ if (!(pageData->flags & GENERIC_XLOG_FULL_IMAGE))
+ computeDelta(pageData, page, (Page) pageData->image);
+
+ /*
+ * Apply the image, being careful to zero the "hole" between
+ * pd_lower and pd_upper in order to avoid divergence between
+ * actual page state and what replay would produce.
+ */
+ memcpy(page, pageData->image, pageHeader->pd_lower);
+ memset(page + pageHeader->pd_lower, 0,
+ pageHeader->pd_upper - pageHeader->pd_lower);
+ memcpy(page + pageHeader->pd_upper,
+ pageData->image + pageHeader->pd_upper,
+ BLCKSZ - pageHeader->pd_upper);
+
+ MarkBufferDirty(pageData->buffer);
+ }
+
+ XLogBeginInsert();
+
+ /* Register buffers */
+ for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ {
+ PageData *pageData = &state->pages[i];
+
+ if (BufferIsInvalid(pageData->buffer))
+ continue;
+
if (pageData->flags & GENERIC_XLOG_FULL_IMAGE)
{
- /*
- * A full-page image does not require us to supply any xlog
- * data. Just apply the image, being careful to zero the
- * "hole" between pd_lower and pd_upper in order to avoid
- * divergence between actual page state and what replay would
- * produce.
- */
- memcpy(page, pageData->image, pageHeader->pd_lower);
- memset(page + pageHeader->pd_lower, 0,
- pageHeader->pd_upper - pageHeader->pd_lower);
- memcpy(page + pageHeader->pd_upper,
- pageData->image + pageHeader->pd_upper,
- BLCKSZ - pageHeader->pd_upper);
-
XLogRegisterBuffer(i, pageData->buffer,
REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
}
else
{
- /*
- * In normal mode, calculate delta and write it as xlog data
- * associated with this page.
- */
- computeDelta(pageData, page, (Page) pageData->image);
-
- /* Apply the image, with zeroed "hole" as above */
- memcpy(page, pageData->image, pageHeader->pd_lower);
- memset(page + pageHeader->pd_lower, 0,
- pageHeader->pd_upper - pageHeader->pd_lower);
- memcpy(page + pageHeader->pd_upper,
- pageData->image + pageHeader->pd_upper,
- BLCKSZ - pageHeader->pd_upper);
-
XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
}
@@ -402,7 +405,7 @@ GenericXLogFinish(GenericXLogState *state)
/* Insert xlog record */
lsn = XLogInsert(RM_GENERIC_ID, 0);
- /* Set LSN and mark buffers dirty */
+ /* Set LSN */
for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
{
PageData *pageData = &state->pages[i];
@@ -410,7 +413,6 @@ GenericXLogFinish(GenericXLogState *state)
if (BufferIsInvalid(pageData->buffer))
continue;
PageSetLSN(BufferGetPage(pageData->buffer), lsn);
- MarkBufferDirty(pageData->buffer);
}
END_CRIT_SECTION();
}
--
2.34.1