On Thu, 22 Oct 2026 at 21:43, Andrey Borodin <[email protected]> wrote: > > Hi hackers, > > A standby can fall behind the WAL retained by its upstream while the > missing segments are still available in the archive. We would like to > keep that history in the archive, not retain all of it locally for a > replication slot. Giving the receiver its own restore_command is not > always an option either: we may be able to grant replication access > without giving it access to the archive. Could the sender provide the > missing WAL over the existing replication connection instead? > > Fujii proposed this in 2010 [0], and Japin posted a patch in 2025 [1]. > I would like to revisit it with the archive access restriction above as > the use case, and with private copies instead of restoring into the > normal WAL namespace. > > Attached is a prototype. With wal_sender_restore = on, a physical sender > that cannot open a segment in pg_wal tries restore_command before > failing. This works on a primary or a [cascading] standby. > > The temporary storage and cleanup questions came up when discussing > reviving lost replication slots [2]. Here each sender keeps at most one > restored segment in its own pg_wal/xlogtemp.*.walsender directory. > > Alexander pointed out the cost of repeated failed restores on the > primary [3]. A request arriving within one second of a failed restore > waits for the remainder of that interval. > > The existing timeline and flush/replay limits still apply. This does not > enable logical decoding from the archive or revive an invalidated slot. > It can serve a receiver without a slot, or with a usable physical slot. > Joao's proposal to revalidate physical slots [4] addresses that separate > part of the problem. > > The prototype runs restore_command synchronously. A slow fetch prevents > keepalives and feedback processing, can cause receiver timeouts, and can > delay shutdown. An interruptible implementation would need to manage the > child command while servicing the replication connection. > > What do you think? > > > Best regards, Andrey Borodin. > > [0] > https://www.postgresql.org/message-id/[email protected] > [1] > https://www.postgresql.org/message-id/me0p300mb0445c80039d6534452a87539b6...@me0p300mb0445.ausp300.prod.outlook.com > [2] > https://www.postgresql.org/message-id/CALj2ACVUgCYVgKc42GDCFZo_=xjyydthxbhzyht3s4uqazl...@mail.gmail.com > [3] > https://www.postgresql.org/message-id/CAFh8B=nH=41scx3f_eah2h5o1w-nj8b7ucmp_0z4p4wsv2t...@mail.gmail.com > [4] https://commitfest.postgresql.org/patch/6865/
Additional code review of 0001: > /* Only physical streaming may fall back to the archive. */ > if (wal_sender_restore && > state->routine.segment_close == WalSndSegmentClose && > WalSndRestoreSegment(state, xlogfname)) > return; This looks too vague. We are checking for func pointers - that's not good coding at all. > /* > * Do not use rmtree(): this can run from a memory context reset > * callback, where its allocations could raise another error. > */ > snprintf(path, sizeof(path), "%s/RECOVERYXLOG", restoreDir); I'm somewhat worried about the fact that with the 0001 string constant "RECOVERYXLOG" is typed in core code twice. I also didn't get why TAP test exercise cascading walsenders. 0001 itself doesn't add anything new for cascading replication, does it? Off-list I proposed requesting WAL from replication upstream, to serve it downstream, in case the wal file is missing locally, but this was not implemented here. -- Best regards, Kirill Reshke
