Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

Поиск
Список
Период
Сортировка
Искать

Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Paul Kim <mok03127@gmail.com>
Дата:
On Wed, Sep 03, 2026 at 11:04:39AM +0900, Michael Paquier wrote:
> On Wed, Sep 02, 2026 at 06:13:26PM -0700, Bharath Rupireddy wrote:
>> Nice! Do you mind adding the reproducer as a TAP test for HEAD?
>
> I'm assuming that it should be possible to use an injection points
> based on the fact that we would be up and running for the inserts.

Thanks, both.  Attached is v2: 0001 is the fix, unchanged from v1, and
0002 adds the reproducer as a TAP test on HEAD.

The test adds a small module, src/test/modules/test_walwriter, with one
C function that requests a segment switch and then stores the resulting
insert position in asyncXactLSN.  After a switch that position is just
past the new segment's long page header, beyond the end of generated
WAL, so the walwriter's next cycle requests a flush past the end of
generated WAL -- the same shape as the production request.

I did look at injection points first, but there is no INJECTION_POINT()
in this path, and what the reproducer needs is a bogus value stored
into asyncXactLSN rather than a backend stopped at a particular point,
which would require a custom callback and hence a test module anyway.
A plain test module also keeps the test runnable in builds without
injection point support.  The timing side needs no help: with the
test's wal_writer_flush_after = 0, XLogSetAsyncXactLSN() wakes the
walwriter, which picks the value up on its next cycle, so the test
just waits for the existing "request to flush past end of generated
WAL" message to show up in the log.

After that message, the test checks that the advertised flush position
is still below the bogus request, that no child process was terminated,
and that normal WAL activity afterwards gets past that position.

On unpatched HEAD the test fails in both assert and production builds:
XLogWrite() hits its "xlog write request ... is past end of log" PANIC
and the walwriter's crash takes the server down (TAP clusters run with
restart_after_crash = off).  (Which sanity check fires first depends
on the WAL buffer state; with 1MB segments I had seen the
Insert >= Write assertion instead.)  With 0001 applied, both build
types pass the test.

Regards,
Paul

Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Paul Kim <mok03127@gmail.com>
Дата:
The cfbot's Linux 32-bit task failed on v3: the TAP test's padding
loop raised "could not align the insert position".  The loop computed
the exact-fill payload as (gap - base), with base measured from a
message with an empty payload.  Once the payload exceeds the short
data header's range, the record switches to the long data header,
which is 3 bytes larger.  With 8-byte MAXALIGN those extra bytes
disappear into alignment padding and the fill still lands exactly on
the target, but with 4-byte MAXALIGN they round up to a 4-byte
overshoot, so on 32-bit builds every attempt missed the window and the
loop gave up.

Here is v4, which approaches the target in small steps once the gap
falls below base + 200 bytes, so the final exact-fill record always
keeps the short data header.  While at it, the window-hit check no
longer hardcodes SizeOfXLogLongPHD as 40 bytes (36 on 32-bit): a
normal switch reports the segment boundary itself and only the
overridden EndPos lies past it, so any nonzero offset into the new
segment marks the hit.

No changes to the fix; 0001 is identical to v3.

Regards,
Paul Kim

Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Дата:
Hi,

On Thu, Sep 3, 2026 at 6:29 PM Paul Kim  wrote:
>
> > Just curious, how did the bogus LSN end up in asyncXactLSN in
> > production when you hit the issue?
>
> Honestly, we still don't know, but the value itself is telling.
>
> The production request was exactly segment boundary + 0x28, i.e.
> SizeOfXLogLongPHD past a segment start. That is precisely what the
> current insert position looks like right after a segment switch,
> before any record has been written into the new segment. So it does
> not look like random corruption; it looks like something captured the
> insert position at that moment and handed it to XLogSetAsyncXactLSN().
> That is also how the TAP test's injector reproduces the incident
> byte-for-byte.
>
> I went looking for an in-core path that could do this naturally on the
> affected version and came up empty: fault-free segment switches
> (~100 runs), async commits, LogStandbySnapshot() calls, and top-level
> aborts never produced the "past end of generated WAL" warning in my
> testing. The affected installation does load third-party preload
> libraries; auditing those for WAL-related symbol use is on my list but
> has not been done yet, so I cannot rule the source in or out.
>
> Either way, I think the fix stands on its own: whatever plants the
> value, WaitXLogInsertionsToFinish() already detects and clamps it, and
> XLogBackgroundFlush() discarding that clamp is what turns one bad
> request into the standby's prev-link retry loop.

Thanks for the patches. I don't see a CF entry yet, so I created one:
https://commitfest.postgresql.org/patch/7294/. Feel free to add
yourself as an author.

I went through this thread today. Here's my take.

What exactly caused the wait-for-any-in-progress-insertions-to-finish
to receive an LSN past the end of the generated WAL is one problem.
And, when that happens for whatever reason, the walwriter not honoring
the adjusted position in the caller and blindly writing such WAL to
WAL files is another problem.

In this case, although we don't yet know the root cause for the first
problem, which could be not necessarily the async commit/abort LSN
being wrong but could be anyone else setting up an LSN beyond what's
written in XLogCtl->LogwrtRqst, I think fixing the second problem is
the right direction (as the patch does here). The backend doing WAL
write already honors the adjusted position, so the walwriter missing
it needs to be fixed too. If the backend gets to write the WAL before
the walwriter, it would not have written this WAL record because
wait-for-any-in-progress-insertions-to-finish in XLogFlush() honors
the adjusted position. This matters because the consequences on the
standby are hard to deal with in production, stuck WAL replay, vacuum
issues on the primary, and possibly failovers.

If I understand correctly, you identified that the walwriter is the
problem by looking at the pid from the "request to flush past end of
generated WAL" log message, right? Nice find.

Also, I'm curious, how did the standby get out of the stuck error loop
"record with incorrect prev-link"?

Also, did you observe any "xlog flush request %X/%08X is not satisfied
--- flushed only to" or other messages on the primary? And I believe
if the primary had crashed before checkpointing this WAL record, it
would have also been stuck in a similar error loop, right?

A few comments on the patch.

1/ Nit. How about using "adjusted" instead of "clamped" in the
comments and commit message?

+ * if 'upto' is past the end of reserved WAL, the request is clamped to the
+ /* honor the clamp if the request was past the end of reserved WAL */
WaitXLogInsertionsToFinish() clamps a request that is past the end of

2/ Why do we need to check the adjusted LSN against the requested LSN
again? Also, is there a reason to compare it with the flush LSN? Why
not just assign the adjusted LSNs like XLogFlush() does?

+ /* honor the clamp if the request was past the end of reserved WAL */
+ if (insertpos < WriteRqst.Write)
+ {
+ WriteRqst.Write = insertpos;
+ if (WriteRqst.Flush > insertpos)
+ WriteRqst.Flush = insertpos;
+ }

3/ Do we need similar adjusted handling in AdvanceXLInsertBuffer()? I
don't think so because there the whole old page from the WAL buffer is
written anyway. Just want to clarify.

-- 
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com


Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Дата:
Hi,

While working on [1], it was identified that
WaitXLogInsertionsToFinish emits a LOG message, and adjusts the upto
ptr to proceed further when caller requests to flush past the end of
generated WAL. There's a comment explaining no caller should ever do
that intentionally except in cases with bogus LSNs. For a similar
situation, XLogWrite emits a PANIC "xlog write request %X/%X is past
end of log %X/%X". Although there's no problem if
WaitXLogInsertionsToFinish emits LOG, but why can't it be a bit more
harsh and emit PANIC something like the attached to detect the corner
case?

Thoughts?

[1] https://www.postgresql.org/message-id/b43615437ac7d7fdef86a36e5d5bf3fc049bc11b.camel%40j-davis.com

On Thu, Feb 22, 2024 at 1:54 AM Jeff Davis  wrote:
>
> WaitXLogInsertionsToFinish() uses a LOG level message
> for the same situation. They should probably be the same log level, and
> I would think it would be either PANIC or WARNING. I have no idea why
> LOG was chosen.

[2]
    /*
     * No-one should request to flush a piece of WAL that hasn't even been
     * reserved yet. However, it can happen if there is a block with a bogus
     * LSN on disk, for example. XLogFlush checks for that situation and
     * complains, but only after the flush. Here we just assume that to mean
     * that all WAL that has been reserved needs to be finished. In this
     * corner-case, the return value can be smaller than 'upto' argument.
     */
    if (upto > reservedUpto)
    {
        ereport(LOG,
                (errmsg("request to flush past end of generated WAL;
request %X/%X, current position %X/%X",
                        LSN_FORMAT_ARGS(upto), LSN_FORMAT_ARGS(reservedUpto))));
        upto = reservedUpto;
    }

--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Дата:
Hi,

On Thu, Sep 3, 2026 at 5:05 PM Paul Kim  wrote:
>
> Thanks, both.  Attached is v2: 0001 is the fix, unchanged from v1, and
> 0002 adds the reproducer as a TAP test on HEAD.
>
> The test adds a small module, src/test/modules/test_walwriter, with one
> C function that requests a segment switch and then stores the resulting
> insert position in asyncXactLSN.  After a switch that position is just
> past the new segment's long page header, beyond the end of generated
> WAL, so the walwriter's next cycle requests a flush past the end of
> generated WAL -- the same shape as the production request.
>
> I did look at injection points first, but there is no INJECTION_POINT()
> in this path, and what the reproducer needs is a bogus value stored
> into asyncXactLSN rather than a backend stopped at a particular point,
> which would require a custom callback and hence a test module anyway.

Thanks for the v2 patches. Just curious, how did the bogus LSN end up
in asyncXactLSN in production when you hit the issue?

-- 
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com


Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish

От:
Jeff Davis <pgsql@j-davis.com>
Дата:
On Fri, 2024-03-15 at 13:12 +0530, Bharath Rupireddy wrote:
> Hi,
> 
> While working on [1], it was identified that
> WaitXLogInsertionsToFinish emits a LOG message, and adjusts the upto
> ptr to proceed further when caller requests to flush past the end of
> generated WAL. There's a comment explaining no caller should ever do
> that intentionally except in cases with bogus LSNs. For a similar
> situation, XLogWrite emits a PANIC "xlog write request %X/%X is past
> end of log %X/%X". Although there's no problem if
> WaitXLogInsertionsToFinish emits LOG, but why can't it be a bit more
> harsh and emit PANIC something like the attached to detect the corner
> case?
> 
> Thoughts?

I'm not clear on why the callers of WaitXLogInsertionsToFinish() are
handling errors the way they are. XLogWrite PANICs, XLogFlush ERRORs
(which is likely to be escalated to a PANIC anyway), and the other
callers ignore the return value and leave it up to XLogWrite() to
PANIC.

As far as I can tell, once WaitXLogInsertionsToFinish() detects this
bogus LSN, a PANIC is a likely outcome, so your proposed change makes
sense. But then why are the callers also checking?

I haven't looked in a lot of detail.

Regards,
	Jeff Davis



FAQ