Thread

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Clean up 019_replslot_limit.pl comments

  2. Stabilize 019_replslot_limit.pl: wait on slot restart_lsn

  3. Fix memory ordering in WAIT FOR LSN wakeup mechanism

  4. Improve WAIT FOR LSN test coverage

  5. Remove redundant WAIT FOR LSN caller-side pre-checks

  6. Use barrier semantics when reading/writing writtenUpto

  7. Use replay position as floor for WAIT FOR LSN standby_(write|flush)

  8. Wake standby_write/standby_flush waiters from the WAL replay loop

  9. Minimal fix for WAIT FOR ... MODE 'standby_flush'

  10. Avoid syscache lookup while building a WAIT FOR tuple descriptor

  11. Document that WAIT FOR may be interrupted by recovery conflicts

  12. Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()

  13. Wake LSN waiters before recovery target stop

  14. Remove redundant pg_unreachable() after elog(ERROR) from ExecWaitStmt()

  15. Revert "Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()"

  16. Fix variable usage in wakeupWaiters()

  17. Add tab completion for the WAIT FOR LSN MODE option

  18. Add the MODE option to the WAIT FOR LSN command

  19. Extend xlogwait infrastructure with write and flush wait types

  20. Unify error messages

  21. Optimize shared memory usage for WaitLSNProcInfo

  22. Fix WaitLSNWakeup() fast-path check for InvalidXLogRecPtr

  23. Fix incorrect function name in comments

  24. Add infrastructure for efficient LSN waiting

  25. Add pairingheap_initialize() for shared memory usage

  26. Implement WAIT FOR command

  1. Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2024-11-27T04:08:51Z

    Hi!
    
    Introduction
    
    The simple way to wait for a given lsn to replay on standby appears to
    be useful because it provides a way to achieve read-your-writes
    consistency while working with both replication leader and standby.
    And it's both handy and cheaper to have built-in functionality for
    that instead of polling pg_last_wal_replay_lsn().
    
    Key problem
    
    While this feature generally looks trivial, there is a surprisingly
    hard problem.  While waiting for an LSN to replay, you should hold any
    snapshots.  If you hold a snapshot on standby, that snapshot could
    prevent the replay of WAL records.  In turn, that could prevent the
    wait to finish, causing a kind of deadlock.  Therefore, waiting for
    LSN to replay couldn't be implemented as a function.  My last attempt
    implements this functionality as a stored procedure [1].  This
    approach generally works but has a couple of serious limitations.
    1) Given that a CALL statement has to lookup a catalog for the stored
    procedure, we can't work inside a transaction of REPEATABLE READ or a
    higher isolation level (even if nothing has been done before in that
    transaction).  It is especially unpleasant that this limitation covers
    the case of the implicit transaction when
    default_transaction_isolation = 'repeatable read' [2].  I had a
    workaround for that [3], but it looks a bit awkward.
    2) Using output parameters for a stored procedure causes an extra
    snapshot to be held.  And that snapshot is difficult (unsafe?) to
    release [3].
    
    Present solution
    
    The present patch implements a new utility command WAIT FOR LSN
    'target_lsn' [, TIMEOUT 'timeout'][, THROW 'throw'].  Unlike previous
    attempts to implement custom syntax, it uses only one extra unreserved
    keyword.  The parameters are implemented as generic_option_list.
    
    Custom syntax eliminates the problem of running within an empty
    transaction of REPEATABLE READ level or higher.  We don't need to
    lookup a system catalog.  Thus, we have to set a transaction snapshot.
    
    Also, revising PlannedStmtRequiresSnapshot() allows us to avoid
    holding a snapshot to return a value.  Therefore, the WAIT command in
    the attached patch returns its result status.
    
    Also, the attached patch explicitly checks if the standby has been
    promoted to throw the most relevant form of an error.  The issue of
    inaccurate error messages has been previously spotted in [5].
    
    Any comments?
    
    Links.
    1. https://www.postgresql.org/message-id/E1sZwuz-002NPQ-Lc%40gemulon.postgresql.org
    2. https://www.postgresql.org/message-id/14de8671-e328-4c3e-b136-664f6f13a39f%40iki.fi
    3. https://www.postgresql.org/message-id/CAPpHfdvRmTzGJw5rQdSMkTxUPZkjwtbQ%3DLJE2u9Jqh9gFXHpmg%40mail.gmail.com
    4. https://www.postgresql.org/message-id/4953563546cb8c8851f84c7debf723ef%40postgrespro.ru
    5. https://www.postgresql.org/message-id/ab0eddce-06d4-4db2-87ce-46fa2427806c%40iki.fi
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  2. Re: Implement waiting for wal lsn replay: reloaded

    Kirill Reshke <reshkekirill@gmail.com> — 2024-12-04T11:12:07Z

    On Wed, 27 Nov 2024 at 09:09, Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi!
    >
    > Introduction
    >
    > The simple way to wait for a given lsn to replay on standby appears to
    > be useful because it provides a way to achieve read-your-writes
    > consistency while working with both replication leader and standby.
    > And it's both handy and cheaper to have built-in functionality for
    > that instead of polling pg_last_wal_replay_lsn().
    >
    > Key problem
    >
    > While this feature generally looks trivial, there is a surprisingly
    > hard problem.  While waiting for an LSN to replay, you should hold any
    > snapshots.  If you hold a snapshot on standby, that snapshot could
    > prevent the replay of WAL records.  In turn, that could prevent the
    > wait to finish, causing a kind of deadlock.  Therefore, waiting for
    > LSN to replay couldn't be implemented as a function.  My last attempt
    > implements this functionality as a stored procedure [1].  This
    > approach generally works but has a couple of serious limitations.
    > 1) Given that a CALL statement has to lookup a catalog for the stored
    > procedure, we can't work inside a transaction of REPEATABLE READ or a
    > higher isolation level (even if nothing has been done before in that
    > transaction).  It is especially unpleasant that this limitation covers
    > the case of the implicit transaction when
    > default_transaction_isolation = 'repeatable read' [2].  I had a
    > workaround for that [3], but it looks a bit awkward.
    > 2) Using output parameters for a stored procedure causes an extra
    > snapshot to be held.  And that snapshot is difficult (unsafe?) to
    > release [3].
    >
    > Present solution
    >
    > The present patch implements a new utility command WAIT FOR LSN
    > 'target_lsn' [, TIMEOUT 'timeout'][, THROW 'throw'].  Unlike previous
    > attempts to implement custom syntax, it uses only one extra unreserved
    > keyword.  The parameters are implemented as generic_option_list.
    >
    > Custom syntax eliminates the problem of running within an empty
    > transaction of REPEATABLE READ level or higher.  We don't need to
    > lookup a system catalog.  Thus, we have to set a transaction snapshot.
    >
    > Also, revising PlannedStmtRequiresSnapshot() allows us to avoid
    > holding a snapshot to return a value.  Therefore, the WAIT command in
    > the attached patch returns its result status.
    >
    > Also, the attached patch explicitly checks if the standby has been
    > promoted to throw the most relevant form of an error.  The issue of
    > inaccurate error messages has been previously spotted in [5].
    >
    > Any comments?
    >
    > Links.
    > 1. https://www.postgresql.org/message-id/E1sZwuz-002NPQ-Lc%40gemulon.postgresql.org
    > 2. https://www.postgresql.org/message-id/14de8671-e328-4c3e-b136-664f6f13a39f%40iki.fi
    > 3. https://www.postgresql.org/message-id/CAPpHfdvRmTzGJw5rQdSMkTxUPZkjwtbQ%3DLJE2u9Jqh9gFXHpmg%40mail.gmail.com
    > 4. https://www.postgresql.org/message-id/4953563546cb8c8851f84c7debf723ef%40postgrespro.ru
    > 5. https://www.postgresql.org/message-id/ab0eddce-06d4-4db2-87ce-46fa2427806c%40iki.fi
    >
    > ------
    > Regards,
    > Alexander Korotkov
    > Supabase
    Hi!
    
    What's the current status of
    https://commitfest.postgresql.org/50/5167/ ? Should we close it or
    reattach to this thread?
    
    
    -- 
    Best regards,
    Kirill Reshke
    
    
    
    
  3. Re: Implement waiting for wal lsn replay: reloaded

    Andrei Lepikhov <lepihov@gmail.com> — 2025-02-06T07:42:12Z

    On 12/4/24 18:12, Kirill Reshke wrote:
    > On Wed, 27 Nov 2024 at 09:09, Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >> Any comments?
    > What's the current status of
    > https://commitfest.postgresql.org/50/5167/ ? Should we close it or
    > reattach to this thread?
    To push this feature further I rebased the patch onto current master.
    Also, let's add a commitfest entry:
    https://commitfest.postgresql.org/52/5550/
    
    -- 
    regards, Andrei Lepikhov
  4. Re: Implement waiting for wal lsn replay: reloaded

    Yura Sokolov <y.sokolov@postgrespro.ru> — 2025-02-06T08:31:28Z

    27.11.2024 07:08, Alexander Korotkov wrote:
    > Present solution
    > 
    > The present patch implements a new utility command WAIT FOR LSN
    > 'target_lsn' [, TIMEOUT 'timeout'][, THROW 'throw'].  Unlike previous
    > attempts to implement custom syntax, it uses only one extra unreserved
    > keyword.  The parameters are implemented as generic_option_list.
    > 
    > Custom syntax eliminates the problem of running within an empty
    > transaction of REPEATABLE READ level or higher.  We don't need to
    > lookup a system catalog.  Thus, we have to set a transaction snapshot.
    > 
    > Also, revising PlannedStmtRequiresSnapshot() allows us to avoid
    > holding a snapshot to return a value.  Therefore, the WAIT command in
    > the attached patch returns its result status.
    > 
    > Also, the attached patch explicitly checks if the standby has been
    > promoted to throw the most relevant form of an error.  The issue of
    > inaccurate error messages has been previously spotted in [5].
    > 
    > Any comments?
    
    Good day, Alexander.
    
    I briefly looked into patch and have couple of minor remarks:
    
    1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    problems, but still don't like it. I'd prefer to see local fixed array, say
    of 16 elements, and loop around remaining function body acting in batch of
    16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    and even then it wont be much heavier than fetching all at once.
    
    2. I'd move `inHeap` field between `procno` and `phNode` to fill the gap
    between fields on 64bit platforms.
    Well, I believe, it would be better to tweak `pairingheap_node` to make it
    clear if it is in heap or not. But such change would be unrelated to
    current patch's sense. So lets stick with `inHeap`, but move it a bit.
    
    Non-code question: do you imagine for `WAIT` command reuse for other cases?
    Is syntax rule in gram.y convenient enough for such reuse? I believe, `LSN`
    is not part of syntax to not introduce new keyword. But is it correct way?
    I have no answer or strong opinion.
    
    Otherwise, the patch looks quite strong to me.
    
    -------
    regards
    Yura Sokolov
    
    
    
    
  5. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-02-16T21:27:43Z

    Hi, Yura!
    
    
    On Thu, Feb 6, 2025 at 10:31 AM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    > I briefly looked into patch and have couple of minor remarks:
    >
    > 1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    > problems, but still don't like it. I'd prefer to see local fixed array, say
    > of 16 elements, and loop around remaining function body acting in batch of
    > 16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    > and even then it wont be much heavier than fetching all at once.
    
    OK, I've refactored this to use static array of 16 size.  palloc() is
    used only if we don't fit static array.
    
    > 2. I'd move `inHeap` field between `procno` and `phNode` to fill the gap
    > between fields on 64bit platforms.
    > Well, I believe, it would be better to tweak `pairingheap_node` to make it
    > clear if it is in heap or not. But such change would be unrelated to
    > current patch's sense. So lets stick with `inHeap`, but move it a bit.
    
    Ok, `inHeap` is moved.
    
    > Non-code question: do you imagine for `WAIT` command reuse for other cases?
    > Is syntax rule in gram.y convenient enough for such reuse? I believe, `LSN`
    > is not part of syntax to not introduce new keyword. But is it correct way?
    > I have no answer or strong opinion.
    
    This is conscious decision.  New rules and new keywords causes extra
    states for parser state machine.  There could be raised a question
    whether feature is valuable enough to justify the slowdown of parser.
    This is why I tried to make this feature as less invasive as possible
    in terms of parser.  And yes, there potentially could be other things
    to wait.  For instance, instead of waiting for lsn replay we could be
    waiting for finishing replay of given xid.
    
    > Otherwise, the patch looks quite strong to me.
    
    Great, thank you!
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  6. Re: Implement waiting for wal lsn replay: reloaded

    Yura Sokolov <y.sokolov@postgrespro.ru> — 2025-02-28T13:03:33Z

    17.02.2025 00:27, Alexander Korotkov wrote:
    > On Thu, Feb 6, 2025 at 10:31 AM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    >> I briefly looked into patch and have couple of minor remarks:
    >>
    >> 1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    >> problems, but still don't like it. I'd prefer to see local fixed array, say
    >> of 16 elements, and loop around remaining function body acting in batch of
    >> 16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    >> and even then it wont be much heavier than fetching all at once.
    > 
    > OK, I've refactored this to use static array of 16 size.  palloc() is
    > used only if we don't fit static array.
    
    I've rebased patch and:
    - fixed compiler warning in wait.c ("maybe uninitialized 'result'").
    - made a loop without call to palloc in WaitLSNWakeup. It is with "goto" to
    keep indentation, perhaps `do {} while` would be better?
    
    -------
    regards
    Yura Sokolov aka funny-falcon
  7. Re: Implement waiting for wal lsn replay: reloaded

    Yura Sokolov <y.sokolov@postgrespro.ru> — 2025-02-28T13:55:21Z

    28.02.2025 16:03, Yura Sokolov пишет:
    > 17.02.2025 00:27, Alexander Korotkov wrote:
    >> On Thu, Feb 6, 2025 at 10:31 AM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    >>> I briefly looked into patch and have couple of minor remarks:
    >>>
    >>> 1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    >>> problems, but still don't like it. I'd prefer to see local fixed array, say
    >>> of 16 elements, and loop around remaining function body acting in batch of
    >>> 16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    >>> and even then it wont be much heavier than fetching all at once.
    >>
    >> OK, I've refactored this to use static array of 16 size.  palloc() is
    >> used only if we don't fit static array.
    > 
    > I've rebased patch and:
    > - fixed compiler warning in wait.c ("maybe uninitialized 'result'").
    > - made a loop without call to palloc in WaitLSNWakeup. It is with "goto" to
    > keep indentation, perhaps `do {} while` would be better?
    
    And fixed:
       'WAIT' is marked as BARE_LABEL in kwlist.h, but it is missing from
    gram.y's bare_label_keyword rule
    
    -------
    regards
    Yura Sokolov aka funny-falcon
  8. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-03-10T11:30:31Z

    On Fri, Feb 28, 2025 at 3:55 PM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    > 28.02.2025 16:03, Yura Sokolov пишет:
    > > 17.02.2025 00:27, Alexander Korotkov wrote:
    > >> On Thu, Feb 6, 2025 at 10:31 AM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    > >>> I briefly looked into patch and have couple of minor remarks:
    > >>>
    > >>> 1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    > >>> problems, but still don't like it. I'd prefer to see local fixed array, say
    > >>> of 16 elements, and loop around remaining function body acting in batch of
    > >>> 16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    > >>> and even then it wont be much heavier than fetching all at once.
    > >>
    > >> OK, I've refactored this to use static array of 16 size.  palloc() is
    > >> used only if we don't fit static array.
    > >
    > > I've rebased patch and:
    > > - fixed compiler warning in wait.c ("maybe uninitialized 'result'").
    > > - made a loop without call to palloc in WaitLSNWakeup. It is with "goto" to
    > > keep indentation, perhaps `do {} while` would be better?
    >
    > And fixed:
    >    'WAIT' is marked as BARE_LABEL in kwlist.h, but it is missing from
    > gram.y's bare_label_keyword rule
    
    Thank you, Yura.  I've further revised the patch.  Mostly added the
    documentation including SQL command reference and few paragraphs in
    the high availability chapter explaining the read-your-writes
    consistency concept.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  9. Re: Implement waiting for wal lsn replay: reloaded

    Yura Sokolov <y.sokolov@postgrespro.ru> — 2025-03-12T14:44:28Z

    10.03.2025 14:30, Alexander Korotkov пишет:
    > On Fri, Feb 28, 2025 at 3:55 PM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    >> 28.02.2025 16:03, Yura Sokolov пишет:
    >>> 17.02.2025 00:27, Alexander Korotkov wrote:
    >>>> On Thu, Feb 6, 2025 at 10:31 AM Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    >>>>> I briefly looked into patch and have couple of minor remarks:
    >>>>>
    >>>>> 1. I don't like `palloc` in the `WaitLSNWakeup`. I believe it wont issue
    >>>>> problems, but still don't like it. I'd prefer to see local fixed array, say
    >>>>> of 16 elements, and loop around remaining function body acting in batch of
    >>>>> 16 wakeups. Doubtfully there will be more than 16 waiting clients often,
    >>>>> and even then it wont be much heavier than fetching all at once.
    >>>>
    >>>> OK, I've refactored this to use static array of 16 size.  palloc() is
    >>>> used only if we don't fit static array.
    >>>
    >>> I've rebased patch and:
    >>> - fixed compiler warning in wait.c ("maybe uninitialized 'result'").
    >>> - made a loop without call to palloc in WaitLSNWakeup. It is with "goto" to
    >>> keep indentation, perhaps `do {} while` would be better?
    >>
    >> And fixed:
    >>    'WAIT' is marked as BARE_LABEL in kwlist.h, but it is missing from
    >> gram.y's bare_label_keyword rule
    > 
    > Thank you, Yura.  I've further revised the patch.  Mostly added the
    > documentation including SQL command reference and few paragraphs in
    > the high availability chapter explaining the read-your-writes
    > consistency concept.
    
    Good day, Alexander.
    
    Looking "for the last time" to the patch I found there remains
    `pg_wal_replay_wait` function in documentation and one comment.
    So I fixed it in documentation, and removed sentence from comment.
    
    Otherwise v6 is just rebased v5.
    
    -------
    regards
    Yura Sokolov aka funny-falcon
  10. Re: Implement waiting for wal lsn replay: reloaded

    Tomas Vondra <tomas@vondra.me> — 2025-03-13T14:15:01Z

    Hi,
    
    I did a quick look at this patch. I haven't found any correctness
    issues, but I have some general review comments and questions about the
    grammar / syntax.
    
    1) The sgml docs don't really show the syntax very nicely, it only shows
    this at the beginning of wait_for.sgml:
    
       WAIT FOR ( <replaceable class="parameter">parameter</replaceable>
    '<replaceable class="parameter">value</replaceable>' [, ... ] ) ]
    
    I kinda understand this comes from using the generic option list (I'll
    get to that shortly), but I think it'd be much better to actually show
    the "full" syntax here, instead of leaving the "parameters" to later.
    
    
    2) The syntax description suggests "(" and ")" are required, but that
    does not seem to be the case - in fact, it's not even optional, and when
    I try using that, I get syntax error.
    
    
    3) I have my doubts about using the generic_option_list for this. Yes, I
    understand this allows using fewer reserved keywords, but it leads to
    some weirdness and I'm not sure it's worth it. Not sure what the right
    trade off is here.
    
    Anyway, some examples of the weird stuff implied by this approach:
    
    - it forces "," between the options, which is a clear difference from
    what we do for every other command
    
    - it forces everything to be a string, i.e. you can' say "TIMEOUT 10",
    it has to be "TIMEOUT '10'"
    
    I don't have a very strong opinion on this, but the result seems a bit
    strange to me.
    
    
    4) I'm not sure I understand the motivation of the "throw false" mode,
    and I'm not sure I understand this description in the sgml docs:
    
        On timeout, or if the server is promoted before
        <parameter>lsn</parameter> is reached, an error is emitted,
        as soon as <parameter>throw</parameter> is not specified or set to
        true.
        If <parameter>throw</parameter> is set to false, then the command
        doesn't throw errors.
    
    I find it a bit confusing. What is the use case for this mode?
    
    
    5) One place in the docs says:
    
          The target log sequence number to wait for.
    
       Thie is literally the only place using "log sequence number" in our
       code base, I'd just use "LSN" just like every other place.
    
    
    6) The docs for the TIMEOUT parameter say this:
    
       <varlistentry>
        <term><replaceable class="parameter">timeout</replaceable></term>
        <listitem>
         <para>
          When specified and greater than zero, the command waits until
          <parameter>lsn</parameter> is reached or the specified
          <parameter>timeout</parameter> has elapsed.  Must be a non-
          negative integer, the default is zero.
         </para>
        </listitem>
       </varlistentry>
    
       That doesn't say what unit does the option use. Is is seconds,
       milliseconds or what?
    
       In fact, it'd be nice to let users specify that in the value, similar
       to other options (e.g. SET statement_timeout = '10s').
    
    
    7) One place in the docs says this:
    
        That is, after this function execution, the value returned by
        <function>pg_last_wal_replay_lsn</function> should be greater ...
    
      I think the reference to "function execution" is obsolete?
    
    
    8) I find this confusing:
    
        However, if <command>WAIT FOR</command> is
        called on primary promoted from standby and <literal>lsn</literal>
        was already replayed, then the <command>WAIT FOR</command> command
        just exits immediately.
    
      Does this mean running the WAIT command on a primary (after it was
      already promoted) will exit immediately? Why does it matter that it
      was promoted from a standby? Shouldn't it exit immediately even for
      a standalone instance?
    
    
    9) xlogwait.c
    
    I think this should start with a basic "design" description of how the
    wait is implemented, in a comment at the top of the file. That is, what
    we keep in the shared memory, what happens during a wait, how it uses
    the pairing heap, etc. After reading this comment I should understand
    how it all fits together.
    
    
    10) WaitForLSNReplay / WaitLSNWakeup
    
    I think the function comment should document the important stuff (e.g.
    return values for various situations, how it groups waiters into chunks
    of 16 elements during wakeup, ...).
    
    
    11) WaitLSNProcInfo / WaitLSNState
    
    Does this need to be exposed in xlogwait.h? These structs seem private
    to xlogwait.c, so maybe declare it there?
    
    
    regards
    
    -- 
    Tomas Vondra
    
    
    
    
    
  11. Re: Implement waiting for wal lsn replay: reloaded

    vignesh C <vignesh21@gmail.com> — 2025-03-16T13:32:11Z

    On Wed, 12 Mar 2025 at 20:14, Yura Sokolov <y.sokolov@postgrespro.ru> wrote:
    >
    > Otherwise v6 is just rebased v5.
    
    I noticed that Tomas's comments from [1] are not yet addressed, I have
    changed the commitfest status to Waiting on Author, please address
    them and update it to Needs review.
    [1] - https://www.postgresql.org/message-id/09a98dc9-eeb1-471d-b990-072513c3d584@vondra.me
    
    Regards,
    Vignesh
    
    
    
    
  12. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-04-29T11:27:25Z

    Hi, Tomas.
    
    Thank you so much for your review!  Please find the revised patchset.
    
    On Thu, Mar 13, 2025 at 4:15 PM Tomas Vondra <tomas@vondra.me> wrote:
    > I did a quick look at this patch. I haven't found any correctness
    > issues, but I have some general review comments and questions about the
    > grammar / syntax.
    >
    > 1) The sgml docs don't really show the syntax very nicely, it only shows
    > this at the beginning of wait_for.sgml:
    >
    >    WAIT FOR ( <replaceable class="parameter">parameter</replaceable>
    > '<replaceable class="parameter">value</replaceable>' [, ... ] ) ]
    >
    > I kinda understand this comes from using the generic option list (I'll
    > get to that shortly), but I think it'd be much better to actually show
    > the "full" syntax here, instead of leaving the "parameters" to later.
    
    Sounds reasonable, changed to show the full syntax in the synopsis.
    
    > 2) The syntax description suggests "(" and ")" are required, but that
    > does not seem to be the case - in fact, it's not even optional, and when
    > I try using that, I get syntax error.
    
    Good catch, fixed.
    
    > 3) I have my doubts about using the generic_option_list for this. Yes, I
    > understand this allows using fewer reserved keywords, but it leads to
    > some weirdness and I'm not sure it's worth it. Not sure what the right
    > trade off is here.
    >
    > Anyway, some examples of the weird stuff implied by this approach:
    >
    > - it forces "," between the options, which is a clear difference from
    > what we do for every other command
    >
    > - it forces everything to be a string, i.e. you can' say "TIMEOUT 10",
    > it has to be "TIMEOUT '10'"
    >
    > I don't have a very strong opinion on this, but the result seems a bit
    > strange to me.
    
    I've improved the syntax.  I still tried to keep the number of new
    keywords and grammar rules minimal.  That leads to moving some parser
    login into wait.c.  This is probably a bit awkward, but saves our
    grammar from bloat.  Let me know what do you think about this
    approach.
    
    > 4) I'm not sure I understand the motivation of the "throw false" mode,
    > and I'm not sure I understand this description in the sgml docs:
    >
    >     On timeout, or if the server is promoted before
    >     <parameter>lsn</parameter> is reached, an error is emitted,
    >     as soon as <parameter>throw</parameter> is not specified or set to
    >     true.
    >     If <parameter>throw</parameter> is set to false, then the command
    >     doesn't throw errors.
    >
    > I find it a bit confusing. What is the use case for this mode?
    
    The idea here is that application could do some handling of these
    errors without having to parse the error messages (parsing error
    messages is inconvenient because of localization etc).
    
    > 5) One place in the docs says:
    >
    >       The target log sequence number to wait for.
    >
    >    Thie is literally the only place using "log sequence number" in our
    >    code base, I'd just use "LSN" just like every other place.
    
    OK fixed.
    
    > 6) The docs for the TIMEOUT parameter say this:
    >
    >    <varlistentry>
    >     <term><replaceable class="parameter">timeout</replaceable></term>
    >     <listitem>
    >      <para>
    >       When specified and greater than zero, the command waits until
    >       <parameter>lsn</parameter> is reached or the specified
    >       <parameter>timeout</parameter> has elapsed.  Must be a non-
    >       negative integer, the default is zero.
    >      </para>
    >     </listitem>
    >    </varlistentry>
    >
    >    That doesn't say what unit does the option use. Is is seconds,
    >    milliseconds or what?
    >
    >    In fact, it'd be nice to let users specify that in the value, similar
    >    to other options (e.g. SET statement_timeout = '10s').
    
    The default unit of milliseconds is specified.  Also, an alternative
    way to specify timeout is now supported.  Timeout might be a string
    literal consisting of numeric and unit specifier.
    
    > 7) One place in the docs says this:
    >
    >     That is, after this function execution, the value returned by
    >     <function>pg_last_wal_replay_lsn</function> should be greater ...
    >
    >   I think the reference to "function execution" is obsolete?
    
    Actually, this is just the function, which reports current replay LSN,
    not function introduced by previous version of this patch.  We refer
    it to just express the constraint that LSN must be replayed after
    execution of the command.
    
    > 8) I find this confusing:
    >
    >     However, if <command>WAIT FOR</command> is
    >     called on primary promoted from standby and <literal>lsn</literal>
    >     was already replayed, then the <command>WAIT FOR</command> command
    >     just exits immediately.
    >
    >   Does this mean running the WAIT command on a primary (after it was
    >   already promoted) will exit immediately? Why does it matter that it
    >   was promoted from a standby? Shouldn't it exit immediately even for
    >   a standalone instance?
    
    I think the previous sentence should give an idea that otherwise error
    gets thrown.  That also happens immediately for sure.
    
    > 9) xlogwait.c
    >
    > I think this should start with a basic "design" description of how the
    > wait is implemented, in a comment at the top of the file. That is, what
    > we keep in the shared memory, what happens during a wait, how it uses
    > the pairing heap, etc. After reading this comment I should understand
    > how it all fits together.
    
    OK, I've added the header comment.
    
    > 10) WaitForLSNReplay / WaitLSNWakeup
    >
    > I think the function comment should document the important stuff (e.g.
    > return values for various situations, how it groups waiters into chunks
    > of 16 elements during wakeup, ...).
    
    Revised header comments for those functions too.
    
    > 11) WaitLSNProcInfo / WaitLSNState
    >
    > Does this need to be exposed in xlogwait.h? These structs seem private
    > to xlogwait.c, so maybe declare it there?
    
    Hmm, I don't remember why I moved them to xlogwait.h.  OK, moved them
    back to xlogwait.c.
    
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  13. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-08-05T13:47:07Z

    On 2025-Apr-29, Alexander Korotkov wrote:
    
    > > 11) WaitLSNProcInfo / WaitLSNState
    > >
    > > Does this need to be exposed in xlogwait.h? These structs seem private
    > > to xlogwait.c, so maybe declare it there?
    > 
    > Hmm, I don't remember why I moved them to xlogwait.h.  OK, moved them
    > back to xlogwait.c.
    
    This change made the code no longer compile, because
    WaitLSNState->minWaitedLSN is used in xlogrecovery.c which no longer has
    access to the field definition.  A rebased version with that change
    reverted is attached.
    
    -- 
    Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
    Thou shalt study thy libraries and strive not to reinvent them without
    cause, that thy code may be short and readable and thy days pleasant
    and productive. (7th Commandment for C Programmers)
    
  14. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-08-07T15:00:50Z

    Hi,
    
    Thanks for working on this.
    
    I’ve just come across this thread and haven’t had a chance to dig into
    the patch yet, but I’m keen to review it soon. In the meantime, I have
    a quick question: is WAIT FOR REPLY intended mainly for user-defined
    functions, or can internal code invoke it as well?
    
    During a recent performance run [1] I noticed heavy polling in
    read_local_xlog_page_guts(). Heikki’s comment from a few months ago
    also hints that we could replace this check–sleep–repeat loop with the
    condition-variable (CV) infrastructure used by walsender:
    
    /*
     * Loop waiting for xlog to be available if necessary
     *
     * TODO: The walsender has its own version of this function, which uses a
     * condition variable to wake up whenever WAL is flushed. We could use the
     * same infrastructure here, instead of the check/sleep/repeat style of
     * loop.
     */
    
    Because read_local_xlog_page_guts() waits for a specific flush or
    replay LSN, polling becomes inefficient when the wait is long. I built
    a POC patch that swaps polling for CVs, but a single global CV (or
    even separate “flush” and “replay” CVs) isn’t ideal:
    
    The wake-up routines don’t know which LSN each waiter cares about, so
    they’d have to broadcast on every flush/replay. Caching the minimum
    outstanding LSN could reduce spuriously awakened waiters, yet wouldn’t
    eliminate them—multiple backends might wait for different LSNs
    simultaneously. A more precise solution would require a request queue
    that maps waiters to target LSNs and issues targeted wake-ups, adding
    complexity.
    
    Walsender accepts the potential broadcast overhead by using two cvs
    for different waiters, so it might be acceptable for
    read_local_xlog_page_guts() as well. However, if WAIT FOR REPLY
    becomes available to backend code, we might leverage it to eliminate
    the polling for waiting replay in read_local_xlog_page_guts() without
    introducing a bespoke dispatcher. I’d appreciate any thoughts on
    whether that use case is in scope.
    
    Best,
    Xuneng
    
    [1] https://www.postgresql.org/message-id/CABPTF7VuFYm9TtA9vY8ZtS77qsT+yL_HtSDxUFnW3XsdB5b9ew@mail.gmail.com
    
    
    
    
  15. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-08-08T06:54:18Z

    Hello, Álvaro!
    
    On Wed, Aug 6, 2025 at 6:01 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    >
    > On 2025-Apr-29, Alexander Korotkov wrote:
    >
    > > > 11) WaitLSNProcInfo / WaitLSNState
    > > >
    > > > Does this need to be exposed in xlogwait.h? These structs seem private
    > > > to xlogwait.c, so maybe declare it there?
    > >
    > > Hmm, I don't remember why I moved them to xlogwait.h.  OK, moved them
    > > back to xlogwait.c.
    >
    > This change made the code no longer compile, because
    > WaitLSNState->minWaitedLSN is used in xlogrecovery.c which no longer has
    > access to the field definition.  A rebased version with that change
    > reverted is attached.
    
    Thank you!  The rebased version looks correct for me.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  16. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-08-08T07:08:49Z

    Hi, Xuneng Zhou!
    
    On Thu, Aug 7, 2025 at 6:01 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > Thanks for working on this.
    >
    > I’ve just come across this thread and haven’t had a chance to dig into
    > the patch yet, but I’m keen to review it soon.
    
    Great.  Thank you for your attention to this patch.  I appreciate your
    intention to review it.
    
    > In the meantime, I have
    > a quick question: is WAIT FOR REPLY intended mainly for user-defined
    > functions, or can internal code invoke it as well?
    
    Currently, WaitForLSNReplay() is assumed to only be called from
    backend, as corresponding shmem is allocated only per-backend.  But
    there is absolutely no problem to tweak the patch to allocate shmem
    for every Postgres process.  This would enable to call
    WaitForLSNReplay() wherever it is needed.  There is only no problem to
    extend this approach to support other kinds of LSNs not just replay
    LSN.
    
    
    > During a recent performance run [1] I noticed heavy polling in
    > read_local_xlog_page_guts(). Heikki’s comment from a few months ago
    > also hints that we could replace this check–sleep–repeat loop with the
    > condition-variable (CV) infrastructure used by walsender:
    >
    > /*
    >  * Loop waiting for xlog to be available if necessary
    >  *
    >  * TODO: The walsender has its own version of this function, which uses a
    >  * condition variable to wake up whenever WAL is flushed. We could use the
    >  * same infrastructure here, instead of the check/sleep/repeat style of
    >  * loop.
    >  */
    >
    > Because read_local_xlog_page_guts() waits for a specific flush or
    > replay LSN, polling becomes inefficient when the wait is long. I built
    > a POC patch that swaps polling for CVs, but a single global CV (or
    > even separate “flush” and “replay” CVs) isn’t ideal:
    >
    > The wake-up routines don’t know which LSN each waiter cares about, so
    > they’d have to broadcast on every flush/replay. Caching the minimum
    > outstanding LSN could reduce spuriously awakened waiters, yet wouldn’t
    > eliminate them—multiple backends might wait for different LSNs
    > simultaneously. A more precise solution would require a request queue
    > that maps waiters to target LSNs and issues targeted wake-ups, adding
    > complexity.
    >
    > Walsender accepts the potential broadcast overhead by using two cvs
    > for different waiters, so it might be acceptable for
    > read_local_xlog_page_guts() as well. However, if WAIT FOR REPLY
    > becomes available to backend code, we might leverage it to eliminate
    > the polling for waiting replay in read_local_xlog_page_guts() without
    > introducing a bespoke dispatcher. I’d appreciate any thoughts on
    > whether that use case is in scope.
    
    This looks like a great new use-case for facilities developed in this
    patch!  I'll remove the restriction to use WaitForLSNReplay() only in
    backend.  I think you can write a patch with additional pairing heap
    for flush LSN and include that into thread about
    read_local_xlog_page_guts() optimization.  Let me know if you need any
    assistance.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  17. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-08-09T10:52:37Z

    Hi Alexander!
    
    > > In the meantime, I have
    > > a quick question: is WAIT FOR REPLY intended mainly for user-defined
    > > functions, or can internal code invoke it as well?
    >
    > Currently, WaitForLSNReplay() is assumed to only be called from
    > backend, as corresponding shmem is allocated only per-backend.  But
    > there is absolutely no problem to tweak the patch to allocate shmem
    > for every Postgres process.  This would enable to call
    > WaitForLSNReplay() wherever it is needed.  There is only no problem to
    > extend this approach to support other kinds of LSNs not just replay
    > LSN.
    
    Thanks for extending the functionality of the Wait For Replay patch!
    
    > This looks like a great new use-case for facilities developed in this
    > patch!  I'll remove the restriction to use WaitForLSNReplay() only in
    > backend.  I think you can write a patch with additional pairing heap
    > for flush LSN and include that into thread about
    > read_local_xlog_page_guts() optimization.  Let me know if you need any
    > assistance.
    
    This could be a more elegant approach which would solve the polling
    issue well. I'll prepare a follow-up patch for it.
    
    Best,
    Xuneng
    
    
    
    
  18. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-08-09T11:27:25Z

    Hi,
    
    > On Thu, Aug 7, 2025 at 6:01 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > Thanks for working on this.
    > >
    > > I’ve just come across this thread and haven’t had a chance to dig into
    > > the patch yet, but I’m keen to review it soon.
    >
    > Great.  Thank you for your attention to this patch.  I appreciate your
    > intention to review it.
    
    I did a quick pass over v7. There are a few thoughts to share—mostly
    around documentation, build, and tests, plus some minor nits. The core
    logic looks solid to me. I’ll take a deeper look as I work on a
    follow‑up patch to add waiting for flush LSNs. And the patch seems to
    need rebase; it can't be applied to HEAD cleanly for now.
    
    Build
    1) Consider adding a comma in `src/test/recovery/meson.build` after
    `'t/048_vacuum_horizon_floor.pl'` so the list remains valid.
    
    Core code
    2) It may be safer for `WaitLSNWakeup()` to assert against the stack array size:
    ) Perhaps `Assert(numWakeUpProcs < WAKEUP_PROC_STATIC_ARRAY_SIZE);`
    rather than `MaxBackends`.
    For option parsing UX in `wait.c`, we might prefer:
    3) Using `ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
    errmsg(...)))` instead of `elog(ERROR, ...)` for consistency and
    translatability.
    4) Explicitly rejecting duplicate `LSN`/`TIMEOUT` options with a syntax error.
    5) The result column label could align better with other utility
    outputs if shortened to `status` (lowercase, no space).
    6) After `parse_real()`, it could help to validate/clamp the timeout
    to avoid overflow when converting to `int64` and when passing a `long`
    to `WaitLatch()`.
    7) If `nodes/print.h` in `src/backend/commands/wait.c` isn’t used, we
    might drop the include.
    8) A couple of comment nits: “do it this outside” → “do this outside”.
    
    Tests
    9) We might consider adding cases for:
    - Negative `TIMEOUT` (to exercise the error path).
    - Syntax errors (unknown option; duplicate `LSN`/`TIMEOUT`; missing `LSN`).
    
    Documentation
    `doc/src/sgml/ref/wait_for.sgml`
    10) The index term could be updated to `<primary>WAIT FOR</primary>`.
    11) The synopsis might read more clearly as:
    - WAIT FOR LSN '<lsn>' [ TIMEOUT <milliseconds |
    'duration-with-units'> ] [ NO_THROW ]
    12) The purpose line might be smoother as “wait for a target LSN to be
    replayed, optionally with a timeout”.
    13) Return values might use `<literal>` for `success`, `timeout`, `not
    in recovery`.
    14) Consistently calling this a “command” (rather than
    function/procedure) could reduce confusion.
    15) The example text might read more cleanly as “If the target LSN is
    not reached before the timeout …”.
    
    `doc/src/sgml/high-availability.sgml`
    16) The sentence could read “However, it is possible to address this
    without switching to synchronous replication.”
    
    `src/backend/utils/activity/wait_event_names.txt`
    17) The description for `WAIT_FOR_WAL_REPLAY` might be clearer as
    “Waiting for WAL replay to reach a target LSN on a standby.”
    
    Best,
    Xuneng
    
    
    
    
  19. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-08-27T15:54:25Z

    Hi all,
    
    I did a rebase for the patch to v8 and incorporated a few changes:
    
    1) Updated documentation, added new tests, and applied minor code
    adjustments based on prior review comments.
    2) Tweaked the initialization of waitReplayLSNState so that
    non-backend processes can call wait for replay.
    
    Started a new thread [1] and attached a patch addressing the polling
    issue in the function
    read_local_xlog_page_guts built on the infra of patch v8.
    
    [1] https://www.postgresql.org/message-id/CABPTF7Vr99gZ5GM_ZYbYnd9MMnoVW3pukBEviVoHKRvJW-dE3g@mail.gmail.com
    
    Feedbacks welcome.
    
    Best,
    Xuneng
    
  20. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-09-13T19:31:32Z

    Hi, Xuneng!
    
    On Wed, Aug 27, 2025 at 6:54 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > I did a rebase for the patch to v8 and incorporated a few changes:
    >
    > 1) Updated documentation, added new tests, and applied minor code
    > adjustments based on prior review comments.
    > 2) Tweaked the initialization of waitReplayLSNState so that
    > non-backend processes can call wait for replay.
    >
    > Started a new thread [1] and attached a patch addressing the polling
    > issue in the function
    > read_local_xlog_page_guts built on the infra of patch v8.
    >
    > [1] https://www.postgresql.org/message-id/CABPTF7Vr99gZ5GM_ZYbYnd9MMnoVW3pukBEviVoHKRvJW-dE3g@mail.gmail.com
    >
    > Feedbacks welcome.
    
    Thank you for your reviewing and revising this patch.
    
    I see you've integrated most of your points expressed in [1].  I went
    though them and I've integrated the rest of them.  Except this one.
    
    > 11) The synopsis might read more clearly as:
    > - WAIT FOR LSN '<lsn>' [ TIMEOUT <milliseconds | 'duration-with-units'> ] [ NO_THROW ]
    
    I didn't find examples on how we do the similar things on other places
    of docs.  This is why I decided to leave this place as it currently
    is.
    
    Also, I found some mess up with typedefs.list.  I've returned the
    changes to typdefs.list back and re-indented the sources.
    
    I'd like to ask your opinion of the way this feature is implemented in
    terms of grammar: generic parsing implemented in gram.y and the rest
    is done in wait.c.  I think this approach should minimize additional
    keywords and states for parsing code.  This comes at the price of more
    complex code in wait.c, but I think this is a fair price.
    
    Links.
    1. https://www.postgresql.org/message-id/CABPTF7VsoGDMBq34MpLrMSZyxNZvVbgH6-zxtJOg5AwOoYURbw%40mail.gmail.com
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  21. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-09-14T13:51:21Z

    Hi Alexander,
    
    On Sun, Sep 14, 2025 at 3:31 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Wed, Aug 27, 2025 at 6:54 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > I did a rebase for the patch to v8 and incorporated a few changes:
    > >
    > > 1) Updated documentation, added new tests, and applied minor code
    > > adjustments based on prior review comments.
    > > 2) Tweaked the initialization of waitReplayLSNState so that
    > > non-backend processes can call wait for replay.
    > >
    > > Started a new thread [1] and attached a patch addressing the polling
    > > issue in the function
    > > read_local_xlog_page_guts built on the infra of patch v8.
    > >
    > > [1] https://www.postgresql.org/message-id/CABPTF7Vr99gZ5GM_ZYbYnd9MMnoVW3pukBEviVoHKRvJW-dE3g@mail.gmail.com
    > >
    > > Feedbacks welcome.
    >
    > Thank you for your reviewing and revising this patch.
    >
    > I see you've integrated most of your points expressed in [1].  I went
    > though them and I've integrated the rest of them.  Except this one.
    >
    > > 11) The synopsis might read more clearly as:
    > > - WAIT FOR LSN '<lsn>' [ TIMEOUT <milliseconds | 'duration-with-units'> ] [ NO_THROW ]
    >
    > I didn't find examples on how we do the similar things on other places
    > of docs.  This is why I decided to leave this place as it currently
    > is.
    
    +1. I re-check other commands with similar parameter patterns, and
    they follow the approach in v9.
    
    >
    > Also, I found some mess up with typedefs.list.  I've returned the
    > changes to typdefs.list back and re-indented the sources.
    
     Thanks for catching and fixing that.
    
    > I'd like to ask your opinion of the way this feature is implemented in
    > terms of grammar: generic parsing implemented in gram.y and the rest
    > is done in wait.c.  I think this approach should minimize additional
    > keywords and states for parsing code.  This comes at the price of more
    > complex code in wait.c, but I think this is a fair price.
    
    It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    PUBLICATION - all use minimal grammar rules that produce generic
    option lists, with the actual interpretation done in their respective
    implementation files. The moderate complexity in wait.c seems
    acceptable.
    
    Best,
    Xuneng
    
    
    
    
  22. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-09-15T18:59:42Z

    Hi, Xuneng!
    
    On Sun, Sep 14, 2025 at 4:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Sun, Sep 14, 2025 at 3:31 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > On Wed, Aug 27, 2025 at 6:54 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > I did a rebase for the patch to v8 and incorporated a few changes:
    > > >
    > > > 1) Updated documentation, added new tests, and applied minor code
    > > > adjustments based on prior review comments.
    > > > 2) Tweaked the initialization of waitReplayLSNState so that
    > > > non-backend processes can call wait for replay.
    > > >
    > > > Started a new thread [1] and attached a patch addressing the polling
    > > > issue in the function
    > > > read_local_xlog_page_guts built on the infra of patch v8.
    > > >
    > > > [1] https://www.postgresql.org/message-id/CABPTF7Vr99gZ5GM_ZYbYnd9MMnoVW3pukBEviVoHKRvJW-dE3g@mail.gmail.com
    > > >
    > > > Feedbacks welcome.
    > >
    > > Thank you for your reviewing and revising this patch.
    > >
    > > I see you've integrated most of your points expressed in [1].  I went
    > > though them and I've integrated the rest of them.  Except this one.
    > >
    > > > 11) The synopsis might read more clearly as:
    > > > - WAIT FOR LSN '<lsn>' [ TIMEOUT <milliseconds | 'duration-with-units'> ] [ NO_THROW ]
    > >
    > > I didn't find examples on how we do the similar things on other places
    > > of docs.  This is why I decided to leave this place as it currently
    > > is.
    >
    > +1. I re-check other commands with similar parameter patterns, and
    > they follow the approach in v9.
    >
    > >
    > > Also, I found some mess up with typedefs.list.  I've returned the
    > > changes to typdefs.list back and re-indented the sources.
    >
    >  Thanks for catching and fixing that.
    >
    > > I'd like to ask your opinion of the way this feature is implemented in
    > > terms of grammar: generic parsing implemented in gram.y and the rest
    > > is done in wait.c.  I think this approach should minimize additional
    > > keywords and states for parsing code.  This comes at the price of more
    > > complex code in wait.c, but I think this is a fair price.
    >
    > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > PUBLICATION - all use minimal grammar rules that produce generic
    > option lists, with the actual interpretation done in their respective
    > implementation files. The moderate complexity in wait.c seems
    > acceptable.
    
    The attached revision of patch contains fix of the typo in the comment
    you reported off-list.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  23. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-09-15T20:24:05Z

    On 2025-Sep-15, Alexander Korotkov wrote:
    
    > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > PUBLICATION - all use minimal grammar rules that produce generic
    > > option lists, with the actual interpretation done in their respective
    > > implementation files. The moderate complexity in wait.c seems
    > > acceptable.
    
    Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    lists of DefElem (a name optionally followed by a value) instead of
    individual scattered elements that must later be matched up.  Why not
    use utility_option_list instead and then loop on the list of DefElems?
    It'd be a lot simpler.
    
    Also, we've found that failing to surround the options by parens leads
    to pain down the road, so maybe add that.  Given that the LSN seems to
    be mandatory, maybe make it something like
    
    WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    
    This requires that you make LSN a keyword, albeit unreserved.  Or you
    could make it
    WAIT FOR Ident [the rest]
    and then ensure in C that the identifier matches the word LSN, such as
    we do for "permissive" and "restrictive" in
    RowSecurityDefaultPermissive.
    
    -- 
    Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
    
    
    
    
  24. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-09-26T11:22:42Z

    Hi Álvaro,
    
    Thanks for your review.
    
    On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    >
    > On 2025-Sep-15, Alexander Korotkov wrote:
    >
    > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > option lists, with the actual interpretation done in their respective
    > > > implementation files. The moderate complexity in wait.c seems
    > > > acceptable.
    >
    > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > lists of DefElem (a name optionally followed by a value) instead of
    > individual scattered elements that must later be matched up.  Why not
    > use utility_option_list instead and then loop on the list of DefElems?
    > It'd be a lot simpler.
    
    I took a look at commands like VACUUM and EXPLAIN and they do follow
    this pattern. v11 will make use of utility_option_list.
    
    > Also, we've found that failing to surround the options by parens leads
    > to pain down the road, so maybe add that.  Given that the LSN seems to
    > be mandatory, maybe make it something like
    >
    > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    >
    > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > could make it
    > WAIT FOR Ident [the rest]
    > and then ensure in C that the identifier matches the word LSN, such as
    > we do for "permissive" and "restrictive" in
    > RowSecurityDefaultPermissive.
    
    Shall make LSN an unreserved keyword as well.
    
    Best,
    Xuneng
    
    
    
    
  25. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-09-28T09:02:43Z

    Hi,
    
    On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Álvaro,
    >
    > Thanks for your review.
    >
    > On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > >
    > > On 2025-Sep-15, Alexander Korotkov wrote:
    > >
    > > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > > option lists, with the actual interpretation done in their respective
    > > > > implementation files. The moderate complexity in wait.c seems
    > > > > acceptable.
    > >
    > > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > > lists of DefElem (a name optionally followed by a value) instead of
    > > individual scattered elements that must later be matched up.  Why not
    > > use utility_option_list instead and then loop on the list of DefElems?
    > > It'd be a lot simpler.
    >
    > I took a look at commands like VACUUM and EXPLAIN and they do follow
    > this pattern. v11 will make use of utility_option_list.
    >
    > > Also, we've found that failing to surround the options by parens leads
    > > to pain down the road, so maybe add that.  Given that the LSN seems to
    > > be mandatory, maybe make it something like
    > >
    > > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > >
    > > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > > could make it
    > > WAIT FOR Ident [the rest]
    > > and then ensure in C that the identifier matches the word LSN, such as
    > > we do for "permissive" and "restrictive" in
    > > RowSecurityDefaultPermissive.
    >
    > Shall make LSN an unreserved keyword as well.
    
    Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    
    Best,
    Xuneng
    
  26. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-04T01:35:32Z

    Hi,
    
    On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Álvaro,
    > >
    > > Thanks for your review.
    > >
    > > On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > >
    > > > On 2025-Sep-15, Alexander Korotkov wrote:
    > > >
    > > > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > > > option lists, with the actual interpretation done in their respective
    > > > > > implementation files. The moderate complexity in wait.c seems
    > > > > > acceptable.
    > > >
    > > > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > > > lists of DefElem (a name optionally followed by a value) instead of
    > > > individual scattered elements that must later be matched up.  Why not
    > > > use utility_option_list instead and then loop on the list of DefElems?
    > > > It'd be a lot simpler.
    > >
    > > I took a look at commands like VACUUM and EXPLAIN and they do follow
    > > this pattern. v11 will make use of utility_option_list.
    > >
    > > > Also, we've found that failing to surround the options by parens leads
    > > > to pain down the road, so maybe add that.  Given that the LSN seems to
    > > > be mandatory, maybe make it something like
    > > >
    > > > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > > >
    > > > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > > > could make it
    > > > WAIT FOR Ident [the rest]
    > > > and then ensure in C that the identifier matches the word LSN, such as
    > > > we do for "permissive" and "restrictive" in
    > > > RowSecurityDefaultPermissive.
    > >
    > > Shall make LSN an unreserved keyword as well.
    >
    > Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    
    v12 removed unused
    +WaitStmt
    +WaitStmtParam in pgindent/typedefs.list.
    
    Best,
    Xuneng
    
  27. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-14T13:03:30Z

    Hi,
    
    On Sat, Oct 4, 2025 at 9:35 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi Álvaro,
    > > >
    > > > Thanks for your review.
    > > >
    > > > On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > >
    > > > > On 2025-Sep-15, Alexander Korotkov wrote:
    > > > >
    > > > > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > > > > option lists, with the actual interpretation done in their respective
    > > > > > > implementation files. The moderate complexity in wait.c seems
    > > > > > > acceptable.
    > > > >
    > > > > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > > > > lists of DefElem (a name optionally followed by a value) instead of
    > > > > individual scattered elements that must later be matched up.  Why not
    > > > > use utility_option_list instead and then loop on the list of DefElems?
    > > > > It'd be a lot simpler.
    > > >
    > > > I took a look at commands like VACUUM and EXPLAIN and they do follow
    > > > this pattern. v11 will make use of utility_option_list.
    > > >
    > > > > Also, we've found that failing to surround the options by parens leads
    > > > > to pain down the road, so maybe add that.  Given that the LSN seems to
    > > > > be mandatory, maybe make it something like
    > > > >
    > > > > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > > > >
    > > > > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > > > > could make it
    > > > > WAIT FOR Ident [the rest]
    > > > > and then ensure in C that the identifier matches the word LSN, such as
    > > > > we do for "permissive" and "restrictive" in
    > > > > RowSecurityDefaultPermissive.
    > > >
    > > > Shall make LSN an unreserved keyword as well.
    > >
    > > Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    >
    > v12 removed unused
    > +WaitStmt
    > +WaitStmtParam in pgindent/typedefs.list.
    >
    
    Hi, I’ve split the patch into multiple patch sets for easier review,
    per Michael’s advice [1].
    
    [1] https://www.postgresql.org/message-id/aOMsv9TszlB1n-W7%40paquier.xyz
    
    Best,
    Xuneng
    
  28. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-15T00:23:09Z

    Hi,
    
    On Tue, Oct 14, 2025 at 9:03 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Sat, Oct 4, 2025 at 9:35 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi Álvaro,
    > > > >
    > > > > Thanks for your review.
    > > > >
    > > > > On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > > >
    > > > > > On 2025-Sep-15, Alexander Korotkov wrote:
    > > > > >
    > > > > > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > > > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > > > > > option lists, with the actual interpretation done in their respective
    > > > > > > > implementation files. The moderate complexity in wait.c seems
    > > > > > > > acceptable.
    > > > > >
    > > > > > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > > > > > lists of DefElem (a name optionally followed by a value) instead of
    > > > > > individual scattered elements that must later be matched up.  Why not
    > > > > > use utility_option_list instead and then loop on the list of DefElems?
    > > > > > It'd be a lot simpler.
    > > > >
    > > > > I took a look at commands like VACUUM and EXPLAIN and they do follow
    > > > > this pattern. v11 will make use of utility_option_list.
    > > > >
    > > > > > Also, we've found that failing to surround the options by parens leads
    > > > > > to pain down the road, so maybe add that.  Given that the LSN seems to
    > > > > > be mandatory, maybe make it something like
    > > > > >
    > > > > > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > > > > >
    > > > > > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > > > > > could make it
    > > > > > WAIT FOR Ident [the rest]
    > > > > > and then ensure in C that the identifier matches the word LSN, such as
    > > > > > we do for "permissive" and "restrictive" in
    > > > > > RowSecurityDefaultPermissive.
    > > > >
    > > > > Shall make LSN an unreserved keyword as well.
    > > >
    > > > Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    > >
    > > v12 removed unused
    > > +WaitStmt
    > > +WaitStmtParam in pgindent/typedefs.list.
    > >
    >
    > Hi, I’ve split the patch into multiple patch sets for easier review,
    > per Michael’s advice [1].
    >
    > [1] https://www.postgresql.org/message-id/aOMsv9TszlB1n-W7%40paquier.xyz
    >
    
    Patch 2 in v13 is corrupted and patch 3 has an error. Sorry for the
    noise. Here's v14.
    
    Best,
    Xuneng
    
  29. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-15T08:40:03Z

    Hi,
    
    On Wed, Oct 15, 2025 at 8:23 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Oct 14, 2025 at 9:03 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Sat, Oct 4, 2025 at 9:35 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi,
    > > > >
    > > > > On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > >
    > > > > > Hi Álvaro,
    > > > > >
    > > > > > Thanks for your review.
    > > > > >
    > > > > > On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > > > >
    > > > > > > On 2025-Sep-15, Alexander Korotkov wrote:
    > > > > > >
    > > > > > > > > It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > > > > > > > > PUBLICATION - all use minimal grammar rules that produce generic
    > > > > > > > > option lists, with the actual interpretation done in their respective
    > > > > > > > > implementation files. The moderate complexity in wait.c seems
    > > > > > > > > acceptable.
    > > > > > >
    > > > > > > Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > > > > > > lists of DefElem (a name optionally followed by a value) instead of
    > > > > > > individual scattered elements that must later be matched up.  Why not
    > > > > > > use utility_option_list instead and then loop on the list of DefElems?
    > > > > > > It'd be a lot simpler.
    > > > > >
    > > > > > I took a look at commands like VACUUM and EXPLAIN and they do follow
    > > > > > this pattern. v11 will make use of utility_option_list.
    > > > > >
    > > > > > > Also, we've found that failing to surround the options by parens leads
    > > > > > > to pain down the road, so maybe add that.  Given that the LSN seems to
    > > > > > > be mandatory, maybe make it something like
    > > > > > >
    > > > > > > WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > > > > > >
    > > > > > > This requires that you make LSN a keyword, albeit unreserved.  Or you
    > > > > > > could make it
    > > > > > > WAIT FOR Ident [the rest]
    > > > > > > and then ensure in C that the identifier matches the word LSN, such as
    > > > > > > we do for "permissive" and "restrictive" in
    > > > > > > RowSecurityDefaultPermissive.
    > > > > >
    > > > > > Shall make LSN an unreserved keyword as well.
    > > > >
    > > > > Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    > > >
    > > > v12 removed unused
    > > > +WaitStmt
    > > > +WaitStmtParam in pgindent/typedefs.list.
    > > >
    > >
    > > Hi, I’ve split the patch into multiple patch sets for easier review,
    > > per Michael’s advice [1].
    > >
    > > [1] https://www.postgresql.org/message-id/aOMsv9TszlB1n-W7%40paquier.xyz
    > >
    >
    > Patch 2 in v13 is corrupted and patch 3 has an error. Sorry for the
    > noise. Here's v14.
    >
    
    Made minor changes to #include of xlogwait.h in patch2 to calm CF-bots down.
    
    Best,
    Xuneng
    
  30. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-10-15T08:51:42Z

    I didn't review the patch other than look at the grammar, but I disagree
    with using opt_with in it.  I think WITH should be a mandatory word, or
    just not be there at all.  The current formulation lets you do one of:
    
    1. WAIT FOR LSN '123/456' WITH (opt = val);
    2. WAIT FOR LSN '123/456' (opt = val);
    3. WAIT FOR LSN '123/456';
    
    and I don't see why you need two ways to specify an option list.
    
    So one option is to remove opt_wait_with_clause and just use
    opt_utility_option_list, which would remove the WITH keyword from there
    (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    just look at the REPACK grammar[1], where we have to have additional
    productions for the optional parenthesized option list.
    
    So why not do just
    
    +opt_wait_with_clause:
    +           WITH '(' utility_option_list ')'        { $$ = $3; }
    +           | /*EMPTY*/                             { $$ = NIL; }
    +           ;
    
    which keeps options 1 and 3 of the list above.
    
    Note: you don't need to worry about WITH_LA, because that's only going
    to show up when the user writes WITH TIME or WITH ORDINALITY (see
    parser.c), and that's a syntax error anyway.
    
    
    [1] https://postgr.es/m/202510101352.vvp4p3p2dblu@alvherre.pgsql
    
    -- 
    Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
    "La virtud es el justo medio entre dos defectos" (Aristóteles)
    
    
    
    
  31. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-15T12:48:29Z

    Hi,
    
    Thank you for the grammar review and the clear recommendation.
    
    On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    >
    > I didn't review the patch other than look at the grammar, but I disagree
    > with using opt_with in it.  I think WITH should be a mandatory word, or
    > just not be there at all.  The current formulation lets you do one of:
    >
    > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > 2. WAIT FOR LSN '123/456' (opt = val);
    > 3. WAIT FOR LSN '123/456';
    >
    > and I don't see why you need two ways to specify an option list.
    
    I agree with this as unnecessary choices are confusing.
    
    >
    > So one option is to remove opt_wait_with_clause and just use
    > opt_utility_option_list, which would remove the WITH keyword from there
    > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > just look at the REPACK grammar[1], where we have to have additional
    > productions for the optional parenthesized option list.
    >
    >
    >
    > So why not do just
    >
    > +opt_wait_with_clause:
    > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > +           | /*EMPTY*/                             { $$ = NIL; }
    > +           ;
    >
    > which keeps options 1 and 3 of the list above.
    
    Your suggested approach of making WITH mandatory when options are
    present looks better.
    I've implemented the change as you recommended. Please see patch 3 in v16.
    
    >
    >
    >
    > Note: you don't need to worry about WITH_LA, because that's only going
    > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > parser.c), and that's a syntax error anyway.
    >
    
    Yeah, we require '(' immediately after WITH in our grammar, the
    lookahead mechanism will keep it as regular WITH, and any attempt to
    write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    which is expected.
    
    Best,
    Xuneng
    
  32. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-16T07:11:58Z

    Hi,
    
    On Wed, Oct 15, 2025 at 8:48 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > Thank you for the grammar review and the clear recommendation.
    >
    > On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > >
    > > I didn't review the patch other than look at the grammar, but I disagree
    > > with using opt_with in it.  I think WITH should be a mandatory word, or
    > > just not be there at all.  The current formulation lets you do one of:
    > >
    > > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > > 2. WAIT FOR LSN '123/456' (opt = val);
    > > 3. WAIT FOR LSN '123/456';
    > >
    > > and I don't see why you need two ways to specify an option list.
    >
    > I agree with this as unnecessary choices are confusing.
    >
    > >
    > > So one option is to remove opt_wait_with_clause and just use
    > > opt_utility_option_list, which would remove the WITH keyword from there
    > > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > > just look at the REPACK grammar[1], where we have to have additional
    > > productions for the optional parenthesized option list.
    > >
    > >
    > >
    > > So why not do just
    > >
    > > +opt_wait_with_clause:
    > > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > > +           | /*EMPTY*/                             { $$ = NIL; }
    > > +           ;
    > >
    > > which keeps options 1 and 3 of the list above.
    >
    > Your suggested approach of making WITH mandatory when options are
    > present looks better.
    > I've implemented the change as you recommended. Please see patch 3 in v16.
    >
    > >
    > >
    > >
    > > Note: you don't need to worry about WITH_LA, because that's only going
    > > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > > parser.c), and that's a syntax error anyway.
    > >
    >
    > Yeah, we require '(' immediately after WITH in our grammar, the
    > lookahead mechanism will keep it as regular WITH, and any attempt to
    > write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    > which is expected.
    >
    
    The filename of patch 1 is incorrect due to coping. Just correct it.
    
    Best,
    Xuneng
    
  33. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-10-23T10:46:27Z

    Hi!
    
    In Thu, Oct 16, 2025 at 10:12 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Wed, Oct 15, 2025 at 8:48 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > Thank you for the grammar review and the clear recommendation.
    > >
    > > On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > >
    > > > I didn't review the patch other than look at the grammar, but I disagree
    > > > with using opt_with in it.  I think WITH should be a mandatory word, or
    > > > just not be there at all.  The current formulation lets you do one of:
    > > >
    > > > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > > > 2. WAIT FOR LSN '123/456' (opt = val);
    > > > 3. WAIT FOR LSN '123/456';
    > > >
    > > > and I don't see why you need two ways to specify an option list.
    > >
    > > I agree with this as unnecessary choices are confusing.
    > >
    > > >
    > > > So one option is to remove opt_wait_with_clause and just use
    > > > opt_utility_option_list, which would remove the WITH keyword from there
    > > > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > > > just look at the REPACK grammar[1], where we have to have additional
    > > > productions for the optional parenthesized option list.
    > > >
    > > >
    > > >
    > > > So why not do just
    > > >
    > > > +opt_wait_with_clause:
    > > > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > > > +           | /*EMPTY*/                             { $$ = NIL; }
    > > > +           ;
    > > >
    > > > which keeps options 1 and 3 of the list above.
    > >
    > > Your suggested approach of making WITH mandatory when options are
    > > present looks better.
    > > I've implemented the change as you recommended. Please see patch 3 in v16.
    > >
    > > >
    > > >
    > > >
    > > > Note: you don't need to worry about WITH_LA, because that's only going
    > > > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > > > parser.c), and that's a syntax error anyway.
    > > >
    > >
    > > Yeah, we require '(' immediately after WITH in our grammar, the
    > > lookahead mechanism will keep it as regular WITH, and any attempt to
    > > write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    > > which is expected.
    > >
    >
    > The filename of patch 1 is incorrect due to coping. Just correct it.
    
    Thank you for rebasing the patch.
    
    I've revised it.  The significant changes has been made to 0002, where
    I reduced the code duplication.  Also, I run pgindent and pgperltidy
    and made other small improvements.
    Please, check.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  34. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-10-23T12:58:46Z

    Hi,
    
    On Thu, Oct 23, 2025 at 6:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi!
    >
    > In Thu, Oct 16, 2025 at 10:12 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Wed, Oct 15, 2025 at 8:48 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > Thank you for the grammar review and the clear recommendation.
    > > >
    > > > On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > >
    > > > > I didn't review the patch other than look at the grammar, but I disagree
    > > > > with using opt_with in it.  I think WITH should be a mandatory word, or
    > > > > just not be there at all.  The current formulation lets you do one of:
    > > > >
    > > > > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > > > > 2. WAIT FOR LSN '123/456' (opt = val);
    > > > > 3. WAIT FOR LSN '123/456';
    > > > >
    > > > > and I don't see why you need two ways to specify an option list.
    > > >
    > > > I agree with this as unnecessary choices are confusing.
    > > >
    > > > >
    > > > > So one option is to remove opt_wait_with_clause and just use
    > > > > opt_utility_option_list, which would remove the WITH keyword from there
    > > > > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > > > > just look at the REPACK grammar[1], where we have to have additional
    > > > > productions for the optional parenthesized option list.
    > > > >
    > > > >
    > > > >
    > > > > So why not do just
    > > > >
    > > > > +opt_wait_with_clause:
    > > > > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > > > > +           | /*EMPTY*/                             { $$ = NIL; }
    > > > > +           ;
    > > > >
    > > > > which keeps options 1 and 3 of the list above.
    > > >
    > > > Your suggested approach of making WITH mandatory when options are
    > > > present looks better.
    > > > I've implemented the change as you recommended. Please see patch 3 in v16.
    > > >
    > > > >
    > > > >
    > > > >
    > > > > Note: you don't need to worry about WITH_LA, because that's only going
    > > > > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > > > > parser.c), and that's a syntax error anyway.
    > > > >
    > > >
    > > > Yeah, we require '(' immediately after WITH in our grammar, the
    > > > lookahead mechanism will keep it as regular WITH, and any attempt to
    > > > write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    > > > which is expected.
    > > >
    > >
    > > The filename of patch 1 is incorrect due to coping. Just correct it.
    >
    > Thank you for rebasing the patch.
    >
    > I've revised it.  The significant changes has been made to 0002, where
    > I reduced the code duplication.  Also, I run pgindent and pgperltidy
    > and made other small improvements.
    > Please, check.
    
    Thanks for updating the patch set!
    Patch 2 looks more elegant after the revision. I’ll review them soon.
    
    Best,
    Xuneng
    
    
    
    
  35. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-02T06:24:48Z

    Hi, Alexander!
    
    On Thu, Oct 23, 2025 at 8:58 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Thu, Oct 23, 2025 at 6:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > Hi!
    > >
    > > In Thu, Oct 16, 2025 at 10:12 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Wed, Oct 15, 2025 at 8:48 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi,
    > > > >
    > > > > Thank you for the grammar review and the clear recommendation.
    > > > >
    > > > > On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > > >
    > > > > > I didn't review the patch other than look at the grammar, but I disagree
    > > > > > with using opt_with in it.  I think WITH should be a mandatory word, or
    > > > > > just not be there at all.  The current formulation lets you do one of:
    > > > > >
    > > > > > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > > > > > 2. WAIT FOR LSN '123/456' (opt = val);
    > > > > > 3. WAIT FOR LSN '123/456';
    > > > > >
    > > > > > and I don't see why you need two ways to specify an option list.
    > > > >
    > > > > I agree with this as unnecessary choices are confusing.
    > > > >
    > > > > >
    > > > > > So one option is to remove opt_wait_with_clause and just use
    > > > > > opt_utility_option_list, which would remove the WITH keyword from there
    > > > > > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > > > > > just look at the REPACK grammar[1], where we have to have additional
    > > > > > productions for the optional parenthesized option list.
    > > > > >
    > > > > >
    > > > > >
    > > > > > So why not do just
    > > > > >
    > > > > > +opt_wait_with_clause:
    > > > > > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > > > > > +           | /*EMPTY*/                             { $$ = NIL; }
    > > > > > +           ;
    > > > > >
    > > > > > which keeps options 1 and 3 of the list above.
    > > > >
    > > > > Your suggested approach of making WITH mandatory when options are
    > > > > present looks better.
    > > > > I've implemented the change as you recommended. Please see patch 3 in v16.
    > > > >
    > > > > >
    > > > > >
    > > > > >
    > > > > > Note: you don't need to worry about WITH_LA, because that's only going
    > > > > > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > > > > > parser.c), and that's a syntax error anyway.
    > > > > >
    > > > >
    > > > > Yeah, we require '(' immediately after WITH in our grammar, the
    > > > > lookahead mechanism will keep it as regular WITH, and any attempt to
    > > > > write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    > > > > which is expected.
    > > > >
    > > >
    > > > The filename of patch 1 is incorrect due to coping. Just correct it.
    > >
    > > Thank you for rebasing the patch.
    > >
    > > I've revised it.  The significant changes has been made to 0002, where
    > > I reduced the code duplication.  Also, I run pgindent and pgperltidy
    > > and made other small improvements.
    > > Please, check.
    >
    > Thanks for updating the patch set!
    > Patch 2 looks more elegant after the revision. I’ll review them soon.
    
    I’ve made a few minor updates to the comments and docs in patches 2
    and 3. The patch set LGTM now.
    
    Best,
    Xuneng
    
  36. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-03T02:20:28Z

    Hi,
    
    On Sun, Nov 2, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi, Alexander!
    >
    > On Thu, Oct 23, 2025 at 8:58 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Thu, Oct 23, 2025 at 6:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > Hi!
    > > >
    > > > In Thu, Oct 16, 2025 at 10:12 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > On Wed, Oct 15, 2025 at 8:48 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > >
    > > > > > Hi,
    > > > > >
    > > > > > Thank you for the grammar review and the clear recommendation.
    > > > > >
    > > > > > On Wed, Oct 15, 2025 at 4:51 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > > > >
    > > > > > > I didn't review the patch other than look at the grammar, but I disagree
    > > > > > > with using opt_with in it.  I think WITH should be a mandatory word, or
    > > > > > > just not be there at all.  The current formulation lets you do one of:
    > > > > > >
    > > > > > > 1. WAIT FOR LSN '123/456' WITH (opt = val);
    > > > > > > 2. WAIT FOR LSN '123/456' (opt = val);
    > > > > > > 3. WAIT FOR LSN '123/456';
    > > > > > >
    > > > > > > and I don't see why you need two ways to specify an option list.
    > > > > >
    > > > > > I agree with this as unnecessary choices are confusing.
    > > > > >
    > > > > > >
    > > > > > > So one option is to remove opt_wait_with_clause and just use
    > > > > > > opt_utility_option_list, which would remove the WITH keyword from there
    > > > > > > (ie. only keep 2 and 3 from the above list).  But I think that's worse:
    > > > > > > just look at the REPACK grammar[1], where we have to have additional
    > > > > > > productions for the optional parenthesized option list.
    > > > > > >
    > > > > > >
    > > > > > >
    > > > > > > So why not do just
    > > > > > >
    > > > > > > +opt_wait_with_clause:
    > > > > > > +           WITH '(' utility_option_list ')'        { $$ = $3; }
    > > > > > > +           | /*EMPTY*/                             { $$ = NIL; }
    > > > > > > +           ;
    > > > > > >
    > > > > > > which keeps options 1 and 3 of the list above.
    > > > > >
    > > > > > Your suggested approach of making WITH mandatory when options are
    > > > > > present looks better.
    > > > > > I've implemented the change as you recommended. Please see patch 3 in v16.
    > > > > >
    > > > > > >
    > > > > > >
    > > > > > >
    > > > > > > Note: you don't need to worry about WITH_LA, because that's only going
    > > > > > > to show up when the user writes WITH TIME or WITH ORDINALITY (see
    > > > > > > parser.c), and that's a syntax error anyway.
    > > > > > >
    > > > > >
    > > > > > Yeah, we require '(' immediately after WITH in our grammar, the
    > > > > > lookahead mechanism will keep it as regular WITH, and any attempt to
    > > > > > write "WITH TIME" or "WITH ORDINALITY" would be a syntax error anyway,
    > > > > > which is expected.
    > > > > >
    > > > >
    > > > > The filename of patch 1 is incorrect due to coping. Just correct it.
    > > >
    > > > Thank you for rebasing the patch.
    > > >
    > > > I've revised it.  The significant changes has been made to 0002, where
    > > > I reduced the code duplication.  Also, I run pgindent and pgperltidy
    > > > and made other small improvements.
    > > > Please, check.
    > >
    > > Thanks for updating the patch set!
    > > Patch 2 looks more elegant after the revision. I’ll review them soon.
    >
    > I’ve made a few minor updates to the comments and docs in patches 2
    > and 3. The patch set LGTM now.
    
    Fix an minor issue in v18: WaitStmt was mistakenly added to
    pgindent/typedefs.list in patch 2, but it should belong to patch 3.
    
    Best,
    Xuneng
    
  37. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-03T11:46:40Z

    Hello, Xuneng!
    
    On Mon, Nov 3, 2025 at 4:20 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Sun, Nov 2, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Thu, Oct 23, 2025 at 8:58 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > I’ve made a few minor updates to the comments and docs in patches 2
    > > and 3. The patch set LGTM now.
    >
    > Fix an minor issue in v18: WaitStmt was mistakenly added to
    > pgindent/typedefs.list in patch 2, but it should belong to patch 3.
    
    Thank you.  I also made some minor changes to 0002 renaming
    "operation" => "lsnType".
    
    I'd like to give this subject another chance for pg19.  I'm going to
    push this if no objections.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  38. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-11-03T15:06:58Z

    On 2025-Nov-03, Alexander Korotkov wrote:
    
    > I'd like to give this subject another chance for pg19.  I'm going to
    > push this if no objections.
    
    Sure.  I don't understand why patches 0002 and 0003 are separate though.
    
    -- 
    Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
    
    
    
    
  39. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2025-11-03T15:13:02Z

    Hi,
    
    On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > On 2025-Nov-03, Alexander Korotkov wrote:
    > 
    > > I'd like to give this subject another chance for pg19.  I'm going to
    > > push this if no objections.
    > 
    > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    
    FWIW, I appreciate such splits. Even if the functionality isn't usable
    independently, it's still different type of code that's affected. And the
    patches are each big enough to make that worthwhile for easier review.
    
    One thing that'd be nice to do once we have WAIT FOR is to make the common
    case of wait_for_catchup() use this facility, instead of polling...
    
    Greetings,
    
    Andres Freund
    
    
    
    
  40. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-05T09:51:18Z

    Hi!
    
    On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > On 2025-Nov-03, Alexander Korotkov wrote:
    > >
    > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > push this if no objections.
    > >
    > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    >
    > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > independently, it's still different type of code that's affected. And the
    > patches are each big enough to make that worthwhile for easier review.
    
    Thank you for the feedback, pushed.
    
    > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > case of wait_for_catchup() use this facility, instead of polling...
    
    The draft patch for that is attached.  WAIT FOR doesn't handle all the
    possible use cases of wait_for_catchup(), but I've added usage when
    it's appropriate.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  41. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-05T14:03:25Z

    Hi,
    
    On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi!
    >
    > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > >
    > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > push this if no objections.
    > > >
    > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > >
    > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > independently, it's still different type of code that's affected. And the
    > > patches are each big enough to make that worthwhile for easier review.
    >
    > Thank you for the feedback, pushed.
    
    Thanks for pushing them!
    
    > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > case of wait_for_catchup() use this facility, instead of polling...
    >
    > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > possible use cases of wait_for_catchup(), but I've added usage when
    > it's appropriate.
    
    Interesting, could this approach be extended to the flush and other
    modes as well? I might need to spend some time to understand it before
    I can provide a meaningful review.
    
    Best,
    Xuneng
    
    
    
    
  42. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-07T22:02:36Z

    On Wed, Nov 5, 2025 at 4:03 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > >
    > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > push this if no objections.
    > > > >
    > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > >
    > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > independently, it's still different type of code that's affected. And the
    > > > patches are each big enough to make that worthwhile for easier review.
    > >
    > > Thank you for the feedback, pushed.
    >
    > Thanks for pushing them!
    >
    > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > case of wait_for_catchup() use this facility, instead of polling...
    > >
    > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > possible use cases of wait_for_catchup(), but I've added usage when
    > > it's appropriate.
    >
    > Interesting, could this approach be extended to the flush and other
    > modes as well? I might need to spend some time to understand it before
    > I can provide a meaningful review.
    
    I think we might end up extending WaitLSNType enum.  However, I hate
    inHeap and heapNode arrays growing in WaitLSNProcInfo as they are
    allocated per process.  I found that we could optimize WaitLSNProcInfo
    struct turning them into simple variables because a single process can
    wait only for a single LSN at a time.  Please, check the attached
    patch.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  43. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-12T07:19:53Z

    Hi,
    
    On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi!
    >
    > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > >
    > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > push this if no objections.
    > > >
    > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > >
    > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > independently, it's still different type of code that's affected. And the
    > > patches are each big enough to make that worthwhile for easier review.
    >
    > Thank you for the feedback, pushed.
    >
    > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > case of wait_for_catchup() use this facility, instead of polling...
    >
    > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > possible use cases of wait_for_catchup(), but I've added usage when
    > it's appropriate.
    >
    > ------
    > Regards,
    > Alexander Korotkov
    > Supabase
    
    I tested the patch using make check-world, and it worked well. I also
    made a few adjustments:
    
    - Added an unconditional chomp($isrecovery) after querying
    pg_is_in_recovery() to prevent newline mismatches when $target_lsn is
    accidently defined.
    - Added chomp($output) to normalize the result from WAIT FOR LSN
    before comparison.
    
    At the moment, the WAIT FOR LSN command supports only the replay mode.
    If we intend to extend its functionality more broadly, one option is
    to add a mode option or something similar. Are users expected to wait
    for flush(or others) completion in such cases? If not, and the TAP
    test is the only intended use, this approach might be a bit of an
    overkill.
    
    --
    Best,
    Xuneng
    
  44. Re: Implement waiting for wal lsn replay: reloaded

    Tomas Vondra <tomas@vondra.me> — 2025-11-13T20:32:31Z

    On 11/5/25 10:51, Alexander Korotkov wrote:
    > Hi!
    > 
    > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    >> On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    >>> On 2025-Nov-03, Alexander Korotkov wrote:
    >>>
    >>>> I'd like to give this subject another chance for pg19.  I'm going to
    >>>> push this if no objections.
    >>>
    >>> Sure.  I don't understand why patches 0002 and 0003 are separate though.
    >>
    >> FWIW, I appreciate such splits. Even if the functionality isn't usable
    >> independently, it's still different type of code that's affected. And the
    >> patches are each big enough to make that worthwhile for easier review.
    > 
    > Thank you for the feedback, pushed.
    > 
    
    Hi,
    
    The new TAP test 049_wait_for_lsn.pl introduced by this commit, because
    it takes a long time - about 65 seconds on my laptop. That's about 25%
    of the whole src/test/recovery, more than any other test.
    
    And most of the time there's nothing happening - these are the two log
    messages showing the 60-second wait:
    
    2025-11-13 21:12:39.949 CET checkpointer[562597] LOG:  checkpoint
    complete: wrote 9 buffers (7.0%), wrote 3 SLRU buffers; 0 WAL file(s)
    added, 0 removed, 2 recycled; write=0.906 s, sync=0.001 s, total=0.907
    s; sync files=0, longest=0.000 s, average=0.000 s; distance=32768 kB,
    estimate=32768 kB; lsn=0/040000B8, redo lsn=0/04000060
    
    2025-11-13 21:13:38.994 CET client backend[562727] 049_wait_for_lsn.pl
    ERROR:  recovery is not in progress
    
    So there's a checkpoint, 60 seconds of nothing, and then a failure. I
    haven't looked into why it waits for 1 minute exactly, but adding 60
    seconds to check-world is somewhat annoying.
    
    While at it, I noticed a couple comments refer to WaitForLSNReplay, but
    but I think that got renamed simply to WaitForLSN.
    
    
    regards
    
    -- 
    Tomas Vondra
    
    
    
    
  45. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-14T01:49:51Z

    Hi Tomas,
    
    On Fri, Nov 14, 2025 at 4:32 AM Tomas Vondra <tomas@vondra.me> wrote:
    >
    > On 11/5/25 10:51, Alexander Korotkov wrote:
    > > Hi!
    > >
    > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > >> On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > >>> On 2025-Nov-03, Alexander Korotkov wrote:
    > >>>
    > >>>> I'd like to give this subject another chance for pg19.  I'm going to
    > >>>> push this if no objections.
    > >>>
    > >>> Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > >>
    > >> FWIW, I appreciate such splits. Even if the functionality isn't usable
    > >> independently, it's still different type of code that's affected. And the
    > >> patches are each big enough to make that worthwhile for easier review.
    > >
    > > Thank you for the feedback, pushed.
    > >
    >
    > Hi,
    >
    > The new TAP test 049_wait_for_lsn.pl introduced by this commit, because
    > it takes a long time - about 65 seconds on my laptop. That's about 25%
    > of the whole src/test/recovery, more than any other test.
    >
    > And most of the time there's nothing happening - these are the two log
    > messages showing the 60-second wait:
    >
    > 2025-11-13 21:12:39.949 CET checkpointer[562597] LOG:  checkpoint
    > complete: wrote 9 buffers (7.0%), wrote 3 SLRU buffers; 0 WAL file(s)
    > added, 0 removed, 2 recycled; write=0.906 s, sync=0.001 s, total=0.907
    > s; sync files=0, longest=0.000 s, average=0.000 s; distance=32768 kB,
    > estimate=32768 kB; lsn=0/040000B8, redo lsn=0/04000060
    >
    > 2025-11-13 21:13:38.994 CET client backend[562727] 049_wait_for_lsn.pl
    > ERROR:  recovery is not in progress
    >
    > So there's a checkpoint, 60 seconds of nothing, and then a failure. I
    > haven't looked into why it waits for 1 minute exactly, but adding 60
    > seconds to check-world is somewhat annoying.
    
    Thanks for looking into this!
    
    I did a quick analysis for this prolonged waiting:
    
    In WaitLSNWakeup() (xlogwait.c:267), the fast-path check incorrectly
    handled InvalidXLogRecPtr:
    /* Fast path check */
    if (pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
        return;  // Issue: Returns early when currentLSN = 0
    
    When currentLSN = InvalidXLogRecPtr (0), meaning "wake all waiters",
    the check compared:
    - minWaitedLSN (e.g., 0x570CC048) > 0 → TRUE
    - Result: function returned early without waking anyone
    
    When It Happened
    During standby promotion, xlog.c:6246 calls:
    
    WaitLSNWakeup(WAIT_LSN_TYPE_REPLAY, InvalidXLogRecPtr);
    
    This should wake all LSN waiters, but the bug prevented it. WAIT FOR
    LSN commands could wait indefinitely. Test 049_wait_for_lsn.pl took 68
    seconds instead of ~9 seconds.
    
    if the above analysis is sound, the fix could be like:
    
    Proposed fix:
    Added a validity check before the comparison:
    /*
     * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
     * "wake all waiters" (e.g., during promotion when recovery ends).
     */
    if (XLogRecPtrIsValid(currentLSN) &&
        pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
        return;
    
    Result:
    Test time: 68s → 9s
    WAIT FOR LSN exits immediately on promotion (62ms vs 60s)
    
    > While at it, I noticed a couple comments refer to WaitForLSNReplay, but
    > but I think that got renamed simply to WaitForLSN.
    
    Please check the attached patch for replacing them.
    
    -- 
    Best,
    Xuneng
    
  46. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-15T10:29:25Z

    Hi, Xuneng!
    
    On Fri, Nov 14, 2025 at 3:50 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Fri, Nov 14, 2025 at 4:32 AM Tomas Vondra <tomas@vondra.me> wrote:
    > >
    > > On 11/5/25 10:51, Alexander Korotkov wrote:
    > > > Hi!
    > > >
    > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > >> On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > >>> On 2025-Nov-03, Alexander Korotkov wrote:
    > > >>>
    > > >>>> I'd like to give this subject another chance for pg19.  I'm going to
    > > >>>> push this if no objections.
    > > >>>
    > > >>> Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > >>
    > > >> FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > >> independently, it's still different type of code that's affected. And the
    > > >> patches are each big enough to make that worthwhile for easier review.
    > > >
    > > > Thank you for the feedback, pushed.
    > > >
    > >
    > > Hi,
    > >
    > > The new TAP test 049_wait_for_lsn.pl introduced by this commit, because
    > > it takes a long time - about 65 seconds on my laptop. That's about 25%
    > > of the whole src/test/recovery, more than any other test.
    > >
    > > And most of the time there's nothing happening - these are the two log
    > > messages showing the 60-second wait:
    > >
    > > 2025-11-13 21:12:39.949 CET checkpointer[562597] LOG:  checkpoint
    > > complete: wrote 9 buffers (7.0%), wrote 3 SLRU buffers; 0 WAL file(s)
    > > added, 0 removed, 2 recycled; write=0.906 s, sync=0.001 s, total=0.907
    > > s; sync files=0, longest=0.000 s, average=0.000 s; distance=32768 kB,
    > > estimate=32768 kB; lsn=0/040000B8, redo lsn=0/04000060
    > >
    > > 2025-11-13 21:13:38.994 CET client backend[562727] 049_wait_for_lsn.pl
    > > ERROR:  recovery is not in progress
    > >
    > > So there's a checkpoint, 60 seconds of nothing, and then a failure. I
    > > haven't looked into why it waits for 1 minute exactly, but adding 60
    > > seconds to check-world is somewhat annoying.
    >
    > Thanks for looking into this!
    >
    > I did a quick analysis for this prolonged waiting:
    >
    > In WaitLSNWakeup() (xlogwait.c:267), the fast-path check incorrectly
    > handled InvalidXLogRecPtr:
    > /* Fast path check */
    > if (pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    >     return;  // Issue: Returns early when currentLSN = 0
    >
    > When currentLSN = InvalidXLogRecPtr (0), meaning "wake all waiters",
    > the check compared:
    > - minWaitedLSN (e.g., 0x570CC048) > 0 → TRUE
    > - Result: function returned early without waking anyone
    >
    > When It Happened
    > During standby promotion, xlog.c:6246 calls:
    >
    > WaitLSNWakeup(WAIT_LSN_TYPE_REPLAY, InvalidXLogRecPtr);
    >
    > This should wake all LSN waiters, but the bug prevented it. WAIT FOR
    > LSN commands could wait indefinitely. Test 049_wait_for_lsn.pl took 68
    > seconds instead of ~9 seconds.
    >
    > if the above analysis is sound, the fix could be like:
    >
    > Proposed fix:
    > Added a validity check before the comparison:
    > /*
    >  * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    >  * "wake all waiters" (e.g., during promotion when recovery ends).
    >  */
    > if (XLogRecPtrIsValid(currentLSN) &&
    >     pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    >     return;
    >
    > Result:
    > Test time: 68s → 9s
    > WAIT FOR LSN exits immediately on promotion (62ms vs 60s)
    >
    > > While at it, I noticed a couple comments refer to WaitForLSNReplay, but
    > > but I think that got renamed simply to WaitForLSN.
    >
    > Please check the attached patch for replacing them.
    
    Thank you so much for your patches!
    Pushed with minor corrections.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  47. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-16T12:08:58Z

    On Sat, Nov 8, 2025 at 12:02 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > On Wed, Nov 5, 2025 at 4:03 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > > >
    > > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > > push this if no objections.
    > > > > >
    > > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > > >
    > > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > > independently, it's still different type of code that's affected. And the
    > > > > patches are each big enough to make that worthwhile for easier review.
    > > >
    > > > Thank you for the feedback, pushed.
    > >
    > > Thanks for pushing them!
    > >
    > > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > > case of wait_for_catchup() use this facility, instead of polling...
    > > >
    > > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > > possible use cases of wait_for_catchup(), but I've added usage when
    > > > it's appropriate.
    > >
    > > Interesting, could this approach be extended to the flush and other
    > > modes as well? I might need to spend some time to understand it before
    > > I can provide a meaningful review.
    >
    > I think we might end up extending WaitLSNType enum.  However, I hate
    > inHeap and heapNode arrays growing in WaitLSNProcInfo as they are
    > allocated per process.  I found that we could optimize WaitLSNProcInfo
    > struct turning them into simple variables because a single process can
    > wait only for a single LSN at a time.  Please, check the attached
    > patch.
    
    Here is the updated patch integrating minor corrections provided by
    Xuneng Zhou off-list.  I'm going to push this if no objections.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  48. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-16T12:36:52Z

    On Wed, Nov 12, 2025 at 9:20 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > >
    > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > push this if no objections.
    > > > >
    > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > >
    > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > independently, it's still different type of code that's affected. And the
    > > > patches are each big enough to make that worthwhile for easier review.
    > >
    > > Thank you for the feedback, pushed.
    > >
    > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > case of wait_for_catchup() use this facility, instead of polling...
    > >
    > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > possible use cases of wait_for_catchup(), but I've added usage when
    > > it's appropriate.
    >
    > I tested the patch using make check-world, and it worked well. I also
    > made a few adjustments:
    >
    > - Added an unconditional chomp($isrecovery) after querying
    > pg_is_in_recovery() to prevent newline mismatches when $target_lsn is
    > accidently defined.
    > - Added chomp($output) to normalize the result from WAIT FOR LSN
    > before comparison.
    >
    > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > If we intend to extend its functionality more broadly, one option is
    > to add a mode option or something similar. Are users expected to wait
    > for flush(or others) completion in such cases? If not, and the TAP
    > test is the only intended use, this approach might be a bit of an
    > overkill.
    
    I would say that adding mode parameter seems to be a pretty natural
    extension of what we have at the moment.  I can imagine some
    clustering solution can use it to wait for certain transaction to be
    flushed at the replica (without delaying the commit at the primary).
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  49. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-16T13:25:39Z

    Hi Alexander,
    
    On Sat, Nov 15, 2025 at 6:29 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Fri, Nov 14, 2025 at 3:50 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Fri, Nov 14, 2025 at 4:32 AM Tomas Vondra <tomas@vondra.me> wrote:
    > > >
    > > > On 11/5/25 10:51, Alexander Korotkov wrote:
    > > > > Hi!
    > > > >
    > > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > >> On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > >>> On 2025-Nov-03, Alexander Korotkov wrote:
    > > > >>>
    > > > >>>> I'd like to give this subject another chance for pg19.  I'm going to
    > > > >>>> push this if no objections.
    > > > >>>
    > > > >>> Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > > >>
    > > > >> FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > >> independently, it's still different type of code that's affected. And the
    > > > >> patches are each big enough to make that worthwhile for easier review.
    > > > >
    > > > > Thank you for the feedback, pushed.
    > > > >
    > > >
    > > > Hi,
    > > >
    > > > The new TAP test 049_wait_for_lsn.pl introduced by this commit, because
    > > > it takes a long time - about 65 seconds on my laptop. That's about 25%
    > > > of the whole src/test/recovery, more than any other test.
    > > >
    > > > And most of the time there's nothing happening - these are the two log
    > > > messages showing the 60-second wait:
    > > >
    > > > 2025-11-13 21:12:39.949 CET checkpointer[562597] LOG:  checkpoint
    > > > complete: wrote 9 buffers (7.0%), wrote 3 SLRU buffers; 0 WAL file(s)
    > > > added, 0 removed, 2 recycled; write=0.906 s, sync=0.001 s, total=0.907
    > > > s; sync files=0, longest=0.000 s, average=0.000 s; distance=32768 kB,
    > > > estimate=32768 kB; lsn=0/040000B8, redo lsn=0/04000060
    > > >
    > > > 2025-11-13 21:13:38.994 CET client backend[562727] 049_wait_for_lsn.pl
    > > > ERROR:  recovery is not in progress
    > > >
    > > > So there's a checkpoint, 60 seconds of nothing, and then a failure. I
    > > > haven't looked into why it waits for 1 minute exactly, but adding 60
    > > > seconds to check-world is somewhat annoying.
    > >
    > > Thanks for looking into this!
    > >
    > > I did a quick analysis for this prolonged waiting:
    > >
    > > In WaitLSNWakeup() (xlogwait.c:267), the fast-path check incorrectly
    > > handled InvalidXLogRecPtr:
    > > /* Fast path check */
    > > if (pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > >     return;  // Issue: Returns early when currentLSN = 0
    > >
    > > When currentLSN = InvalidXLogRecPtr (0), meaning "wake all waiters",
    > > the check compared:
    > > - minWaitedLSN (e.g., 0x570CC048) > 0 → TRUE
    > > - Result: function returned early without waking anyone
    > >
    > > When It Happened
    > > During standby promotion, xlog.c:6246 calls:
    > >
    > > WaitLSNWakeup(WAIT_LSN_TYPE_REPLAY, InvalidXLogRecPtr);
    > >
    > > This should wake all LSN waiters, but the bug prevented it. WAIT FOR
    > > LSN commands could wait indefinitely. Test 049_wait_for_lsn.pl took 68
    > > seconds instead of ~9 seconds.
    > >
    > > if the above analysis is sound, the fix could be like:
    > >
    > > Proposed fix:
    > > Added a validity check before the comparison:
    > > /*
    > >  * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > >  * "wake all waiters" (e.g., during promotion when recovery ends).
    > >  */
    > > if (XLogRecPtrIsValid(currentLSN) &&
    > >     pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > >     return;
    > >
    > > Result:
    > > Test time: 68s → 9s
    > > WAIT FOR LSN exits immediately on promotion (62ms vs 60s)
    > >
    > > > While at it, I noticed a couple comments refer to WaitForLSNReplay, but
    > > > but I think that got renamed simply to WaitForLSN.
    > >
    > > Please check the attached patch for replacing them.
    >
    > Thank you so much for your patches!
    > Pushed with minor corrections.
    
    Thanks for pushing! It appears I should be running pgindent more regularly :).
    
    
    --
    Best,
    Xuneng
    
    
    
    
  50. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-16T13:30:08Z

    Hi!
    
    On Sun, Nov 16, 2025 at 8:09 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Sat, Nov 8, 2025 at 12:02 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > On Wed, Nov 5, 2025 at 4:03 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > > > >
    > > > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > > > push this if no objections.
    > > > > > >
    > > > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > > > >
    > > > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > > > independently, it's still different type of code that's affected. And the
    > > > > > patches are each big enough to make that worthwhile for easier review.
    > > > >
    > > > > Thank you for the feedback, pushed.
    > > >
    > > > Thanks for pushing them!
    > > >
    > > > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > > > case of wait_for_catchup() use this facility, instead of polling...
    > > > >
    > > > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > > > possible use cases of wait_for_catchup(), but I've added usage when
    > > > > it's appropriate.
    > > >
    > > > Interesting, could this approach be extended to the flush and other
    > > > modes as well? I might need to spend some time to understand it before
    > > > I can provide a meaningful review.
    > >
    > > I think we might end up extending WaitLSNType enum.  However, I hate
    > > inHeap and heapNode arrays growing in WaitLSNProcInfo as they are
    > > allocated per process.  I found that we could optimize WaitLSNProcInfo
    > > struct turning them into simple variables because a single process can
    > > wait only for a single LSN at a time.  Please, check the attached
    > > patch.
    >
    > Here is the updated patch integrating minor corrections provided by
    > Xuneng Zhou off-list.  I'm going to push this if no objections.
    >
    > ------
    > Regards,
    > Alexander Korotkov
    > Supabase
    
    LGTM. Thanks.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  51. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-16T14:01:04Z

    Hi!
    
    On Sun, Nov 16, 2025 at 8:37 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Wed, Nov 12, 2025 at 9:20 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > > >
    > > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > > push this if no objections.
    > > > > >
    > > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > > >
    > > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > > independently, it's still different type of code that's affected. And the
    > > > > patches are each big enough to make that worthwhile for easier review.
    > > >
    > > > Thank you for the feedback, pushed.
    > > >
    > > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > > case of wait_for_catchup() use this facility, instead of polling...
    > > >
    > > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > > possible use cases of wait_for_catchup(), but I've added usage when
    > > > it's appropriate.
    > >
    > > I tested the patch using make check-world, and it worked well. I also
    > > made a few adjustments:
    > >
    > > - Added an unconditional chomp($isrecovery) after querying
    > > pg_is_in_recovery() to prevent newline mismatches when $target_lsn is
    > > accidently defined.
    > > - Added chomp($output) to normalize the result from WAIT FOR LSN
    > > before comparison.
    > >
    > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > If we intend to extend its functionality more broadly, one option is
    > > to add a mode option or something similar. Are users expected to wait
    > > for flush(or others) completion in such cases? If not, and the TAP
    > > test is the only intended use, this approach might be a bit of an
    > > overkill.
    >
    > I would say that adding mode parameter seems to be a pretty natural
    > extension of what we have at the moment.  I can imagine some
    > clustering solution can use it to wait for certain transaction to be
    > flushed at the replica (without delaying the commit at the primary).
    >
    > ------
    > Regards,
    > Alexander Korotkov
    > Supabase
    
    Makes sense. I'll play with it and try to prepare a follow-up patch.
    
    --
    Best,
    Xuneng
    
    
    
    
  52. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-11-16T15:20:25Z

    On Sun, Nov 16, 2025 at 3:25 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Sat, Nov 15, 2025 at 6:29 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > Thank you so much for your patches!
    > > Pushed with minor corrections.
    >
    > Thanks for pushing! It appears I should be running pgindent more regularly :).
    
    Thank you.  pgindent is not a problem for me, cause I anyway run it
    every time before pushing a patch.  But yes, if you make it a habit to
    run pgindent every time before publishing a patch, it would become
    cleaner.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  53. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-20T12:43:06Z

    Hi Alexander, Hackers,
    
    On Sun, Nov 16, 2025 at 10:01 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi!
    >
    > On Sun, Nov 16, 2025 at 8:37 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Wed, Nov 12, 2025 at 9:20 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Wed, Nov 5, 2025 at 5:51 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > On Mon, Nov 3, 2025 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
    > > > > > On 2025-11-03 16:06:58 +0100, Álvaro Herrera wrote:
    > > > > > > On 2025-Nov-03, Alexander Korotkov wrote:
    > > > > > >
    > > > > > > > I'd like to give this subject another chance for pg19.  I'm going to
    > > > > > > > push this if no objections.
    > > > > > >
    > > > > > > Sure.  I don't understand why patches 0002 and 0003 are separate though.
    > > > > >
    > > > > > FWIW, I appreciate such splits. Even if the functionality isn't usable
    > > > > > independently, it's still different type of code that's affected. And the
    > > > > > patches are each big enough to make that worthwhile for easier review.
    > > > >
    > > > > Thank you for the feedback, pushed.
    > > > >
    > > > > > One thing that'd be nice to do once we have WAIT FOR is to make the common
    > > > > > case of wait_for_catchup() use this facility, instead of polling...
    > > > >
    > > > > The draft patch for that is attached.  WAIT FOR doesn't handle all the
    > > > > possible use cases of wait_for_catchup(), but I've added usage when
    > > > > it's appropriate.
    > > >
    > > > I tested the patch using make check-world, and it worked well. I also
    > > > made a few adjustments:
    > > >
    > > > - Added an unconditional chomp($isrecovery) after querying
    > > > pg_is_in_recovery() to prevent newline mismatches when $target_lsn is
    > > > accidently defined.
    > > > - Added chomp($output) to normalize the result from WAIT FOR LSN
    > > > before comparison.
    > > >
    > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > If we intend to extend its functionality more broadly, one option is
    > > > to add a mode option or something similar. Are users expected to wait
    > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > test is the only intended use, this approach might be a bit of an
    > > > overkill.
    > >
    > > I would say that adding mode parameter seems to be a pretty natural
    > > extension of what we have at the moment.  I can imagine some
    > > clustering solution can use it to wait for certain transaction to be
    > > flushed at the replica (without delaying the commit at the primary).
    > >
    > > ------
    > > Regards,
    > > Alexander Korotkov
    > > Supabase
    >
    > Makes sense. I'll play with it and try to prepare a follow-up patch.
    >
    > --
    > Best,
    > Xuneng
    
    In terms of extending the functionality of the command, I see two
    possible approaches here. One is to keep mode as a mandatory keyword,
    and the other is to introduce it as an option in the WITH clause.
    
    Syntax Option A: Mode in the WITH Clause
    
    WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    WAIT FOR LSN '0/12345' WITH (mode = 'write');
    
    With this option, we can keep "replay" as the default mode. That means
    existing TAP tests won’t need to be refactored unless they explicitly
    want a different mode.
    
    Syntax Option B: Mode as Part of the Main Command
    
    WAIT FOR LSN '0/12345' MODE 'replay';
    WAIT FOR LSN '0/12345' MODE 'flush';
    WAIT FOR LSN '0/12345' MODE 'write';
    
    Or a more concise variant using keywords:
    
    WAIT FOR LSN '0/12345' REPLAY;
    WAIT FOR LSN '0/12345' FLUSH;
    WAIT FOR LSN '0/12345' WRITE;
    
    This option produces a cleaner syntax if the intent is simply to wait
    for a particular LSN type, without specifying additional options like
    timeout or no_throw.
    
    I don’t have a clear preference among them. I’d be interested to hear
    what you or others think is the better direction.
    
    
    --
    Best,
    Xuneng
    
    
    
    
  54. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-11-25T11:51:19Z

    Hi!
    
    > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > If we intend to extend its functionality more broadly, one option is
    > > > > to add a mode option or something similar. Are users expected to wait
    > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > test is the only intended use, this approach might be a bit of an
    > > > > overkill.
    > > >
    > > > I would say that adding mode parameter seems to be a pretty natural
    > > > extension of what we have at the moment.  I can imagine some
    > > > clustering solution can use it to wait for certain transaction to be
    > > > flushed at the replica (without delaying the commit at the primary).
    > > >
    > > > ------
    > > > Regards,
    > > > Alexander Korotkov
    > > > Supabase
    > >
    > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > >
    > > --
    > > Best,
    > > Xuneng
    >
    > In terms of extending the functionality of the command, I see two
    > possible approaches here. One is to keep mode as a mandatory keyword,
    > and the other is to introduce it as an option in the WITH clause.
    >
    > Syntax Option A: Mode in the WITH Clause
    >
    > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    >
    > With this option, we can keep "replay" as the default mode. That means
    > existing TAP tests won’t need to be refactored unless they explicitly
    > want a different mode.
    >
    > Syntax Option B: Mode as Part of the Main Command
    >
    > WAIT FOR LSN '0/12345' MODE 'replay';
    > WAIT FOR LSN '0/12345' MODE 'flush';
    > WAIT FOR LSN '0/12345' MODE 'write';
    >
    > Or a more concise variant using keywords:
    >
    > WAIT FOR LSN '0/12345' REPLAY;
    > WAIT FOR LSN '0/12345' FLUSH;
    > WAIT FOR LSN '0/12345' WRITE;
    >
    > This option produces a cleaner syntax if the intent is simply to wait
    > for a particular LSN type, without specifying additional options like
    > timeout or no_throw.
    >
    > I don’t have a clear preference among them. I’d be interested to hear
    > what you or others think is the better direction.
    >
    
    I've implemented a patch that adds MODE support to WAIT FOR LSN
    
    The new grammar looks like:
    
    ——
    WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    ——
    
    Two modes added: flush and write
    
    Design decisions:
    
    1. MODE as a separate keyword (not in WITH clause) - This follows the
    pattern used by LOCK command. It also makes the common case more
    concise.
    
    2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    
    3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    
    The patch set includes:
    -------
    0001 - Extend xlogwait infrastructure with write and flush wait types
    
    Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    along with corresponding wait events and pairing heaps. Introduces
    GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    wait type, and adds wakeup calls in walreceiver for write/flush
    events.
    
    -------
    0002 - Add pg_last_wal_write_lsn() SQL function
    
    Adds a new SQL function that returns the current WAL write position on
    a standby using GetWalRcvWriteRecPtr(). This complements existing
    pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    tests.
    
    -------
    0003 - Add MODE parameter to WAIT FOR LSN command
    
    Extends the parser and executor to support the optional MODE
    parameter. Updates documentation with new syntax and mode
    descriptions. Adds TAP tests covering all three modes including
    mixed-mode concurrent waiters.
    
    -------
    0004 - Add tab completion for WAIT FOR LSN MODE parameter
    
    Adds psql tab completion support: completes MODE after LSN value,
    completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    after mode selection.
    
    -------
    0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    
    Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    target is a standby in recovery, improving test efficiency by avoiding
    repeated queries.
    
    The WRITE and FLUSH modes enable scenarios where applications need to
    ensure WAL has been received or persisted on the standby without
    waiting for replay to complete.
    
    Feedback welcome.
    
    
    --
    Best,
    Xuneng
    
  55. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-01T04:33:27Z

    Hi hackers,
    
    On Tue, Nov 25, 2025 at 7:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi!
    >
    > > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > > If we intend to extend its functionality more broadly, one option is
    > > > > > to add a mode option or something similar. Are users expected to wait
    > > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > > test is the only intended use, this approach might be a bit of an
    > > > > > overkill.
    > > > >
    > > > > I would say that adding mode parameter seems to be a pretty natural
    > > > > extension of what we have at the moment.  I can imagine some
    > > > > clustering solution can use it to wait for certain transaction to be
    > > > > flushed at the replica (without delaying the commit at the primary).
    > > > >
    > > > > ------
    > > > > Regards,
    > > > > Alexander Korotkov
    > > > > Supabase
    > > >
    > > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > > >
    > > > --
    > > > Best,
    > > > Xuneng
    > >
    > > In terms of extending the functionality of the command, I see two
    > > possible approaches here. One is to keep mode as a mandatory keyword,
    > > and the other is to introduce it as an option in the WITH clause.
    > >
    > > Syntax Option A: Mode in the WITH Clause
    > >
    > > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    > >
    > > With this option, we can keep "replay" as the default mode. That means
    > > existing TAP tests won’t need to be refactored unless they explicitly
    > > want a different mode.
    > >
    > > Syntax Option B: Mode as Part of the Main Command
    > >
    > > WAIT FOR LSN '0/12345' MODE 'replay';
    > > WAIT FOR LSN '0/12345' MODE 'flush';
    > > WAIT FOR LSN '0/12345' MODE 'write';
    > >
    > > Or a more concise variant using keywords:
    > >
    > > WAIT FOR LSN '0/12345' REPLAY;
    > > WAIT FOR LSN '0/12345' FLUSH;
    > > WAIT FOR LSN '0/12345' WRITE;
    > >
    > > This option produces a cleaner syntax if the intent is simply to wait
    > > for a particular LSN type, without specifying additional options like
    > > timeout or no_throw.
    > >
    > > I don’t have a clear preference among them. I’d be interested to hear
    > > what you or others think is the better direction.
    > >
    >
    > I've implemented a patch that adds MODE support to WAIT FOR LSN
    >
    > The new grammar looks like:
    >
    > ——
    > WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    > ——
    >
    > Two modes added: flush and write
    >
    > Design decisions:
    >
    > 1. MODE as a separate keyword (not in WITH clause) - This follows the
    > pattern used by LOCK command. It also makes the common case more
    > concise.
    >
    > 2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    >
    > 3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    >
    > The patch set includes:
    > -------
    > 0001 - Extend xlogwait infrastructure with write and flush wait types
    >
    > Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    > along with corresponding wait events and pairing heaps. Introduces
    > GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    > wait type, and adds wakeup calls in walreceiver for write/flush
    > events.
    >
    > -------
    > 0002 - Add pg_last_wal_write_lsn() SQL function
    >
    > Adds a new SQL function that returns the current WAL write position on
    > a standby using GetWalRcvWriteRecPtr(). This complements existing
    > pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    > functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    > tests.
    >
    > -------
    > 0003 - Add MODE parameter to WAIT FOR LSN command
    >
    > Extends the parser and executor to support the optional MODE
    > parameter. Updates documentation with new syntax and mode
    > descriptions. Adds TAP tests covering all three modes including
    > mixed-mode concurrent waiters.
    >
    > -------
    > 0004 - Add tab completion for WAIT FOR LSN MODE parameter
    >
    > Adds psql tab completion support: completes MODE after LSN value,
    > completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    > after mode selection.
    >
    > -------
    > 0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    >
    > Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    > target is a standby in recovery, improving test efficiency by avoiding
    > repeated queries.
    >
    > The WRITE and FLUSH modes enable scenarios where applications need to
    > ensure WAL has been received or persisted on the standby without
    > waiting for replay to complete.
    >
    > Feedback welcome.
    >
    
    Here is the updated v2 patch set. Most of the updates are in patch 3.
    
    Changes from v1:
    
    Patch 1 (Extend wait types in xlogwait infra)
    - Renamed enum values for consistency (WAIT_LSN_TYPE_REPLAY →
    WAIT_LSN_TYPE_REPLAY_STANDBY, etc.)
    
    Patch 2 (pg_last_wal_write_lsn):
    - Clarified documentation and comment
    - Improved pg_proc.dat description
    
    Patch 3 (MODE parameter):
    - Replaced direct cast with explicit switch statement for WaitLSNMode
    → WaitLSNType conversion
    - Improved FLUSH/WRITE mode documentation with verification function references
    - TAP tests (7b, 7c, 7d): Added walreceiver control for concurrency,
    explicit blocking verification via poll_query_until, and log-based
    completion verification via wait_for_log
    - Fix the timing issue in wait for all three sessions to get the
    errors after promotion of tap test 8.
    
    -- 
    Best,
    Xuneng
    
  56. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-02T03:08:01Z

    Hi,
    
    On Mon, Dec 1, 2025 at 12:33 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > On Tue, Nov 25, 2025 at 7:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi!
    > >
    > > > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > > > If we intend to extend its functionality more broadly, one option is
    > > > > > > to add a mode option or something similar. Are users expected to wait
    > > > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > > > test is the only intended use, this approach might be a bit of an
    > > > > > > overkill.
    > > > > >
    > > > > > I would say that adding mode parameter seems to be a pretty natural
    > > > > > extension of what we have at the moment.  I can imagine some
    > > > > > clustering solution can use it to wait for certain transaction to be
    > > > > > flushed at the replica (without delaying the commit at the primary).
    > > > > >
    > > > > > ------
    > > > > > Regards,
    > > > > > Alexander Korotkov
    > > > > > Supabase
    > > > >
    > > > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > > > >
    > > > > --
    > > > > Best,
    > > > > Xuneng
    > > >
    > > > In terms of extending the functionality of the command, I see two
    > > > possible approaches here. One is to keep mode as a mandatory keyword,
    > > > and the other is to introduce it as an option in the WITH clause.
    > > >
    > > > Syntax Option A: Mode in the WITH Clause
    > > >
    > > > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > > > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > > > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    > > >
    > > > With this option, we can keep "replay" as the default mode. That means
    > > > existing TAP tests won’t need to be refactored unless they explicitly
    > > > want a different mode.
    > > >
    > > > Syntax Option B: Mode as Part of the Main Command
    > > >
    > > > WAIT FOR LSN '0/12345' MODE 'replay';
    > > > WAIT FOR LSN '0/12345' MODE 'flush';
    > > > WAIT FOR LSN '0/12345' MODE 'write';
    > > >
    > > > Or a more concise variant using keywords:
    > > >
    > > > WAIT FOR LSN '0/12345' REPLAY;
    > > > WAIT FOR LSN '0/12345' FLUSH;
    > > > WAIT FOR LSN '0/12345' WRITE;
    > > >
    > > > This option produces a cleaner syntax if the intent is simply to wait
    > > > for a particular LSN type, without specifying additional options like
    > > > timeout or no_throw.
    > > >
    > > > I don’t have a clear preference among them. I’d be interested to hear
    > > > what you or others think is the better direction.
    > > >
    > >
    > > I've implemented a patch that adds MODE support to WAIT FOR LSN
    > >
    > > The new grammar looks like:
    > >
    > > ——
    > > WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    > > ——
    > >
    > > Two modes added: flush and write
    > >
    > > Design decisions:
    > >
    > > 1. MODE as a separate keyword (not in WITH clause) - This follows the
    > > pattern used by LOCK command. It also makes the common case more
    > > concise.
    > >
    > > 2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    > >
    > > 3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    > >
    > > The patch set includes:
    > > -------
    > > 0001 - Extend xlogwait infrastructure with write and flush wait types
    > >
    > > Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    > > along with corresponding wait events and pairing heaps. Introduces
    > > GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    > > wait type, and adds wakeup calls in walreceiver for write/flush
    > > events.
    > >
    > > -------
    > > 0002 - Add pg_last_wal_write_lsn() SQL function
    > >
    > > Adds a new SQL function that returns the current WAL write position on
    > > a standby using GetWalRcvWriteRecPtr(). This complements existing
    > > pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    > > functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    > > tests.
    > >
    > > -------
    > > 0003 - Add MODE parameter to WAIT FOR LSN command
    > >
    > > Extends the parser and executor to support the optional MODE
    > > parameter. Updates documentation with new syntax and mode
    > > descriptions. Adds TAP tests covering all three modes including
    > > mixed-mode concurrent waiters.
    > >
    > > -------
    > > 0004 - Add tab completion for WAIT FOR LSN MODE parameter
    > >
    > > Adds psql tab completion support: completes MODE after LSN value,
    > > completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    > > after mode selection.
    > >
    > > -------
    > > 0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    > >
    > > Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    > > target is a standby in recovery, improving test efficiency by avoiding
    > > repeated queries.
    > >
    > > The WRITE and FLUSH modes enable scenarios where applications need to
    > > ensure WAL has been received or persisted on the standby without
    > > waiting for replay to complete.
    > >
    > > Feedback welcome.
    > >
    >
    > Here is the updated v2 patch set. Most of the updates are in patch 3.
    >
    > Changes from v1:
    >
    > Patch 1 (Extend wait types in xlogwait infra)
    > - Renamed enum values for consistency (WAIT_LSN_TYPE_REPLAY →
    > WAIT_LSN_TYPE_REPLAY_STANDBY, etc.)
    >
    > Patch 2 (pg_last_wal_write_lsn):
    > - Clarified documentation and comment
    > - Improved pg_proc.dat description
    >
    > Patch 3 (MODE parameter):
    > - Replaced direct cast with explicit switch statement for WaitLSNMode
    > → WaitLSNType conversion
    > - Improved FLUSH/WRITE mode documentation with verification function references
    > - TAP tests (7b, 7c, 7d): Added walreceiver control for concurrency,
    > explicit blocking verification via poll_query_until, and log-based
    > completion verification via wait_for_log
    > - Fix the timing issue in wait for all three sessions to get the
    > errors after promotion of tap test 8.
    >
    > --
    > Best,
    > Xuneng
    
    Here is the updated v3. The changes are made to patch 3:
    
    - Refactor duplicated TAP test code by extracting helper routines for
    starting and stopping walreceiver.
    - Increase the number of concurrent WRITE and FLUSH waiters in tests
    7b and 7c from three to five, matching the number in test 7a.
    
    -- 
    Best,
    Xuneng
    
  57. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-02T10:10:02Z

    Hi,
    
    On Tue, Dec 2, 2025 at 11:08 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Mon, Dec 1, 2025 at 12:33 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi hackers,
    > >
    > > On Tue, Nov 25, 2025 at 7:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi!
    > > >
    > > > > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > > > > If we intend to extend its functionality more broadly, one option is
    > > > > > > > to add a mode option or something similar. Are users expected to wait
    > > > > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > > > > test is the only intended use, this approach might be a bit of an
    > > > > > > > overkill.
    > > > > > >
    > > > > > > I would say that adding mode parameter seems to be a pretty natural
    > > > > > > extension of what we have at the moment.  I can imagine some
    > > > > > > clustering solution can use it to wait for certain transaction to be
    > > > > > > flushed at the replica (without delaying the commit at the primary).
    > > > > > >
    > > > > > > ------
    > > > > > > Regards,
    > > > > > > Alexander Korotkov
    > > > > > > Supabase
    > > > > >
    > > > > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > > > > >
    > > > > > --
    > > > > > Best,
    > > > > > Xuneng
    > > > >
    > > > > In terms of extending the functionality of the command, I see two
    > > > > possible approaches here. One is to keep mode as a mandatory keyword,
    > > > > and the other is to introduce it as an option in the WITH clause.
    > > > >
    > > > > Syntax Option A: Mode in the WITH Clause
    > > > >
    > > > > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > > > > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > > > > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    > > > >
    > > > > With this option, we can keep "replay" as the default mode. That means
    > > > > existing TAP tests won’t need to be refactored unless they explicitly
    > > > > want a different mode.
    > > > >
    > > > > Syntax Option B: Mode as Part of the Main Command
    > > > >
    > > > > WAIT FOR LSN '0/12345' MODE 'replay';
    > > > > WAIT FOR LSN '0/12345' MODE 'flush';
    > > > > WAIT FOR LSN '0/12345' MODE 'write';
    > > > >
    > > > > Or a more concise variant using keywords:
    > > > >
    > > > > WAIT FOR LSN '0/12345' REPLAY;
    > > > > WAIT FOR LSN '0/12345' FLUSH;
    > > > > WAIT FOR LSN '0/12345' WRITE;
    > > > >
    > > > > This option produces a cleaner syntax if the intent is simply to wait
    > > > > for a particular LSN type, without specifying additional options like
    > > > > timeout or no_throw.
    > > > >
    > > > > I don’t have a clear preference among them. I’d be interested to hear
    > > > > what you or others think is the better direction.
    > > > >
    > > >
    > > > I've implemented a patch that adds MODE support to WAIT FOR LSN
    > > >
    > > > The new grammar looks like:
    > > >
    > > > ——
    > > > WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    > > > ——
    > > >
    > > > Two modes added: flush and write
    > > >
    > > > Design decisions:
    > > >
    > > > 1. MODE as a separate keyword (not in WITH clause) - This follows the
    > > > pattern used by LOCK command. It also makes the common case more
    > > > concise.
    > > >
    > > > 2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    > > >
    > > > 3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    > > >
    > > > The patch set includes:
    > > > -------
    > > > 0001 - Extend xlogwait infrastructure with write and flush wait types
    > > >
    > > > Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    > > > along with corresponding wait events and pairing heaps. Introduces
    > > > GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    > > > wait type, and adds wakeup calls in walreceiver for write/flush
    > > > events.
    > > >
    > > > -------
    > > > 0002 - Add pg_last_wal_write_lsn() SQL function
    > > >
    > > > Adds a new SQL function that returns the current WAL write position on
    > > > a standby using GetWalRcvWriteRecPtr(). This complements existing
    > > > pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    > > > functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    > > > tests.
    > > >
    > > > -------
    > > > 0003 - Add MODE parameter to WAIT FOR LSN command
    > > >
    > > > Extends the parser and executor to support the optional MODE
    > > > parameter. Updates documentation with new syntax and mode
    > > > descriptions. Adds TAP tests covering all three modes including
    > > > mixed-mode concurrent waiters.
    > > >
    > > > -------
    > > > 0004 - Add tab completion for WAIT FOR LSN MODE parameter
    > > >
    > > > Adds psql tab completion support: completes MODE after LSN value,
    > > > completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    > > > after mode selection.
    > > >
    > > > -------
    > > > 0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    > > >
    > > > Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    > > > target is a standby in recovery, improving test efficiency by avoiding
    > > > repeated queries.
    > > >
    > > > The WRITE and FLUSH modes enable scenarios where applications need to
    > > > ensure WAL has been received or persisted on the standby without
    > > > waiting for replay to complete.
    > > >
    > > > Feedback welcome.
    > > >
    > >
    > > Here is the updated v2 patch set. Most of the updates are in patch 3.
    > >
    > > Changes from v1:
    > >
    > > Patch 1 (Extend wait types in xlogwait infra)
    > > - Renamed enum values for consistency (WAIT_LSN_TYPE_REPLAY →
    > > WAIT_LSN_TYPE_REPLAY_STANDBY, etc.)
    > >
    > > Patch 2 (pg_last_wal_write_lsn):
    > > - Clarified documentation and comment
    > > - Improved pg_proc.dat description
    > >
    > > Patch 3 (MODE parameter):
    > > - Replaced direct cast with explicit switch statement for WaitLSNMode
    > > → WaitLSNType conversion
    > > - Improved FLUSH/WRITE mode documentation with verification function references
    > > - TAP tests (7b, 7c, 7d): Added walreceiver control for concurrency,
    > > explicit blocking verification via poll_query_until, and log-based
    > > completion verification via wait_for_log
    > > - Fix the timing issue in wait for all three sessions to get the
    > > errors after promotion of tap test 8.
    > >
    > > --
    > > Best,
    > > Xuneng
    >
    > Here is the updated v3. The changes are made to patch 3:
    >
    > - Refactor duplicated TAP test code by extracting helper routines for
    > starting and stopping walreceiver.
    > - Increase the number of concurrent WRITE and FLUSH waiters in tests
    > 7b and 7c from three to five, matching the number in test 7a.
    >
    > --
    > Best,
    > Xuneng
    
    Just realized that patch 2 in prior emails could be dropped for
    simplicity. Since the write LSN can be retrieved directly from
    pg_stat_wal_receiver, the TAP test in patch 3 does not require a
    separate SQL function for this purpose alone.
    
    
    -- 
    Best,
    Xuneng
    
  58. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-16T03:28:51Z

    Hi,
    
    On Tue, Dec 2, 2025 at 6:10 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Dec 2, 2025 at 11:08 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Mon, Dec 1, 2025 at 12:33 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi hackers,
    > > >
    > > > On Tue, Nov 25, 2025 at 7:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi!
    > > > >
    > > > > > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > > > > > If we intend to extend its functionality more broadly, one option is
    > > > > > > > > to add a mode option or something similar. Are users expected to wait
    > > > > > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > > > > > test is the only intended use, this approach might be a bit of an
    > > > > > > > > overkill.
    > > > > > > >
    > > > > > > > I would say that adding mode parameter seems to be a pretty natural
    > > > > > > > extension of what we have at the moment.  I can imagine some
    > > > > > > > clustering solution can use it to wait for certain transaction to be
    > > > > > > > flushed at the replica (without delaying the commit at the primary).
    > > > > > > >
    > > > > > > > ------
    > > > > > > > Regards,
    > > > > > > > Alexander Korotkov
    > > > > > > > Supabase
    > > > > > >
    > > > > > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > > > > > >
    > > > > > > --
    > > > > > > Best,
    > > > > > > Xuneng
    > > > > >
    > > > > > In terms of extending the functionality of the command, I see two
    > > > > > possible approaches here. One is to keep mode as a mandatory keyword,
    > > > > > and the other is to introduce it as an option in the WITH clause.
    > > > > >
    > > > > > Syntax Option A: Mode in the WITH Clause
    > > > > >
    > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    > > > > >
    > > > > > With this option, we can keep "replay" as the default mode. That means
    > > > > > existing TAP tests won’t need to be refactored unless they explicitly
    > > > > > want a different mode.
    > > > > >
    > > > > > Syntax Option B: Mode as Part of the Main Command
    > > > > >
    > > > > > WAIT FOR LSN '0/12345' MODE 'replay';
    > > > > > WAIT FOR LSN '0/12345' MODE 'flush';
    > > > > > WAIT FOR LSN '0/12345' MODE 'write';
    > > > > >
    > > > > > Or a more concise variant using keywords:
    > > > > >
    > > > > > WAIT FOR LSN '0/12345' REPLAY;
    > > > > > WAIT FOR LSN '0/12345' FLUSH;
    > > > > > WAIT FOR LSN '0/12345' WRITE;
    > > > > >
    > > > > > This option produces a cleaner syntax if the intent is simply to wait
    > > > > > for a particular LSN type, without specifying additional options like
    > > > > > timeout or no_throw.
    > > > > >
    > > > > > I don’t have a clear preference among them. I’d be interested to hear
    > > > > > what you or others think is the better direction.
    > > > > >
    > > > >
    > > > > I've implemented a patch that adds MODE support to WAIT FOR LSN
    > > > >
    > > > > The new grammar looks like:
    > > > >
    > > > > ——
    > > > > WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    > > > > ——
    > > > >
    > > > > Two modes added: flush and write
    > > > >
    > > > > Design decisions:
    > > > >
    > > > > 1. MODE as a separate keyword (not in WITH clause) - This follows the
    > > > > pattern used by LOCK command. It also makes the common case more
    > > > > concise.
    > > > >
    > > > > 2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    > > > >
    > > > > 3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    > > > >
    > > > > The patch set includes:
    > > > > -------
    > > > > 0001 - Extend xlogwait infrastructure with write and flush wait types
    > > > >
    > > > > Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    > > > > along with corresponding wait events and pairing heaps. Introduces
    > > > > GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    > > > > wait type, and adds wakeup calls in walreceiver for write/flush
    > > > > events.
    > > > >
    > > > > -------
    > > > > 0002 - Add pg_last_wal_write_lsn() SQL function
    > > > >
    > > > > Adds a new SQL function that returns the current WAL write position on
    > > > > a standby using GetWalRcvWriteRecPtr(). This complements existing
    > > > > pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    > > > > functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    > > > > tests.
    > > > >
    > > > > -------
    > > > > 0003 - Add MODE parameter to WAIT FOR LSN command
    > > > >
    > > > > Extends the parser and executor to support the optional MODE
    > > > > parameter. Updates documentation with new syntax and mode
    > > > > descriptions. Adds TAP tests covering all three modes including
    > > > > mixed-mode concurrent waiters.
    > > > >
    > > > > -------
    > > > > 0004 - Add tab completion for WAIT FOR LSN MODE parameter
    > > > >
    > > > > Adds psql tab completion support: completes MODE after LSN value,
    > > > > completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    > > > > after mode selection.
    > > > >
    > > > > -------
    > > > > 0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    > > > >
    > > > > Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    > > > > target is a standby in recovery, improving test efficiency by avoiding
    > > > > repeated queries.
    > > > >
    > > > > The WRITE and FLUSH modes enable scenarios where applications need to
    > > > > ensure WAL has been received or persisted on the standby without
    > > > > waiting for replay to complete.
    > > > >
    > > > > Feedback welcome.
    > > > >
    > > >
    > > > Here is the updated v2 patch set. Most of the updates are in patch 3.
    > > >
    > > > Changes from v1:
    > > >
    > > > Patch 1 (Extend wait types in xlogwait infra)
    > > > - Renamed enum values for consistency (WAIT_LSN_TYPE_REPLAY →
    > > > WAIT_LSN_TYPE_REPLAY_STANDBY, etc.)
    > > >
    > > > Patch 2 (pg_last_wal_write_lsn):
    > > > - Clarified documentation and comment
    > > > - Improved pg_proc.dat description
    > > >
    > > > Patch 3 (MODE parameter):
    > > > - Replaced direct cast with explicit switch statement for WaitLSNMode
    > > > → WaitLSNType conversion
    > > > - Improved FLUSH/WRITE mode documentation with verification function references
    > > > - TAP tests (7b, 7c, 7d): Added walreceiver control for concurrency,
    > > > explicit blocking verification via poll_query_until, and log-based
    > > > completion verification via wait_for_log
    > > > - Fix the timing issue in wait for all three sessions to get the
    > > > errors after promotion of tap test 8.
    > > >
    > > > --
    > > > Best,
    > > > Xuneng
    > >
    > > Here is the updated v3. The changes are made to patch 3:
    > >
    > > - Refactor duplicated TAP test code by extracting helper routines for
    > > starting and stopping walreceiver.
    > > - Increase the number of concurrent WRITE and FLUSH waiters in tests
    > > 7b and 7c from three to five, matching the number in test 7a.
    > >
    > > --
    > > Best,
    > > Xuneng
    >
    > Just realized that patch 2 in prior emails could be dropped for
    > simplicity. Since the write LSN can be retrieved directly from
    > pg_stat_wal_receiver, the TAP test in patch 3 does not require a
    > separate SQL function for this purpose alone.
    >
    
    Just rebase with minor changes to the wait-lsn types.
    
    -- 
    Best,
    Xuneng
    
  59. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-16T04:46:27Z

    Hi,
    
    On Tue, Dec 16, 2025 at 11:28 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Dec 2, 2025 at 6:10 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Tue, Dec 2, 2025 at 11:08 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Mon, Dec 1, 2025 at 12:33 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi hackers,
    > > > >
    > > > > On Tue, Nov 25, 2025 at 7:51 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > >
    > > > > > Hi!
    > > > > >
    > > > > > > > > > At the moment, the WAIT FOR LSN command supports only the replay mode.
    > > > > > > > > > If we intend to extend its functionality more broadly, one option is
    > > > > > > > > > to add a mode option or something similar. Are users expected to wait
    > > > > > > > > > for flush(or others) completion in such cases? If not, and the TAP
    > > > > > > > > > test is the only intended use, this approach might be a bit of an
    > > > > > > > > > overkill.
    > > > > > > > >
    > > > > > > > > I would say that adding mode parameter seems to be a pretty natural
    > > > > > > > > extension of what we have at the moment.  I can imagine some
    > > > > > > > > clustering solution can use it to wait for certain transaction to be
    > > > > > > > > flushed at the replica (without delaying the commit at the primary).
    > > > > > > > >
    > > > > > > > > ------
    > > > > > > > > Regards,
    > > > > > > > > Alexander Korotkov
    > > > > > > > > Supabase
    > > > > > > >
    > > > > > > > Makes sense. I'll play with it and try to prepare a follow-up patch.
    > > > > > > >
    > > > > > > > --
    > > > > > > > Best,
    > > > > > > > Xuneng
    > > > > > >
    > > > > > > In terms of extending the functionality of the command, I see two
    > > > > > > possible approaches here. One is to keep mode as a mandatory keyword,
    > > > > > > and the other is to introduce it as an option in the WITH clause.
    > > > > > >
    > > > > > > Syntax Option A: Mode in the WITH Clause
    > > > > > >
    > > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'replay');
    > > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'flush');
    > > > > > > WAIT FOR LSN '0/12345' WITH (mode = 'write');
    > > > > > >
    > > > > > > With this option, we can keep "replay" as the default mode. That means
    > > > > > > existing TAP tests won’t need to be refactored unless they explicitly
    > > > > > > want a different mode.
    > > > > > >
    > > > > > > Syntax Option B: Mode as Part of the Main Command
    > > > > > >
    > > > > > > WAIT FOR LSN '0/12345' MODE 'replay';
    > > > > > > WAIT FOR LSN '0/12345' MODE 'flush';
    > > > > > > WAIT FOR LSN '0/12345' MODE 'write';
    > > > > > >
    > > > > > > Or a more concise variant using keywords:
    > > > > > >
    > > > > > > WAIT FOR LSN '0/12345' REPLAY;
    > > > > > > WAIT FOR LSN '0/12345' FLUSH;
    > > > > > > WAIT FOR LSN '0/12345' WRITE;
    > > > > > >
    > > > > > > This option produces a cleaner syntax if the intent is simply to wait
    > > > > > > for a particular LSN type, without specifying additional options like
    > > > > > > timeout or no_throw.
    > > > > > >
    > > > > > > I don’t have a clear preference among them. I’d be interested to hear
    > > > > > > what you or others think is the better direction.
    > > > > > >
    > > > > >
    > > > > > I've implemented a patch that adds MODE support to WAIT FOR LSN
    > > > > >
    > > > > > The new grammar looks like:
    > > > > >
    > > > > > ——
    > > > > > WAIT FOR LSN '<lsn>' [MODE { REPLAY | WRITE | FLUSH }] [WITH (...)]
    > > > > > ——
    > > > > >
    > > > > > Two modes added: flush and write
    > > > > >
    > > > > > Design decisions:
    > > > > >
    > > > > > 1. MODE as a separate keyword (not in WITH clause) - This follows the
    > > > > > pattern used by LOCK command. It also makes the common case more
    > > > > > concise.
    > > > > >
    > > > > > 2. REPLAY as the default - When MODE is not specified, it defaults to REPLAY.
    > > > > >
    > > > > > 3. Keywords rather than strings - Using `MODE WRITE` rather than `MODE 'write'`
    > > > > >
    > > > > > The patch set includes:
    > > > > > -------
    > > > > > 0001 - Extend xlogwait infrastructure with write and flush wait types
    > > > > >
    > > > > > Adds WAIT_LSN_TYPE_WRITE and WAIT_LSN_TYPE_FLUSH to WaitLSNType enum,
    > > > > > along with corresponding wait events and pairing heaps. Introduces
    > > > > > GetCurrentLSNForWaitType() to retrieve the appropriate LSN based on
    > > > > > wait type, and adds wakeup calls in walreceiver for write/flush
    > > > > > events.
    > > > > >
    > > > > > -------
    > > > > > 0002 - Add pg_last_wal_write_lsn() SQL function
    > > > > >
    > > > > > Adds a new SQL function that returns the current WAL write position on
    > > > > > a standby using GetWalRcvWriteRecPtr(). This complements existing
    > > > > > pg_last_wal_receive_lsn() (flush) and pg_last_wal_replay_lsn()
    > > > > > functions, enabling verification of WAIT FOR LSN MODE WRITE in TAP
    > > > > > tests.
    > > > > >
    > > > > > -------
    > > > > > 0003 - Add MODE parameter to WAIT FOR LSN command
    > > > > >
    > > > > > Extends the parser and executor to support the optional MODE
    > > > > > parameter. Updates documentation with new syntax and mode
    > > > > > descriptions. Adds TAP tests covering all three modes including
    > > > > > mixed-mode concurrent waiters.
    > > > > >
    > > > > > -------
    > > > > > 0004 - Add tab completion for WAIT FOR LSN MODE parameter
    > > > > >
    > > > > > Adds psql tab completion support: completes MODE after LSN value,
    > > > > > completes REPLAY/WRITE/FLUSH after MODE keyword, and completes WITH
    > > > > > after mode selection.
    > > > > >
    > > > > > -------
    > > > > > 0005 - Use WAIT FOR LSN in PostgreSQL::Test::Cluster::wait_for_catchup()
    > > > > >
    > > > > > Replaces polling-based wait_for_catchup() with WAIT FOR LSN when the
    > > > > > target is a standby in recovery, improving test efficiency by avoiding
    > > > > > repeated queries.
    > > > > >
    > > > > > The WRITE and FLUSH modes enable scenarios where applications need to
    > > > > > ensure WAL has been received or persisted on the standby without
    > > > > > waiting for replay to complete.
    > > > > >
    > > > > > Feedback welcome.
    > > > > >
    > > > >
    > > > > Here is the updated v2 patch set. Most of the updates are in patch 3.
    > > > >
    > > > > Changes from v1:
    > > > >
    > > > > Patch 1 (Extend wait types in xlogwait infra)
    > > > > - Renamed enum values for consistency (WAIT_LSN_TYPE_REPLAY →
    > > > > WAIT_LSN_TYPE_REPLAY_STANDBY, etc.)
    > > > >
    > > > > Patch 2 (pg_last_wal_write_lsn):
    > > > > - Clarified documentation and comment
    > > > > - Improved pg_proc.dat description
    > > > >
    > > > > Patch 3 (MODE parameter):
    > > > > - Replaced direct cast with explicit switch statement for WaitLSNMode
    > > > > → WaitLSNType conversion
    > > > > - Improved FLUSH/WRITE mode documentation with verification function references
    > > > > - TAP tests (7b, 7c, 7d): Added walreceiver control for concurrency,
    > > > > explicit blocking verification via poll_query_until, and log-based
    > > > > completion verification via wait_for_log
    > > > > - Fix the timing issue in wait for all three sessions to get the
    > > > > errors after promotion of tap test 8.
    > > > >
    > > > > --
    > > > > Best,
    > > > > Xuneng
    > > >
    > > > Here is the updated v3. The changes are made to patch 3:
    > > >
    > > > - Refactor duplicated TAP test code by extracting helper routines for
    > > > starting and stopping walreceiver.
    > > > - Increase the number of concurrent WRITE and FLUSH waiters in tests
    > > > 7b and 7c from three to five, matching the number in test 7a.
    > > >
    > > > --
    > > > Best,
    > > > Xuneng
    > >
    > > Just realized that patch 2 in prior emails could be dropped for
    > > simplicity. Since the write LSN can be retrieved directly from
    > > pg_stat_wal_receiver, the TAP test in patch 3 does not require a
    > > separate SQL function for this purpose alone.
    > >
    >
    > Just rebase with minor changes to the wait-lsn types.
    >
    
    Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    statement in v5 patch 1.
    
    -- 
    Best,
    Xuneng
    
  60. Re: Implement waiting for wal lsn replay: reloaded

    Chao Li <li.evan.chao@gmail.com> — 2025-12-16T05:48:37Z

    
    > On Oct 4, 2025, at 09:35, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > 
    > Hi,
    > 
    > On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >> 
    >> Hi,
    >> 
    >> On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >>> 
    >>> Hi Álvaro,
    >>> 
    >>> Thanks for your review.
    >>> 
    >>> On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    >>>> 
    >>>> On 2025-Sep-15, Alexander Korotkov wrote:
    >>>> 
    >>>>>> It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    >>>>>> PUBLICATION - all use minimal grammar rules that produce generic
    >>>>>> option lists, with the actual interpretation done in their respective
    >>>>>> implementation files. The moderate complexity in wait.c seems
    >>>>>> acceptable.
    >>>> 
    >>>> Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    >>>> lists of DefElem (a name optionally followed by a value) instead of
    >>>> individual scattered elements that must later be matched up.  Why not
    >>>> use utility_option_list instead and then loop on the list of DefElems?
    >>>> It'd be a lot simpler.
    >>> 
    >>> I took a look at commands like VACUUM and EXPLAIN and they do follow
    >>> this pattern. v11 will make use of utility_option_list.
    >>> 
    >>>> Also, we've found that failing to surround the options by parens leads
    >>>> to pain down the road, so maybe add that.  Given that the LSN seems to
    >>>> be mandatory, maybe make it something like
    >>>> 
    >>>> WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    >>>> 
    >>>> This requires that you make LSN a keyword, albeit unreserved.  Or you
    >>>> could make it
    >>>> WAIT FOR Ident [the rest]
    >>>> and then ensure in C that the identifier matches the word LSN, such as
    >>>> we do for "permissive" and "restrictive" in
    >>>> RowSecurityDefaultPermissive.
    >>> 
    >>> Shall make LSN an unreserved keyword as well.
    >> 
    >> Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    > 
    > v12 removed unused
    > +WaitStmt
    > +WaitStmtParam in pgindent/typedefs.list.
    > 
    > Best,
    > Xuneng
    > <v12-0001-Implement-WAIT-FOR-command.patch>
    
    I just tried to review v12 but failed to “git am”. Can you please rebase the change?
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  61. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-16T06:42:00Z

    Hi,
    
    On Tue, Dec 16, 2025 at 1:49 PM Chao Li <li.evan.chao@gmail.com> wrote:
    >
    >
    >
    > > On Oct 4, 2025, at 09:35, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Sun, Sep 28, 2025 at 5:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >>
    > >> Hi,
    > >>
    > >> On Fri, Sep 26, 2025 at 7:22 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >>>
    > >>> Hi Álvaro,
    > >>>
    > >>> Thanks for your review.
    > >>>
    > >>> On Tue, Sep 16, 2025 at 4:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > >>>>
    > >>>> On 2025-Sep-15, Alexander Korotkov wrote:
    > >>>>
    > >>>>>> It's LGTM. The same pattern is observed in VACUUM, EXPLAIN, and CREATE
    > >>>>>> PUBLICATION - all use minimal grammar rules that produce generic
    > >>>>>> option lists, with the actual interpretation done in their respective
    > >>>>>> implementation files. The moderate complexity in wait.c seems
    > >>>>>> acceptable.
    > >>>>
    > >>>> Actually I find the code in ExecWaitStmt pretty unusual.  We tend to use
    > >>>> lists of DefElem (a name optionally followed by a value) instead of
    > >>>> individual scattered elements that must later be matched up.  Why not
    > >>>> use utility_option_list instead and then loop on the list of DefElems?
    > >>>> It'd be a lot simpler.
    > >>>
    > >>> I took a look at commands like VACUUM and EXPLAIN and they do follow
    > >>> this pattern. v11 will make use of utility_option_list.
    > >>>
    > >>>> Also, we've found that failing to surround the options by parens leads
    > >>>> to pain down the road, so maybe add that.  Given that the LSN seems to
    > >>>> be mandatory, maybe make it something like
    > >>>>
    > >>>> WAIT FOR LSN 'xy/zzy' [ WITH ( utility_option_list ) ]
    > >>>>
    > >>>> This requires that you make LSN a keyword, albeit unreserved.  Or you
    > >>>> could make it
    > >>>> WAIT FOR Ident [the rest]
    > >>>> and then ensure in C that the identifier matches the word LSN, such as
    > >>>> we do for "permissive" and "restrictive" in
    > >>>> RowSecurityDefaultPermissive.
    > >>>
    > >>> Shall make LSN an unreserved keyword as well.
    > >>
    > >> Here's the updated v11.  Many thanks Jian for off-list discussions and review.
    > >
    > > v12 removed unused
    > > +WaitStmt
    > > +WaitStmtParam in pgindent/typedefs.list.
    > >
    > > Best,
    > > Xuneng
    > > <v12-0001-Implement-WAIT-FOR-command.patch>
    >
    > I just tried to review v12 but failed to “git am”. Can you please rebase the change?
    >
    
    Thanks for looking into this.
    
    That series of patches implementing the WAIT FOR REPLAY command was
    applied last month (8af3ae0d , 447aae13, 3b4e53a0, a1f7f91b) in its
    version 20. The current v6 patch set [1] [2] primarily extends the
    WAIT FOR functionality to support waiting for flush and write LSNs on
    a replica by adding a MODE parameter [3]. This made me wonder whether
    it would be more appropriate to start a new thread for the extension,
    though it is still part of the same WAIT FOR command.
    
    [1] https://commitfest.postgresql.org/patch/6265/
    [2] https://www.postgresql.org/message-id/CABPTF7XKti620ZAOXPGuhSZxCKyaV_9stq7ruhnuxvshUxCeRQ@mail.gmail.com
    [3] https://www.postgresql.org/message-id/CAPpHfdt4b0wBC4+Oopp_eFQnNjDvxwQLrQ1r4GMJfCY0XWP0dA@mail.gmail.com
    
    -- 
    Best,
    Xuneng
    
    
    
    
  62. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-12-18T10:38:43Z

    Hi, Xuneng!
    
    On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > statement in v5 patch 1.
    
    Thank you for your work on this patchset.  Generally, it looks like
    good and quite straightforward extension of the current functionality.
    But this patch adds 4 new unreserved keywords to our grammar.  Do you
    think we can put mode into with options clause?
    
    ------
    Regards,
    Alexander Korotkov
    
    
    
    
  63. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-18T12:24:38Z

    Hi Alexander,
    
    On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > statement in v5 patch 1.
    >
    > Thank you for your work on this patchset.  Generally, it looks like
    > good and quite straightforward extension of the current functionality.
    > But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > think we can put mode into with options clause?
    >
    
    Thanks for pointing this out. Yeah, 4 unreserved keywords add
    complexity to the parser and it may not be worthwhile since replay is
    expected to be the common use scenario. Maybe we can do something like
    this:
    
    -- Default (REPLAY mode)
    WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    
    -- Explicit REPLAY mode
    WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    
    -- WRITE mode
    WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    
    If no mode is set explicitly in the options clause, it defaults to
    replay. I'll update the patch per your suggestion.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  64. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-12-18T12:25:43Z

    On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > Hi, Xuneng!
    > >
    > > On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > > statement in v5 patch 1.
    > >
    > > Thank you for your work on this patchset.  Generally, it looks like
    > > good and quite straightforward extension of the current functionality.
    > > But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > > think we can put mode into with options clause?
    > >
    >
    > Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > complexity to the parser and it may not be worthwhile since replay is
    > expected to be the common use scenario. Maybe we can do something like
    > this:
    >
    > -- Default (REPLAY mode)
    > WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    >
    > -- Explicit REPLAY mode
    > WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    >
    > -- WRITE mode
    > WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    >
    > If no mode is set explicitly in the options clause, it defaults to
    > replay. I'll update the patch per your suggestion.
    
    This is exactly what I meant.  Please, go ahead.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  65. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-19T02:49:46Z

    Hi,
    
    On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > Hi, Xuneng!
    > > >
    > > > On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > > > statement in v5 patch 1.
    > > >
    > > > Thank you for your work on this patchset.  Generally, it looks like
    > > > good and quite straightforward extension of the current functionality.
    > > > But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > > > think we can put mode into with options clause?
    > > >
    > >
    > > Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > > complexity to the parser and it may not be worthwhile since replay is
    > > expected to be the common use scenario. Maybe we can do something like
    > > this:
    > >
    > > -- Default (REPLAY mode)
    > > WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    > >
    > > -- Explicit REPLAY mode
    > > WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    > >
    > > -- WRITE mode
    > > WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    > >
    > > If no mode is set explicitly in the options clause, it defaults to
    > > replay. I'll update the patch per your suggestion.
    >
    > This is exactly what I meant.  Please, go ahead.
    >
    
    Here is the updated patch set (v7). Please check.
    
    -- 
    Best,
    Xuneng
    
  66. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-12-20T21:09:43Z

    On Fri, Dec 19, 2025 at 4:50 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > >
    > > > > Hi, Xuneng!
    > > > >
    > > > > On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > > Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > > > > statement in v5 patch 1.
    > > > >
    > > > > Thank you for your work on this patchset.  Generally, it looks like
    > > > > good and quite straightforward extension of the current functionality.
    > > > > But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > > > > think we can put mode into with options clause?
    > > > >
    > > >
    > > > Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > > > complexity to the parser and it may not be worthwhile since replay is
    > > > expected to be the common use scenario. Maybe we can do something like
    > > > this:
    > > >
    > > > -- Default (REPLAY mode)
    > > > WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    > > >
    > > > -- Explicit REPLAY mode
    > > > WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    > > >
    > > > -- WRITE mode
    > > > WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    > > >
    > > > If no mode is set explicitly in the options clause, it defaults to
    > > > replay. I'll update the patch per your suggestion.
    > >
    > > This is exactly what I meant.  Please, go ahead.
    > >
    >
    > Here is the updated patch set (v7). Please check.
    
    I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    mode parameter.  Should we allow this?
    
    If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    principle, we could encode both as just 'flush' mode, and detect which
    WaitLSNType to pick by checking if recovery is in progress.  However,
    how should we then react to unreached flush location after standby
    promotion (technically it could be still reached but on the different
    timeline)?
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  67. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-21T04:37:18Z

    Hi Alexander,
    
    Thanks for your feedback!
    
    > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > mode parameter.  Should we allow this?
    
    I think this constraint could be relaxed if needed. I was previously
    unsure about the use cases.
    
    > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > principle, we could encode both as just 'flush' mode, and detect which
    > WaitLSNType to pick by checking if recovery is in progress.  However,
    > how should we then react to unreached flush location after standby
    > promotion (technically it could be still reached but on the different
    > timeline)?
    >
    
    Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    both primary and replica. Currently, wait for commands error out if
    promotion occurs since: either the requested LSN type does not exist
    on the primary, or we do not yet have the infrastructure to support
    continuing the wait. If we allow waiting for flush on the primary as a
    user-visible command and the wake-up calls for flush in primary are
    introduced, the question becomes whether we should still abort the
    wait on promotion, or continue waiting—as you noted—given that the
    target LSN might still be reached, albeit on a different timeline. The
    question behind this might be: do users care and should be aware of
    the state change of the server while waiting? If they do, then we
    better stop the waiting and report the error. In this case, I am
    inclined to to break the unified flush mode to something like
    primary_flush/standby_flush mode and
    WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    
    --
    Best,
    Xuneng
    
    
    
    
  68. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-22T07:56:59Z

    Hi,
    
    On Sun, Dec 21, 2025 at 12:37 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Alexander,
    >
    > Thanks for your feedback!
    >
    > > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > > mode parameter.  Should we allow this?
    >
    > I think this constraint could be relaxed if needed. I was previously
    > unsure about the use cases.
    
    Flush mode on the primary seems useful when synchronous_commit is set
    to off [1]. In that mode, a transaction in primary may return success
    before its WAL is durably flushed to disk, trading durability for
    lower latency. A “wait for primary flush” operation provides an
    explicit durability barrier for cases where applications or tools
    occasionally need stronger guarantees.
    
    [1] https://postgresqlco.nf/doc/en/param/synchronous_commit/
    
    > > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > > principle, we could encode both as just 'flush' mode, and detect which
    > > WaitLSNType to pick by checking if recovery is in progress.  However,
    > > how should we then react to unreached flush location after standby
    > > promotion (technically it could be still reached but on the different
    > > timeline)?
    > >
    >
    > Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    > both primary and replica. Currently, wait for commands error out if
    > promotion occurs since: either the requested LSN type does not exist
    > on the primary, or we do not yet have the infrastructure to support
    > continuing the wait. If we allow waiting for flush on the primary as a
    > user-visible command and the wake-up calls for flush in primary are
    > introduced, the question becomes whether we should still abort the
    > wait on promotion, or continue waiting—as you noted—given that the
    > target LSN might still be reached, albeit on a different timeline. The
    > question behind this might be: do users care and should be aware of
    > the state change of the server while waiting? If they do, then we
    > better stop the waiting and report the error. In this case, I am
    > inclined to to break the unified flush mode to something like
    > primary_flush/standby_flush mode and
    > WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    >
    
    After further consideration, it also seems reasonable to use a single,
    unified flush mode that works on both primary and standby servers,
    provided its semantics are clearly documented to avoid the potential
    confusion on failure. I don’t have a strong preference between these
    two and would be interested in your thoughts.
    
    If a standby is promoted while a session is waiting, the command
    better abort and return an error (or report “not in recovery” when
    using NO_THROW). At that point, the meaning of the LSN being waited
    for may have changed due to the timeline switch and the transition
    from standby to primary. An LSN such as 0/5000000 on TLI 2 can
    represent entirely different WAL content from 0/5000000 on TLI 1.
    Allowing the wait to silently continue across promotion risks giving
    users a false sense of safety—for example, interpreting “wait
    completed” as “the original data is now durable,” which would no
    longer be true.
    
    --
    Best,
    Xuneng
    
  69. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-12-25T11:13:36Z

    Hi, Xuneng!
    
    On Mon, Dec 22, 2025 at 9:57 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Sun, Dec 21, 2025 at 12:37 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Alexander,
    > >
    > > Thanks for your feedback!
    > >
    > > > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > > > mode parameter.  Should we allow this?
    > >
    > > I think this constraint could be relaxed if needed. I was previously
    > > unsure about the use cases.
    >
    > Flush mode on the primary seems useful when synchronous_commit is set
    > to off [1]. In that mode, a transaction in primary may return success
    > before its WAL is durably flushed to disk, trading durability for
    > lower latency. A “wait for primary flush” operation provides an
    > explicit durability barrier for cases where applications or tools
    > occasionally need stronger guarantees.
    >
    > [1] https://postgresqlco.nf/doc/en/param/synchronous_commit/
    >
    > > > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > > > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > > > principle, we could encode both as just 'flush' mode, and detect which
    > > > WaitLSNType to pick by checking if recovery is in progress.  However,
    > > > how should we then react to unreached flush location after standby
    > > > promotion (technically it could be still reached but on the different
    > > > timeline)?
    > > >
    > >
    > > Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    > > both primary and replica. Currently, wait for commands error out if
    > > promotion occurs since: either the requested LSN type does not exist
    > > on the primary, or we do not yet have the infrastructure to support
    > > continuing the wait. If we allow waiting for flush on the primary as a
    > > user-visible command and the wake-up calls for flush in primary are
    > > introduced, the question becomes whether we should still abort the
    > > wait on promotion, or continue waiting—as you noted—given that the
    > > target LSN might still be reached, albeit on a different timeline. The
    > > question behind this might be: do users care and should be aware of
    > > the state change of the server while waiting? If they do, then we
    > > better stop the waiting and report the error. In this case, I am
    > > inclined to to break the unified flush mode to something like
    > > primary_flush/standby_flush mode and
    > > WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    > >
    >
    > After further consideration, it also seems reasonable to use a single,
    > unified flush mode that works on both primary and standby servers,
    > provided its semantics are clearly documented to avoid the potential
    > confusion on failure. I don’t have a strong preference between these
    > two and would be interested in your thoughts.
    >
    > If a standby is promoted while a session is waiting, the command
    > better abort and return an error (or report “not in recovery” when
    > using NO_THROW). At that point, the meaning of the LSN being waited
    > for may have changed due to the timeline switch and the transition
    > from standby to primary. An LSN such as 0/5000000 on TLI 2 can
    > represent entirely different WAL content from 0/5000000 on TLI 1.
    > Allowing the wait to silently continue across promotion risks giving
    > users a false sense of safety—for example, interpreting “wait
    > completed” as “the original data is now durable,” which would no
    > longer be true.
    
    Agree, but there is still risk that promotion happens after user send
    the query but before we started to wait.  In this case we will still
    silently start to wait on primary, while user probably meant to wait
    on replica.  Probably it would be safer to have separate user-visible
    modes for waiting on primary and on replica?
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  70. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-25T12:52:34Z

    Hi Alexander,
    
    On Thu, Dec 25, 2025 at 7:13 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Mon, Dec 22, 2025 at 9:57 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Sun, Dec 21, 2025 at 12:37 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi Alexander,
    > > >
    > > > Thanks for your feedback!
    > > >
    > > > > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > > > > mode parameter.  Should we allow this?
    > > >
    > > > I think this constraint could be relaxed if needed. I was previously
    > > > unsure about the use cases.
    > >
    > > Flush mode on the primary seems useful when synchronous_commit is set
    > > to off [1]. In that mode, a transaction in primary may return success
    > > before its WAL is durably flushed to disk, trading durability for
    > > lower latency. A “wait for primary flush” operation provides an
    > > explicit durability barrier for cases where applications or tools
    > > occasionally need stronger guarantees.
    > >
    > > [1] https://postgresqlco.nf/doc/en/param/synchronous_commit/
    > >
    > > > > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > > > > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > > > > principle, we could encode both as just 'flush' mode, and detect which
    > > > > WaitLSNType to pick by checking if recovery is in progress.  However,
    > > > > how should we then react to unreached flush location after standby
    > > > > promotion (technically it could be still reached but on the different
    > > > > timeline)?
    > > > >
    > > >
    > > > Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    > > > both primary and replica. Currently, wait for commands error out if
    > > > promotion occurs since: either the requested LSN type does not exist
    > > > on the primary, or we do not yet have the infrastructure to support
    > > > continuing the wait. If we allow waiting for flush on the primary as a
    > > > user-visible command and the wake-up calls for flush in primary are
    > > > introduced, the question becomes whether we should still abort the
    > > > wait on promotion, or continue waiting—as you noted—given that the
    > > > target LSN might still be reached, albeit on a different timeline. The
    > > > question behind this might be: do users care and should be aware of
    > > > the state change of the server while waiting? If they do, then we
    > > > better stop the waiting and report the error. In this case, I am
    > > > inclined to to break the unified flush mode to something like
    > > > primary_flush/standby_flush mode and
    > > > WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    > > >
    > >
    > > After further consideration, it also seems reasonable to use a single,
    > > unified flush mode that works on both primary and standby servers,
    > > provided its semantics are clearly documented to avoid the potential
    > > confusion on failure. I don’t have a strong preference between these
    > > two and would be interested in your thoughts.
    > >
    > > If a standby is promoted while a session is waiting, the command
    > > better abort and return an error (or report “not in recovery” when
    > > using NO_THROW). At that point, the meaning of the LSN being waited
    > > for may have changed due to the timeline switch and the transition
    > > from standby to primary. An LSN such as 0/5000000 on TLI 2 can
    > > represent entirely different WAL content from 0/5000000 on TLI 1.
    > > Allowing the wait to silently continue across promotion risks giving
    > > users a false sense of safety—for example, interpreting “wait
    > > completed” as “the original data is now durable,” which would no
    > > longer be true.
    >
    > Agree, but there is still risk that promotion happens after user send
    > the query but before we started to wait.  In this case we will still
    > silently start to wait on primary, while user probably meant to wait
    > on replica.  Probably it would be safer to have separate user-visible
    > modes for waiting on primary and on replica?
    >
    
    Thanks for your thoughts. You're right about the race condition. If
    promotion happens between query submission and execution, a unified
    'flush' mode could silently switch semantics without the user knowing.
    Separate modes like 'standby_flush' and 'primary_flush' would make
    user intent explicit and catch this case with an error, which is
    safer. Do these two terms look reasonable to you, or would you suggest
    better names? If they look ok, I plan to update the implementation to
    use these two modes.
    
    
    --
    Best,
    Xuneng
    
    
    
    
  71. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2025-12-25T16:34:02Z

    On Thu, Dec 25, 2025 at 2:52 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Thu, Dec 25, 2025 at 7:13 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > Hi, Xuneng!
    > >
    > > On Mon, Dec 22, 2025 at 9:57 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > On Sun, Dec 21, 2025 at 12:37 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi Alexander,
    > > > >
    > > > > Thanks for your feedback!
    > > > >
    > > > > > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > > > > > mode parameter.  Should we allow this?
    > > > >
    > > > > I think this constraint could be relaxed if needed. I was previously
    > > > > unsure about the use cases.
    > > >
    > > > Flush mode on the primary seems useful when synchronous_commit is set
    > > > to off [1]. In that mode, a transaction in primary may return success
    > > > before its WAL is durably flushed to disk, trading durability for
    > > > lower latency. A “wait for primary flush” operation provides an
    > > > explicit durability barrier for cases where applications or tools
    > > > occasionally need stronger guarantees.
    > > >
    > > > [1] https://postgresqlco.nf/doc/en/param/synchronous_commit/
    > > >
    > > > > > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > > > > > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > > > > > principle, we could encode both as just 'flush' mode, and detect which
    > > > > > WaitLSNType to pick by checking if recovery is in progress.  However,
    > > > > > how should we then react to unreached flush location after standby
    > > > > > promotion (technically it could be still reached but on the different
    > > > > > timeline)?
    > > > > >
    > > > >
    > > > > Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    > > > > both primary and replica. Currently, wait for commands error out if
    > > > > promotion occurs since: either the requested LSN type does not exist
    > > > > on the primary, or we do not yet have the infrastructure to support
    > > > > continuing the wait. If we allow waiting for flush on the primary as a
    > > > > user-visible command and the wake-up calls for flush in primary are
    > > > > introduced, the question becomes whether we should still abort the
    > > > > wait on promotion, or continue waiting—as you noted—given that the
    > > > > target LSN might still be reached, albeit on a different timeline. The
    > > > > question behind this might be: do users care and should be aware of
    > > > > the state change of the server while waiting? If they do, then we
    > > > > better stop the waiting and report the error. In this case, I am
    > > > > inclined to to break the unified flush mode to something like
    > > > > primary_flush/standby_flush mode and
    > > > > WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    > > > >
    > > >
    > > > After further consideration, it also seems reasonable to use a single,
    > > > unified flush mode that works on both primary and standby servers,
    > > > provided its semantics are clearly documented to avoid the potential
    > > > confusion on failure. I don’t have a strong preference between these
    > > > two and would be interested in your thoughts.
    > > >
    > > > If a standby is promoted while a session is waiting, the command
    > > > better abort and return an error (or report “not in recovery” when
    > > > using NO_THROW). At that point, the meaning of the LSN being waited
    > > > for may have changed due to the timeline switch and the transition
    > > > from standby to primary. An LSN such as 0/5000000 on TLI 2 can
    > > > represent entirely different WAL content from 0/5000000 on TLI 1.
    > > > Allowing the wait to silently continue across promotion risks giving
    > > > users a false sense of safety—for example, interpreting “wait
    > > > completed” as “the original data is now durable,” which would no
    > > > longer be true.
    > >
    > > Agree, but there is still risk that promotion happens after user send
    > > the query but before we started to wait.  In this case we will still
    > > silently start to wait on primary, while user probably meant to wait
    > > on replica.  Probably it would be safer to have separate user-visible
    > > modes for waiting on primary and on replica?
    > >
    >
    > Thanks for your thoughts. You're right about the race condition. If
    > promotion happens between query submission and execution, a unified
    > 'flush' mode could silently switch semantics without the user knowing.
    > Separate modes like 'standby_flush' and 'primary_flush' would make
    > user intent explicit and catch this case with an error, which is
    > safer. Do these two terms look reasonable to you, or would you suggest
    > better names? If they look ok, I plan to update the implementation to
    > use these two modes.
    
    Thank you, Xuneng.  'standby_flush' and 'primary_flush' look good for
    me.  Please, go ahead.  I think we should name other modes
    'standby_write' and 'standby_replay' for the sake of unity.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  72. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-26T00:31:26Z

    Hi,
    
    On Fri, Dec 26, 2025 at 12:34 AM Alexander Korotkov
    <aekorotkov@gmail.com> wrote:
    >
    > On Thu, Dec 25, 2025 at 2:52 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Thu, Dec 25, 2025 at 7:13 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > Hi, Xuneng!
    > > >
    > > > On Mon, Dec 22, 2025 at 9:57 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > On Sun, Dec 21, 2025 at 12:37 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > >
    > > > > > Hi Alexander,
    > > > > >
    > > > > > Thanks for your feedback!
    > > > > >
    > > > > > > I see that we can't specify WAIT_LSN_TYPE_PRIMARY_FLUSH by setting
    > > > > > > mode parameter.  Should we allow this?
    > > > > >
    > > > > > I think this constraint could be relaxed if needed. I was previously
    > > > > > unsure about the use cases.
    > > > >
    > > > > Flush mode on the primary seems useful when synchronous_commit is set
    > > > > to off [1]. In that mode, a transaction in primary may return success
    > > > > before its WAL is durably flushed to disk, trading durability for
    > > > > lower latency. A “wait for primary flush” operation provides an
    > > > > explicit durability barrier for cases where applications or tools
    > > > > occasionally need stronger guarantees.
    > > > >
    > > > > [1] https://postgresqlco.nf/doc/en/param/synchronous_commit/
    > > > >
    > > > > > > If we allow specifying WAIT_LSN_TYPE_PRIMARY_FLUSH, should it be
    > > > > > > separate mode value or the same with WAIT_LSN_TYPE_STANDBY_FLUSH?  In
    > > > > > > principle, we could encode both as just 'flush' mode, and detect which
    > > > > > > WaitLSNType to pick by checking if recovery is in progress.  However,
    > > > > > > how should we then react to unreached flush location after standby
    > > > > > > promotion (technically it could be still reached but on the different
    > > > > > > timeline)?
    > > > > > >
    > > > > >
    > > > > > Technically, we can use 'flush' mode to specify WAIT FOR behavior in
    > > > > > both primary and replica. Currently, wait for commands error out if
    > > > > > promotion occurs since: either the requested LSN type does not exist
    > > > > > on the primary, or we do not yet have the infrastructure to support
    > > > > > continuing the wait. If we allow waiting for flush on the primary as a
    > > > > > user-visible command and the wake-up calls for flush in primary are
    > > > > > introduced, the question becomes whether we should still abort the
    > > > > > wait on promotion, or continue waiting—as you noted—given that the
    > > > > > target LSN might still be reached, albeit on a different timeline. The
    > > > > > question behind this might be: do users care and should be aware of
    > > > > > the state change of the server while waiting? If they do, then we
    > > > > > better stop the waiting and report the error. In this case, I am
    > > > > > inclined to to break the unified flush mode to something like
    > > > > > primary_flush/standby_flush mode and
    > > > > > WAIT_LSN_TYPE_PRIMARY_FLUSH/WAIT_LSN_TYPE_STANDBY_FLUSH respectively.
    > > > > >
    > > > >
    > > > > After further consideration, it also seems reasonable to use a single,
    > > > > unified flush mode that works on both primary and standby servers,
    > > > > provided its semantics are clearly documented to avoid the potential
    > > > > confusion on failure. I don’t have a strong preference between these
    > > > > two and would be interested in your thoughts.
    > > > >
    > > > > If a standby is promoted while a session is waiting, the command
    > > > > better abort and return an error (or report “not in recovery” when
    > > > > using NO_THROW). At that point, the meaning of the LSN being waited
    > > > > for may have changed due to the timeline switch and the transition
    > > > > from standby to primary. An LSN such as 0/5000000 on TLI 2 can
    > > > > represent entirely different WAL content from 0/5000000 on TLI 1.
    > > > > Allowing the wait to silently continue across promotion risks giving
    > > > > users a false sense of safety—for example, interpreting “wait
    > > > > completed” as “the original data is now durable,” which would no
    > > > > longer be true.
    > > >
    > > > Agree, but there is still risk that promotion happens after user send
    > > > the query but before we started to wait.  In this case we will still
    > > > silently start to wait on primary, while user probably meant to wait
    > > > on replica.  Probably it would be safer to have separate user-visible
    > > > modes for waiting on primary and on replica?
    > > >
    > >
    > > Thanks for your thoughts. You're right about the race condition. If
    > > promotion happens between query submission and execution, a unified
    > > 'flush' mode could silently switch semantics without the user knowing.
    > > Separate modes like 'standby_flush' and 'primary_flush' would make
    > > user intent explicit and catch this case with an error, which is
    > > safer. Do these two terms look reasonable to you, or would you suggest
    > > better names? If they look ok, I plan to update the implementation to
    > > use these two modes.
    >
    > Thank you, Xuneng.  'standby_flush' and 'primary_flush' look good for
    > me.  Please, go ahead.  I think we should name other modes
    > 'standby_write' and 'standby_replay' for the sake of unity.
    >
    
    Thanks. Yeah, renaming existing modes to  'standby_write' and
    'standby_replay' also makes sense to me.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  73. Re: Implement waiting for wal lsn replay: reloaded

    Chao Li <li.evan.chao@gmail.com> — 2025-12-26T08:24:39Z

    
    > On Dec 19, 2025, at 10:49, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > 
    > Hi,
    > 
    > On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >> 
    >> On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >>> On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >>>> 
    >>>> Hi, Xuneng!
    >>>> 
    >>>> On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >>>>> Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    >>>>> statement in v5 patch 1.
    >>>> 
    >>>> Thank you for your work on this patchset.  Generally, it looks like
    >>>> good and quite straightforward extension of the current functionality.
    >>>> But this patch adds 4 new unreserved keywords to our grammar.  Do you
    >>>> think we can put mode into with options clause?
    >>>> 
    >>> 
    >>> Thanks for pointing this out. Yeah, 4 unreserved keywords add
    >>> complexity to the parser and it may not be worthwhile since replay is
    >>> expected to be the common use scenario. Maybe we can do something like
    >>> this:
    >>> 
    >>> -- Default (REPLAY mode)
    >>> WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    >>> 
    >>> -- Explicit REPLAY mode
    >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    >>> 
    >>> -- WRITE mode
    >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    >>> 
    >>> If no mode is set explicitly in the options clause, it defaults to
    >>> replay. I'll update the patch per your suggestion.
    >> 
    >> This is exactly what I meant.  Please, go ahead.
    >> 
    > 
    > Here is the updated patch set (v7). Please check.
    > 
    > -- 
    > Best,
    > Xuneng
    > <v7-0001-Extend-xlogwait-infrastructure-with-write-and-flu.patch><v7-0004-Use-WAIT-FOR-LSN-in.patch><v7-0003-Add-tab-completion-for-WAIT-FOR-LSN-MODE-option.patch><v7-0002-Add-MODE-option-to-WAIT-FOR-LSN-command.patch>
    
    Hi Xuneng,
    
    A solid patch! Just a few small comments:
    
    1 - 0001
    ```
    +XLogRecPtr
    +GetCurrentLSNForWaitType(WaitLSNType lsnType)
    +{
    +	switch (lsnType)
    +	{
    +		case WAIT_LSN_TYPE_STANDBY_REPLAY:
    +			return GetXLogReplayRecPtr(NULL);
    +
    +		case WAIT_LSN_TYPE_STANDBY_WRITE:
    +			return GetWalRcvWriteRecPtr();
    +
    +		case WAIT_LSN_TYPE_STANDBY_FLUSH:
    +			return GetWalRcvFlushRecPtr(NULL, NULL);
    +
    +		case WAIT_LSN_TYPE_PRIMARY_FLUSH:
    +			return GetFlushRecPtr(NULL);
    +	}
    +
    +	elog(ERROR, "invalid LSN wait type: %d", lsnType);
    +	pg_unreachable();
    +}
    ```
    
    As you add pg_unreachable() in the new function GetCurrentLSNForWaitType(), I’m thinking if we should just do an Assert(), I saw every existing related function has done such an assert, for example addLSNWaiter(), it does “Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);”. I guess we can just following the current mechanism to verify lsnType. So, for GetCurrentLSNForWaitType(), we can just add a default clause and Assert(false).
    
    2 - 0002
    ```
    +			else
    +				ereport(ERROR,
    +						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    +						 errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    +								"MODE", mode_str),
    ```
    
    I wonder why don’t we directly put MODE into the error message?
    
    3 - 0002
    ```
     		case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
     			if (throw)
     			{
    +				const		WaitLSNTypeDesc *desc = &WaitLSNTypeDescs[lsnType];
    +				XLogRecPtr	currentLSN = GetCurrentLSNForWaitType(lsnType);
    +
     				if (PromoteIsTriggered())
     				{
     					ereport(ERROR,
     							errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     							errmsg("recovery is not in progress"),
    -							errdetail("Recovery ended before replaying target LSN %X/%08X; last replay LSN %X/%08X.",
    +							errdetail("Recovery ended before target LSN %X/%08X was %s; last %s LSN %X/%08X.",
     									  LSN_FORMAT_ARGS(lsn),
    -									  LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    +									  desc->verb,
    +									  desc->noun,
    +									  LSN_FORMAT_ARGS(currentLSN)));
     				}
     				else
     					ereport(ERROR,
     							errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     							errmsg("recovery is not in progress"),
    -							errhint("Waiting for the replay LSN can only be executed during recovery."));
    +							errhint("Waiting for the %s LSN can only be executed during recovery.",
    +									desc->noun));
     			}
    ```
    
    currentLSN is only used in the if clause, thus it can be defined inside the if clause.
    
    3 - 0002
    ```
    +	/*
    +	 * If we wrote an LSN that someone was waiting for then walk over the
    +	 * shared memory array and set latches to notify the waiters.
    +	 */
    +	if (waitLSNState &&
    +		(LogstreamResult.Write >=
    +		 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    +		WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    ```
    
    Do we need to mention "walk over the shared memory array and set latches” in the comment? The logic belongs to WaitLSNWakeup(). What about if the wake up logic changes in future, then this comment would become stale. So I think we only need to mention “notify the waiters”.
    
    
    4 - 0003
    ```
    +	/*
    +	 * Handle parenthesized option list.  This fires when we're in an
    +	 * unfinished parenthesized option list.  get_previous_words treats a
    +	 * completed parenthesized option list as one word, so the above test is
    +	 * correct.  mode takes a string value ('replay', 'write', 'flush'),
    +	 * timeout takes a string value, no_throw takes no value.
    +	 */
     	else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*") &&
     			 !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*)"))
     	{
    -		/*
    -		 * This fires if we're in an unfinished parenthesized option list.
    -		 * get_previous_words treats a completed parenthesized option list as
    -		 * one word, so the above test is correct.
    -		 */
     		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    -			COMPLETE_WITH("timeout", "no_throw");
    -
    -		/*
    -		 * timeout takes a string value, no_throw takes no value. We don't
    -		 * offer completions for these values.
    -		 */
    ```
    
    The new comment has lost the meaning of “We don’t offer completions for these values (timeout and no_throw)”, to be explicit, I feel we can retain the sentence.
    
    5 - 0004
    ```
    +	my $isrecovery =
    +	  $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    +	chomp($isrecovery);
     	croak "unknown mode $mode for 'wait_for_catchup', valid modes are "
     	  . join(', ', keys(%valid_modes))
     	  unless exists($valid_modes{$mode});
    @@ -3347,9 +3350,6 @@ sub wait_for_catchup
     	}
     	if (!defined($target_lsn))
     	{
    -		my $isrecovery =
    -		  $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    -		chomp($isrecovery);
    ```
    
    I wonder why pull up pg_is_in_recovery to an early place and unconditionally call it?
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  74. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-26T16:15:05Z

    Hi Chao,
    
    Thanks a lot for your review!
    
    On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    >
    >
    >
    > > On Dec 19, 2025, at 10:49, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >>
    > >> On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >>> On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >>>>
    > >>>> Hi, Xuneng!
    > >>>>
    > >>>> On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >>>>> Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > >>>>> statement in v5 patch 1.
    > >>>>
    > >>>> Thank you for your work on this patchset.  Generally, it looks like
    > >>>> good and quite straightforward extension of the current functionality.
    > >>>> But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > >>>> think we can put mode into with options clause?
    > >>>>
    > >>>
    > >>> Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > >>> complexity to the parser and it may not be worthwhile since replay is
    > >>> expected to be the common use scenario. Maybe we can do something like
    > >>> this:
    > >>>
    > >>> -- Default (REPLAY mode)
    > >>> WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    > >>>
    > >>> -- Explicit REPLAY mode
    > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    > >>>
    > >>> -- WRITE mode
    > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    > >>>
    > >>> If no mode is set explicitly in the options clause, it defaults to
    > >>> replay. I'll update the patch per your suggestion.
    > >>
    > >> This is exactly what I meant.  Please, go ahead.
    > >>
    > >
    > > Here is the updated patch set (v7). Please check.
    > >
    > > --
    > > Best,
    > > Xuneng
    > > <v7-0001-Extend-xlogwait-infrastructure-with-write-and-flu.patch><v7-0004-Use-WAIT-FOR-LSN-in.patch><v7-0003-Add-tab-completion-for-WAIT-FOR-LSN-MODE-option.patch><v7-0002-Add-MODE-option-to-WAIT-FOR-LSN-command.patch>
    >
    > Hi Xuneng,
    >
    > A solid patch! Just a few small comments:
    >
    > 1 - 0001
    > ```
    > +XLogRecPtr
    > +GetCurrentLSNForWaitType(WaitLSNType lsnType)
    > +{
    > +       switch (lsnType)
    > +       {
    > +               case WAIT_LSN_TYPE_STANDBY_REPLAY:
    > +                       return GetXLogReplayRecPtr(NULL);
    > +
    > +               case WAIT_LSN_TYPE_STANDBY_WRITE:
    > +                       return GetWalRcvWriteRecPtr();
    > +
    > +               case WAIT_LSN_TYPE_STANDBY_FLUSH:
    > +                       return GetWalRcvFlushRecPtr(NULL, NULL);
    > +
    > +               case WAIT_LSN_TYPE_PRIMARY_FLUSH:
    > +                       return GetFlushRecPtr(NULL);
    > +       }
    > +
    > +       elog(ERROR, "invalid LSN wait type: %d", lsnType);
    > +       pg_unreachable();
    > +}
    > ```
    >
    > As you add pg_unreachable() in the new function GetCurrentLSNForWaitType(), I’m thinking if we should just do an Assert(), I saw every existing related function has done such an assert, for example addLSNWaiter(), it does “Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);”. I guess we can just following the current mechanism to verify lsnType. So, for GetCurrentLSNForWaitType(), we can just add a default clause and Assert(false).
    
    My take is that Assert(false) alone might not be enough here, since
    assertions vanish in non-assert builds. An unexpected lsnType is a
    real bug even in production, so keeping a hard error plus
    pg_unreachable() seems to be a safer pattern. It also acts as a
    guardrail for future extensions — if new wait types are added without
    updating this code, we’ll fail loudly rather than silently returning
    an incorrect LSN. Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT) was added
    to the top of the function.
    
    > 2 - 0002
    > ```
    > +                       else
    > +                               ereport(ERROR,
    > +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    > +                                                               "MODE", mode_str),
    > ```
    >
    > I wonder why don’t we directly put MODE into the error message?
    
    Yeah, putting MODE into the error message is cleaner. It's done in v8.
    
    > 3 - 0002
    > ```
    >                 case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
    >                         if (throw)
    >                         {
    > +                               const           WaitLSNTypeDesc *desc = &WaitLSNTypeDescs[lsnType];
    > +                               XLogRecPtr      currentLSN = GetCurrentLSNForWaitType(lsnType);
    > +
    >                                 if (PromoteIsTriggered())
    >                                 {
    >                                         ereport(ERROR,
    >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    >                                                         errmsg("recovery is not in progress"),
    > -                                                       errdetail("Recovery ended before replaying target LSN %X/%08X; last replay LSN %X/%08X.",
    > +                                                       errdetail("Recovery ended before target LSN %X/%08X was %s; last %s LSN %X/%08X.",
    >                                                                           LSN_FORMAT_ARGS(lsn),
    > -                                                                         LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > +                                                                         desc->verb,
    > +                                                                         desc->noun,
    > +                                                                         LSN_FORMAT_ARGS(currentLSN)));
    >                                 }
    >                                 else
    >                                         ereport(ERROR,
    >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    >                                                         errmsg("recovery is not in progress"),
    > -                                                       errhint("Waiting for the replay LSN can only be executed during recovery."));
    > +                                                       errhint("Waiting for the %s LSN can only be executed during recovery.",
    > +                                                                       desc->noun));
    >                         }
    > ```
    >
    > currentLSN is only used in the if clause, thus it can be defined inside the if clause.
    
    + 1.
    
    > 3 - 0002
    > ```
    > +       /*
    > +        * If we wrote an LSN that someone was waiting for then walk over the
    > +        * shared memory array and set latches to notify the waiters.
    > +        */
    > +       if (waitLSNState &&
    > +               (LogstreamResult.Write >=
    > +                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > +               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > ```
    >
    > Do we need to mention "walk over the shared memory array and set latches” in the comment? The logic belongs to WaitLSNWakeup(). What about if the wake up logic changes in future, then this comment would become stale. So I think we only need to mention “notify the waiters”.
    >
    
    It makes sense to me. They are incorporated into v8.
    
    >
    > 4 - 0003
    > ```
    > +       /*
    > +        * Handle parenthesized option list.  This fires when we're in an
    > +        * unfinished parenthesized option list.  get_previous_words treats a
    > +        * completed parenthesized option list as one word, so the above test is
    > +        * correct.  mode takes a string value ('replay', 'write', 'flush'),
    > +        * timeout takes a string value, no_throw takes no value.
    > +        */
    >         else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*") &&
    >                          !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*)"))
    >         {
    > -               /*
    > -                * This fires if we're in an unfinished parenthesized option list.
    > -                * get_previous_words treats a completed parenthesized option list as
    > -                * one word, so the above test is correct.
    > -                */
    >                 if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    > -                       COMPLETE_WITH("timeout", "no_throw");
    > -
    > -               /*
    > -                * timeout takes a string value, no_throw takes no value. We don't
    > -                * offer completions for these values.
    > -                */
    > ```
    >
    > The new comment has lost the meaning of “We don’t offer completions for these values (timeout and no_throw)”, to be explicit, I feel we can retain the sentence.
    
     The sentence is retained.
    
    > 5 - 0004
    > ```
    > +       my $isrecovery =
    > +         $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > +       chomp($isrecovery);
    >         croak "unknown mode $mode for 'wait_for_catchup', valid modes are "
    >           . join(', ', keys(%valid_modes))
    >           unless exists($valid_modes{$mode});
    > @@ -3347,9 +3350,6 @@ sub wait_for_catchup
    >         }
    >         if (!defined($target_lsn))
    >         {
    > -               my $isrecovery =
    > -                 $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > -               chomp($isrecovery);
    > ```
    >
    > I wonder why pull up pg_is_in_recovery to an early place and unconditionally call it?
    >
    
    This seems unnecessary. I also realized that my earlier approach in
    patch 4 may have been semantically incorrect — it could end up waiting
    for the LSN to replay/write/flush on the node itself, rather than on
    the downstream standby, which defeats the purpose of
    wait_for_catchup(). Patch 4 attempts to address this by running WAIT
    FOR LSN on the standby itself.
    
    Support for primary-flush waiting and the refactoring of existing
    modes have been also incorporated in v8 following Alexander’s
    feedback. The major updates are in patches 2 and 4. Please check.
    
    --
    Best,
    Xuneng
    
  75. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-30T02:12:28Z

    Hi,
    
    On Sat, Dec 27, 2025 at 12:15 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Chao,
    >
    > Thanks a lot for your review!
    >
    > On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > >
    > >
    > >
    > > > On Dec 19, 2025, at 10:49, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >>
    > > >> On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >>> On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >>>>
    > > >>>> Hi, Xuneng!
    > > >>>>
    > > >>>> On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >>>>> Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > >>>>> statement in v5 patch 1.
    > > >>>>
    > > >>>> Thank you for your work on this patchset.  Generally, it looks like
    > > >>>> good and quite straightforward extension of the current functionality.
    > > >>>> But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > > >>>> think we can put mode into with options clause?
    > > >>>>
    > > >>>
    > > >>> Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > > >>> complexity to the parser and it may not be worthwhile since replay is
    > > >>> expected to be the common use scenario. Maybe we can do something like
    > > >>> this:
    > > >>>
    > > >>> -- Default (REPLAY mode)
    > > >>> WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    > > >>>
    > > >>> -- Explicit REPLAY mode
    > > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    > > >>>
    > > >>> -- WRITE mode
    > > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    > > >>>
    > > >>> If no mode is set explicitly in the options clause, it defaults to
    > > >>> replay. I'll update the patch per your suggestion.
    > > >>
    > > >> This is exactly what I meant.  Please, go ahead.
    > > >>
    > > >
    > > > Here is the updated patch set (v7). Please check.
    > > >
    > > > --
    > > > Best,
    > > > Xuneng
    > > > <v7-0001-Extend-xlogwait-infrastructure-with-write-and-flu.patch><v7-0004-Use-WAIT-FOR-LSN-in.patch><v7-0003-Add-tab-completion-for-WAIT-FOR-LSN-MODE-option.patch><v7-0002-Add-MODE-option-to-WAIT-FOR-LSN-command.patch>
    > >
    > > Hi Xuneng,
    > >
    > > A solid patch! Just a few small comments:
    > >
    > > 1 - 0001
    > > ```
    > > +XLogRecPtr
    > > +GetCurrentLSNForWaitType(WaitLSNType lsnType)
    > > +{
    > > +       switch (lsnType)
    > > +       {
    > > +               case WAIT_LSN_TYPE_STANDBY_REPLAY:
    > > +                       return GetXLogReplayRecPtr(NULL);
    > > +
    > > +               case WAIT_LSN_TYPE_STANDBY_WRITE:
    > > +                       return GetWalRcvWriteRecPtr();
    > > +
    > > +               case WAIT_LSN_TYPE_STANDBY_FLUSH:
    > > +                       return GetWalRcvFlushRecPtr(NULL, NULL);
    > > +
    > > +               case WAIT_LSN_TYPE_PRIMARY_FLUSH:
    > > +                       return GetFlushRecPtr(NULL);
    > > +       }
    > > +
    > > +       elog(ERROR, "invalid LSN wait type: %d", lsnType);
    > > +       pg_unreachable();
    > > +}
    > > ```
    > >
    > > As you add pg_unreachable() in the new function GetCurrentLSNForWaitType(), I’m thinking if we should just do an Assert(), I saw every existing related function has done such an assert, for example addLSNWaiter(), it does “Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);”. I guess we can just following the current mechanism to verify lsnType. So, for GetCurrentLSNForWaitType(), we can just add a default clause and Assert(false).
    >
    > My take is that Assert(false) alone might not be enough here, since
    > assertions vanish in non-assert builds. An unexpected lsnType is a
    > real bug even in production, so keeping a hard error plus
    > pg_unreachable() seems to be a safer pattern. It also acts as a
    > guardrail for future extensions — if new wait types are added without
    > updating this code, we’ll fail loudly rather than silently returning
    > an incorrect LSN. Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT) was added
    > to the top of the function.
    >
    > > 2 - 0002
    > > ```
    > > +                       else
    > > +                               ereport(ERROR,
    > > +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > > +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    > > +                                                               "MODE", mode_str),
    > > ```
    > >
    > > I wonder why don’t we directly put MODE into the error message?
    >
    > Yeah, putting MODE into the error message is cleaner. It's done in v8.
    >
    > > 3 - 0002
    > > ```
    > >                 case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
    > >                         if (throw)
    > >                         {
    > > +                               const           WaitLSNTypeDesc *desc = &WaitLSNTypeDescs[lsnType];
    > > +                               XLogRecPtr      currentLSN = GetCurrentLSNForWaitType(lsnType);
    > > +
    > >                                 if (PromoteIsTriggered())
    > >                                 {
    > >                                         ereport(ERROR,
    > >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > >                                                         errmsg("recovery is not in progress"),
    > > -                                                       errdetail("Recovery ended before replaying target LSN %X/%08X; last replay LSN %X/%08X.",
    > > +                                                       errdetail("Recovery ended before target LSN %X/%08X was %s; last %s LSN %X/%08X.",
    > >                                                                           LSN_FORMAT_ARGS(lsn),
    > > -                                                                         LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > +                                                                         desc->verb,
    > > +                                                                         desc->noun,
    > > +                                                                         LSN_FORMAT_ARGS(currentLSN)));
    > >                                 }
    > >                                 else
    > >                                         ereport(ERROR,
    > >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > >                                                         errmsg("recovery is not in progress"),
    > > -                                                       errhint("Waiting for the replay LSN can only be executed during recovery."));
    > > +                                                       errhint("Waiting for the %s LSN can only be executed during recovery.",
    > > +                                                                       desc->noun));
    > >                         }
    > > ```
    > >
    > > currentLSN is only used in the if clause, thus it can be defined inside the if clause.
    >
    > + 1.
    >
    > > 3 - 0002
    > > ```
    > > +       /*
    > > +        * If we wrote an LSN that someone was waiting for then walk over the
    > > +        * shared memory array and set latches to notify the waiters.
    > > +        */
    > > +       if (waitLSNState &&
    > > +               (LogstreamResult.Write >=
    > > +                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > +               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > ```
    > >
    > > Do we need to mention "walk over the shared memory array and set latches” in the comment? The logic belongs to WaitLSNWakeup(). What about if the wake up logic changes in future, then this comment would become stale. So I think we only need to mention “notify the waiters”.
    > >
    >
    > It makes sense to me. They are incorporated into v8.
    >
    > >
    > > 4 - 0003
    > > ```
    > > +       /*
    > > +        * Handle parenthesized option list.  This fires when we're in an
    > > +        * unfinished parenthesized option list.  get_previous_words treats a
    > > +        * completed parenthesized option list as one word, so the above test is
    > > +        * correct.  mode takes a string value ('replay', 'write', 'flush'),
    > > +        * timeout takes a string value, no_throw takes no value.
    > > +        */
    > >         else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*") &&
    > >                          !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*)"))
    > >         {
    > > -               /*
    > > -                * This fires if we're in an unfinished parenthesized option list.
    > > -                * get_previous_words treats a completed parenthesized option list as
    > > -                * one word, so the above test is correct.
    > > -                */
    > >                 if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    > > -                       COMPLETE_WITH("timeout", "no_throw");
    > > -
    > > -               /*
    > > -                * timeout takes a string value, no_throw takes no value. We don't
    > > -                * offer completions for these values.
    > > -                */
    > > ```
    > >
    > > The new comment has lost the meaning of “We don’t offer completions for these values (timeout and no_throw)”, to be explicit, I feel we can retain the sentence.
    >
    >  The sentence is retained.
    >
    > > 5 - 0004
    > > ```
    > > +       my $isrecovery =
    > > +         $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > > +       chomp($isrecovery);
    > >         croak "unknown mode $mode for 'wait_for_catchup', valid modes are "
    > >           . join(', ', keys(%valid_modes))
    > >           unless exists($valid_modes{$mode});
    > > @@ -3347,9 +3350,6 @@ sub wait_for_catchup
    > >         }
    > >         if (!defined($target_lsn))
    > >         {
    > > -               my $isrecovery =
    > > -                 $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > > -               chomp($isrecovery);
    > > ```
    > >
    > > I wonder why pull up pg_is_in_recovery to an early place and unconditionally call it?
    > >
    >
    > This seems unnecessary. I also realized that my earlier approach in
    > patch 4 may have been semantically incorrect — it could end up waiting
    > for the LSN to replay/write/flush on the node itself, rather than on
    > the downstream standby, which defeats the purpose of
    > wait_for_catchup(). Patch 4 attempts to address this by running WAIT
    > FOR LSN on the standby itself.
    >
    > Support for primary-flush waiting and the refactoring of existing
    > modes have been also incorporated in v8 following Alexander’s
    > feedback. The major updates are in patches 2 and 4. Please check.
    >
    
    Added WaitLSNTypeDesc to typedefs.list in v9 patch 2.
    
    -- 
    Best,
    Xuneng
    
  76. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-30T02:42:09Z

    Hi,
    
    On Tue, Dec 30, 2025 at 10:12 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Sat, Dec 27, 2025 at 12:15 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Chao,
    > >
    > > Thanks a lot for your review!
    > >
    > > On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > > >
    > > >
    > > >
    > > > > On Dec 19, 2025, at 10:49, Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >
    > > > > Hi,
    > > > >
    > > > > On Thu, Dec 18, 2025 at 8:25 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > >>
    > > > >> On Thu, Dec 18, 2025 at 2:24 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >>> On Thu, Dec 18, 2025 at 6:38 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > >>>>
    > > > >>>> Hi, Xuneng!
    > > > >>>>
    > > > >>>> On Tue, Dec 16, 2025 at 6:46 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > >>>>> Remove the erroneous WAIT_LSN_TYPE_COUNT case from the switch
    > > > >>>>> statement in v5 patch 1.
    > > > >>>>
    > > > >>>> Thank you for your work on this patchset.  Generally, it looks like
    > > > >>>> good and quite straightforward extension of the current functionality.
    > > > >>>> But this patch adds 4 new unreserved keywords to our grammar.  Do you
    > > > >>>> think we can put mode into with options clause?
    > > > >>>>
    > > > >>>
    > > > >>> Thanks for pointing this out. Yeah, 4 unreserved keywords add
    > > > >>> complexity to the parser and it may not be worthwhile since replay is
    > > > >>> expected to be the common use scenario. Maybe we can do something like
    > > > >>> this:
    > > > >>>
    > > > >>> -- Default (REPLAY mode)
    > > > >>> WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
    > > > >>>
    > > > >>> -- Explicit REPLAY mode
    > > > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'replay', TIMEOUT '1s');
    > > > >>>
    > > > >>> -- WRITE mode
    > > > >>> WAIT FOR LSN '0/306EE20' WITH (MODE 'write', TIMEOUT '1s');
    > > > >>>
    > > > >>> If no mode is set explicitly in the options clause, it defaults to
    > > > >>> replay. I'll update the patch per your suggestion.
    > > > >>
    > > > >> This is exactly what I meant.  Please, go ahead.
    > > > >>
    > > > >
    > > > > Here is the updated patch set (v7). Please check.
    > > > >
    > > > > --
    > > > > Best,
    > > > > Xuneng
    > > > > <v7-0001-Extend-xlogwait-infrastructure-with-write-and-flu.patch><v7-0004-Use-WAIT-FOR-LSN-in.patch><v7-0003-Add-tab-completion-for-WAIT-FOR-LSN-MODE-option.patch><v7-0002-Add-MODE-option-to-WAIT-FOR-LSN-command.patch>
    > > >
    > > > Hi Xuneng,
    > > >
    > > > A solid patch! Just a few small comments:
    > > >
    > > > 1 - 0001
    > > > ```
    > > > +XLogRecPtr
    > > > +GetCurrentLSNForWaitType(WaitLSNType lsnType)
    > > > +{
    > > > +       switch (lsnType)
    > > > +       {
    > > > +               case WAIT_LSN_TYPE_STANDBY_REPLAY:
    > > > +                       return GetXLogReplayRecPtr(NULL);
    > > > +
    > > > +               case WAIT_LSN_TYPE_STANDBY_WRITE:
    > > > +                       return GetWalRcvWriteRecPtr();
    > > > +
    > > > +               case WAIT_LSN_TYPE_STANDBY_FLUSH:
    > > > +                       return GetWalRcvFlushRecPtr(NULL, NULL);
    > > > +
    > > > +               case WAIT_LSN_TYPE_PRIMARY_FLUSH:
    > > > +                       return GetFlushRecPtr(NULL);
    > > > +       }
    > > > +
    > > > +       elog(ERROR, "invalid LSN wait type: %d", lsnType);
    > > > +       pg_unreachable();
    > > > +}
    > > > ```
    > > >
    > > > As you add pg_unreachable() in the new function GetCurrentLSNForWaitType(), I’m thinking if we should just do an Assert(), I saw every existing related function has done such an assert, for example addLSNWaiter(), it does “Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);”. I guess we can just following the current mechanism to verify lsnType. So, for GetCurrentLSNForWaitType(), we can just add a default clause and Assert(false).
    > >
    > > My take is that Assert(false) alone might not be enough here, since
    > > assertions vanish in non-assert builds. An unexpected lsnType is a
    > > real bug even in production, so keeping a hard error plus
    > > pg_unreachable() seems to be a safer pattern. It also acts as a
    > > guardrail for future extensions — if new wait types are added without
    > > updating this code, we’ll fail loudly rather than silently returning
    > > an incorrect LSN. Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT) was added
    > > to the top of the function.
    > >
    > > > 2 - 0002
    > > > ```
    > > > +                       else
    > > > +                               ereport(ERROR,
    > > > +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > > > +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    > > > +                                                               "MODE", mode_str),
    > > > ```
    > > >
    > > > I wonder why don’t we directly put MODE into the error message?
    > >
    > > Yeah, putting MODE into the error message is cleaner. It's done in v8.
    > >
    > > > 3 - 0002
    > > > ```
    > > >                 case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
    > > >                         if (throw)
    > > >                         {
    > > > +                               const           WaitLSNTypeDesc *desc = &WaitLSNTypeDescs[lsnType];
    > > > +                               XLogRecPtr      currentLSN = GetCurrentLSNForWaitType(lsnType);
    > > > +
    > > >                                 if (PromoteIsTriggered())
    > > >                                 {
    > > >                                         ereport(ERROR,
    > > >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > > >                                                         errmsg("recovery is not in progress"),
    > > > -                                                       errdetail("Recovery ended before replaying target LSN %X/%08X; last replay LSN %X/%08X.",
    > > > +                                                       errdetail("Recovery ended before target LSN %X/%08X was %s; last %s LSN %X/%08X.",
    > > >                                                                           LSN_FORMAT_ARGS(lsn),
    > > > -                                                                         LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > > +                                                                         desc->verb,
    > > > +                                                                         desc->noun,
    > > > +                                                                         LSN_FORMAT_ARGS(currentLSN)));
    > > >                                 }
    > > >                                 else
    > > >                                         ereport(ERROR,
    > > >                                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > > >                                                         errmsg("recovery is not in progress"),
    > > > -                                                       errhint("Waiting for the replay LSN can only be executed during recovery."));
    > > > +                                                       errhint("Waiting for the %s LSN can only be executed during recovery.",
    > > > +                                                                       desc->noun));
    > > >                         }
    > > > ```
    > > >
    > > > currentLSN is only used in the if clause, thus it can be defined inside the if clause.
    > >
    > > + 1.
    > >
    > > > 3 - 0002
    > > > ```
    > > > +       /*
    > > > +        * If we wrote an LSN that someone was waiting for then walk over the
    > > > +        * shared memory array and set latches to notify the waiters.
    > > > +        */
    > > > +       if (waitLSNState &&
    > > > +               (LogstreamResult.Write >=
    > > > +                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > > +               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > > ```
    > > >
    > > > Do we need to mention "walk over the shared memory array and set latches” in the comment? The logic belongs to WaitLSNWakeup(). What about if the wake up logic changes in future, then this comment would become stale. So I think we only need to mention “notify the waiters”.
    > > >
    > >
    > > It makes sense to me. They are incorporated into v8.
    > >
    > > >
    > > > 4 - 0003
    > > > ```
    > > > +       /*
    > > > +        * Handle parenthesized option list.  This fires when we're in an
    > > > +        * unfinished parenthesized option list.  get_previous_words treats a
    > > > +        * completed parenthesized option list as one word, so the above test is
    > > > +        * correct.  mode takes a string value ('replay', 'write', 'flush'),
    > > > +        * timeout takes a string value, no_throw takes no value.
    > > > +        */
    > > >         else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*") &&
    > > >                          !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*)"))
    > > >         {
    > > > -               /*
    > > > -                * This fires if we're in an unfinished parenthesized option list.
    > > > -                * get_previous_words treats a completed parenthesized option list as
    > > > -                * one word, so the above test is correct.
    > > > -                */
    > > >                 if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    > > > -                       COMPLETE_WITH("timeout", "no_throw");
    > > > -
    > > > -               /*
    > > > -                * timeout takes a string value, no_throw takes no value. We don't
    > > > -                * offer completions for these values.
    > > > -                */
    > > > ```
    > > >
    > > > The new comment has lost the meaning of “We don’t offer completions for these values (timeout and no_throw)”, to be explicit, I feel we can retain the sentence.
    > >
    > >  The sentence is retained.
    > >
    > > > 5 - 0004
    > > > ```
    > > > +       my $isrecovery =
    > > > +         $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > > > +       chomp($isrecovery);
    > > >         croak "unknown mode $mode for 'wait_for_catchup', valid modes are "
    > > >           . join(', ', keys(%valid_modes))
    > > >           unless exists($valid_modes{$mode});
    > > > @@ -3347,9 +3350,6 @@ sub wait_for_catchup
    > > >         }
    > > >         if (!defined($target_lsn))
    > > >         {
    > > > -               my $isrecovery =
    > > > -                 $self->safe_psql('postgres', "SELECT pg_is_in_recovery()");
    > > > -               chomp($isrecovery);
    > > > ```
    > > >
    > > > I wonder why pull up pg_is_in_recovery to an early place and unconditionally call it?
    > > >
    > >
    > > This seems unnecessary. I also realized that my earlier approach in
    > > patch 4 may have been semantically incorrect — it could end up waiting
    > > for the LSN to replay/write/flush on the node itself, rather than on
    > > the downstream standby, which defeats the purpose of
    > > wait_for_catchup(). Patch 4 attempts to address this by running WAIT
    > > FOR LSN on the standby itself.
    > >
    > > Support for primary-flush waiting and the refactoring of existing
    > > modes have been also incorporated in v8 following Alexander’s
    > > feedback. The major updates are in patches 2 and 4. Please check.
    > >
    >
    > Added WaitLSNTypeDesc to typedefs.list in v9 patch 2.
    >
    
    Run pgindent using the updated typedefs.list.
    
    -- 
    Best,
    Xuneng
    
  77. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2025-12-30T03:14:42Z

    On 2025-Dec-27, Xuneng Zhou wrote:
    
    > On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    
    > > 2 - 0002
    > > ```
    > > +                       else
    > > +                               ereport(ERROR,
    > > +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > > +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    > > +                                                               "MODE", mode_str),
    > > ```
    > >
    > > I wonder why don’t we directly put MODE into the error message?
    > 
    > Yeah, putting MODE into the error message is cleaner. It's done in v8.
    
    The reason not to do that (and also put WAIT in a separate string) is so
    that the message is identicla to other messages and thus requires no
    separate translation, specifically
      errmsg("unrecognized value for %s option \"%s\": \"%s\"", ...)
    
    See commit 502e256f2262.  Please use that form.
    
    -- 
    Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
    "El sabio habla porque tiene algo que decir;
    el tonto, porque tiene que decir algo" (Platon).
    
    
    
    
  78. Re: Implement waiting for wal lsn replay: reloaded

    Chao Li <li.evan.chao@gmail.com> — 2025-12-30T03:24:37Z

    
    > On Dec 30, 2025, at 11:14, Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > 
    > On 2025-Dec-27, Xuneng Zhou wrote:
    > 
    >> On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > 
    >>> 2 - 0002
    >>> ```
    >>> +                       else
    >>> +                               ereport(ERROR,
    >>> +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    >>> +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    >>> +                                                               "MODE", mode_str),
    >>> ```
    >>> 
    >>> I wonder why don’t we directly put MODE into the error message?
    >> 
    >> Yeah, putting MODE into the error message is cleaner. It's done in v8.
    > 
    > The reason not to do that (and also put WAIT in a separate string) is so
    > that the message is identicla to other messages and thus requires no
    > separate translation, specifically
    >  errmsg("unrecognized value for %s option \"%s\": \"%s\"", ...)
    > 
    > See commit 502e256f2262.  Please use that form.
    > 
    
    To follow 502e256f2262, it should use “%s” for “WAIT” as well. I raised the comment because I saw “WAIT” is the format strings, thus “MODE” can be there as well.
    
    So, we should do a similar change like:
    ```
    -                                                errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
    -                                                               opt->defname, p),
    +                                                errmsg("unrecognized value for %s option \"%s\": \"%s\"",
    +                                                               "EXPLAIN", opt->defname, p),
    ```
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  79. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2025-12-30T06:19:27Z

    Hi,
    
    On Tue, Dec 30, 2025 at 11:25 AM Chao Li <li.evan.chao@gmail.com> wrote:
    >
    >
    >
    > > On Dec 30, 2025, at 11:14, Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > >
    > > On 2025-Dec-27, Xuneng Zhou wrote:
    > >
    > >> On Fri, Dec 26, 2025 at 4:25 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > >
    > >>> 2 - 0002
    > >>> ```
    > >>> +                       else
    > >>> +                               ereport(ERROR,
    > >>> +                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > >>> +                                                errmsg("unrecognized value for WAIT option \"%s\": \"%s\"",
    > >>> +                                                               "MODE", mode_str),
    > >>> ```
    > >>>
    > >>> I wonder why don’t we directly put MODE into the error message?
    > >>
    > >> Yeah, putting MODE into the error message is cleaner. It's done in v8.
    > >
    > > The reason not to do that (and also put WAIT in a separate string) is so
    > > that the message is identicla to other messages and thus requires no
    > > separate translation, specifically
    > >  errmsg("unrecognized value for %s option \"%s\": \"%s\"", ...)
    > >
    > > See commit 502e256f2262.  Please use that form.
    > >
    >
    > To follow 502e256f2262, it should use “%s” for “WAIT” as well. I raised the comment because I saw “WAIT” is the format strings, thus “MODE” can be there as well.
    >
    > So, we should do a similar change like:
    > ```
    > -                                                errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
    > -                                                               opt->defname, p),
    > +                                                errmsg("unrecognized value for %s option \"%s\": \"%s\"",
    > +                                                               "EXPLAIN", opt->defname, p),
    > ```
    >
    
    Thanks for raising this and clarifying the rationale. I've made the
    modification per your input.
    
    -- 
    Best,
    Xuneng
    
  80. Re: Implement waiting for wal lsn replay: reloaded

    Álvaro Herrera <alvherre@kurilemu.de> — 2026-01-01T17:16:05Z

    In 0002 you have this kind of thing:
    
    >  				ereport(ERROR,
    >  						errcode(ERRCODE_QUERY_CANCELED),
    > -						errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current replay LSN %X/%08X",
    > +						errmsg("timed out while waiting for target LSN %X/%08X to be %s; current %s LSN %X/%08X",
    >  							   LSN_FORMAT_ARGS(lsn),
    > -							   LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > +							   desc->verb,
    > +							   desc->noun,
    > +							   LSN_FORMAT_ARGS(currentLSN)));
    > +			}
    
    
    I'm afraid this technique doesn't work, for translatability reasons.
    Your whole design of having a struct with ->verb and ->noun is not
    workable (which is a pity, but you can't really fight this.) You need to
    spell out the whole messages for each case, something like
    
    if (lsntype == replay)
       ereport(ERROR,
               errcode(ERRCODE_QUERY_CANCELED),
               errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current standby_replay LSN %X/%08X",
    else if (lsntype == flush)
        ereport( ... )
    
    and so on.  This means four separate messages for translation for each
    message your patch is adding, which is IMO the correct approach.
    
    -- 
    Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
    "...  In accounting terms this makes perfect sense.  To rational humans, it
    is insane.  Welcome to IBM."                           (Robert X. Cringely)
    https://www.cringely.com/2015/06/03/autodesks-john-walker-explained-hp-and-ibm-in-1991/
    
    
    
    
  81. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-01T23:42:34Z

    On Thu, Jan 1, 2026 at 7:16 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > In 0002 you have this kind of thing:
    >
    > >                               ereport(ERROR,
    > >                                               errcode(ERRCODE_QUERY_CANCELED),
    > > -                                             errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current replay LSN %X/%08X",
    > > +                                             errmsg("timed out while waiting for target LSN %X/%08X to be %s; current %s LSN %X/%08X",
    > >                                                          LSN_FORMAT_ARGS(lsn),
    > > -                                                        LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > +                                                        desc->verb,
    > > +                                                        desc->noun,
    > > +                                                        LSN_FORMAT_ARGS(currentLSN)));
    > > +                     }
    >
    >
    > I'm afraid this technique doesn't work, for translatability reasons.
    > Your whole design of having a struct with ->verb and ->noun is not
    > workable (which is a pity, but you can't really fight this.) You need to
    > spell out the whole messages for each case, something like
    >
    > if (lsntype == replay)
    >    ereport(ERROR,
    >            errcode(ERRCODE_QUERY_CANCELED),
    >            errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current standby_replay LSN %X/%08X",
    > else if (lsntype == flush)
    >     ereport( ... )
    >
    > and so on.  This means four separate messages for translation for each
    > message your patch is adding, which is IMO the correct approach.
    
    +1
    Thank you for catching this, Alvaro.  Yes, I think we need to get rid
    of WaitLSNTypeDesc.  It's nice idea, but we support too many languages
    to have something like this.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  82. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-02T09:17:34Z

    Hi Alvaro, Alexander,
    
    On Fri, Jan 2, 2026 at 7:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Thu, Jan 1, 2026 at 7:16 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > In 0002 you have this kind of thing:
    > >
    > > >                               ereport(ERROR,
    > > >                                               errcode(ERRCODE_QUERY_CANCELED),
    > > > -                                             errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current replay LSN %X/%08X",
    > > > +                                             errmsg("timed out while waiting for target LSN %X/%08X to be %s; current %s LSN %X/%08X",
    > > >                                                          LSN_FORMAT_ARGS(lsn),
    > > > -                                                        LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > > +                                                        desc->verb,
    > > > +                                                        desc->noun,
    > > > +                                                        LSN_FORMAT_ARGS(currentLSN)));
    > > > +                     }
    > >
    > >
    > > I'm afraid this technique doesn't work, for translatability reasons.
    > > Your whole design of having a struct with ->verb and ->noun is not
    > > workable (which is a pity, but you can't really fight this.) You need to
    > > spell out the whole messages for each case, something like
    > >
    > > if (lsntype == replay)
    > >    ereport(ERROR,
    > >            errcode(ERRCODE_QUERY_CANCELED),
    > >            errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current standby_replay LSN %X/%08X",
    > > else if (lsntype == flush)
    > >     ereport( ... )
    > >
    > > and so on.  This means four separate messages for translation for each
    > > message your patch is adding, which is IMO the correct approach.
    >
    > +1
    > Thank you for catching this, Alvaro.  Yes, I think we need to get rid
    > of WaitLSNTypeDesc.  It's nice idea, but we support too many languages
    > to have something like this.
    >
    
    Thanks for pointing this out. This approach doesn’t scale to multiple
    languages. While switch statements are more verbose, the extra clarity
    is justified to preserve proper internationalization. Please check the
    updated v12.
    
    -- 
    Best,
    Xuneng
    
  83. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-02T22:53:56Z

    Hi, Xuneng!
    
    On Fri, Jan 2, 2026 at 11:17 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Fri, Jan 2, 2026 at 7:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Thu, Jan 1, 2026 at 7:16 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > In 0002 you have this kind of thing:
    > > >
    > > > >                               ereport(ERROR,
    > > > >                                               errcode(ERRCODE_QUERY_CANCELED),
    > > > > -                                             errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current replay LSN %X/%08X",
    > > > > +                                             errmsg("timed out while waiting for target LSN %X/%08X to be %s; current %s LSN %X/%08X",
    > > > >                                                          LSN_FORMAT_ARGS(lsn),
    > > > > -                                                        LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > > > +                                                        desc->verb,
    > > > > +                                                        desc->noun,
    > > > > +                                                        LSN_FORMAT_ARGS(currentLSN)));
    > > > > +                     }
    > > >
    > > >
    > > > I'm afraid this technique doesn't work, for translatability reasons.
    > > > Your whole design of having a struct with ->verb and ->noun is not
    > > > workable (which is a pity, but you can't really fight this.) You need to
    > > > spell out the whole messages for each case, something like
    > > >
    > > > if (lsntype == replay)
    > > >    ereport(ERROR,
    > > >            errcode(ERRCODE_QUERY_CANCELED),
    > > >            errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current standby_replay LSN %X/%08X",
    > > > else if (lsntype == flush)
    > > >     ereport( ... )
    > > >
    > > > and so on.  This means four separate messages for translation for each
    > > > message your patch is adding, which is IMO the correct approach.
    > >
    > > +1
    > > Thank you for catching this, Alvaro.  Yes, I think we need to get rid
    > > of WaitLSNTypeDesc.  It's nice idea, but we support too many languages
    > > to have something like this.
    > >
    >
    > Thanks for pointing this out. This approach doesn’t scale to multiple
    > languages. While switch statements are more verbose, the extra clarity
    > is justified to preserve proper internationalization. Please check the
    > updated v12.
    
    I've corrected the patchset.  Mostly changed just comments, formatting
    etc.  I'm going to push it if no objections.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  84. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-03T16:10:44Z

    Hi Alexander,
    
    On Sat, Jan 3, 2026 at 6:54 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Fri, Jan 2, 2026 at 11:17 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Fri, Jan 2, 2026 at 7:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > On Thu, Jan 1, 2026 at 7:16 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
    > > > > In 0002 you have this kind of thing:
    > > > >
    > > > > >                               ereport(ERROR,
    > > > > >                                               errcode(ERRCODE_QUERY_CANCELED),
    > > > > > -                                             errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current replay LSN %X/%08X",
    > > > > > +                                             errmsg("timed out while waiting for target LSN %X/%08X to be %s; current %s LSN %X/%08X",
    > > > > >                                                          LSN_FORMAT_ARGS(lsn),
    > > > > > -                                                        LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL))));
    > > > > > +                                                        desc->verb,
    > > > > > +                                                        desc->noun,
    > > > > > +                                                        LSN_FORMAT_ARGS(currentLSN)));
    > > > > > +                     }
    > > > >
    > > > >
    > > > > I'm afraid this technique doesn't work, for translatability reasons.
    > > > > Your whole design of having a struct with ->verb and ->noun is not
    > > > > workable (which is a pity, but you can't really fight this.) You need to
    > > > > spell out the whole messages for each case, something like
    > > > >
    > > > > if (lsntype == replay)
    > > > >    ereport(ERROR,
    > > > >            errcode(ERRCODE_QUERY_CANCELED),
    > > > >            errmsg("timed out while waiting for target LSN %X/%08X to be replayed; current standby_replay LSN %X/%08X",
    > > > > else if (lsntype == flush)
    > > > >     ereport( ... )
    > > > >
    > > > > and so on.  This means four separate messages for translation for each
    > > > > message your patch is adding, which is IMO the correct approach.
    > > >
    > > > +1
    > > > Thank you for catching this, Alvaro.  Yes, I think we need to get rid
    > > > of WaitLSNTypeDesc.  It's nice idea, but we support too many languages
    > > > to have something like this.
    > > >
    > >
    > > Thanks for pointing this out. This approach doesn’t scale to multiple
    > > languages. While switch statements are more verbose, the extra clarity
    > > is justified to preserve proper internationalization. Please check the
    > > updated v12.
    >
    > I've corrected the patchset.  Mostly changed just comments, formatting
    > etc.  I'm going to push it if no objections.
    >
    
    Thanks for updating the patchset. LGTM.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  85. Re: Implement waiting for wal lsn replay: reloaded

    Thomas Munro <thomas.munro@gmail.com> — 2026-01-06T05:42:59Z

    Could this be causing the recent flapping failures on CI/macOS in
    recovery/031_recovery_conflict?  I didn't have time to dig personally
    but f30848cb looks relevant:
    
    Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    conflict with recovery
    DETAIL:  User was or might have been using tablespace that must be dropped.'
    while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    --dbname port=25195
    host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    line 2300.
    
    https://cirrus-ci.com/task/5771274900733952
    
    The master branch in time-descending order, macOS tasks only:
    
         task_id      | substring |  status
    ------------------+-----------+-----------
     6460882231754752 | c970bdc0  | FAILED
     5771274900733952 | 6ca8506e  | FAILED
     6217757068361728 | 63ed3bc7  | FAILED
     5980650261446656 | ae283736  | FAILED
     6585898394976256 | 5f13999a  | COMPLETED
     4527474786172928 | 7f9acc9b  | COMPLETED
     4826100842364928 | e8d4e94a  | COMPLETED
     4540563027918848 | b9ee5f2d  | FAILED
     6358528648019968 | c5af141c  | FAILED
     5998005284765696 | e212a0f8  | COMPLETED
     6488580526178304 | b85d5dc0  | FAILED
     5034091344560128 | 7dc95cc3  | ABORTED
     5688692477526016 | bb048e31  | COMPLETED
     5481187977723904 | d351063e  | COMPLETED
     5101831568752640 | f30848cb  | COMPLETED <-- the change
     6395317408497664 | 3f33b63d  | COMPLETED
     6741325208354816 | 877ae5db  | COMPLETED
     4594007789010944 | de746e0d  | COMPLETED
     6497208998035456 | 461b8cc9  | COMPLETED
    
    
    
    
  86. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-06T07:29:06Z

    Hi Thomas,
    
    On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    >
    > Could this be causing the recent flapping failures on CI/macOS in
    > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > but f30848cb looks relevant:
    >
    > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > conflict with recovery
    > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > --dbname port=25195
    > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > line 2300.
    >
    > https://cirrus-ci.com/task/5771274900733952
    >
    > The master branch in time-descending order, macOS tasks only:
    >
    >      task_id      | substring |  status
    > ------------------+-----------+-----------
    >  6460882231754752 | c970bdc0  | FAILED
    >  5771274900733952 | 6ca8506e  | FAILED
    >  6217757068361728 | 63ed3bc7  | FAILED
    >  5980650261446656 | ae283736  | FAILED
    >  6585898394976256 | 5f13999a  | COMPLETED
    >  4527474786172928 | 7f9acc9b  | COMPLETED
    >  4826100842364928 | e8d4e94a  | COMPLETED
    >  4540563027918848 | b9ee5f2d  | FAILED
    >  6358528648019968 | c5af141c  | FAILED
    >  5998005284765696 | e212a0f8  | COMPLETED
    >  6488580526178304 | b85d5dc0  | FAILED
    >  5034091344560128 | 7dc95cc3  | ABORTED
    >  5688692477526016 | bb048e31  | COMPLETED
    >  5481187977723904 | d351063e  | COMPLETED
    >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    >  6395317408497664 | 3f33b63d  | COMPLETED
    >  6741325208354816 | 877ae5db  | COMPLETED
    >  4594007789010944 | de746e0d  | COMPLETED
    >  6497208998035456 | 461b8cc9  | COMPLETED
    
    Thanks for raising this issue. I think it is related to f30848cb after
    some analysis. I'll prepare a follow-up patch to fix it.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  87. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-06T11:54:46Z

    On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > Could this be causing the recent flapping failures on CI/macOS in
    > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > but f30848cb looks relevant:
    > >
    > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > conflict with recovery
    > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > --dbname port=25195
    > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > line 2300.
    > >
    > > https://cirrus-ci.com/task/5771274900733952
    > >
    > > The master branch in time-descending order, macOS tasks only:
    > >
    > >      task_id      | substring |  status
    > > ------------------+-----------+-----------
    > >  6460882231754752 | c970bdc0  | FAILED
    > >  5771274900733952 | 6ca8506e  | FAILED
    > >  6217757068361728 | 63ed3bc7  | FAILED
    > >  5980650261446656 | ae283736  | FAILED
    > >  6585898394976256 | 5f13999a  | COMPLETED
    > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > >  4826100842364928 | e8d4e94a  | COMPLETED
    > >  4540563027918848 | b9ee5f2d  | FAILED
    > >  6358528648019968 | c5af141c  | FAILED
    > >  5998005284765696 | e212a0f8  | COMPLETED
    > >  6488580526178304 | b85d5dc0  | FAILED
    > >  5034091344560128 | 7dc95cc3  | ABORTED
    > >  5688692477526016 | bb048e31  | COMPLETED
    > >  5481187977723904 | d351063e  | COMPLETED
    > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > >  6395317408497664 | 3f33b63d  | COMPLETED
    > >  6741325208354816 | 877ae5db  | COMPLETED
    > >  4594007789010944 | de746e0d  | COMPLETED
    > >  6497208998035456 | 461b8cc9  | COMPLETED
    >
    > Thanks for raising this issue. I think it is related to f30848cb after
    > some analysis. I'll prepare a follow-up patch to fix it.
    
    Sorry, I've mistakenly referenced this report from commit [1].  I
    thought it was related, but it appears to be not.  [1] is related to
    the report I've got from Ruikai Peng off-list.
    
    Regarding the present failure, could it happen before ExecWaitStmt()
    calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    should do preliminary efforts to release these snapshots.
    
    1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  88. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-06T13:12:41Z

    Hi,
    
    On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > but f30848cb looks relevant:
    > > >
    > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > conflict with recovery
    > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > --dbname port=25195
    > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > line 2300.
    > > >
    > > > https://cirrus-ci.com/task/5771274900733952
    > > >
    > > > The master branch in time-descending order, macOS tasks only:
    > > >
    > > >      task_id      | substring |  status
    > > > ------------------+-----------+-----------
    > > >  6460882231754752 | c970bdc0  | FAILED
    > > >  5771274900733952 | 6ca8506e  | FAILED
    > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > >  5980650261446656 | ae283736  | FAILED
    > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > >  6358528648019968 | c5af141c  | FAILED
    > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > >  6488580526178304 | b85d5dc0  | FAILED
    > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > >  5688692477526016 | bb048e31  | COMPLETED
    > > >  5481187977723904 | d351063e  | COMPLETED
    > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > >  4594007789010944 | de746e0d  | COMPLETED
    > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > >
    > > Thanks for raising this issue. I think it is related to f30848cb after
    > > some analysis. I'll prepare a follow-up patch to fix it.
    >
    > Sorry, I've mistakenly referenced this report from commit [1].  I
    > thought it was related, but it appears to be not.  [1] is related to
    > the report I've got from Ruikai Peng off-list.
    >
    > Regarding the present failure, could it happen before ExecWaitStmt()
    > calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    > should do preliminary efforts to release these snapshots.
    >
    > 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    >
    
    I agree that moving PopActiveSnapshot() and
    InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
    appears to be a sensible optimization. However, in this particular
    failure scenario, it may not address the issue.
    
    For tablespace conflicts, recovery conflict resolution uses
    GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
    returns all active backends, regardless of their snapshot state. As a
    result, even if all snapshots are released at the start of
    ExecWaitStmt(), the session would still be canceled during replay of
    DROP TABLESPACE.
    
    Given this, I am considering handling this conflict class explicitly:
    if the WAIT FOR statement is terminated and the error indicates a
    recovery conflict, we fall back to the existing polling-based
    approach.
    
    * Ask everybody to cancel their queries immediately so we can ensure no
    * temp files remain and we can remove the tablespace. Nuke the entire
    * site from orbit, it's the only way to be sure.
    *
    * XXX: We could work out the pids of active backends using this
    * tablespace by examining the temp filenames in the directory. We would
    * then convert the pids into VirtualXIDs before attempting to cancel
    * them.
    
    I am also wondering whether this optimization would be helpful.
    
    -- 
    Best,
    Xuneng
    
  89. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-06T15:34:22Z

    On Tue, Jan 6, 2026 at 3:12 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > > but f30848cb looks relevant:
    > > > >
    > > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > > conflict with recovery
    > > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > > --dbname port=25195
    > > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > > line 2300.
    > > > >
    > > > > https://cirrus-ci.com/task/5771274900733952
    > > > >
    > > > > The master branch in time-descending order, macOS tasks only:
    > > > >
    > > > >      task_id      | substring |  status
    > > > > ------------------+-----------+-----------
    > > > >  6460882231754752 | c970bdc0  | FAILED
    > > > >  5771274900733952 | 6ca8506e  | FAILED
    > > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > > >  5980650261446656 | ae283736  | FAILED
    > > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > > >  6358528648019968 | c5af141c  | FAILED
    > > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > > >  6488580526178304 | b85d5dc0  | FAILED
    > > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > > >  5688692477526016 | bb048e31  | COMPLETED
    > > > >  5481187977723904 | d351063e  | COMPLETED
    > > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > > >  4594007789010944 | de746e0d  | COMPLETED
    > > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > > >
    > > > Thanks for raising this issue. I think it is related to f30848cb after
    > > > some analysis. I'll prepare a follow-up patch to fix it.
    > >
    > > Sorry, I've mistakenly referenced this report from commit [1].  I
    > > thought it was related, but it appears to be not.  [1] is related to
    > > the report I've got from Ruikai Peng off-list.
    > >
    > > Regarding the present failure, could it happen before ExecWaitStmt()
    > > calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    > > should do preliminary efforts to release these snapshots.
    > >
    > > 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    > >
    >
    > I agree that moving PopActiveSnapshot() and
    > InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
    > appears to be a sensible optimization. However, in this particular
    > failure scenario, it may not address the issue.
    >
    > For tablespace conflicts, recovery conflict resolution uses
    > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
    > returns all active backends, regardless of their snapshot state. As a
    > result, even if all snapshots are released at the start of
    > ExecWaitStmt(), the session would still be canceled during replay of
    > DROP TABLESPACE.
    
    GetConflictingVirtualXIDs() uses proc->xmin to detect the conflicts.
    ExecWaitStmt() asserts MyProc->xmin == InvalidTransactionId after
    releasing all the snapshots.  I still think this happens because
    conflict handling happens before ExecWaitStmt() manages to release the
    snapshots.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  90. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-06T15:53:11Z

    Hi,
    
    On Tue, Jan 6, 2026 at 9:12 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > > but f30848cb looks relevant:
    > > > >
    > > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > > conflict with recovery
    > > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > > --dbname port=25195
    > > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > > line 2300.
    > > > >
    > > > > https://cirrus-ci.com/task/5771274900733952
    > > > >
    > > > > The master branch in time-descending order, macOS tasks only:
    > > > >
    > > > >      task_id      | substring |  status
    > > > > ------------------+-----------+-----------
    > > > >  6460882231754752 | c970bdc0  | FAILED
    > > > >  5771274900733952 | 6ca8506e  | FAILED
    > > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > > >  5980650261446656 | ae283736  | FAILED
    > > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > > >  6358528648019968 | c5af141c  | FAILED
    > > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > > >  6488580526178304 | b85d5dc0  | FAILED
    > > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > > >  5688692477526016 | bb048e31  | COMPLETED
    > > > >  5481187977723904 | d351063e  | COMPLETED
    > > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > > >  4594007789010944 | de746e0d  | COMPLETED
    > > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > > >
    > > > Thanks for raising this issue. I think it is related to f30848cb after
    > > > some analysis. I'll prepare a follow-up patch to fix it.
    > >
    > > Sorry, I've mistakenly referenced this report from commit [1].  I
    > > thought it was related, but it appears to be not.  [1] is related to
    > > the report I've got from Ruikai Peng off-list.
    > >
    > > Regarding the present failure, could it happen before ExecWaitStmt()
    > > calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    > > should do preliminary efforts to release these snapshots.
    > >
    > > 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    > >
    >
    > I agree that moving PopActiveSnapshot() and
    > InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
    > appears to be a sensible optimization. However, in this particular
    > failure scenario, it may not address the issue.
    >
    > For tablespace conflicts, recovery conflict resolution uses
    > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
    > returns all active backends, regardless of their snapshot state. As a
    > result, even if all snapshots are released at the start of
    > ExecWaitStmt(), the session would still be canceled during replay of
    > DROP TABLESPACE.
    >
    > Given this, I am considering handling this conflict class explicitly:
    > if the WAIT FOR statement is terminated and the error indicates a
    > recovery conflict, we fall back to the existing polling-based
    > approach.
    >
    > * Ask everybody to cancel their queries immediately so we can ensure no
    > * temp files remain and we can remove the tablespace. Nuke the entire
    > * site from orbit, it's the only way to be sure.
    > *
    > * XXX: We could work out the pids of active backends using this
    > * tablespace by examining the temp filenames in the directory. We would
    > * then convert the pids into VirtualXIDs before attempting to cancel
    > * them.
    >
    > I am also wondering whether this optimization would be helpful.
    >
    
    Just format the commit message.
    
    -- 
    Best,
    Xuneng
    
  91. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-06T15:58:18Z

    Hi,
    
    
    On Tue, Jan 6, 2026 at 11:34 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Tue, Jan 6, 2026 at 3:12 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > > > but f30848cb looks relevant:
    > > > > >
    > > > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > > > conflict with recovery
    > > > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > > > --dbname port=25195
    > > > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > > > line 2300.
    > > > > >
    > > > > > https://cirrus-ci.com/task/5771274900733952
    > > > > >
    > > > > > The master branch in time-descending order, macOS tasks only:
    > > > > >
    > > > > >      task_id      | substring |  status
    > > > > > ------------------+-----------+-----------
    > > > > >  6460882231754752 | c970bdc0  | FAILED
    > > > > >  5771274900733952 | 6ca8506e  | FAILED
    > > > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > > > >  5980650261446656 | ae283736  | FAILED
    > > > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > > > >  6358528648019968 | c5af141c  | FAILED
    > > > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > > > >  6488580526178304 | b85d5dc0  | FAILED
    > > > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > > > >  5688692477526016 | bb048e31  | COMPLETED
    > > > > >  5481187977723904 | d351063e  | COMPLETED
    > > > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > > > >  4594007789010944 | de746e0d  | COMPLETED
    > > > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > > > >
    > > > > Thanks for raising this issue. I think it is related to f30848cb after
    > > > > some analysis. I'll prepare a follow-up patch to fix it.
    > > >
    > > > Sorry, I've mistakenly referenced this report from commit [1].  I
    > > > thought it was related, but it appears to be not.  [1] is related to
    > > > the report I've got from Ruikai Peng off-list.
    > > >
    > > > Regarding the present failure, could it happen before ExecWaitStmt()
    > > > calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    > > > should do preliminary efforts to release these snapshots.
    > > >
    > > > 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    > > >
    > >
    > > I agree that moving PopActiveSnapshot() and
    > > InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
    > > appears to be a sensible optimization. However, in this particular
    > > failure scenario, it may not address the issue.
    > >
    > > For tablespace conflicts, recovery conflict resolution uses
    > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
    > > returns all active backends, regardless of their snapshot state. As a
    > > result, even if all snapshots are released at the start of
    > > ExecWaitStmt(), the session would still be canceled during replay of
    > > DROP TABLESPACE.
    >
    > GetConflictingVirtualXIDs() uses proc->xmin to detect the conflicts.
    > ExecWaitStmt() asserts MyProc->xmin == InvalidTransactionId after
    > releasing all the snapshots.  I still think this happens because
    > conflict handling happens before ExecWaitStmt() manages to release the
    > snapshots.
    >
    
    I did not notice this message before. I'll look more closely at this case.
    
    --
    Best,
    Xuneng
    
    
    
    
  92. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-06T17:04:06Z

    Hi,
    
    On Tue, Jan 6, 2026 at 11:58 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    >
    > On Tue, Jan 6, 2026 at 11:34 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Tue, Jan 6, 2026 at 3:12 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Tue, Jan 6, 2026 at 7:54 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > >
    > > > > On Tue, Jan 6, 2026 at 9:29 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > > On Tue, Jan 6, 2026 at 1:43 PM Thomas Munro <thomas.munro@gmail.com> wrote:
    > > > > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > > > > but f30848cb looks relevant:
    > > > > > >
    > > > > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > > > > conflict with recovery
    > > > > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > > > > --dbname port=25195
    > > > > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > > > > line 2300.
    > > > > > >
    > > > > > > https://cirrus-ci.com/task/5771274900733952
    > > > > > >
    > > > > > > The master branch in time-descending order, macOS tasks only:
    > > > > > >
    > > > > > >      task_id      | substring |  status
    > > > > > > ------------------+-----------+-----------
    > > > > > >  6460882231754752 | c970bdc0  | FAILED
    > > > > > >  5771274900733952 | 6ca8506e  | FAILED
    > > > > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > > > > >  5980650261446656 | ae283736  | FAILED
    > > > > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > > > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > > > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > > > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > > > > >  6358528648019968 | c5af141c  | FAILED
    > > > > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > > > > >  6488580526178304 | b85d5dc0  | FAILED
    > > > > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > > > > >  5688692477526016 | bb048e31  | COMPLETED
    > > > > > >  5481187977723904 | d351063e  | COMPLETED
    > > > > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > > > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > > > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > > > > >  4594007789010944 | de746e0d  | COMPLETED
    > > > > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > > > > >
    > > > > > Thanks for raising this issue. I think it is related to f30848cb after
    > > > > > some analysis. I'll prepare a follow-up patch to fix it.
    > > > >
    > > > > Sorry, I've mistakenly referenced this report from commit [1].  I
    > > > > thought it was related, but it appears to be not.  [1] is related to
    > > > > the report I've got from Ruikai Peng off-list.
    > > > >
    > > > > Regarding the present failure, could it happen before ExecWaitStmt()
    > > > > calls PopActiveSnapshot() and InvalidateCatalogSnapshot()?  If so, we
    > > > > should do preliminary efforts to release these snapshots.
    > > > >
    > > > > 1. https://git.postgresql.org/pg/commitdiff/bf308639bfcfa38541e24733e074184153a8ab7f
    > > > >
    > > >
    > > > I agree that moving PopActiveSnapshot() and
    > > > InvalidateCatalogSnapshot() to the very beginning of ExecWaitStmt()
    > > > appears to be a sensible optimization. However, in this particular
    > > > failure scenario, it may not address the issue.
    > > >
    > > > For tablespace conflicts, recovery conflict resolution uses
    > > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid), which
    > > > returns all active backends, regardless of their snapshot state. As a
    > > > result, even if all snapshots are released at the start of
    > > > ExecWaitStmt(), the session would still be canceled during replay of
    > > > DROP TABLESPACE.
    > >
    > > GetConflictingVirtualXIDs() uses proc->xmin to detect the conflicts.
    > > ExecWaitStmt() asserts MyProc->xmin == InvalidTransactionId after
    > > releasing all the snapshots.  I still think this happens because
    > > conflict handling happens before ExecWaitStmt() manages to release the
    > > snapshots.
    > >
    >
    > I did not notice this message before. I'll look more closely at this case.
    
    # VACUUM FREEZE, pruning those dead tuples
    $node_primary->safe_psql($test_db, qq[VACUUM FREEZE $table1;]);
    
    # Wait for attempted replay of PRUNE records
    $node_primary->wait_for_replay_catchup($node_standby);
    
    check_conflict_log(
    "User query might have needed to see row versions that must be removed");
    $psql_standby->reconnect_and_clear();
    check_conflict_stat("snapshot");
    
    Yeah, this code path could be problematic for the conflict type
    PROCSIG_RECOVERY_CONFLICT_SNAPSHOT.  I created a patch to reduce the
    false conflict detecting window as you suggested. Please check it too.
    
    
    --
    Best,
    Xuneng
    
  93. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-01-07T00:32:26Z

    Hi,
    
    On 2026-01-06 18:42:59 +1300, Thomas Munro wrote:
    > Could this be causing the recent flapping failures on CI/macOS in
    > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > but f30848cb looks relevant:
    > 
    > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > conflict with recovery
    > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > --dbname port=25195
    > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > line 2300.
    > 
    > https://cirrus-ci.com/task/5771274900733952
    > 
    > The master branch in time-descending order, macOS tasks only:
    > 
    >      task_id      | substring |  status
    > ------------------+-----------+-----------
    >  6460882231754752 | c970bdc0  | FAILED
    >  5771274900733952 | 6ca8506e  | FAILED
    >  6217757068361728 | 63ed3bc7  | FAILED
    >  5980650261446656 | ae283736  | FAILED
    >  6585898394976256 | 5f13999a  | COMPLETED
    >  4527474786172928 | 7f9acc9b  | COMPLETED
    >  4826100842364928 | e8d4e94a  | COMPLETED
    >  4540563027918848 | b9ee5f2d  | FAILED
    >  6358528648019968 | c5af141c  | FAILED
    >  5998005284765696 | e212a0f8  | COMPLETED
    >  6488580526178304 | b85d5dc0  | FAILED
    >  5034091344560128 | 7dc95cc3  | ABORTED
    >  5688692477526016 | bb048e31  | COMPLETED
    >  5481187977723904 | d351063e  | COMPLETED
    >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    >  6395317408497664 | 3f33b63d  | COMPLETED
    >  6741325208354816 | 877ae5db  | COMPLETED
    >  4594007789010944 | de746e0d  | COMPLETED
    >  6497208998035456 | 461b8cc9  | COMPLETED
    
    The failure rates of this are very high - the majority of the CI runs on the
    postgres/postgres repos failed since the change went in. Which then also means
    cfbot has a very high spurious failure rate. I think we need to revert this
    change until the problem has been verified as fixed.
    
    Greetings,
    
    Andres Freund
    
    
    
    
  94. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-07T04:08:18Z

    Hi,
    
    On Wed, Jan 7, 2026 at 8:32 AM Andres Freund <andres@anarazel.de> wrote:
    >
    > Hi,
    >
    > On 2026-01-06 18:42:59 +1300, Thomas Munro wrote:
    > > Could this be causing the recent flapping failures on CI/macOS in
    > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > but f30848cb looks relevant:
    > >
    > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > conflict with recovery
    > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > --dbname port=25195
    > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > line 2300.
    > >
    > > https://cirrus-ci.com/task/5771274900733952
    > >
    > > The master branch in time-descending order, macOS tasks only:
    > >
    > >      task_id      | substring |  status
    > > ------------------+-----------+-----------
    > >  6460882231754752 | c970bdc0  | FAILED
    > >  5771274900733952 | 6ca8506e  | FAILED
    > >  6217757068361728 | 63ed3bc7  | FAILED
    > >  5980650261446656 | ae283736  | FAILED
    > >  6585898394976256 | 5f13999a  | COMPLETED
    > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > >  4826100842364928 | e8d4e94a  | COMPLETED
    > >  4540563027918848 | b9ee5f2d  | FAILED
    > >  6358528648019968 | c5af141c  | FAILED
    > >  5998005284765696 | e212a0f8  | COMPLETED
    > >  6488580526178304 | b85d5dc0  | FAILED
    > >  5034091344560128 | 7dc95cc3  | ABORTED
    > >  5688692477526016 | bb048e31  | COMPLETED
    > >  5481187977723904 | d351063e  | COMPLETED
    > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > >  6395317408497664 | 3f33b63d  | COMPLETED
    > >  6741325208354816 | 877ae5db  | COMPLETED
    > >  4594007789010944 | de746e0d  | COMPLETED
    > >  6497208998035456 | 461b8cc9  | COMPLETED
    >
    > The failure rates of this are very high - the majority of the CI runs on the
    > postgres/postgres repos failed since the change went in. Which then also means
    > cfbot has a very high spurious failure rate. I think we need to revert this
    > change until the problem has been verified as fixed.
    
    This specific failure can be reproduced with this patch v1.
    
    I guess the potential race condition is: when
    wait_for_replay_catchup() runs WAIT FOR LSN on the standby, if a
    tablespace conflict fires during that wait, the WAIT FOR LSN session
    is killed even though it doesn't use the tablespace.
    
    In my test, the failure won't occur after applying the v2 patch.
    
    -- 
    Best,
    Xuneng
    
  95. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-07T08:06:23Z

    On Wed, Jan 7, 2026, 02:32 Andres Freund <andres@anarazel.de> wrote:
    
    > Hi,
    >
    > On 2026-01-06 18:42:59 +1300, Thomas Munro wrote:
    > > Could this be causing the recent flapping failures on CI/macOS in
    > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > but f30848cb looks relevant:
    > >
    > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on
    > primary
    > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > conflict with recovery
    > > DETAIL:  User was or might have been using tablespace that must be
    > dropped.'
    > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > --dbname port=25195
    > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > no_throw);' at
    > /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > line 2300.
    > >
    > > https://cirrus-ci.com/task/5771274900733952
    > >
    > > The master branch in time-descending order, macOS tasks only:
    > >
    > >      task_id      | substring |  status
    > > ------------------+-----------+-----------
    > >  6460882231754752 | c970bdc0  | FAILED
    > >  5771274900733952 | 6ca8506e  | FAILED
    > >  6217757068361728 | 63ed3bc7  | FAILED
    > >  5980650261446656 | ae283736  | FAILED
    > >  6585898394976256 | 5f13999a  | COMPLETED
    > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > >  4826100842364928 | e8d4e94a  | COMPLETED
    > >  4540563027918848 | b9ee5f2d  | FAILED
    > >  6358528648019968 | c5af141c  | FAILED
    > >  5998005284765696 | e212a0f8  | COMPLETED
    > >  6488580526178304 | b85d5dc0  | FAILED
    > >  5034091344560128 | 7dc95cc3  | ABORTED
    > >  5688692477526016 | bb048e31  | COMPLETED
    > >  5481187977723904 | d351063e  | COMPLETED
    > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > >  6395317408497664 | 3f33b63d  | COMPLETED
    > >  6741325208354816 | 877ae5db  | COMPLETED
    > >  4594007789010944 | de746e0d  | COMPLETED
    > >  6497208998035456 | 461b8cc9  | COMPLETED
    >
    > The failure rates of this are very high - the majority of the CI runs on
    > the
    > postgres/postgres repos failed since the change went in. Which then also
    > means
    > cfbot has a very high spurious failure rate. I think we need to revert this
    > change until the problem has been verified as fixed.
    >
    
    This is fair. I will revert the commit causing the failures in the next few
    hours.
    
    ------
    Regards,
    Alexander Korotkov
    
    >
    
  96. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-08T14:19:08Z

    On Wed, Jan 7, 2026 at 6:08 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Wed, Jan 7, 2026 at 8:32 AM Andres Freund <andres@anarazel.de> wrote:
    > > On 2026-01-06 18:42:59 +1300, Thomas Munro wrote:
    > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > but f30848cb looks relevant:
    > > >
    > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > conflict with recovery
    > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > --dbname port=25195
    > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > line 2300.
    > > >
    > > > https://cirrus-ci.com/task/5771274900733952
    > > >
    > > > The master branch in time-descending order, macOS tasks only:
    > > >
    > > >      task_id      | substring |  status
    > > > ------------------+-----------+-----------
    > > >  6460882231754752 | c970bdc0  | FAILED
    > > >  5771274900733952 | 6ca8506e  | FAILED
    > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > >  5980650261446656 | ae283736  | FAILED
    > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > >  6358528648019968 | c5af141c  | FAILED
    > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > >  6488580526178304 | b85d5dc0  | FAILED
    > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > >  5688692477526016 | bb048e31  | COMPLETED
    > > >  5481187977723904 | d351063e  | COMPLETED
    > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > >  4594007789010944 | de746e0d  | COMPLETED
    > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > >
    > > The failure rates of this are very high - the majority of the CI runs on the
    > > postgres/postgres repos failed since the change went in. Which then also means
    > > cfbot has a very high spurious failure rate. I think we need to revert this
    > > change until the problem has been verified as fixed.
    >
    > This specific failure can be reproduced with this patch v1.
    >
    > I guess the potential race condition is: when
    > wait_for_replay_catchup() runs WAIT FOR LSN on the standby, if a
    > tablespace conflict fires during that wait, the WAIT FOR LSN session
    > is killed even though it doesn't use the tablespace.
    >
    > In my test, the failure won't occur after applying the v2 patch.
    
    I see, you were right.  This is not related to the MyProc->xmin.
    ResolveRecoveryConflictWithTablespace() calls
    GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    would kill WAIT FOR LSN query independently on its xmin.  I guess your
    patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    with retry loop, but it would still consume less resources than
    polling.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  97. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-08T16:29:01Z

    Hi,
    
    On Thu, Jan 8, 2026 at 10:19 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Wed, Jan 7, 2026 at 6:08 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Wed, Jan 7, 2026 at 8:32 AM Andres Freund <andres@anarazel.de> wrote:
    > > > On 2026-01-06 18:42:59 +1300, Thomas Munro wrote:
    > > > > Could this be causing the recent flapping failures on CI/macOS in
    > > > > recovery/031_recovery_conflict?  I didn't have time to dig personally
    > > > > but f30848cb looks relevant:
    > > > >
    > > > > Waiting for replication conn standby's replay_lsn to pass 0/03467F58 on primary
    > > > > error running SQL: 'psql:<stdin>:1: ERROR:  canceling statement due to
    > > > > conflict with recovery
    > > > > DETAIL:  User was or might have been using tablespace that must be dropped.'
    > > > > while running 'psql --no-psqlrc --no-align --tuples-only --quiet
    > > > > --dbname port=25195
    > > > > host=/var/folders/g9/7rkt8rt1241bwwhd3_s8ndp40000gn/T/LqcCJnsueI
    > > > > dbname='postgres' --file - --variable ON_ERROR_STOP=1' with sql 'WAIT
    > > > > FOR LSN '0/03467F58' WITH (MODE 'standby_replay', timeout '180s',
    > > > > no_throw);' at /Users/admin/pgsql/src/test/perl/PostgreSQL/Test/Cluster.pm
    > > > > line 2300.
    > > > >
    > > > > https://cirrus-ci.com/task/5771274900733952
    > > > >
    > > > > The master branch in time-descending order, macOS tasks only:
    > > > >
    > > > >      task_id      | substring |  status
    > > > > ------------------+-----------+-----------
    > > > >  6460882231754752 | c970bdc0  | FAILED
    > > > >  5771274900733952 | 6ca8506e  | FAILED
    > > > >  6217757068361728 | 63ed3bc7  | FAILED
    > > > >  5980650261446656 | ae283736  | FAILED
    > > > >  6585898394976256 | 5f13999a  | COMPLETED
    > > > >  4527474786172928 | 7f9acc9b  | COMPLETED
    > > > >  4826100842364928 | e8d4e94a  | COMPLETED
    > > > >  4540563027918848 | b9ee5f2d  | FAILED
    > > > >  6358528648019968 | c5af141c  | FAILED
    > > > >  5998005284765696 | e212a0f8  | COMPLETED
    > > > >  6488580526178304 | b85d5dc0  | FAILED
    > > > >  5034091344560128 | 7dc95cc3  | ABORTED
    > > > >  5688692477526016 | bb048e31  | COMPLETED
    > > > >  5481187977723904 | d351063e  | COMPLETED
    > > > >  5101831568752640 | f30848cb  | COMPLETED <-- the change
    > > > >  6395317408497664 | 3f33b63d  | COMPLETED
    > > > >  6741325208354816 | 877ae5db  | COMPLETED
    > > > >  4594007789010944 | de746e0d  | COMPLETED
    > > > >  6497208998035456 | 461b8cc9  | COMPLETED
    > > >
    > > > The failure rates of this are very high - the majority of the CI runs on the
    > > > postgres/postgres repos failed since the change went in. Which then also means
    > > > cfbot has a very high spurious failure rate. I think we need to revert this
    > > > change until the problem has been verified as fixed.
    > >
    > > This specific failure can be reproduced with this patch v1.
    > >
    > > I guess the potential race condition is: when
    > > wait_for_replay_catchup() runs WAIT FOR LSN on the standby, if a
    > > tablespace conflict fires during that wait, the WAIT FOR LSN session
    > > is killed even though it doesn't use the tablespace.
    > >
    > > In my test, the failure won't occur after applying the v2 patch.
    >
    > I see, you were right.  This is not related to the MyProc->xmin.
    > ResolveRecoveryConflictWithTablespace() calls
    > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    > would kill WAIT FOR LSN query independently on its xmin.
    
    I think the concern is valid --- conflicts like
    PROCSIG_RECOVERY_CONFLICT_SNAPSHOT could occur and terminate the
    backend if the timing is unlucky. It's more difficult to reproduce
    though. A check for the log containing "conflict with recovery" would
    likely catch these conflicts as well.
    
    > I guess your
    > patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    > with retry loop, but it would still consume less resources than
    > polling.
    >
    
    Assuming recovery conflicts are relatively rare in tap tests, except
    for the explicitly designed tests like 031_recovery_conflict and the
    narrow timing window that the standby has not caught up while the wait
    for gets invoked, a simple fallback seems appropriate to me.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  98. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-08T20:42:03Z

    On Thu, Jan 8, 2026 at 6:29 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Thu, Jan 8, 2026 at 10:19 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > I see, you were right.  This is not related to the MyProc->xmin.
    > > ResolveRecoveryConflictWithTablespace() calls
    > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    > > would kill WAIT FOR LSN query independently on its xmin.
    >
    > I think the concern is valid --- conflicts like
    > PROCSIG_RECOVERY_CONFLICT_SNAPSHOT could occur and terminate the
    > backend if the timing is unlucky. It's more difficult to reproduce
    > though. A check for the log containing "conflict with recovery" would
    > likely catch these conflicts as well.
    
    Yes, I found multiple reasons why xmin gets temporarily set during
    processing of WAIT FOR LSN query.  I'll soon post a draft patch to fix
    that.
    
    > > I guess your
    > > patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    > > with retry loop, but it would still consume less resources than
    > > polling.
    > >
    >
    > Assuming recovery conflicts are relatively rare in tap tests, except
    > for the explicitly designed tests like 031_recovery_conflict and the
    > narrow timing window that the standby has not caught up while the wait
    > for gets invoked, a simple fallback seems appropriate to me.
    
    Yes, I see.  Seems acceptable given this seems the only feasible way to go.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  99. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-09T13:44:17Z

    Hi,
    
    On Fri, Jan 9, 2026 at 4:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Thu, Jan 8, 2026 at 6:29 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Thu, Jan 8, 2026 at 10:19 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > I see, you were right.  This is not related to the MyProc->xmin.
    > > > ResolveRecoveryConflictWithTablespace() calls
    > > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    > > > would kill WAIT FOR LSN query independently on its xmin.
    > >
    > > I think the concern is valid --- conflicts like
    > > PROCSIG_RECOVERY_CONFLICT_SNAPSHOT could occur and terminate the
    > > backend if the timing is unlucky. It's more difficult to reproduce
    > > though. A check for the log containing "conflict with recovery" would
    > > likely catch these conflicts as well.
    >
    > Yes, I found multiple reasons why xmin gets temporarily set during
    > processing of WAIT FOR LSN query.  I'll soon post a draft patch to fix
    > that.
    >
    > > > I guess your
    > > > patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    > > > with retry loop, but it would still consume less resources than
    > > > polling.
    > > >
    > >
    > > Assuming recovery conflicts are relatively rare in tap tests, except
    > > for the explicitly designed tests like 031_recovery_conflict and the
    > > narrow timing window that the standby has not caught up while the wait
    > > for gets invoked, a simple fallback seems appropriate to me.
    >
    > Yes, I see.  Seems acceptable given this seems the only feasible way to go.
    >
    
    Here is the updated patch with recovery conflicts handled.
    
    -- 
    Best,
    Xuneng
    
  100. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-10T04:47:13Z

    On Fri, Jan 9, 2026 at 9:44 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Jan 9, 2026 at 4:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Thu, Jan 8, 2026 at 6:29 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > On Thu, Jan 8, 2026 at 10:19 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > I see, you were right.  This is not related to the MyProc->xmin.
    > > > > ResolveRecoveryConflictWithTablespace() calls
    > > > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    > > > > would kill WAIT FOR LSN query independently on its xmin.
    > > >
    > > > I think the concern is valid --- conflicts like
    > > > PROCSIG_RECOVERY_CONFLICT_SNAPSHOT could occur and terminate the
    > > > backend if the timing is unlucky. It's more difficult to reproduce
    > > > though. A check for the log containing "conflict with recovery" would
    > > > likely catch these conflicts as well.
    > >
    > > Yes, I found multiple reasons why xmin gets temporarily set during
    > > processing of WAIT FOR LSN query.  I'll soon post a draft patch to fix
    > > that.
    > >
    > > > > I guess your
    > > > > patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    > > > > with retry loop, but it would still consume less resources than
    > > > > polling.
    > > > >
    > > >
    > > > Assuming recovery conflicts are relatively rare in tap tests, except
    > > > for the explicitly designed tests like 031_recovery_conflict and the
    > > > narrow timing window that the standby has not caught up while the wait
    > > > for gets invoked, a simple fallback seems appropriate to me.
    > >
    > > Yes, I see.  Seems acceptable given this seems the only feasible way to go.
    > >
    >
    > Here is the updated patch with recovery conflicts handled.
    
    V2 corrected the commit message to state " if the WAIT FOR LSN session
    is interrupted by a recovery conflict (e.g., DROP TABLESPACE
    triggering conflicts on all backends),".  In this case, the statement
    is canceled when possible; in some states (idle in transaction or
    subtransaction) the session may be terminated.
    
    
    -- 
    Best,
    Xuneng
    
  101. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-12T06:53:46Z

    Hi Alexander,
    
    On Sat, Jan 10, 2026 at 12:47 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Fri, Jan 9, 2026 at 9:44 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Fri, Jan 9, 2026 at 4:42 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > On Thu, Jan 8, 2026 at 6:29 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > On Thu, Jan 8, 2026 at 10:19 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > > I see, you were right.  This is not related to the MyProc->xmin.
    > > > > > ResolveRecoveryConflictWithTablespace() calls
    > > > > > GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid).  That
    > > > > > would kill WAIT FOR LSN query independently on its xmin.
    > > > >
    > > > > I think the concern is valid --- conflicts like
    > > > > PROCSIG_RECOVERY_CONFLICT_SNAPSHOT could occur and terminate the
    > > > > backend if the timing is unlucky. It's more difficult to reproduce
    > > > > though. A check for the log containing "conflict with recovery" would
    > > > > likely catch these conflicts as well.
    > > >
    > > > Yes, I found multiple reasons why xmin gets temporarily set during
    > > > processing of WAIT FOR LSN query.  I'll soon post a draft patch to fix
    > > > that.
    > > >
    > > > > > I guess your
    > > > > > patch is the only way to go.  It's clumsy to wrap WAIT FOR LSN call
    > > > > > with retry loop, but it would still consume less resources than
    > > > > > polling.
    > > > > >
    > > > >
    > > > > Assuming recovery conflicts are relatively rare in tap tests, except
    > > > > for the explicitly designed tests like 031_recovery_conflict and the
    > > > > narrow timing window that the standby has not caught up while the wait
    > > > > for gets invoked, a simple fallback seems appropriate to me.
    > > >
    > > > Yes, I see.  Seems acceptable given this seems the only feasible way to go.
    > > >
    > >
    > > Here is the updated patch with recovery conflicts handled.
    >
    > V2 corrected the commit message to state " if the WAIT FOR LSN session
    > is interrupted by a recovery conflict (e.g., DROP TABLESPACE
    > triggering conflicts on all backends),".  In this case, the statement
    > is canceled when possible; in some states (idle in transaction or
    > subtransaction) the session may be terminated.
    >
    
    The attached patch avoids a syscache lookup while constructing the
    tuple descriptor for WAIT FOR LSN, so that a catalog snapshot is not
    re-established after the wait finishes.
    
    The standard output path (printtup) may still briefly establish a
    catalog snapshot during result emission, but this seems acceptable:
    the snapshot window is narrow to emit a single row. A fully
    catalog-free output path would require either bypassing the
    DestReceiver lifecycle (breaking layering) or adding a custom receiver
    (added complexity for marginal benefit). The current approach is
    simpler and might be sufficient unless output-phase conflicts are
    observed a lot in practice. Does this make sense to you?
    
    --
    Best,
    Xuneng
    
  102. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-20T01:28:31Z

    Hi,
    
    Peter pointed out redundant pg_unreachable() calls after elog(ERROR)
    in wait.c. Attached patch removes them.
    
    --
    Best,
    Xuneng
    
  103. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-01-27T01:14:43Z

    Hi Alexander,
    
    Heikki spotted a misplaced wake-up call for replay waiters in
    PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    invoked immediately after wal record is applied to avoid the potential
    missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    me. Please check the attached patch to fix that.
    
    -- 
    Best,
    Xuneng
    
  104. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-01-29T07:47:46Z

    On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > Heikki spotted a misplaced wake-up call for replay waiters in
    > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > invoked immediately after wal record is applied to avoid the potential
    > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > me. Please check the attached patch to fix that.
    
    Pushed, thank you!
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  105. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-05T12:31:46Z

    On Thu, Jan 29, 2026 at 9:47 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > Heikki spotted a misplaced wake-up call for replay waiters in
    > > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > > invoked immediately after wal record is applied to avoid the potential
    > > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > > me. Please check the attached patch to fix that.
    >
    > Pushed, thank you!
    
    I've assembled small patches, which I think worth pushing before v19 FF.
    
    1. Avoid syscache lookup in WaitStmtResultDesc().  This is [1] patch,
    but I've removed redundant comment in ExecWaitStmt(), which explained
    the same as WaitStmtResultDesc() comment.
    2. Use WAIT FOR LSN in wait_for_catchup().  I made the following
    changes: fallback to polling on not_in_recovery result instead of
    croaking, avoid separate pg_is_in_recovery() query, comment why we
    may face the recovery conflict and why it is safe to detect this by
    the error string.
    3. A paragraph to the docs about possible recovery conflicts and their reason.
    
    I'm going to push this on Monday if no objections.
    
    Links.
    1. https://www.postgresql.org/message-id/CABPTF7U%2BSUnJX_woQYGe%3D%3DR9Oz%2B-V6X0VO2stBLPGfJmH_LEhw%40mail.gmail.com
    2. https://www.postgresql.org/message-id/CABPTF7X0n%3DR50z2fBpj3EbYYz04Ab0-DHJa%2BJfoAEny62QmUdg%40mail.gmail.com
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  106. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-06T03:26:59Z

    Hi Alexander,
    
    On Sun, Apr 5, 2026 at 8:31 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Thu, Jan 29, 2026 at 9:47 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > Heikki spotted a misplaced wake-up call for replay waiters in
    > > > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > > > invoked immediately after wal record is applied to avoid the potential
    > > > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > > > me. Please check the attached patch to fix that.
    > >
    > > Pushed, thank you!
    >
    > I've assembled small patches, which I think worth pushing before v19 FF.
    >
    > 1. Avoid syscache lookup in WaitStmtResultDesc().  This is [1] patch,
    > but I've removed redundant comment in ExecWaitStmt(), which explained
    > the same as WaitStmtResultDesc() comment.
    > 2. Use WAIT FOR LSN in wait_for_catchup().  I made the following
    > changes: fallback to polling on not_in_recovery result instead of
    > croaking, avoid separate pg_is_in_recovery() query, comment why we
    > may face the recovery conflict and why it is safe to detect this by
    > the error string.
    > 3. A paragraph to the docs about possible recovery conflicts and their reason.
    >
    > I'm going to push this on Monday if no objections.
    >
    > Links.
    > 1. https://www.postgresql.org/message-id/CABPTF7U%2BSUnJX_woQYGe%3D%3DR9Oz%2B-V6X0VO2stBLPGfJmH_LEhw%40mail.gmail.com
    > 2. https://www.postgresql.org/message-id/CABPTF7X0n%3DR50z2fBpj3EbYYz04Ab0-DHJa%2BJfoAEny62QmUdg%40mail.gmail.com
    
    I'll review them shortly.
    
    
    --
    Best,
    Xuneng
    
    
    
    
  107. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-06T06:01:11Z

    On Mon, Apr 6, 2026 at 11:26 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Alexander,
    >
    > On Sun, Apr 5, 2026 at 8:31 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Thu, Jan 29, 2026 at 9:47 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > Heikki spotted a misplaced wake-up call for replay waiters in
    > > > > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > > > > invoked immediately after wal record is applied to avoid the potential
    > > > > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > > > > me. Please check the attached patch to fix that.
    > > >
    > > > Pushed, thank you!
    > >
    > > I've assembled small patches, which I think worth pushing before v19 FF.
    > >
    > > 1. Avoid syscache lookup in WaitStmtResultDesc().  This is [1] patch,
    > > but I've removed redundant comment in ExecWaitStmt(), which explained
    > > the same as WaitStmtResultDesc() comment.
    > > 2. Use WAIT FOR LSN in wait_for_catchup().  I made the following
    > > changes: fallback to polling on not_in_recovery result instead of
    > > croaking, avoid separate pg_is_in_recovery() query, comment why we
    > > may face the recovery conflict and why it is safe to detect this by
    > > the error string.
    > > 3. A paragraph to the docs about possible recovery conflicts and their reason.
    > >
    > > I'm going to push this on Monday if no objections.
    > >
    > > Links.
    > > 1. https://www.postgresql.org/message-id/CABPTF7U%2BSUnJX_woQYGe%3D%3DR9Oz%2B-V6X0VO2stBLPGfJmH_LEhw%40mail.gmail.com
    > > 2. https://www.postgresql.org/message-id/CABPTF7X0n%3DR50z2fBpj3EbYYz04Ab0-DHJa%2BJfoAEny62QmUdg%40mail.gmail.com
    >
    > I'll review them shortly.
    >
    
    Here're some comments:
    
    1) Patch 2 checks for not_in_recovery, but WAIT FOR ... NO_THROW
    returns “not in recovery”, which appears to prevent the
    promoted-standby fallback described in the patch from triggering.
    Instead, it seems to result in a hard failure.
    
    There are some behavior changes that I overlooked earlier:
    
    2)  Patch 2 changes the helper from "wait until timeout if there is no
    active replication connection" to "connect to the standby immediately
    and fail on ordinary connection failures". This appears to differ from
    the current contract and may affect cases where the standby is down,
    restarting, or not yet accepting SQL.
    
    "If there is no active replication connection from this peer, waits
    until poll_query_until timeout."
    
    3) In patch 2, we returns success as soon as the standby locally
    reaches the target LSN, but the existing helper is explicitly defined
    in terms of pg_stat_replication ... state = 'streaming' on the
    upstream. So the new path can report success even when there is no
    active replication connection from that peer anymore.
    
    "The replication connection must be in a streaming state."
    
    I’m not sure whether these semantic changes are intended.
    
    
    --
    Best,
    Xuneng
    
    
    
    
  108. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-06T07:15:15Z

    On Mon, Apr 6, 2026 at 2:01 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Mon, Apr 6, 2026 at 11:26 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Alexander,
    > >
    > > On Sun, Apr 5, 2026 at 8:31 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > On Thu, Jan 29, 2026 at 9:47 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > > Heikki spotted a misplaced wake-up call for replay waiters in
    > > > > > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > > > > > invoked immediately after wal record is applied to avoid the potential
    > > > > > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > > > > > me. Please check the attached patch to fix that.
    > > > >
    > > > > Pushed, thank you!
    > > >
    > > > I've assembled small patches, which I think worth pushing before v19 FF.
    > > >
    > > > 1. Avoid syscache lookup in WaitStmtResultDesc().  This is [1] patch,
    > > > but I've removed redundant comment in ExecWaitStmt(), which explained
    > > > the same as WaitStmtResultDesc() comment.
    > > > 2. Use WAIT FOR LSN in wait_for_catchup().  I made the following
    > > > changes: fallback to polling on not_in_recovery result instead of
    > > > croaking, avoid separate pg_is_in_recovery() query, comment why we
    > > > may face the recovery conflict and why it is safe to detect this by
    > > > the error string.
    > > > 3. A paragraph to the docs about possible recovery conflicts and their reason.
    > > >
    > > > I'm going to push this on Monday if no objections.
    > > >
    > > > Links.
    > > > 1. https://www.postgresql.org/message-id/CABPTF7U%2BSUnJX_woQYGe%3D%3DR9Oz%2B-V6X0VO2stBLPGfJmH_LEhw%40mail.gmail.com
    > > > 2. https://www.postgresql.org/message-id/CABPTF7X0n%3DR50z2fBpj3EbYYz04Ab0-DHJa%2BJfoAEny62QmUdg%40mail.gmail.com
    > >
    > > I'll review them shortly.
    > >
    >
    > Here're some comments:
    >
    > 1) Patch 2 checks for not_in_recovery, but WAIT FOR ... NO_THROW
    > returns “not in recovery”, which appears to prevent the
    > promoted-standby fallback described in the patch from triggering.
    > Instead, it seems to result in a hard failure.
    >
    > There are some behavior changes that I overlooked earlier:
    >
    > 2)  Patch 2 changes the helper from "wait until timeout if there is no
    > active replication connection" to "connect to the standby immediately
    > and fail on ordinary connection failures". This appears to differ from
    > the current contract and may affect cases where the standby is down,
    > restarting, or not yet accepting SQL.
    >
    > "If there is no active replication connection from this peer, waits
    > until poll_query_until timeout."
    >
    > 3) In patch 2, we returns success as soon as the standby locally
    > reaches the target LSN, but the existing helper is explicitly defined
    > in terms of pg_stat_replication ... state = 'streaming' on the
    > upstream. So the new path can report success even when there is no
    > active replication connection from that peer anymore.
    >
    > "The replication connection must be in a streaming state."
    >
    > I’m not sure whether these semantic changes are intended.
    >
    
    After some thoughts, these semantic changes appear to be improvements
    over the artifacts of polling-based behavior. The pg_stat_replication
    ... state = 'streaming' is a precondition of polling works rather than
    a post-condition check. The retry timeout also has nothing to do with
    the edge cases and the state of the standby.
    
    In practice, wait_for_catchup() is called when the standby is expected
    to be up. If it's not, one of two things is true:
    
    The standby is briefly restarting -> start() already waits for
    readiness before returning, so this is a non-issue.
    The standby is genuinely down -> waiting 180 seconds to time out
    wastes CI time compared to failing fast.
    
    I’ve addressed point 1 and updated the comments accordingly to reflect
    the new behavior. Please check it.
    
    
    --
    Best,
    Xuneng
    
  109. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-06T19:49:08Z

    On Mon, Apr 6, 2026 at 10:15 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Mon, Apr 6, 2026 at 2:01 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Mon, Apr 6, 2026 at 11:26 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi Alexander,
    > > >
    > > > On Sun, Apr 5, 2026 at 8:31 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > >
    > > > > On Thu, Jan 29, 2026 at 9:47 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > > > > On Tue, Jan 27, 2026 at 3:14 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > > > Heikki spotted a misplaced wake-up call for replay waiters in
    > > > > > > PerformWalRecovery. He suggested that the WaitLSNWakeup needs to be
    > > > > > > invoked immediately after wal record is applied to avoid the potential
    > > > > > > missed wake-ups when recovery stops/pauses/promotes. It makes sense to
    > > > > > > me. Please check the attached patch to fix that.
    > > > > >
    > > > > > Pushed, thank you!
    > > > >
    > > > > I've assembled small patches, which I think worth pushing before v19 FF.
    > > > >
    > > > > 1. Avoid syscache lookup in WaitStmtResultDesc().  This is [1] patch,
    > > > > but I've removed redundant comment in ExecWaitStmt(), which explained
    > > > > the same as WaitStmtResultDesc() comment.
    > > > > 2. Use WAIT FOR LSN in wait_for_catchup().  I made the following
    > > > > changes: fallback to polling on not_in_recovery result instead of
    > > > > croaking, avoid separate pg_is_in_recovery() query, comment why we
    > > > > may face the recovery conflict and why it is safe to detect this by
    > > > > the error string.
    > > > > 3. A paragraph to the docs about possible recovery conflicts and their reason.
    > > > >
    > > > > I'm going to push this on Monday if no objections.
    > > > >
    > > > > Links.
    > > > > 1. https://www.postgresql.org/message-id/CABPTF7U%2BSUnJX_woQYGe%3D%3DR9Oz%2B-V6X0VO2stBLPGfJmH_LEhw%40mail.gmail.com
    > > > > 2. https://www.postgresql.org/message-id/CABPTF7X0n%3DR50z2fBpj3EbYYz04Ab0-DHJa%2BJfoAEny62QmUdg%40mail.gmail.com
    > > >
    > > > I'll review them shortly.
    > > >
    > >
    > > Here're some comments:
    > >
    > > 1) Patch 2 checks for not_in_recovery, but WAIT FOR ... NO_THROW
    > > returns “not in recovery”, which appears to prevent the
    > > promoted-standby fallback described in the patch from triggering.
    > > Instead, it seems to result in a hard failure.
    > >
    > > There are some behavior changes that I overlooked earlier:
    > >
    > > 2)  Patch 2 changes the helper from "wait until timeout if there is no
    > > active replication connection" to "connect to the standby immediately
    > > and fail on ordinary connection failures". This appears to differ from
    > > the current contract and may affect cases where the standby is down,
    > > restarting, or not yet accepting SQL.
    > >
    > > "If there is no active replication connection from this peer, waits
    > > until poll_query_until timeout."
    > >
    > > 3) In patch 2, we returns success as soon as the standby locally
    > > reaches the target LSN, but the existing helper is explicitly defined
    > > in terms of pg_stat_replication ... state = 'streaming' on the
    > > upstream. So the new path can report success even when there is no
    > > active replication connection from that peer anymore.
    > >
    > > "The replication connection must be in a streaming state."
    > >
    > > I’m not sure whether these semantic changes are intended.
    > >
    >
    > After some thoughts, these semantic changes appear to be improvements
    > over the artifacts of polling-based behavior. The pg_stat_replication
    > ... state = 'streaming' is a precondition of polling works rather than
    > a post-condition check. The retry timeout also has nothing to do with
    > the edge cases and the state of the standby.
    >
    > In practice, wait_for_catchup() is called when the standby is expected
    > to be up. If it's not, one of two things is true:
    >
    > The standby is briefly restarting -> start() already waits for
    > readiness before returning, so this is a non-issue.
    > The standby is genuinely down -> waiting 180 seconds to time out
    > wastes CI time compared to failing fast.
    >
    > I’ve addressed point 1 and updated the comments accordingly to reflect
    > the new behavior. Please check it.
    
    Thank you, I've pushed your version of patchset.  I made two minor
    corrections for patch #2: mention default mode value in the header
    comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  110. Re: Implement waiting for wal lsn replay: reloaded

    Tom Lane <tgl@sss.pgh.pa.us> — 2026-04-07T01:52:54Z

    Alexander Korotkov <aekorotkov@gmail.com> writes:
    > Thank you, I've pushed your version of patchset.  I made two minor
    > corrections for patch #2: mention default mode value in the header
    > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    
    I wondered why my buildfarm animals got noticeably slower today.
    There seem to be a couple of culprits, but one of them is that
    7e8aeb9e4 (Use WAIT FOR LSN) has caused the runtime of pg_rewind's
    t/003_extrafiles.pl to go through the roof.  On indri's host, that
    TAP test took about 3 seconds immediately before that commit, and
    about 45 seconds immediately after.  (For scale, the core regression
    tests take about 10 seconds on this machine.)  I see roughly
    comparable slowdowns on other machines too.
    
    I have not dug into why, nor do I understand why it seems like
    only this one test is affected.  In any case, surely this is
    unacceptable.
    
    			regards, tom lane
    
    
    
    
  111. Re: Implement waiting for wal lsn replay: reloaded

    Tom Lane <tgl@sss.pgh.pa.us> — 2026-04-07T02:08:13Z

    I wrote:
    > I wondered why my buildfarm animals got noticeably slower today.
    > There seem to be a couple of culprits, but one of them is that
    > 7e8aeb9e4 (Use WAIT FOR LSN) has caused the runtime of pg_rewind's
    > t/003_extrafiles.pl to go through the roof.  On indri's host, that
    > TAP test took about 3 seconds immediately before that commit, and
    > about 45 seconds immediately after.
    
    I'm wrong: there's only one culprit.  The other big change in runtime
    today is that src/test/recovery's t/033_replay_tsp_drops.pl went from
    about 4 seconds to about 46, and that jump also happened at 7e8aeb9e4.
    So we still have a mystery, but it's "what do those two tests have in
    common that is shared by no others?".
    
    			regards, tom lane
    
    
    
    
  112. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T02:28:52Z

    Hi Tom,
    
    On Tue, Apr 7, 2026 at 10:08 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >
    > I wrote:
    > > I wondered why my buildfarm animals got noticeably slower today.
    > > There seem to be a couple of culprits, but one of them is that
    > > 7e8aeb9e4 (Use WAIT FOR LSN) has caused the runtime of pg_rewind's
    > > t/003_extrafiles.pl to go through the roof.  On indri's host, that
    > > TAP test took about 3 seconds immediately before that commit, and
    > > about 45 seconds immediately after.
    >
    > I'm wrong: there's only one culprit.  The other big change in runtime
    > today is that src/test/recovery's t/033_replay_tsp_drops.pl went from
    > about 4 seconds to about 46, and that jump also happened at 7e8aeb9e4.
    > So we still have a mystery, but it's "what do those two tests have in
    > common that is shared by no others?".
    >
    >                         regards, tom lane
    
    Thanks for reporting this. I think it could be related to the read of
    not-yet-updated writtenUpto position.  I'll look into this and propose
    a fix shortly.
    
    
    --
    Best,
    Xuneng
    
    
    
    
  113. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-07T03:07:45Z

    Hi,
    
    On 2026-04-07 10:28:52 +0800, Xuneng Zhou wrote:
    > On Tue, Apr 7, 2026 at 10:08 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > >
    > > I wrote:
    > > > I wondered why my buildfarm animals got noticeably slower today.
    > > > There seem to be a couple of culprits, but one of them is that
    > > > 7e8aeb9e4 (Use WAIT FOR LSN) has caused the runtime of pg_rewind's
    > > > t/003_extrafiles.pl to go through the roof.  On indri's host, that
    > > > TAP test took about 3 seconds immediately before that commit, and
    > > > about 45 seconds immediately after.
    > >
    > > I'm wrong: there's only one culprit.  The other big change in runtime
    > > today is that src/test/recovery's t/033_replay_tsp_drops.pl went from
    > > about 4 seconds to about 46, and that jump also happened at 7e8aeb9e4.
    > > So we still have a mystery, but it's "what do those two tests have in
    > > common that is shared by no others?".
    > >
    > Thanks for reporting this. I think it could be related to the read of
    > not-yet-updated writtenUpto position.  I'll look into this and propose
    > a fix shortly.
    
    Yes, it's not yet initialized and thus the WAIT FOR waits, even though the
    position had already been reached.
    
    
    But, leaving that aside, looking at this code I'm somewhat concerned - it
    seems to not worry at all about memory ordering?
    
    
    static void
    XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    ...
    	/* Update shared-memory status */
    	pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    
    	/*
    	 * If we wrote an LSN that someone was waiting for, notify the waiters.
    	 */
    	if (waitLSNState &&
    		(LogstreamResult.Write >=
    		 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    		WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    
    There are no memory barriers here, so the CPU would be entirely free to not
    make the writtenUpto write visible to a waiter that's in the process of
    registering and is checking whether it needs to wait in WaitForLSN().
    
    And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    loop entry and maybe kinda you can think that WaitLatch() will somehow also
    have barrier semantic.  But if so, that would need to be very carefully
    documented.  And it seems completely unnecessary here, it's hard to believe
    using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    performance issue
    
    Greetings,
    
    Andres Freund
    
    
    
    
  114. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-07T03:31:04Z

    Hi,
    
    On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > seems to not worry at all about memory ordering?
    > 
    > 
    > static void
    > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > ...
    > 	/* Update shared-memory status */
    > 	pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > 
    > 	/*
    > 	 * If we wrote an LSN that someone was waiting for, notify the waiters.
    > 	 */
    > 	if (waitLSNState &&
    > 		(LogstreamResult.Write >=
    > 		 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > 		WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > 
    > There are no memory barriers here, so the CPU would be entirely free to not
    > make the writtenUpto write visible to a waiter that's in the process of
    > registering and is checking whether it needs to wait in WaitForLSN().
    > 
    > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > have barrier semantic.  But if so, that would need to be very carefully
    > documented.  And it seems completely unnecessary here, it's hard to believe
    > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > performance issue
    
    And separately from the memory ordering, how can it make sense that there's
    at least 5 copies of this
    
    		if (waitLSNState &&
    			(LogstreamResult.Flush >=
    			 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    
    around?  That needs to be encapsulated so that if you have a bug, like the
    memory ordering problem I describe above, it can be fixed once, not in
    multiple places.
    
    And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    does so itself?
    
    	/*
    	 * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    	 * "wake all waiters" (e.g., during promotion when recovery ends).
    	 */
    	if (XLogRecPtrIsValid(currentLSN) &&
    		pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    		return;
    
    And why is the code checking if waitLSNState is non-NULL?
    
    Greetings,
    
    Andres Freund
    
    
    
    
  115. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T04:02:47Z

    Hi Andres,
    
    On Tue, Apr 7, 2026 at 11:31 AM Andres Freund <andres@anarazel.de> wrote:
    >
    > Hi,
    >
    > On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > > seems to not worry at all about memory ordering?
    > >
    > >
    > > static void
    > > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > > ...
    > >       /* Update shared-memory status */
    > >       pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > >
    > >       /*
    > >        * If we wrote an LSN that someone was waiting for, notify the waiters.
    > >        */
    > >       if (waitLSNState &&
    > >               (LogstreamResult.Write >=
    > >                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > >               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > >
    > > There are no memory barriers here, so the CPU would be entirely free to not
    > > make the writtenUpto write visible to a waiter that's in the process of
    > > registering and is checking whether it needs to wait in WaitForLSN().
    > >
    > > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > > have barrier semantic.  But if so, that would need to be very carefully
    > > documented.  And it seems completely unnecessary here, it's hard to believe
    > > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > > performance issue
    
    Thanks for pointing this out.  This is indeed a store-load ordering issue.
    
    > And separately from the memory ordering, how can it make sense that there's
    > at least 5 copies of this
    >
    >                 if (waitLSNState &&
    >                         (LogstreamResult.Flush >=
    >                          pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    >                         WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    >
    > around?  That needs to be encapsulated so that if you have a bug, like the
    > memory ordering problem I describe above, it can be fixed once, not in
    > multiple places.
    
    Yeah, this duplication is not ok.
    
    > And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    > does so itself?
    >
    >         /*
    >          * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    >          * "wake all waiters" (e.g., during promotion when recovery ends).
    >          */
    >         if (XLogRecPtrIsValid(currentLSN) &&
    >                 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    >                 return;
    >
    > And why is the code checking if waitLSNState is non-NULL?
    >
    
    These fast checks are unnecessary copy-pastos and waitLSNState checks
    also do not make sense except for the one in WaitLSNCleanup.
    
    I'll prepare a patch set addressing them.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  116. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-07T12:52:03Z

    On Tue, Apr 7, 2026 at 7:03 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Tue, Apr 7, 2026 at 11:31 AM Andres Freund <andres@anarazel.de> wrote:
    > >
    > > Hi,
    > >
    > > On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > > > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > > > seems to not worry at all about memory ordering?
    > > >
    > > >
    > > > static void
    > > > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > > > ...
    > > >       /* Update shared-memory status */
    > > >       pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > > >
    > > >       /*
    > > >        * If we wrote an LSN that someone was waiting for, notify the waiters.
    > > >        */
    > > >       if (waitLSNState &&
    > > >               (LogstreamResult.Write >=
    > > >                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > >               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > >
    > > > There are no memory barriers here, so the CPU would be entirely free to not
    > > > make the writtenUpto write visible to a waiter that's in the process of
    > > > registering and is checking whether it needs to wait in WaitForLSN().
    > > >
    > > > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > > > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > > > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > > > have barrier semantic.  But if so, that would need to be very carefully
    > > > documented.  And it seems completely unnecessary here, it's hard to believe
    > > > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > > > performance issue
    >
    > Thanks for pointing this out.  This is indeed a store-load ordering issue.
    >
    > > And separately from the memory ordering, how can it make sense that there's
    > > at least 5 copies of this
    > >
    > >                 if (waitLSNState &&
    > >                         (LogstreamResult.Flush >=
    > >                          pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    > >                         WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    > >
    > > around?  That needs to be encapsulated so that if you have a bug, like the
    > > memory ordering problem I describe above, it can be fixed once, not in
    > > multiple places.
    >
    > Yeah, this duplication is not ok.
    >
    > > And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    > > does so itself?
    > >
    > >         /*
    > >          * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > >          * "wake all waiters" (e.g., during promotion when recovery ends).
    > >          */
    > >         if (XLogRecPtrIsValid(currentLSN) &&
    > >                 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > >                 return;
    > >
    > > And why is the code checking if waitLSNState is non-NULL?
    > >
    >
    > These fast checks are unnecessary copy-pastos and waitLSNState checks
    > also do not make sense except for the one in WaitLSNCleanup.
    >
    > I'll prepare a patch set addressing them.
    
    Thanks to Tom and Andres for catching these issues!
    I'm planning to work on this during this evening.  I'll review
    Xuneng's patches (or write my own if they wouldn't arrive yet).
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  117. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T12:59:58Z

    On Tue, Apr 7, 2026 at 12:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Andres,
    >
    > On Tue, Apr 7, 2026 at 11:31 AM Andres Freund <andres@anarazel.de> wrote:
    > >
    > > Hi,
    > >
    > > On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > > > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > > > seems to not worry at all about memory ordering?
    > > >
    > > >
    > > > static void
    > > > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > > > ...
    > > >       /* Update shared-memory status */
    > > >       pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > > >
    > > >       /*
    > > >        * If we wrote an LSN that someone was waiting for, notify the waiters.
    > > >        */
    > > >       if (waitLSNState &&
    > > >               (LogstreamResult.Write >=
    > > >                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > >               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > >
    > > > There are no memory barriers here, so the CPU would be entirely free to not
    > > > make the writtenUpto write visible to a waiter that's in the process of
    > > > registering and is checking whether it needs to wait in WaitForLSN().
    > > >
    > > > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > > > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > > > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > > > have barrier semantic.  But if so, that would need to be very carefully
    > > > documented.  And it seems completely unnecessary here, it's hard to believe
    > > > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > > > performance issue
    >
    > Thanks for pointing this out.  This is indeed a store-load ordering issue.
    >
    > > And separately from the memory ordering, how can it make sense that there's
    > > at least 5 copies of this
    > >
    > >                 if (waitLSNState &&
    > >                         (LogstreamResult.Flush >=
    > >                          pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    > >                         WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    > >
    > > around?  That needs to be encapsulated so that if you have a bug, like the
    > > memory ordering problem I describe above, it can be fixed once, not in
    > > multiple places.
    >
    > Yeah, this duplication is not ok.
    >
    > > And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    > > does so itself?
    > >
    > >         /*
    > >          * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > >          * "wake all waiters" (e.g., during promotion when recovery ends).
    > >          */
    > >         if (XLogRecPtrIsValid(currentLSN) &&
    > >                 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > >                 return;
    > >
    > > And why is the code checking if waitLSNState is non-NULL?
    > >
    >
    > These fast checks are unnecessary copy-pastos and waitLSNState checks
    > also do not make sense except for the one in WaitLSNCleanup.
    >
    > I'll prepare a patch set addressing them.
    >
    
    Here is some analysis of the issue reported by Tom:
    
    1) The problem
    
    WAIT FOR LSN with standby_write or standby_flush mode can block
    indefinitely on an idle primary even when the target LSN is already
    satisfied by WAL on disk.
    
    The walreceiver initializes its process-local LogstreamResult.Write
    and LogstreamResult.Flush from GetXLogReplayRecPtr() at connect time,
    reflecting all WAL already present on the standby (from a base backup,
    archive restore, or prior streaming). The shared-memory positions used
    by WAIT FOR LSN, however, are not seeded from this value:
    
    WalRcv->writtenUpto is zero-initialized by ShmemInitStruct and remains
    0 until XLogWalRcvWrite() processes incoming streaming data.
    WalRcv->flushedUpto is initialized to the segment-aligned streaming
    start point by RequestXLogStreaming(), which may be significantly
    behind the replay position. It advances only when XLogWalRcvFlush()
    processes new data — which itself requires LogstreamResult.Flush <
    LogstreamResult.Write, a condition that never holds at startup since
    both fields are initialized to the same value.
    
    When the primary is idle and sends no new WAL, both positions stay at
    their initial stale values indefinitely.
    
    2) The fix
    Seed writtenUpto and flushedUpto from LogstreamResult immediately
    after the walreceiver initializes those process-local fields, then
    call WaitLSNWakeup() to wake any already-blocked waiters.
    
    This broadens the semantics of these fields. writtenUpto and
    flushedUpto  used to track only WAL written or flushed by the current
    walreceiver session — WAL received from the primary since the most
    recent connect. After this change, they are initialized to the replay
    position, so they also cover WAL that was already on disk before
    streaming began. This affects pg_stat_wal_receiver.written_lsn and
    flushed_lsn, which will now report the replay position immediately at
    walreceiver startup rather than 0 and the segment boundary
    respectively. I am still considering whether this semantic change is
    acceptable though it does shorten the runtime of the tap tests
    reported by Tom in my test. Another approach is to modify the logic of
    GetCurrentLSNForWaitType to cope with this special case and leave the
    publisher side alone without changing the semantics. But this seems to
    be more subtle.
    
    -- 
    Best,
    Xuneng
    
  118. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T13:05:40Z

    Hi Alexander,
    
    On Tue, Apr 7, 2026 at 8:52 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Tue, Apr 7, 2026 at 7:03 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Tue, Apr 7, 2026 at 11:31 AM Andres Freund <andres@anarazel.de> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > > > > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > > > > seems to not worry at all about memory ordering?
    > > > >
    > > > >
    > > > > static void
    > > > > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > > > > ...
    > > > >       /* Update shared-memory status */
    > > > >       pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > > > >
    > > > >       /*
    > > > >        * If we wrote an LSN that someone was waiting for, notify the waiters.
    > > > >        */
    > > > >       if (waitLSNState &&
    > > > >               (LogstreamResult.Write >=
    > > > >                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > > >               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > > >
    > > > > There are no memory barriers here, so the CPU would be entirely free to not
    > > > > make the writtenUpto write visible to a waiter that's in the process of
    > > > > registering and is checking whether it needs to wait in WaitForLSN().
    > > > >
    > > > > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > > > > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > > > > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > > > > have barrier semantic.  But if so, that would need to be very carefully
    > > > > documented.  And it seems completely unnecessary here, it's hard to believe
    > > > > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > > > > performance issue
    > >
    > > Thanks for pointing this out.  This is indeed a store-load ordering issue.
    > >
    > > > And separately from the memory ordering, how can it make sense that there's
    > > > at least 5 copies of this
    > > >
    > > >                 if (waitLSNState &&
    > > >                         (LogstreamResult.Flush >=
    > > >                          pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    > > >                         WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    > > >
    > > > around?  That needs to be encapsulated so that if you have a bug, like the
    > > > memory ordering problem I describe above, it can be fixed once, not in
    > > > multiple places.
    > >
    > > Yeah, this duplication is not ok.
    > >
    > > > And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    > > > does so itself?
    > > >
    > > >         /*
    > > >          * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > > >          * "wake all waiters" (e.g., during promotion when recovery ends).
    > > >          */
    > > >         if (XLogRecPtrIsValid(currentLSN) &&
    > > >                 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > > >                 return;
    > > >
    > > > And why is the code checking if waitLSNState is non-NULL?
    > > >
    > >
    > > These fast checks are unnecessary copy-pastos and waitLSNState checks
    > > also do not make sense except for the one in WaitLSNCleanup.
    > >
    > > I'll prepare a patch set addressing them.
    >
    > Thanks to Tom and Andres for catching these issues!
    > I'm planning to work on this during this evening.  I'll review
    > Xuneng's patches (or write my own if they wouldn't arrive yet).
    >
    
    I’ve posted two patches. The first fixes the duplication issue
    reported by Andres and is fairly straightforward. The second turned
    out to be more complex than expected, and I’m still working through
    possible solutions. Feedback or alternative approaches would be very
    helpful.
    I also spent some time drafting a patch to address the memory ordering
    issue and will post it later.
    
    
    -- 
    Best,
    Xuneng
    
    
    
    
  119. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-07T13:18:37Z

    Hi,
    
    On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > I’ve posted two patches. The first fixes the duplication issue
    > reported by Andres and is fairly straightforward. The second turned
    > out to be more complex than expected, and I’m still working through
    > possible solutions. Feedback or alternative approaches would be very
    > helpful.
    > I also spent some time drafting a patch to address the memory ordering
    > issue and will post it later.
    
    I propose quickly applying a minimal patch like the attached, to get the test
    performance back to normal.
    
    Will do so unless somebody protests within in one CI cycle and one coffee.
    
    Greetings,
    
    Andres Freund
    
  120. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-07T13:46:29Z

    On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    
    > Hi,
    >
    > On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > > I’ve posted two patches. The first fixes the duplication issue
    > > reported by Andres and is fairly straightforward. The second turned
    > > out to be more complex than expected, and I’m still working through
    > > possible solutions. Feedback or alternative approaches would be very
    > > helpful.
    > > I also spent some time drafting a patch to address the memory ordering
    > > issue and will post it later.
    >
    > I propose quickly applying a minimal patch like the attached, to get the
    > test
    > performance back to normal.
    >
    > Will do so unless somebody protests within in one CI cycle and one coffee.
    >
    
    I would be able to review them only after several hours. But +1 for
    applying now to get rid of buildfarm slowdown.
    
    ------
    Regards,
    Alexander Korotkov
    
  121. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-07T13:52:33Z

    Hi,
    
    On 2026-04-07 16:46:29 +0300, Alexander Korotkov wrote:
    > On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    > > On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > > > I’ve posted two patches. The first fixes the duplication issue
    > > > reported by Andres and is fairly straightforward. The second turned
    > > > out to be more complex than expected, and I’m still working through
    > > > possible solutions. Feedback or alternative approaches would be very
    > > > helpful.
    > > > I also spent some time drafting a patch to address the memory ordering
    > > > issue and will post it later.
    > >
    > > I propose quickly applying a minimal patch like the attached, to get the
    > > test
    > > performance back to normal.
    > >
    > > Will do so unless somebody protests within in one CI cycle and one coffee.
    > >
    > 
    > I would be able to review them only after several hours. But +1 for
    > applying now to get rid of buildfarm slowdown.
    
    Done.
    
    Just to be clear, I didn't apply Xuneng's patches, just the minimal fix for
    the slowdown.
    
    Greetings,
    
    Andres Freund
    
    
    
    
  122. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T14:29:05Z

    On Tue, Apr 7, 2026 at 9:52 PM Andres Freund <andres@anarazel.de> wrote:
    >
    > Hi,
    >
    > On 2026-04-07 16:46:29 +0300, Alexander Korotkov wrote:
    > > On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    > > > On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > > > > I’ve posted two patches. The first fixes the duplication issue
    > > > > reported by Andres and is fairly straightforward. The second turned
    > > > > out to be more complex than expected, and I’m still working through
    > > > > possible solutions. Feedback or alternative approaches would be very
    > > > > helpful.
    > > > > I also spent some time drafting a patch to address the memory ordering
    > > > > issue and will post it later.
    > > >
    > > > I propose quickly applying a minimal patch like the attached, to get the
    > > > test
    > > > performance back to normal.
    > > >
    > > > Will do so unless somebody protests within in one CI cycle and one coffee.
    > > >
    > >
    > > I would be able to review them only after several hours. But +1 for
    > > applying now to get rid of buildfarm slowdown.
    >
    > Done.
    
    Thanks for dealing with it!
    
    > Just to be clear, I didn't apply Xuneng's patches, just the minimal fix for
    > the slowdown.
    >
    
    Ok, I don’t think that patch is in a committable state; posting it for
    reference.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  123. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-07T15:55:43Z

    On Tue, Apr 7, 2026 at 9:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    >>
    >> Hi,
    >>
    >> On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    >> > I’ve posted two patches. The first fixes the duplication issue
    >> > reported by Andres and is fairly straightforward. The second turned
    >> > out to be more complex than expected, and I’m still working through
    >> > possible solutions. Feedback or alternative approaches would be very
    >> > helpful.
    >> > I also spent some time drafting a patch to address the memory ordering
    >> > issue and will post it later.
    >>
    >> I propose quickly applying a minimal patch like the attached, to get the test
    >> performance back to normal.
    >>
    >> Will do so unless somebody protests within in one CI cycle and one coffee.
    >
    >
    > I would be able to review them only after several hours. But +1 for applying now to get rid of buildfarm slowdown.
    
    Here is a patch addressing the memory order issue reported earlier.
    
    -- 
    Best,
    Xuneng
    
  124. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-07T23:23:29Z

    Hi, Xuneng!
    
    On Tue, Apr 7, 2026 at 4:00 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Tue, Apr 7, 2026 at 12:02 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Andres,
    > >
    > > On Tue, Apr 7, 2026 at 11:31 AM Andres Freund <andres@anarazel.de> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On 2026-04-06 23:07:45 -0400, Andres Freund wrote:
    > > > > But, leaving that aside, looking at this code I'm somewhat concerned - it
    > > > > seems to not worry at all about memory ordering?
    > > > >
    > > > >
    > > > > static void
    > > > > XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr, TimeLineID tli)
    > > > > ...
    > > > >       /* Update shared-memory status */
    > > > >       pg_atomic_write_u64(&WalRcv->writtenUpto, LogstreamResult.Write);
    > > > >
    > > > >       /*
    > > > >        * If we wrote an LSN that someone was waiting for, notify the waiters.
    > > > >        */
    > > > >       if (waitLSNState &&
    > > > >               (LogstreamResult.Write >=
    > > > >                pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_WRITE])))
    > > > >               WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
    > > > >
    > > > > There are no memory barriers here, so the CPU would be entirely free to not
    > > > > make the writtenUpto write visible to a waiter that's in the process of
    > > > > registering and is checking whether it needs to wait in WaitForLSN().
    > > > >
    > > > > And WaitForLSN()->GetCurrentLSNForWaitType()->GetWalRcvWriteRecPtr() also has
    > > > > no barriers.  That MAYBE is ok, due addLSNWaiter() providing the barrier at
    > > > > loop entry and maybe kinda you can think that WaitLatch() will somehow also
    > > > > have barrier semantic.  But if so, that would need to be very carefully
    > > > > documented.  And it seems completely unnecessary here, it's hard to believe
    > > > > using a barrier (via pg_atomic_read_membarrier_u64() or such) would be a
    > > > > performance issue
    > >
    > > Thanks for pointing this out.  This is indeed a store-load ordering issue.
    > >
    > > > And separately from the memory ordering, how can it make sense that there's
    > > > at least 5 copies of this
    > > >
    > > >                 if (waitLSNState &&
    > > >                         (LogstreamResult.Flush >=
    > > >                          pg_atomic_read_u64(&waitLSNState->minWaitedLSN[WAIT_LSN_TYPE_STANDBY_FLUSH])))
    > > >                         WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
    > > >
    > > > around?  That needs to be encapsulated so that if you have a bug, like the
    > > > memory ordering problem I describe above, it can be fixed once, not in
    > > > multiple places.
    > >
    > > Yeah, this duplication is not ok.
    > >
    > > > And why do these callers even have that pre-check?  Seems WaitLSNWakeup()
    > > > does so itself?
    > > >
    > > >         /*
    > > >          * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > > >          * "wake all waiters" (e.g., during promotion when recovery ends).
    > > >          */
    > > >         if (XLogRecPtrIsValid(currentLSN) &&
    > > >                 pg_atomic_read_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
    > > >                 return;
    > > >
    > > > And why is the code checking if waitLSNState is non-NULL?
    > > >
    > >
    > > These fast checks are unnecessary copy-pastos and waitLSNState checks
    > > also do not make sense except for the one in WaitLSNCleanup.
    > >
    > > I'll prepare a patch set addressing them.
    > >
    >
    > Here is some analysis of the issue reported by Tom:
    >
    > 1) The problem
    >
    > WAIT FOR LSN with standby_write or standby_flush mode can block
    > indefinitely on an idle primary even when the target LSN is already
    > satisfied by WAL on disk.
    >
    > The walreceiver initializes its process-local LogstreamResult.Write
    > and LogstreamResult.Flush from GetXLogReplayRecPtr() at connect time,
    > reflecting all WAL already present on the standby (from a base backup,
    > archive restore, or prior streaming). The shared-memory positions used
    > by WAIT FOR LSN, however, are not seeded from this value:
    >
    > WalRcv->writtenUpto is zero-initialized by ShmemInitStruct and remains
    > 0 until XLogWalRcvWrite() processes incoming streaming data.
    > WalRcv->flushedUpto is initialized to the segment-aligned streaming
    > start point by RequestXLogStreaming(), which may be significantly
    > behind the replay position. It advances only when XLogWalRcvFlush()
    > processes new data — which itself requires LogstreamResult.Flush <
    > LogstreamResult.Write, a condition that never holds at startup since
    > both fields are initialized to the same value.
    >
    > When the primary is idle and sends no new WAL, both positions stay at
    > their initial stale values indefinitely.
    >
    > 2) The fix
    > Seed writtenUpto and flushedUpto from LogstreamResult immediately
    > after the walreceiver initializes those process-local fields, then
    > call WaitLSNWakeup() to wake any already-blocked waiters.
    >
    > This broadens the semantics of these fields. writtenUpto and
    > flushedUpto  used to track only WAL written or flushed by the current
    > walreceiver session — WAL received from the primary since the most
    > recent connect. After this change, they are initialized to the replay
    > position, so they also cover WAL that was already on disk before
    > streaming began. This affects pg_stat_wal_receiver.written_lsn and
    > flushed_lsn, which will now report the replay position immediately at
    > walreceiver startup rather than 0 and the segment boundary
    > respectively. I am still considering whether this semantic change is
    > acceptable though it does shorten the runtime of the tap tests
    > reported by Tom in my test. Another approach is to modify the logic of
    > GetCurrentLSNForWaitType to cope with this special case and leave the
    > publisher side alone without changing the semantics. But this seems to
    > be more subtle.
    
    Patch 0001 looks OK for me.
    Regarding patch 0002.  Changes made for GetCurrentLSNForWaitType()
    looks reliable for me.  PerformWalRecovery() sets replayed positions
    before starting recovery, and in turn before standby can accept
    connections.  So, changes to WalReceiverMain() don't look necessary to
    me.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  125. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-07T23:30:44Z

    On Tue, Apr 7, 2026 at 6:55 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Tue, Apr 7, 2026 at 9:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    > >>
    > >> Hi,
    > >>
    > >> On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > >> > I’ve posted two patches. The first fixes the duplication issue
    > >> > reported by Andres and is fairly straightforward. The second turned
    > >> > out to be more complex than expected, and I’m still working through
    > >> > possible solutions. Feedback or alternative approaches would be very
    > >> > helpful.
    > >> > I also spent some time drafting a patch to address the memory ordering
    > >> > issue and will post it later.
    > >>
    > >> I propose quickly applying a minimal patch like the attached, to get the test
    > >> performance back to normal.
    > >>
    > >> Will do so unless somebody protests within in one CI cycle and one coffee.
    > >
    > >
    > > I would be able to review them only after several hours. But +1 for applying now to get rid of buildfarm slowdown.
    >
    > Here is a patch addressing the memory order issue reported earlier.
    
    I agree to change in WaitLSNWakeup(), memory barrier looks necessary there.
    Regarding GetCurrentLSNForWaitType(), I don't think barrier is needed
    here, nor think it makes things clearer.  I think it would be enough
    to comment that LWLock operations in addLSNWaiter()/deleteLSNWaiter()
    provide necessary barriers.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  126. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-08T00:20:04Z

    On Wed, Apr 8, 2026 at 7:30 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Tue, Apr 7, 2026 at 6:55 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Tue, Apr 7, 2026 at 9:46 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 7, 2026, 16:18 Andres Freund <andres@anarazel.de> wrote:
    > > >>
    > > >> Hi,
    > > >>
    > > >> On 2026-04-07 21:05:40 +0800, Xuneng Zhou wrote:
    > > >> > I’ve posted two patches. The first fixes the duplication issue
    > > >> > reported by Andres and is fairly straightforward. The second turned
    > > >> > out to be more complex than expected, and I’m still working through
    > > >> > possible solutions. Feedback or alternative approaches would be very
    > > >> > helpful.
    > > >> > I also spent some time drafting a patch to address the memory ordering
    > > >> > issue and will post it later.
    > > >>
    > > >> I propose quickly applying a minimal patch like the attached, to get the test
    > > >> performance back to normal.
    > > >>
    > > >> Will do so unless somebody protests within in one CI cycle and one coffee.
    > > >
    > > >
    > > > I would be able to review them only after several hours. But +1 for applying now to get rid of buildfarm slowdown.
    > >
    > > Here is a patch addressing the memory order issue reported earlier.
    >
    > I agree to change in WaitLSNWakeup(), memory barrier looks necessary there.
    > Regarding GetCurrentLSNForWaitType(), I don't think barrier is needed
    > here, nor think it makes things clearer.  I think it would be enough
    > to comment that LWLock operations in addLSNWaiter()/deleteLSNWaiter()
    > provide necessary barriers.
    >
    
    I’m fine with that if Andres has no objection. That said, callers of
    WaitLSNWakeup except STANDBY_WRITE do acquire locks before the wakeup.
    I’m not sure whether a memory barrier is required for all of them,
    though placing the barrier inside WaitLSNWakeup would make the
    handling less scattered.
    
    -- 
    Best,
    Xuneng
    
    
    
    
  127. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-08T00:50:11Z

    Hi,
    
    On 2026-04-08 02:30:44 +0300, Alexander Korotkov wrote:
    > On Tue, Apr 7, 2026 at 6:55 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > I agree to change in WaitLSNWakeup(), memory barrier looks necessary there.
    > Regarding GetCurrentLSNForWaitType(), I don't think barrier is needed
    > here, nor think it makes things clearer.  I think it would be enough
    > to comment that LWLock operations in addLSNWaiter()/deleteLSNWaiter()
    > provide necessary barriers.
    
    That's sufficient for the first iteration, but what guarantees it once you do
    WaitLatch()?  That's likely going to imply a barrier somewhere in the kernel,
    but I don't think there's any actual guarantee.
    
    Greetings,
    
    Andres Freund
    
    
    
    
  128. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-08T03:23:54Z

    On Wed, Apr 8, 2026 at 7:23 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > > Here is some analysis of the issue reported by Tom:
    > >
    > > 1) The problem
    > >
    > > WAIT FOR LSN with standby_write or standby_flush mode can block
    > > indefinitely on an idle primary even when the target LSN is already
    > > satisfied by WAL on disk.
    > >
    > > The walreceiver initializes its process-local LogstreamResult.Write
    > > and LogstreamResult.Flush from GetXLogReplayRecPtr() at connect time,
    > > reflecting all WAL already present on the standby (from a base backup,
    > > archive restore, or prior streaming). The shared-memory positions used
    > > by WAIT FOR LSN, however, are not seeded from this value:
    > >
    > > WalRcv->writtenUpto is zero-initialized by ShmemInitStruct and remains
    > > 0 until XLogWalRcvWrite() processes incoming streaming data.
    > > WalRcv->flushedUpto is initialized to the segment-aligned streaming
    > > start point by RequestXLogStreaming(), which may be significantly
    > > behind the replay position. It advances only when XLogWalRcvFlush()
    > > processes new data — which itself requires LogstreamResult.Flush <
    > > LogstreamResult.Write, a condition that never holds at startup since
    > > both fields are initialized to the same value.
    > >
    > > When the primary is idle and sends no new WAL, both positions stay at
    > > their initial stale values indefinitely.
    > >
    > > 2) The fix
    > > Seed writtenUpto and flushedUpto from LogstreamResult immediately
    > > after the walreceiver initializes those process-local fields, then
    > > call WaitLSNWakeup() to wake any already-blocked waiters.
    > >
    > > This broadens the semantics of these fields. writtenUpto and
    > > flushedUpto  used to track only WAL written or flushed by the current
    > > walreceiver session — WAL received from the primary since the most
    > > recent connect. After this change, they are initialized to the replay
    > > position, so they also cover WAL that was already on disk before
    > > streaming began. This affects pg_stat_wal_receiver.written_lsn and
    > > flushed_lsn, which will now report the replay position immediately at
    > > walreceiver startup rather than 0 and the segment boundary
    > > respectively. I am still considering whether this semantic change is
    > > acceptable though it does shorten the runtime of the tap tests
    > > reported by Tom in my test. Another approach is to modify the logic of
    > > GetCurrentLSNForWaitType to cope with this special case and leave the
    > > publisher side alone without changing the semantics. But this seems to
    > > be more subtle.
    >
    > Patch 0001 looks OK for me.
    > Regarding patch 0002.  Changes made for GetCurrentLSNForWaitType()
    > looks reliable for me.  PerformWalRecovery() sets replayed positions
    > before starting recovery, and in turn before standby can accept
    > connections.  So, changes to WalReceiverMain() don't look necessary to
    > me.
    
    Yeah, GetCurrentLSNForWaitType seems to be the right place to place
    the fix. Please see the attached patch 2.
    
    I also noticed another relevent problem:
    
    During pure archive recovery (no walreceiver), a backend that issues
    'WAIT FOR LSN ... MODE 'standby_write' with a target ahead of the
    current replay position will sleep forever; the startup process
    replays past the target but only wakes 'STANDBY_REPLAY' waiters.
    
    This also affects mixed scenarios: the walreceiver may lag behind
    replay (e.g., archive restore has delivered WAL faster than
    streaming), so a 'standby_write' waiter could be waiting on WAL that
    replay has already consumed.
    
    I will write a patch to address this soon.
    
    --
    Best,
    Xuneng
    
  129. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-08T04:59:03Z

    > > Patch 0001 looks OK for me.
    > > Regarding patch 0002.  Changes made for GetCurrentLSNForWaitType()
    > > looks reliable for me.  PerformWalRecovery() sets replayed positions
    > > before starting recovery, and in turn before standby can accept
    > > connections.  So, changes to WalReceiverMain() don't look necessary to
    > > me.
    >
    > Yeah, GetCurrentLSNForWaitType seems to be the right place to place
    > the fix. Please see the attached patch 2.
    >
    > I also noticed another relevent problem:
    >
    > During pure archive recovery (no walreceiver), a backend that issues
    > 'WAIT FOR LSN ... MODE 'standby_write' with a target ahead of the
    > current replay position will sleep forever; the startup process
    > replays past the target but only wakes 'STANDBY_REPLAY' waiters.
    >
    > This also affects mixed scenarios: the walreceiver may lag behind
    > replay (e.g., archive restore has delivered WAL faster than
    > streaming), so a 'standby_write' waiter could be waiting on WAL that
    > replay has already consumed.
    >
    > I will write a patch to address this soon.
    >
    
    Here is the patch.
    
    -- 
    Best,
    Xuneng
    
  130. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-08T07:31:07Z

    On Wed, Apr 8, 2026 at 3:50 AM Andres Freund <andres@anarazel.de> wrote:
    > On 2026-04-08 02:30:44 +0300, Alexander Korotkov wrote:
    > > On Tue, Apr 7, 2026 at 6:55 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > I agree to change in WaitLSNWakeup(), memory barrier looks necessary there.
    > > Regarding GetCurrentLSNForWaitType(), I don't think barrier is needed
    > > here, nor think it makes things clearer.  I think it would be enough
    > > to comment that LWLock operations in addLSNWaiter()/deleteLSNWaiter()
    > > provide necessary barriers.
    >
    > That's sufficient for the first iteration, but what guarantees it once you do
    > WaitLatch()?  That's likely going to imply a barrier somewhere in the kernel,
    > but I don't think there's any actual guarantee.
    
    After WaitLatch(), ResetLatch() contains memory barrier.  And as I
    understand, this memory barrier includes guarantees for reading fresh
    values after WaitLatch() in typical latch usage scenario.  However, I
    see in WaitForLSN() we can exit from WaitLatch() on timeout, and then
    potentially exit from loop on timeout without rechecking for the most
    fresh LSN.  I suppose we can just do ResetLatch() unconditionally to
    fix that.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  131. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-09T15:21:24Z

    On Wed, Apr 8, 2026 at 7:59 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > Patch 0001 looks OK for me.
    > > > Regarding patch 0002.  Changes made for GetCurrentLSNForWaitType()
    > > > looks reliable for me.  PerformWalRecovery() sets replayed positions
    > > > before starting recovery, and in turn before standby can accept
    > > > connections.  So, changes to WalReceiverMain() don't look necessary to
    > > > me.
    > >
    > > Yeah, GetCurrentLSNForWaitType seems to be the right place to place
    > > the fix. Please see the attached patch 2.
    > >
    > > I also noticed another relevent problem:
    > >
    > > During pure archive recovery (no walreceiver), a backend that issues
    > > 'WAIT FOR LSN ... MODE 'standby_write' with a target ahead of the
    > > current replay position will sleep forever; the startup process
    > > replays past the target but only wakes 'STANDBY_REPLAY' waiters.
    > >
    > > This also affects mixed scenarios: the walreceiver may lag behind
    > > replay (e.g., archive restore has delivered WAL faster than
    > > streaming), so a 'standby_write' waiter could be waiting on WAL that
    > > replay has already consumed.
    > >
    > > I will write a patch to address this soon.
    > >
    >
    > Here is the patch.
    
    I've assembled all the pending patches together.
    0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    Andres off-list.
    0002 is basically [1] by Xuneng, but revised given we have a memory
    barrier in 0001, and my proposal to do ResetLatch() unconditionally
    similar to our other Latch-based loops.
    0003 and 0004 are [2] by Xuneng.
    0005 is [3] by Xuneng.
    
    I'm going to add them to Commitfest to run CI over them, and have a
    closer look over them tomorrow.
    
    
    Links.
    1. https://www.postgresql.org/message-id/CABPTF7Wjk_FbOghyr09Rzu6T2bh-L_KBMqHK%2BzhRXpssU0STyQ%40mail.gmail.com
    2. https://www.postgresql.org/message-id/CABPTF7X0iV%3DkGC4gjsTj4NvK_NNEJGM3YTc7Obxs5GOiYoMhEw%40mail.gmail.com
    3. https://www.postgresql.org/message-id/CABPTF7UBdEfyxATWntmCfoJrwB6iPrnhkXO7y_Avmqc2bOn27A%40mail.gmail.com
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  132. Re: Implement waiting for wal lsn replay: reloaded

    Andres Freund <andres@anarazel.de> — 2026-04-09T16:18:14Z

    On 2026-04-09 18:21:24 +0300, Alexander Korotkov wrote:
    > I've assembled all the pending patches together.
    > 0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    > Andres off-list.
    
    I'd make it a pg_atomic_read_membarrier_u64().
    
    
    > 0002 is basically [1] by Xuneng, but revised given we have a memory
    > barrier in 0001, and my proposal to do ResetLatch() unconditionally
    > similar to our other Latch-based loops.
    > 0003 and 0004 are [2] by Xuneng.
    > 0005 is [3] by Xuneng.
    > 
    > I'm going to add them to Commitfest to run CI over them, and have a
    > closer look over them tomorrow.
    
    Briefly skimming the patches, none makes the writes to writtenUpto use
    something bearing barrier semantics. I'd just make both of them a
    pg_atomic_write_membarrier_u64().
    
    
    I think this also needs a few more tests, e.g. for the scenario that
    29e7dbf5e4d fixed.  I think it'd also be good to do some testing for
    off-by-one dangers. E.g. making sure that we don't stop waiting too early /
    too late.  Another one that I think might deserve more testing is waits on the
    standby while crossing timeline boundaries.
    
    
    
    > From 0e5b4d1b9311a628a70218d89abf12308c9d782f Mon Sep 17 00:00:00 2001
    > From: Alexander Korotkov <akorotkov@postgresql.org>
    > Date: Thu, 9 Apr 2026 16:49:04 +0300
    > Subject: [PATCH v3 1/5] Add a memory barrier to GetWalRcvWriteRecPtr()
    > 
    > Add pg_memory_barrier() before reading writtenUpto so that callers see
    > up-to-date shared memory state.  This matches the barrier semantics that
    > GetWalRcvFlushRecPtr() and other LSN-position functions get implicitly from
    > their spinlock acquire/release, and in turn protects from bugs caused by
    > expectations of similar barrier guarantees from different LSN-position functions.
    > 
    > Reported-by: Andres Freund <andres@anarazel.de>
    > Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k%40w4bdf4z3wqoz
    > ---
    >  src/backend/replication/walreceiverfuncs.c | 12 ++++++++++--
    >  1 file changed, 10 insertions(+), 2 deletions(-)
    > 
    > diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
    > index bd5d47be964..0408ddff43e 100644
    > --- a/src/backend/replication/walreceiverfuncs.c
    > +++ b/src/backend/replication/walreceiverfuncs.c
    > @@ -363,14 +363,22 @@ GetWalRcvFlushRecPtr(XLogRecPtr *latestChunkStart, TimeLineID *receiveTLI)
    >  
    >  /*
    >   * Returns the last+1 byte position that walreceiver has written.
    > - * This returns a recently written value without taking a lock.
    > + *
    > + * Use a memory barrier to ensure that callers see up-to-date shared memory
    > + * state, matching the barrier semantics provided by the spinlock in
    > + * GetWalRcvFlushRecPtr() and other LSN-position functions.
    >   */
    >  XLogRecPtr
    >  GetWalRcvWriteRecPtr(void)
    >  {
    >  	WalRcvData *walrcv = WalRcv;
    > +	XLogRecPtr	recptr;
    > +
    > +	pg_memory_barrier();
    >  
    > -	return pg_atomic_read_u64(&walrcv->writtenUpto);
    > +	recptr = pg_atomic_read_u64(&walrcv->writtenUpto);
    > +
    > +	return recptr;
    >  }
    >  
    >  /*
    > -- 
    > 2.39.5 (Apple Git-154)
    > 
    
    > Subject: [PATCH v3 2/5] Fix memory ordering in WAIT FOR LSN wakeup mechanism
    
    > +	/*
    > +	 * Ensure the waker's prior position store (writtenUpto, flushedUpto,
    > +	 * lastReplayedEndRecPtr, etc.) is globally visible before we read
    > +	 * minWaitedLSN.  Without this barrier, the CPU could load minWaitedLSN
    > +	 * before draining the position store, leaving the position invisible to a
    > +	 * concurrently-registering waiter.
    > +	 *
    > +	 * This is the waker side of a Dekker-style handshake; pairs with
    > +	 * pg_memory_barrier() in GetCurrentLSNForWaitType() on the waiter side.
    > +	 */
    > +	pg_memory_barrier();
    > +
    >  	/*
    >  	 * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    >  	 * "wake all waiters" (e.g., during promotion when recovery ends).
    
    I'd also make this a pg_atomic_read_membarrier_u64() and the write a
    pg_atomic_write_membarrier_u64().  It's a lot easier to reason about this
    stuff if you make sure that the individual reads / write pair and have
    ordering implied.
    
    
    
    Greetings,
    
    Andres Freund
    
    
    
    
  133. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-10T03:59:22Z

    On Fri, Apr 10, 2026 at 12:18 AM Andres Freund <andres@anarazel.de> wrote:
    >
    > On 2026-04-09 18:21:24 +0300, Alexander Korotkov wrote:
    > > I've assembled all the pending patches together.
    > > 0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    > > Andres off-list.
    >
    > I'd make it a pg_atomic_read_membarrier_u64().
    >
    >
    > > 0002 is basically [1] by Xuneng, but revised given we have a memory
    > > barrier in 0001, and my proposal to do ResetLatch() unconditionally
    > > similar to our other Latch-based loops.
    > > 0003 and 0004 are [2] by Xuneng.
    > > 0005 is [3] by Xuneng.
    > >
    > > I'm going to add them to Commitfest to run CI over them, and have a
    > > closer look over them tomorrow.
    >
    > Briefly skimming the patches, none makes the writes to writtenUpto use
    > something bearing barrier semantics. I'd just make both of them a
    > pg_atomic_write_membarrier_u64().
    >
    
    Makes sense to me. Done.
    
    > I think this also needs a few more tests, e.g. for the scenario that
    > 29e7dbf5e4d fixed.  I think it'd also be good to do some testing for
    > off-by-one dangers. E.g. making sure that we don't stop waiting too early /
    > too late.  Another one that I think might deserve more testing is waits on the
    > standby while crossing timeline boundaries.
    >
    
    I'll prepare a new patch for more test harnessing.
    
    >
    > > From 0e5b4d1b9311a628a70218d89abf12308c9d782f Mon Sep 17 00:00:00 2001
    > > From: Alexander Korotkov <akorotkov@postgresql.org>
    > > Date: Thu, 9 Apr 2026 16:49:04 +0300
    > > Subject: [PATCH v3 1/5] Add a memory barrier to GetWalRcvWriteRecPtr()
    > >
    > > Add pg_memory_barrier() before reading writtenUpto so that callers see
    > > up-to-date shared memory state.  This matches the barrier semantics that
    > > GetWalRcvFlushRecPtr() and other LSN-position functions get implicitly from
    > > their spinlock acquire/release, and in turn protects from bugs caused by
    > > expectations of similar barrier guarantees from different LSN-position functions.
    > >
    > > Reported-by: Andres Freund <andres@anarazel.de>
    > > Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k%40w4bdf4z3wqoz
    > > ---
    > >  src/backend/replication/walreceiverfuncs.c | 12 ++++++++++--
    > >  1 file changed, 10 insertions(+), 2 deletions(-)
    > >
    > > diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
    > > index bd5d47be964..0408ddff43e 100644
    > > --- a/src/backend/replication/walreceiverfuncs.c
    > > +++ b/src/backend/replication/walreceiverfuncs.c
    > > @@ -363,14 +363,22 @@ GetWalRcvFlushRecPtr(XLogRecPtr *latestChunkStart, TimeLineID *receiveTLI)
    > >
    > >  /*
    > >   * Returns the last+1 byte position that walreceiver has written.
    > > - * This returns a recently written value without taking a lock.
    > > + *
    > > + * Use a memory barrier to ensure that callers see up-to-date shared memory
    > > + * state, matching the barrier semantics provided by the spinlock in
    > > + * GetWalRcvFlushRecPtr() and other LSN-position functions.
    > >   */
    > >  XLogRecPtr
    > >  GetWalRcvWriteRecPtr(void)
    > >  {
    > >       WalRcvData *walrcv = WalRcv;
    > > +     XLogRecPtr      recptr;
    > > +
    > > +     pg_memory_barrier();
    > >
    > > -     return pg_atomic_read_u64(&walrcv->writtenUpto);
    > > +     recptr = pg_atomic_read_u64(&walrcv->writtenUpto);
    > > +
    > > +     return recptr;
    > >  }
    > >
    > >  /*
    > > --
    > > 2.39.5 (Apple Git-154)
    > >
    >
    > > Subject: [PATCH v3 2/5] Fix memory ordering in WAIT FOR LSN wakeup mechanism
    >
    > > +     /*
    > > +      * Ensure the waker's prior position store (writtenUpto, flushedUpto,
    > > +      * lastReplayedEndRecPtr, etc.) is globally visible before we read
    > > +      * minWaitedLSN.  Without this barrier, the CPU could load minWaitedLSN
    > > +      * before draining the position store, leaving the position invisible to a
    > > +      * concurrently-registering waiter.
    > > +      *
    > > +      * This is the waker side of a Dekker-style handshake; pairs with
    > > +      * pg_memory_barrier() in GetCurrentLSNForWaitType() on the waiter side.
    > > +      */
    > > +     pg_memory_barrier();
    > > +
    > >       /*
    > >        * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > >        * "wake all waiters" (e.g., during promotion when recovery ends).
    >
    > I'd also make this a pg_atomic_read_membarrier_u64() and the write a
    > pg_atomic_write_membarrier_u64().  It's a lot easier to reason about this
    > stuff if you make sure that the individual reads / write pair and have
    > ordering implied.
    >
    
    It does look more selft-contained to me.
    
    Here is the updated patch set based on Alexander’s earlier version.
    
    -- 
    Best,
    Xuneng
    
  134. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-10T06:13:05Z

    On Fri, Apr 10, 2026 at 11:59 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Fri, Apr 10, 2026 at 12:18 AM Andres Freund <andres@anarazel.de> wrote:
    > >
    > > On 2026-04-09 18:21:24 +0300, Alexander Korotkov wrote:
    > > > I've assembled all the pending patches together.
    > > > 0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    > > > Andres off-list.
    > >
    > > I'd make it a pg_atomic_read_membarrier_u64().
    > >
    > >
    > > > 0002 is basically [1] by Xuneng, but revised given we have a memory
    > > > barrier in 0001, and my proposal to do ResetLatch() unconditionally
    > > > similar to our other Latch-based loops.
    > > > 0003 and 0004 are [2] by Xuneng.
    > > > 0005 is [3] by Xuneng.
    > > >
    > > > I'm going to add them to Commitfest to run CI over them, and have a
    > > > closer look over them tomorrow.
    > >
    > > Briefly skimming the patches, none makes the writes to writtenUpto use
    > > something bearing barrier semantics. I'd just make both of them a
    > > pg_atomic_write_membarrier_u64().
    > >
    >
    > Makes sense to me. Done.
    >
    > > I think this also needs a few more tests, e.g. for the scenario that
    > > 29e7dbf5e4d fixed.  I think it'd also be good to do some testing for
    > > off-by-one dangers. E.g. making sure that we don't stop waiting too early /
    > > too late.  Another one that I think might deserve more testing is waits on the
    > > standby while crossing timeline boundaries.
    > >
    >
    > I'll prepare a new patch for more test harnessing.
    >
    > >
    > > > From 0e5b4d1b9311a628a70218d89abf12308c9d782f Mon Sep 17 00:00:00 2001
    > > > From: Alexander Korotkov <akorotkov@postgresql.org>
    > > > Date: Thu, 9 Apr 2026 16:49:04 +0300
    > > > Subject: [PATCH v3 1/5] Add a memory barrier to GetWalRcvWriteRecPtr()
    > > >
    > > > Add pg_memory_barrier() before reading writtenUpto so that callers see
    > > > up-to-date shared memory state.  This matches the barrier semantics that
    > > > GetWalRcvFlushRecPtr() and other LSN-position functions get implicitly from
    > > > their spinlock acquire/release, and in turn protects from bugs caused by
    > > > expectations of similar barrier guarantees from different LSN-position functions.
    > > >
    > > > Reported-by: Andres Freund <andres@anarazel.de>
    > > > Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k%40w4bdf4z3wqoz
    > > > ---
    > > >  src/backend/replication/walreceiverfuncs.c | 12 ++++++++++--
    > > >  1 file changed, 10 insertions(+), 2 deletions(-)
    > > >
    > > > diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
    > > > index bd5d47be964..0408ddff43e 100644
    > > > --- a/src/backend/replication/walreceiverfuncs.c
    > > > +++ b/src/backend/replication/walreceiverfuncs.c
    > > > @@ -363,14 +363,22 @@ GetWalRcvFlushRecPtr(XLogRecPtr *latestChunkStart, TimeLineID *receiveTLI)
    > > >
    > > >  /*
    > > >   * Returns the last+1 byte position that walreceiver has written.
    > > > - * This returns a recently written value without taking a lock.
    > > > + *
    > > > + * Use a memory barrier to ensure that callers see up-to-date shared memory
    > > > + * state, matching the barrier semantics provided by the spinlock in
    > > > + * GetWalRcvFlushRecPtr() and other LSN-position functions.
    > > >   */
    > > >  XLogRecPtr
    > > >  GetWalRcvWriteRecPtr(void)
    > > >  {
    > > >       WalRcvData *walrcv = WalRcv;
    > > > +     XLogRecPtr      recptr;
    > > > +
    > > > +     pg_memory_barrier();
    > > >
    > > > -     return pg_atomic_read_u64(&walrcv->writtenUpto);
    > > > +     recptr = pg_atomic_read_u64(&walrcv->writtenUpto);
    > > > +
    > > > +     return recptr;
    > > >  }
    > > >
    > > >  /*
    > > > --
    > > > 2.39.5 (Apple Git-154)
    > > >
    > >
    > > > Subject: [PATCH v3 2/5] Fix memory ordering in WAIT FOR LSN wakeup mechanism
    > >
    > > > +     /*
    > > > +      * Ensure the waker's prior position store (writtenUpto, flushedUpto,
    > > > +      * lastReplayedEndRecPtr, etc.) is globally visible before we read
    > > > +      * minWaitedLSN.  Without this barrier, the CPU could load minWaitedLSN
    > > > +      * before draining the position store, leaving the position invisible to a
    > > > +      * concurrently-registering waiter.
    > > > +      *
    > > > +      * This is the waker side of a Dekker-style handshake; pairs with
    > > > +      * pg_memory_barrier() in GetCurrentLSNForWaitType() on the waiter side.
    > > > +      */
    > > > +     pg_memory_barrier();
    > > > +
    > > >       /*
    > > >        * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > > >        * "wake all waiters" (e.g., during promotion when recovery ends).
    > >
    > > I'd also make this a pg_atomic_read_membarrier_u64() and the write a
    > > pg_atomic_write_membarrier_u64().  It's a lot easier to reason about this
    > > stuff if you make sure that the individual reads / write pair and have
    > > ordering implied.
    > >
    >
    > It does look more selft-contained to me.
    >
    > Here is the updated patch set based on Alexander’s earlier version.
    
    The last para of commit message in patch 2 is inaccurate after the
    getter-side barrier changes, "could read a stale position and wrongly
    timeout" is no longer the primary rationale.
    
    -- 
    Best,
    Xuneng
    
  135. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-15T08:30:17Z

    On Thu, Apr 9, 2026 at 11:21 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Wed, Apr 8, 2026 at 7:59 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > > > Patch 0001 looks OK for me.
    > > > > Regarding patch 0002.  Changes made for GetCurrentLSNForWaitType()
    > > > > looks reliable for me.  PerformWalRecovery() sets replayed positions
    > > > > before starting recovery, and in turn before standby can accept
    > > > > connections.  So, changes to WalReceiverMain() don't look necessary to
    > > > > me.
    > > >
    > > > Yeah, GetCurrentLSNForWaitType seems to be the right place to place
    > > > the fix. Please see the attached patch 2.
    > > >
    > > > I also noticed another relevent problem:
    > > >
    > > > During pure archive recovery (no walreceiver), a backend that issues
    > > > 'WAIT FOR LSN ... MODE 'standby_write' with a target ahead of the
    > > > current replay position will sleep forever; the startup process
    > > > replays past the target but only wakes 'STANDBY_REPLAY' waiters.
    > > >
    > > > This also affects mixed scenarios: the walreceiver may lag behind
    > > > replay (e.g., archive restore has delivered WAL faster than
    > > > streaming), so a 'standby_write' waiter could be waiting on WAL that
    > > > replay has already consumed.
    > > >
    > > > I will write a patch to address this soon.
    > > >
    > >
    > > Here is the patch.
    >
    > I've assembled all the pending patches together.
    > 0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    > Andres off-list.
    > 0002 is basically [1] by Xuneng, but revised given we have a memory
    > barrier in 0001, and my proposal to do ResetLatch() unconditionally
    > similar to our other Latch-based loops.
    > 0003 and 0004 are [2] by Xuneng.
    > 0005 is [3] by Xuneng.
    >
    > I'm going to add them to Commitfest to run CI over them, and have a
    > closer look over them tomorrow.
    >
    >
    > Links.
    > 1. https://www.postgresql.org/message-id/CABPTF7Wjk_FbOghyr09Rzu6T2bh-L_KBMqHK%2BzhRXpssU0STyQ%40mail.gmail.com
    > 2. https://www.postgresql.org/message-id/CABPTF7X0iV%3DkGC4gjsTj4NvK_NNEJGM3YTc7Obxs5GOiYoMhEw%40mail.gmail.com
    > 3. https://www.postgresql.org/message-id/CABPTF7UBdEfyxATWntmCfoJrwB6iPrnhkXO7y_Avmqc2bOn27A%40mail.gmail.com
    
    I've added the patches to Commitfest.
    
    [1] https://commitfest.postgresql.org/patch/6678/
    
    -- 
    Best,
    Xuneng
    
    
    
    
  136. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-20T18:45:51Z

    On Fri, Apr 10, 2026 at 9:13 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Fri, Apr 10, 2026 at 11:59 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Fri, Apr 10, 2026 at 12:18 AM Andres Freund <andres@anarazel.de> wrote:
    > > >
    > > > On 2026-04-09 18:21:24 +0300, Alexander Korotkov wrote:
    > > > > I've assembled all the pending patches together.
    > > > > 0001 adds memory barrier to GetWalRcvWriteRecPtr() as suggested by
    > > > > Andres off-list.
    > > >
    > > > I'd make it a pg_atomic_read_membarrier_u64().
    > > >
    > > >
    > > > > 0002 is basically [1] by Xuneng, but revised given we have a memory
    > > > > barrier in 0001, and my proposal to do ResetLatch() unconditionally
    > > > > similar to our other Latch-based loops.
    > > > > 0003 and 0004 are [2] by Xuneng.
    > > > > 0005 is [3] by Xuneng.
    > > > >
    > > > > I'm going to add them to Commitfest to run CI over them, and have a
    > > > > closer look over them tomorrow.
    > > >
    > > > Briefly skimming the patches, none makes the writes to writtenUpto use
    > > > something bearing barrier semantics. I'd just make both of them a
    > > > pg_atomic_write_membarrier_u64().
    > > >
    > >
    > > Makes sense to me. Done.
    > >
    > > > I think this also needs a few more tests, e.g. for the scenario that
    > > > 29e7dbf5e4d fixed.  I think it'd also be good to do some testing for
    > > > off-by-one dangers. E.g. making sure that we don't stop waiting too early /
    > > > too late.  Another one that I think might deserve more testing is waits on the
    > > > standby while crossing timeline boundaries.
    > > >
    > >
    > > I'll prepare a new patch for more test harnessing.
    > >
    > > >
    > > > > From 0e5b4d1b9311a628a70218d89abf12308c9d782f Mon Sep 17 00:00:00 2001
    > > > > From: Alexander Korotkov <akorotkov@postgresql.org>
    > > > > Date: Thu, 9 Apr 2026 16:49:04 +0300
    > > > > Subject: [PATCH v3 1/5] Add a memory barrier to GetWalRcvWriteRecPtr()
    > > > >
    > > > > Add pg_memory_barrier() before reading writtenUpto so that callers see
    > > > > up-to-date shared memory state.  This matches the barrier semantics that
    > > > > GetWalRcvFlushRecPtr() and other LSN-position functions get implicitly from
    > > > > their spinlock acquire/release, and in turn protects from bugs caused by
    > > > > expectations of similar barrier guarantees from different LSN-position functions.
    > > > >
    > > > > Reported-by: Andres Freund <andres@anarazel.de>
    > > > > Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k%40w4bdf4z3wqoz
    > > > > ---
    > > > >  src/backend/replication/walreceiverfuncs.c | 12 ++++++++++--
    > > > >  1 file changed, 10 insertions(+), 2 deletions(-)
    > > > >
    > > > > diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
    > > > > index bd5d47be964..0408ddff43e 100644
    > > > > --- a/src/backend/replication/walreceiverfuncs.c
    > > > > +++ b/src/backend/replication/walreceiverfuncs.c
    > > > > @@ -363,14 +363,22 @@ GetWalRcvFlushRecPtr(XLogRecPtr *latestChunkStart, TimeLineID *receiveTLI)
    > > > >
    > > > >  /*
    > > > >   * Returns the last+1 byte position that walreceiver has written.
    > > > > - * This returns a recently written value without taking a lock.
    > > > > + *
    > > > > + * Use a memory barrier to ensure that callers see up-to-date shared memory
    > > > > + * state, matching the barrier semantics provided by the spinlock in
    > > > > + * GetWalRcvFlushRecPtr() and other LSN-position functions.
    > > > >   */
    > > > >  XLogRecPtr
    > > > >  GetWalRcvWriteRecPtr(void)
    > > > >  {
    > > > >       WalRcvData *walrcv = WalRcv;
    > > > > +     XLogRecPtr      recptr;
    > > > > +
    > > > > +     pg_memory_barrier();
    > > > >
    > > > > -     return pg_atomic_read_u64(&walrcv->writtenUpto);
    > > > > +     recptr = pg_atomic_read_u64(&walrcv->writtenUpto);
    > > > > +
    > > > > +     return recptr;
    > > > >  }
    > > > >
    > > > >  /*
    > > > > --
    > > > > 2.39.5 (Apple Git-154)
    > > > >
    > > >
    > > > > Subject: [PATCH v3 2/5] Fix memory ordering in WAIT FOR LSN wakeup mechanism
    > > >
    > > > > +     /*
    > > > > +      * Ensure the waker's prior position store (writtenUpto, flushedUpto,
    > > > > +      * lastReplayedEndRecPtr, etc.) is globally visible before we read
    > > > > +      * minWaitedLSN.  Without this barrier, the CPU could load minWaitedLSN
    > > > > +      * before draining the position store, leaving the position invisible to a
    > > > > +      * concurrently-registering waiter.
    > > > > +      *
    > > > > +      * This is the waker side of a Dekker-style handshake; pairs with
    > > > > +      * pg_memory_barrier() in GetCurrentLSNForWaitType() on the waiter side.
    > > > > +      */
    > > > > +     pg_memory_barrier();
    > > > > +
    > > > >       /*
    > > > >        * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
    > > > >        * "wake all waiters" (e.g., during promotion when recovery ends).
    > > >
    > > > I'd also make this a pg_atomic_read_membarrier_u64() and the write a
    > > > pg_atomic_write_membarrier_u64().  It's a lot easier to reason about this
    > > > stuff if you make sure that the individual reads / write pair and have
    > > > ordering implied.
    > > >
    > >
    > > It does look more selft-contained to me.
    > >
    > > Here is the updated patch set based on Alexander’s earlier version.
    >
    > The last para of commit message in patch 2 is inaccurate after the
    > getter-side barrier changes, "could read a stale position and wrongly
    > timeout" is no longer the primary rationale.
    
    The updated patchset is attached.  It includes improved coverage as
    suggested by Andres upthread.  And documentation that WAIT FOR LSN is
    timeline-blind (per off-list discussion with Xuneng).
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  137. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-04-21T04:03:30Z

    On Tue, Apr 21, 2026 at 2:46 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    
    > The updated patchset is attached.  It includes improved coverage as
    > suggested by Andres upthread.  And documentation that WAIT FOR LSN is
    > timeline-blind (per off-list discussion with Xuneng).
    
    I revised the test patch 6 to make the new cases check the intended
    WAIT FOR behavior more directly, and to avoid cases where the test
    could pass for the wrong reason.
    
    The fresh walreceiver restart test now distinguishes what we can
    observe from what is only covered indirectly.
    'pg_last_wal_receive_lsn()' reports 'flushedUpto', not 'writtenUpto',
    so the test now describes that state accurately and covers
    'writtenUpto' through the 'standby_write' result. This seems
    appropriate to me since the two positions are seeded in the places and
    conditions. Test for flush lsn should also help verify write lsn.
    
    The fencepost tests were split by the actual frontier being tested.
    'standby_replay' uses 'pg_last_wal_replay_lsn()', while
    'standby_flush' uses 'pg_last_wal_receive_lsn()'. This avoids treating
    a replay-derived LSN as if it were also the exact write/flush
    boundary. I left 'standby_write' out of the exact fencepost helper
    because its frontier is not SQL-visible once walreceiver is stopped.
    The async wakeup case now starts the waiter while replay is still
    paused, so it must actually sleep before replay and walreceiver are
    allowed to advance.
    
    The cascading timeline-switch test now checks the 'WAIT FOR ...
    NO_THROW' status from background psql stdout. The previous log-marker
    pattern could pass after unexpected returned status, includingn
    'timeout', because the following statement would still run. The
    'received_tli > 1' check remains, but only as confirmation that the
    downstream followed the new timeline; the 'success' status proves the
    wait completed as intended.
    
    Please check it.
    
    --
    Best,
    Xuneng
    
  138. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-04-28T21:01:32Z

    On Tue, Apr 21, 2026 at 7:03 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Tue, Apr 21, 2026 at 2:46 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > > The updated patchset is attached.  It includes improved coverage as
    > > suggested by Andres upthread.  And documentation that WAIT FOR LSN is
    > > timeline-blind (per off-list discussion with Xuneng).
    >
    > I revised the test patch 6 to make the new cases check the intended
    > WAIT FOR behavior more directly, and to avoid cases where the test
    > could pass for the wrong reason.
    >
    > The fresh walreceiver restart test now distinguishes what we can
    > observe from what is only covered indirectly.
    > 'pg_last_wal_receive_lsn()' reports 'flushedUpto', not 'writtenUpto',
    > so the test now describes that state accurately and covers
    > 'writtenUpto' through the 'standby_write' result. This seems
    > appropriate to me since the two positions are seeded in the places and
    > conditions. Test for flush lsn should also help verify write lsn.
    >
    > The fencepost tests were split by the actual frontier being tested.
    > 'standby_replay' uses 'pg_last_wal_replay_lsn()', while
    > 'standby_flush' uses 'pg_last_wal_receive_lsn()'. This avoids treating
    > a replay-derived LSN as if it were also the exact write/flush
    > boundary. I left 'standby_write' out of the exact fencepost helper
    > because its frontier is not SQL-visible once walreceiver is stopped.
    > The async wakeup case now starts the waiter while replay is still
    > paused, so it must actually sleep before replay and walreceiver are
    > allowed to advance.
    >
    > The cascading timeline-switch test now checks the 'WAIT FOR ...
    > NO_THROW' status from background psql stdout. The previous log-marker
    > pattern could pass after unexpected returned status, includingn
    > 'timeout', because the following statement would still run. The
    > 'received_tli > 1' check remains, but only as confirmation that the
    > downstream followed the new timeline; the 'success' status proves the
    > wait completed as intended.
    >
    > Please check it.
    
    LGTM, I've added some comments for new functions in 0006.  I propose
    to push this patchset.  Probably something is still missing and we
    will have to go back to this.  But it seems to make a lot of aspects
    much better.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  139. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-01T02:44:00Z

    Hi Alexander,
    
    On Wed, Apr 29, 2026 at 5:01 AM Alexander Korotkov <aekorotkov@gmail.com>
    wrote:
    
    > On Tue, Apr 21, 2026 at 7:03 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Tue, Apr 21, 2026 at 2:46 AM Alexander Korotkov <aekorotkov@gmail.com>
    > wrote:
    > >
    > > > The updated patchset is attached.  It includes improved coverage as
    > > > suggested by Andres upthread.  And documentation that WAIT FOR LSN is
    > > > timeline-blind (per off-list discussion with Xuneng).
    > >
    > > I revised the test patch 6 to make the new cases check the intended
    > > WAIT FOR behavior more directly, and to avoid cases where the test
    > > could pass for the wrong reason.
    > >
    > > The fresh walreceiver restart test now distinguishes what we can
    > > observe from what is only covered indirectly.
    > > 'pg_last_wal_receive_lsn()' reports 'flushedUpto', not 'writtenUpto',
    > > so the test now describes that state accurately and covers
    > > 'writtenUpto' through the 'standby_write' result. This seems
    > > appropriate to me since the two positions are seeded in the places and
    > > conditions. Test for flush lsn should also help verify write lsn.
    > >
    > > The fencepost tests were split by the actual frontier being tested.
    > > 'standby_replay' uses 'pg_last_wal_replay_lsn()', while
    > > 'standby_flush' uses 'pg_last_wal_receive_lsn()'. This avoids treating
    > > a replay-derived LSN as if it were also the exact write/flush
    > > boundary. I left 'standby_write' out of the exact fencepost helper
    > > because its frontier is not SQL-visible once walreceiver is stopped.
    > > The async wakeup case now starts the waiter while replay is still
    > > paused, so it must actually sleep before replay and walreceiver are
    > > allowed to advance.
    > >
    > > The cascading timeline-switch test now checks the 'WAIT FOR ...
    > > NO_THROW' status from background psql stdout. The previous log-marker
    > > pattern could pass after unexpected returned status, includingn
    > > 'timeout', because the following statement would still run. The
    > > 'received_tli > 1' check remains, but only as confirmation that the
    > > downstream followed the new timeline; the 'success' status proves the
    > > wait completed as intended.
    > >
    > > Please check it.
    >
    > LGTM, I've added some comments for new functions in 0006.  I propose
    > to push this patchset.  Probably something is still missing and we
    > will have to go back to this.  But it seems to make a lot of aspects
    > much better.
    >
    
    I reviewed the patchset and found a potential issue in the test for patch
    5, similar to the log-checking problem in the cascading timeline-switch
    test. I've applied a minor fix to address it. Other parts LGTM.
    
    Best,
    Xuneng
    
  140. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Lakhin <exclusion@gmail.com> — 2026-05-19T20:00:00Z

    Hello Alexander and Xuneng,
    
    06.04.2026 22:49, Alexander Korotkov wrote:
    > Thank you, I've pushed your version of patchset.  I made two minor
    > corrections for patch #2: mention default mode value in the header
    > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    
    I discovered a new test failure, that is apparently caused by new
    wait_for_catchup() implementation [1]:
    [06:20:23.110](1.069s) not ok 8 - check that the slot state changes to "extended"
    [06:20:23.110](0.001s) #   Failed test 'check that the slot state changes to "extended"'
    #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 140.
    [06:20:23.111](0.000s) #          got: 'unreserved'
    #     expected: 'extended'
    [06:20:23.231](0.120s) not ok 9 - check that the slot state changes to "unreserved"
    [06:20:23.231](0.000s) #   Failed test 'check that the slot state changes to "unreserved"'
    #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 152.
    [06:20:23.231](0.000s) #          got: 'lost|'
    #     expected: 'unreserved|t'
    
    I've managed to reproduce such failures with:
    diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
    index 07eac07b9ce..493ce92674e 100644
    --- a/src/backend/replication/walreceiver.c
    +++ b/src/backend/replication/walreceiver.c
    @@ -1143,2 +1143,3 @@ XLogWalRcvSendReply(bool force, bool requestReply, bool checkApply)
    
    +pg_usleep(10000);
          /* Get current timestamp. */
    diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
    index 04aa770d981..19cda3a6b51 100644
    --- a/src/backend/replication/walsender.c
    +++ b/src/backend/replication/walsender.c
    @@ -2521,2 +2521,3 @@ ProcessStandbyReplyMessage(void)
    
    +pg_usleep(100000);
          /* the caller already consumed the msgtype byte */
    
    Concretely, a loop:
    for i in {1..100}; do echo "ITERATION $i"; PROVE_TESTS="t/019*" make -s check -C src/test/recovery/ || break; done
    failed for me on iterations 2, 1, 7:
    ITERATION 7
    # +++ tap check in src/test/recovery +++
    t/019_replslot_limit.pl .. 8/?
    #   Failed test 'check that the slot state changes to "extended"'
    #   at t/019_replslot_limit.pl line 140.
    #          got: 'unreserved'
    #     expected: 'extended'
    t/019_replslot_limit.pl .. 26/? # Looks like you failed 1 test of 26.
    t/019_replslot_limit.pl .. Dubious, test returned 1 (wstat 256, 0x100)
    Failed 1/26 subtests
    
    With "WAIT FOR LSN" in wait_for_catchup() disabled, 100 iterations
    passed.
    
    Having extra logging added, I could see the key difference.
    Failed run:
    2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 
    0/016C0000, targetSeg: 22, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 22
    2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM 
    pg_replication_slots WHERE slot_name = 'rep1'
    vs
    Successful run:
    2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 
    0/01700000, targetSeg: 23, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 23
    2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM 
    pg_replication_slots WHERE slot_name = 'rep1'
    
    That is, with WAIT FOR LSN, primary in this test may advance
    slot->data.restart_lsn to the expected position after wait_for_catchup()
    returns.
    
    [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=goldfish&dt=2026-05-13%2006%3A15%3A03
    
    Best regards,
    Alexander
  141. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-20T03:30:10Z

    Hi,
    
    On Tue, May 19, 2026 at 1:00 PM Alexander Lakhin <exclusion@gmail.com> wrote:
    >
    > Hello Alexander and Xuneng,
    >
    > 06.04.2026 22:49, Alexander Korotkov wrote:
    >
    > Thank you, I've pushed your version of patchset.  I made two minor
    > corrections for patch #2: mention default mode value in the header
    > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    >
    >
    > I discovered a new test failure, that is apparently caused by new
    > wait_for_catchup() implementation [1]:
    > [06:20:23.110](1.069s) not ok 8 - check that the slot state changes to "extended"
    > [06:20:23.110](0.001s) #   Failed test 'check that the slot state changes to "extended"'
    > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 140.
    > [06:20:23.111](0.000s) #          got: 'unreserved'
    > #     expected: 'extended'
    > [06:20:23.231](0.120s) not ok 9 - check that the slot state changes to "unreserved"
    > [06:20:23.231](0.000s) #   Failed test 'check that the slot state changes to "unreserved"'
    > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 152.
    > [06:20:23.231](0.000s) #          got: 'lost|'
    > #     expected: 'unreserved|t'
    >
    > I've managed to reproduce such failures with:
    > diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
    > index 07eac07b9ce..493ce92674e 100644
    > --- a/src/backend/replication/walreceiver.c
    > +++ b/src/backend/replication/walreceiver.c
    > @@ -1143,2 +1143,3 @@ XLogWalRcvSendReply(bool force, bool requestReply, bool checkApply)
    >
    > +pg_usleep(10000);
    >      /* Get current timestamp. */
    > diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
    > index 04aa770d981..19cda3a6b51 100644
    > --- a/src/backend/replication/walsender.c
    > +++ b/src/backend/replication/walsender.c
    > @@ -2521,2 +2521,3 @@ ProcessStandbyReplyMessage(void)
    >
    > +pg_usleep(100000);
    >      /* the caller already consumed the msgtype byte */
    >
    > Concretely, a loop:
    > for i in {1..100}; do echo "ITERATION $i"; PROVE_TESTS="t/019*" make -s check -C src/test/recovery/ || break; done
    > failed for me on iterations 2, 1, 7:
    > ITERATION 7
    > # +++ tap check in src/test/recovery +++
    > t/019_replslot_limit.pl .. 8/?
    > #   Failed test 'check that the slot state changes to "extended"'
    > #   at t/019_replslot_limit.pl line 140.
    > #          got: 'unreserved'
    > #     expected: 'extended'
    > t/019_replslot_limit.pl .. 26/? # Looks like you failed 1 test of 26.
    > t/019_replslot_limit.pl .. Dubious, test returned 1 (wstat 256, 0x100)
    > Failed 1/26 subtests
    >
    > With "WAIT FOR LSN" in wait_for_catchup() disabled, 100 iterations
    > passed.
    >
    > Having extra logging added, I could see the key difference.
    > Failed run:
    > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/016C0000, targetSeg: 22, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 22
    > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > vs
    > Successful run:
    > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/01700000, targetSeg: 23, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 23
    > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    >
    > That is, with WAIT FOR LSN, primary in this test may advance
    > slot->data.restart_lsn to the expected position after wait_for_catchup()
    > returns.
    >
    > [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=goldfish&dt=2026-05-13%2006%3A15%3A03
    
    Thanks for reporting this issue.
    
    I think this is related to the semantic change made earlier:
    wait_for_catchup() now returns once the standby itself has reached the
    target LSN, rather than waiting until the primary observes that
    position via pg_stat_replication.
    
    As a result, the primary may not yet have processed the standby
    feedback needed to advance the slot's restart_lsn when
    wait_for_catchup() returns.
    
    Actually, I was aware of this semantic change and previously thought
    it might be harmless. But this failure appears to disprove that. I'll
    prepare a patch to fix this shortly.
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
    
    
    
  142. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-20T05:18:07Z

    On Tue, May 19, 2026 at 8:30 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, May 19, 2026 at 1:00 PM Alexander Lakhin <exclusion@gmail.com> wrote:
    > >
    > > Hello Alexander and Xuneng,
    > >
    > > 06.04.2026 22:49, Alexander Korotkov wrote:
    > >
    > > Thank you, I've pushed your version of patchset.  I made two minor
    > > corrections for patch #2: mention default mode value in the header
    > > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    > >
    > >
    > > I discovered a new test failure, that is apparently caused by new
    > > wait_for_catchup() implementation [1]:
    > > [06:20:23.110](1.069s) not ok 8 - check that the slot state changes to "extended"
    > > [06:20:23.110](0.001s) #   Failed test 'check that the slot state changes to "extended"'
    > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 140.
    > > [06:20:23.111](0.000s) #          got: 'unreserved'
    > > #     expected: 'extended'
    > > [06:20:23.231](0.120s) not ok 9 - check that the slot state changes to "unreserved"
    > > [06:20:23.231](0.000s) #   Failed test 'check that the slot state changes to "unreserved"'
    > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 152.
    > > [06:20:23.231](0.000s) #          got: 'lost|'
    > > #     expected: 'unreserved|t'
    > >
    > > I've managed to reproduce such failures with:
    > > diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
    > > index 07eac07b9ce..493ce92674e 100644
    > > --- a/src/backend/replication/walreceiver.c
    > > +++ b/src/backend/replication/walreceiver.c
    > > @@ -1143,2 +1143,3 @@ XLogWalRcvSendReply(bool force, bool requestReply, bool checkApply)
    > >
    > > +pg_usleep(10000);
    > >      /* Get current timestamp. */
    > > diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
    > > index 04aa770d981..19cda3a6b51 100644
    > > --- a/src/backend/replication/walsender.c
    > > +++ b/src/backend/replication/walsender.c
    > > @@ -2521,2 +2521,3 @@ ProcessStandbyReplyMessage(void)
    > >
    > > +pg_usleep(100000);
    > >      /* the caller already consumed the msgtype byte */
    > >
    > > Concretely, a loop:
    > > for i in {1..100}; do echo "ITERATION $i"; PROVE_TESTS="t/019*" make -s check -C src/test/recovery/ || break; done
    > > failed for me on iterations 2, 1, 7:
    > > ITERATION 7
    > > # +++ tap check in src/test/recovery +++
    > > t/019_replslot_limit.pl .. 8/?
    > > #   Failed test 'check that the slot state changes to "extended"'
    > > #   at t/019_replslot_limit.pl line 140.
    > > #          got: 'unreserved'
    > > #     expected: 'extended'
    > > t/019_replslot_limit.pl .. 26/? # Looks like you failed 1 test of 26.
    > > t/019_replslot_limit.pl .. Dubious, test returned 1 (wstat 256, 0x100)
    > > Failed 1/26 subtests
    > >
    > > With "WAIT FOR LSN" in wait_for_catchup() disabled, 100 iterations
    > > passed.
    > >
    > > Having extra logging added, I could see the key difference.
    > > Failed run:
    > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/016C0000, targetSeg: 22, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 22
    > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > > vs
    > > Successful run:
    > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/01700000, targetSeg: 23, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 23
    > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > >
    > > That is, with WAIT FOR LSN, primary in this test may advance
    > > slot->data.restart_lsn to the expected position after wait_for_catchup()
    > > returns.
    > >
    > > [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=goldfish&dt=2026-05-13%2006%3A15%3A03
    >
    > Thanks for reporting this issue.
    >
    > I think this is related to the semantic change made earlier:
    > wait_for_catchup() now returns once the standby itself has reached the
    > target LSN, rather than waiting until the primary observes that
    > position via pg_stat_replication.
    >
    > As a result, the primary may not yet have processed the standby
    > feedback needed to advance the slot's restart_lsn when
    > wait_for_catchup() returns.
    >
    > Actually, I was aware of this semantic change and previously thought
    > it might be harmless. But this failure appears to disprove that. I'll
    > prepare a patch to fix this shortly.
    
    After some consideration, 019_replslot_limit.pl appears to the better
    place to place the fix rather than by restoring the old primary-side
    polling barrier in wait_for_catchup().
    
    The new wait_for_catchup() behavior is closer to its natural
    semantics: for replay/write/flush modes, it waits until the standby
    itself has reached the requested LSN. The old implementation used
    pg_stat_replication on the primary, which also implied that the
    primary had received and processed standby feedback. That was a useful
    side effect for this test, but it is not required by most callers.
    
    019_replslot_limit.pl is different because it checks primary-side slot
    state. For a physical slot, restart_lsn advances only after the
    primary's walsender processes standby feedback. So the test needs an
    extra condition beyond ordinary standby catchup.
    
    The patch makes that dependency explicit: wait for the standby to
    replay the target LSN, then wait for the slot's restart_lsn on the
    primary to pass the same LSN. This keeps wait_for_catchup() focused on
    standby catchup while making the slot-specific synchronization visible
    in the test that needs it.
    
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
  143. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-05-20T12:16:45Z

    Hi, Xuneng!
    
    On Wed, May 20, 2026 at 8:18 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Tue, May 19, 2026 at 8:30 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Tue, May 19, 2026 at 1:00 PM Alexander Lakhin <exclusion@gmail.com> wrote:
    > > >
    > > > Hello Alexander and Xuneng,
    > > >
    > > > 06.04.2026 22:49, Alexander Korotkov wrote:
    > > >
    > > > Thank you, I've pushed your version of patchset.  I made two minor
    > > > corrections for patch #2: mention default mode value in the header
    > > > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    > > >
    > > >
    > > > I discovered a new test failure, that is apparently caused by new
    > > > wait_for_catchup() implementation [1]:
    > > > [06:20:23.110](1.069s) not ok 8 - check that the slot state changes to "extended"
    > > > [06:20:23.110](0.001s) #   Failed test 'check that the slot state changes to "extended"'
    > > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 140.
    > > > [06:20:23.111](0.000s) #          got: 'unreserved'
    > > > #     expected: 'extended'
    > > > [06:20:23.231](0.120s) not ok 9 - check that the slot state changes to "unreserved"
    > > > [06:20:23.231](0.000s) #   Failed test 'check that the slot state changes to "unreserved"'
    > > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 152.
    > > > [06:20:23.231](0.000s) #          got: 'lost|'
    > > > #     expected: 'unreserved|t'
    > > >
    > > > I've managed to reproduce such failures with:
    > > > diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
    > > > index 07eac07b9ce..493ce92674e 100644
    > > > --- a/src/backend/replication/walreceiver.c
    > > > +++ b/src/backend/replication/walreceiver.c
    > > > @@ -1143,2 +1143,3 @@ XLogWalRcvSendReply(bool force, bool requestReply, bool checkApply)
    > > >
    > > > +pg_usleep(10000);
    > > >      /* Get current timestamp. */
    > > > diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
    > > > index 04aa770d981..19cda3a6b51 100644
    > > > --- a/src/backend/replication/walsender.c
    > > > +++ b/src/backend/replication/walsender.c
    > > > @@ -2521,2 +2521,3 @@ ProcessStandbyReplyMessage(void)
    > > >
    > > > +pg_usleep(100000);
    > > >      /* the caller already consumed the msgtype byte */
    > > >
    > > > Concretely, a loop:
    > > > for i in {1..100}; do echo "ITERATION $i"; PROVE_TESTS="t/019*" make -s check -C src/test/recovery/ || break; done
    > > > failed for me on iterations 2, 1, 7:
    > > > ITERATION 7
    > > > # +++ tap check in src/test/recovery +++
    > > > t/019_replslot_limit.pl .. 8/?
    > > > #   Failed test 'check that the slot state changes to "extended"'
    > > > #   at t/019_replslot_limit.pl line 140.
    > > > #          got: 'unreserved'
    > > > #     expected: 'extended'
    > > > t/019_replslot_limit.pl .. 26/? # Looks like you failed 1 test of 26.
    > > > t/019_replslot_limit.pl .. Dubious, test returned 1 (wstat 256, 0x100)
    > > > Failed 1/26 subtests
    > > >
    > > > With "WAIT FOR LSN" in wait_for_catchup() disabled, 100 iterations
    > > > passed.
    > > >
    > > > Having extra logging added, I could see the key difference.
    > > > Failed run:
    > > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/016C0000, targetSeg: 22, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 22
    > > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > > > vs
    > > > Successful run:
    > > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/01700000, targetSeg: 23, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 23
    > > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > > >
    > > > That is, with WAIT FOR LSN, primary in this test may advance
    > > > slot->data.restart_lsn to the expected position after wait_for_catchup()
    > > > returns.
    > > >
    > > > [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=goldfish&dt=2026-05-13%2006%3A15%3A03
    > >
    > > Thanks for reporting this issue.
    > >
    > > I think this is related to the semantic change made earlier:
    > > wait_for_catchup() now returns once the standby itself has reached the
    > > target LSN, rather than waiting until the primary observes that
    > > position via pg_stat_replication.
    > >
    > > As a result, the primary may not yet have processed the standby
    > > feedback needed to advance the slot's restart_lsn when
    > > wait_for_catchup() returns.
    > >
    > > Actually, I was aware of this semantic change and previously thought
    > > it might be harmless. But this failure appears to disprove that. I'll
    > > prepare a patch to fix this shortly.
    >
    > After some consideration, 019_replslot_limit.pl appears to the better
    > place to place the fix rather than by restoring the old primary-side
    > polling barrier in wait_for_catchup().
    >
    > The new wait_for_catchup() behavior is closer to its natural
    > semantics: for replay/write/flush modes, it waits until the standby
    > itself has reached the requested LSN. The old implementation used
    > pg_stat_replication on the primary, which also implied that the
    > primary had received and processed standby feedback. That was a useful
    > side effect for this test, but it is not required by most callers.
    >
    > 019_replslot_limit.pl is different because it checks primary-side slot
    > state. For a physical slot, restart_lsn advances only after the
    > primary's walsender processes standby feedback. So the test needs an
    > extra condition beyond ordinary standby catchup.
    >
    > The patch makes that dependency explicit: wait for the standby to
    > replay the target LSN, then wait for the slot's restart_lsn on the
    > primary to pass the same LSN. This keeps wait_for_catchup() focused on
    > standby catchup while making the slot-specific synchronization visible
    > in the test that needs it.
    
    I agree with you.  But do we actually need a
    wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    the attached patch.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  144. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-23T17:09:52Z

    Hi Alexander,
    
    On Wed, May 20, 2026 at 5:17 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > Hi, Xuneng!
    >
    > On Wed, May 20, 2026 at 8:18 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > On Tue, May 19, 2026 at 8:30 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > On Tue, May 19, 2026 at 1:00 PM Alexander Lakhin <exclusion@gmail.com> wrote:
    > > > >
    > > > > Hello Alexander and Xuneng,
    > > > >
    > > > > 06.04.2026 22:49, Alexander Korotkov wrote:
    > > > >
    > > > > Thank you, I've pushed your version of patchset.  I made two minor
    > > > > corrections for patch #2: mention default mode value in the header
    > > > > comment, and fallback to polling on has_wal_read_bug sparc64+ext4 bug.
    > > > >
    > > > >
    > > > > I discovered a new test failure, that is apparently caused by new
    > > > > wait_for_catchup() implementation [1]:
    > > > > [06:20:23.110](1.069s) not ok 8 - check that the slot state changes to "extended"
    > > > > [06:20:23.110](0.001s) #   Failed test 'check that the slot state changes to "extended"'
    > > > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 140.
    > > > > [06:20:23.111](0.000s) #          got: 'unreserved'
    > > > > #     expected: 'extended'
    > > > > [06:20:23.231](0.120s) not ok 9 - check that the slot state changes to "unreserved"
    > > > > [06:20:23.231](0.000s) #   Failed test 'check that the slot state changes to "unreserved"'
    > > > > #   at /Users/ec2-user/bf/goldfish/HEAD/pgsql/src/test/recovery/t/019_replslot_limit.pl line 152.
    > > > > [06:20:23.231](0.000s) #          got: 'lost|'
    > > > > #     expected: 'unreserved|t'
    > > > >
    > > > > I've managed to reproduce such failures with:
    > > > > diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
    > > > > index 07eac07b9ce..493ce92674e 100644
    > > > > --- a/src/backend/replication/walreceiver.c
    > > > > +++ b/src/backend/replication/walreceiver.c
    > > > > @@ -1143,2 +1143,3 @@ XLogWalRcvSendReply(bool force, bool requestReply, bool checkApply)
    > > > >
    > > > > +pg_usleep(10000);
    > > > >      /* Get current timestamp. */
    > > > > diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
    > > > > index 04aa770d981..19cda3a6b51 100644
    > > > > --- a/src/backend/replication/walsender.c
    > > > > +++ b/src/backend/replication/walsender.c
    > > > > @@ -2521,2 +2521,3 @@ ProcessStandbyReplyMessage(void)
    > > > >
    > > > > +pg_usleep(100000);
    > > > >      /* the caller already consumed the msgtype byte */
    > > > >
    > > > > Concretely, a loop:
    > > > > for i in {1..100}; do echo "ITERATION $i"; PROVE_TESTS="t/019*" make -s check -C src/test/recovery/ || break; done
    > > > > failed for me on iterations 2, 1, 7:
    > > > > ITERATION 7
    > > > > # +++ tap check in src/test/recovery +++
    > > > > t/019_replslot_limit.pl .. 8/?
    > > > > #   Failed test 'check that the slot state changes to "extended"'
    > > > > #   at t/019_replslot_limit.pl line 140.
    > > > > #          got: 'unreserved'
    > > > > #     expected: 'extended'
    > > > > t/019_replslot_limit.pl .. 26/? # Looks like you failed 1 test of 26.
    > > > > t/019_replslot_limit.pl .. Dubious, test returned 1 (wstat 256, 0x100)
    > > > > Failed 1/26 subtests
    > > > >
    > > > > With "WAIT FOR LSN" in wait_for_catchup() disabled, 100 iterations
    > > > > passed.
    > > > >
    > > > > Having extra logging added, I could see the key difference.
    > > > > Failed run:
    > > > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/016C0000, targetSeg: 22, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 22
    > > > > 2026-05-19 22:01:37.968 EEST client backend[3632148] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > > > > vs
    > > > > Successful run:
    > > > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl LOG:  !!!GetWALAvailability| targetLSN: 0/01700000, targetSeg: 23, oldestSlotSeg: 23, oldestSegMaxWalSize: 24, oldestSeg: 23
    > > > > 2026-05-19 22:04:18.102 EEST client backend[3633761] 019_replslot_limit.pl STATEMENT:  SELECT wal_status FROM pg_replication_slots WHERE slot_name = 'rep1'
    > > > >
    > > > > That is, with WAIT FOR LSN, primary in this test may advance
    > > > > slot->data.restart_lsn to the expected position after wait_for_catchup()
    > > > > returns.
    > > > >
    > > > > [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=goldfish&dt=2026-05-13%2006%3A15%3A03
    > > >
    > > > Thanks for reporting this issue.
    > > >
    > > > I think this is related to the semantic change made earlier:
    > > > wait_for_catchup() now returns once the standby itself has reached the
    > > > target LSN, rather than waiting until the primary observes that
    > > > position via pg_stat_replication.
    > > >
    > > > As a result, the primary may not yet have processed the standby
    > > > feedback needed to advance the slot's restart_lsn when
    > > > wait_for_catchup() returns.
    > > >
    > > > Actually, I was aware of this semantic change and previously thought
    > > > it might be harmless. But this failure appears to disprove that. I'll
    > > > prepare a patch to fix this shortly.
    > >
    > > After some consideration, 019_replslot_limit.pl appears to the better
    > > place to place the fix rather than by restoring the old primary-side
    > > polling barrier in wait_for_catchup().
    > >
    > > The new wait_for_catchup() behavior is closer to its natural
    > > semantics: for replay/write/flush modes, it waits until the standby
    > > itself has reached the requested LSN. The old implementation used
    > > pg_stat_replication on the primary, which also implied that the
    > > primary had received and processed standby feedback. That was a useful
    > > side effect for this test, but it is not required by most callers.
    > >
    > > 019_replslot_limit.pl is different because it checks primary-side slot
    > > state. For a physical slot, restart_lsn advances only after the
    > > primary's walsender processes standby feedback. So the test needs an
    > > extra condition beyond ordinary standby catchup.
    > >
    > > The patch makes that dependency explicit: wait for the standby to
    > > replay the target LSN, then wait for the slot's restart_lsn on the
    > > primary to pass the same LSN. This keeps wait_for_catchup() focused on
    > > standby catchup while making the slot-specific synchronization visible
    > > in the test that needs it.
    >
    > I agree with you.  But do we actually need a
    > wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    > $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    > the attached patch.
    >
    
    The patch looks good to me. I agree that the wait_for_slot_catchup is
    not needed and could be misleading. This change would make the exact
    synchronization point and its intention clearer. The only price we
    need to pay here is bringing back the polling. But it seems acceptable
    since the cost was there in the pre-wait-for-lsn era. And thanks for
    writing the great commit message!
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
    
    
    
  145. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-23T18:40:30Z

    >
    >
    > > I agree with you.  But do we actually need a
    > > wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    > > $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    > > the attached patch.
    > >
    >
    > The patch looks good to me. I agree that the wait_for_slot_catchup is
    > not needed and could be misleading. This change would make the exact
    > synchronization point and its intention clearer. The only price we
    > need to pay here is bringing back the polling. But it seems acceptable
    > since the cost was there in the pre-wait-for-lsn era. And thanks for
    > writing the great commit message!
    >
    
    Sorry for copy-pasting the wrong function name. It should be
    wait_for_catchup().
    
    >
    
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
  146. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-05-25T09:00:20Z

    On Sat, May 23, 2026 at 9:40 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >> > I agree with you.  But do we actually need a
    >> > wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    >> > $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    >> > the attached patch.
    >> >
    >>
    >> The patch looks good to me. I agree that the wait_for_slot_catchup is
    >> not needed and could be misleading. This change would make the exact
    >> synchronization point and its intention clearer. The only price we
    >> need to pay here is bringing back the polling. But it seems acceptable
    >> since the cost was there in the pre-wait-for-lsn era. And thanks for
    >> writing the great commit message!
    >
    >
    > Sorry for copy-pasting the wrong function name. It should be wait_for_catchup().
    
    Good, thank you.  I'll push it if no objections.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  147. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-26T01:48:26Z

    On Mon, May 25, 2026 at 5:00 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    >
    > On Sat, May 23, 2026 at 9:40 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >> > I agree with you.  But do we actually need a
    > >> > wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    > >> > $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    > >> > the attached patch.
    > >> >
    > >>
    > >> The patch looks good to me. I agree that the wait_for_slot_catchup is
    > >> not needed and could be misleading. This change would make the exact
    > >> synchronization point and its intention clearer. The only price we
    > >> need to pay here is bringing back the polling. But it seems acceptable
    > >> since the cost was there in the pre-wait-for-lsn era. And thanks for
    > >> writing the great commit message!
    > >
    > >
    > > Sorry for copy-pasting the wrong function name. It should be wait_for_catchup().
    >
    > Good, thank you.  I'll push it if no objections.
    
    While reading 019_replslot_limit.pl, Codex pointed out a few
    inconsistencies in the comments. I verified them and they look real.
    Would you mind doing a small cleanup as well?
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
  148. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-05-26T15:53:14Z

    On Tue, May 26, 2026 at 9:48 AM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Mon, May 25, 2026 at 5:00 PM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > >
    > > On Sat, May 23, 2026 at 9:40 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >> > I agree with you.  But do we actually need a
    > > >> > wait_for_standby_and_slot_catchup() wrapper.  I think we can call
    > > >> > $node->wait_for_slot_catchup() directly and simplify the fix.  Check
    > > >> > the attached patch.
    > > >> >
    > > >>
    > > >> The patch looks good to me. I agree that the wait_for_slot_catchup is
    > > >> not needed and could be misleading. This change would make the exact
    > > >> synchronization point and its intention clearer. The only price we
    > > >> need to pay here is bringing back the polling. But it seems acceptable
    > > >> since the cost was there in the pre-wait-for-lsn era. And thanks for
    > > >> writing the great commit message!
    > > >
    > > >
    > > > Sorry for copy-pasting the wrong function name. It should be wait_for_catchup().
    > >
    > > Good, thank you.  I'll push it if no objections.
    >
    > While reading 019_replslot_limit.pl, Codex pointed out a few
    > inconsistencies in the comments. I verified them and they look real.
    > Would you mind doing a small cleanup as well?
    
    I updated the comment above wait_for_slot_catchup to reflect its usage.
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
  149. Re: Implement waiting for wal lsn replay: reloaded

    Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-06T11:04:08Z

    On 01/05/2026 05:44, Xuneng Zhou wrote:
    > On Wed, Apr 29, 2026 at 5:01 AM Alexander Korotkov <aekorotkov@gmail.com 
    > <mailto:aekorotkov@gmail.com>> wrote:
    > 
    >> LGTM, I've added some comments for new functions in 0006.  I propose
    >> to push this patchset.  Probably something is still missing and we
    >> will have to go back to this.  But it seems to make a lot of aspects
    >> much better.
    > 
    > I reviewed the patchset and found a potential issue in the test for 
    > patch 5, similar to the log-checking problem in the cascading timeline- 
    > switch test. I've applied a minor fix to address it. Other parts LGTM.
    I happened to look around this code now. To recap, the code in the main 
    WAL redo loop now looks like this:
    
    > 
    > 			/*
    > 			 * Apply the record
    > 			 */
    > 			ApplyWalRecord(xlogreader, record, &replayTLI);
    > 
    > 			/*
    > 			 * Wake up processes waiting for standby replay, write, or flush
    > 			 * LSN to reach current replay position.  Replay implies that the
    > 			 * WAL was already written and flushed to disk, so write and flush
    > 			 * waiters can be woken at the replay position too.
    > 			 */
    > 			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > 						  XLogRecoveryCtl->lastReplayedEndRecPtr);
    > 			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > 						  XLogRecoveryCtl->lastReplayedEndRecPtr);
    > 			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > 						  XLogRecoveryCtl->lastReplayedEndRecPtr);
    
    That's not wrong, but I've got some comments:
    
    1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or 
    atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but 
    it feels a little dirty.
    
    2. We're now doing three extra function calls on every WAL record. This 
    is a very hot path, and most of the time, we'll just take the fast path 
    in WaitLSNWakeup to return without doing anything. Andres and others 
    assumed up-thread that it's negligible (we used to have pre-checks here 
    in the caller), but I wonder if you did any performance testing?
    
    3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up 
    walsenders and walreceivers. They could perhaps use the same wait-lsn 
    machinery now, but that's v20 material. However, I think these 
    WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so 
    that we'd have all the wakeup actions in one place.
    
    4. Once you move those calls inside ApplyWalRecord(), like this:
    
    > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    >         /*
    >          * Update lastReplayedEndRecPtr after this record has been successfully
    >          * replayed.
    >          */
    >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    >  
    > +       /*
    > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > +        * reach current replay position.  Replay implies that the WAL was already
    > +        * written and flushed to disk, so write and flush waiters can be woken at
    > +        * the replay position too.
    > +        */
    > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > +
    >         /* ------
    >          * Wakeup walsenders:
    >          *
    >          * On the standby, the WAL is flushed first (which will only wake up
    >          * physical walsenders) and then applied, which will only wake up logical
    >          * walsenders.
    
    It becomes clear that you don't actually need the memory barrier inside 
    WaitLSNWakeup(). Not sure if they're needed for other callers, but here 
    we have just released a spinlock, which acts as a memory barrier. It 
    might not be worth relaxing, but it does seem a little silly.
    
    
    If nothing else, I'd like to move those calls into ApplyWalRecord() for 
    clarity (point 3 above). What do you think?
    
    - Heikki
    
    
    
    
    
  150. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-07-06T13:49:26Z

    Hi Heikki,
    
    Thanks for looking into this!
    
    On Mon, Jul 6, 2026 at 7:04 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
                          /*
    > >                        * Apply the record
    > >                        */
    > >                       ApplyWalRecord(xlogreader, record, &replayTLI);
    > >
    > >                       /*
    > >                        * Wake up processes waiting for standby replay, write, or flush
    > >                        * LSN to reach current replay position.  Replay implies that the
    > >                        * WAL was already written and flushed to disk, so write and flush
    > >                        * waiters can be woken at the replay position too.
    > >                        */
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    >
    > That's not wrong, but I've got some comments:
    >
    > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or
    > atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but
    > it feels a little dirty.
    >
    > 2. We're now doing three extra function calls on every WAL record. This
    > is a very hot path, and most of the time, we'll just take the fast path
    > in WaitLSNWakeup to return without doing anything. Andres and others
    > assumed up-thread that it's negligible (we used to have pre-checks here
    > in the caller), but I wonder if you did any performance testing?
    
    Agreed, this is a hot path. The performance impact of these extra
    calls doing real work hasn't been measured yet. I'll do some testing.
    
    > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up
    > walsenders and walreceivers. They could perhaps use the same wait-lsn
    > machinery now, but that's v20 material. However, I think these
    > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so
    > that we'd have all the wakeup actions in one place.
    
    + 1. This makes the code safer and more readable.
    
    > 4. Once you move those calls inside ApplyWalRecord(), like this:
    >
    > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    > >         /*
    > >          * Update lastReplayedEndRecPtr after this record has been successfully
    > >          * replayed.
    > >          */
    > >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    > >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    > >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    > >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    > >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    > >
    > > +       /*
    > > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > > +        * reach current replay position.  Replay implies that the WAL was already
    > > +        * written and flushed to disk, so write and flush waiters can be woken at
    > > +        * the replay position too.
    > > +        */
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > > +
    > >         /* ------
    > >          * Wakeup walsenders:
    > >          *
    > >          * On the standby, the WAL is flushed first (which will only wake up
    > >          * physical walsenders) and then applied, which will only wake up logical
    > >          * walsenders.
    >
    > It becomes clear that you don't actually need the memory barrier inside
    > WaitLSNWakeup(). Not sure if they're needed for other callers, but here
    > we have just released a spinlock, which acts as a memory barrier. It
    > might not be worth relaxing, but it does seem a little silly.
    
    If we made the move here, I think the memory barrier could be relaxed
    since other callers are guarded by either the spinlock or full-barrier
    atomic write already.  We might also want to make the contract of
    WaitLSNWakeup() explicit: callers should not publish the LSN with an
    unsynchronized plain store and then immediately probe minWaitedLSN.
    Another motivation for doing this might be slightly better performance
    though untested.
    
    > If nothing else, I'd like to move those calls into ApplyWalRecord() for
    > clarity (point 3 above). What do you think?
    
    Personally + 1.
    --
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
    
    
    
  151. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-07-06T14:17:41Z

    On Mon, Jul 6, 2026 at 9:49 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > Hi Heikki,
    >
    > Thanks for looking into this!
    >
    > On Mon, Jul 6, 2026 at 7:04 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
    >                       /*
    > > >                        * Apply the record
    > > >                        */
    > > >                       ApplyWalRecord(xlogreader, record, &replayTLI);
    > > >
    > > >                       /*
    > > >                        * Wake up processes waiting for standby replay, write, or flush
    > > >                        * LSN to reach current replay position.  Replay implies that the
    > > >                        * WAL was already written and flushed to disk, so write and flush
    > > >                        * waiters can be woken at the replay position too.
    > > >                        */
    > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > >
    > > That's not wrong, but I've got some comments:
    > >
    > > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or
    > > atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but
    > > it feels a little dirty.
    > >
    > > 2. We're now doing three extra function calls on every WAL record. This
    > > is a very hot path, and most of the time, we'll just take the fast path
    > > in WaitLSNWakeup to return without doing anything. Andres and others
    > > assumed up-thread that it's negligible (we used to have pre-checks here
    > > in the caller), but I wonder if you did any performance testing?
    >
    > Agreed, this is a hot path. The performance impact of these extra
    > calls doing real work hasn't been measured yet. I'll do some testing.
    >
    > > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up
    > > walsenders and walreceivers. They could perhaps use the same wait-lsn
    > > machinery now, but that's v20 material. However, I think these
    > > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so
    > > that we'd have all the wakeup actions in one place.
    >
    > + 1. This makes the code safer and more readable.
    >
    > > 4. Once you move those calls inside ApplyWalRecord(), like this:
    > >
    > > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    > > >         /*
    > > >          * Update lastReplayedEndRecPtr after this record has been successfully
    > > >          * replayed.
    > > >          */
    > > >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    > > >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    > > >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    > > >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    > > >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    > > >
    > > > +       /*
    > > > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > > > +        * reach current replay position.  Replay implies that the WAL was already
    > > > +        * written and flushed to disk, so write and flush waiters can be woken at
    > > > +        * the replay position too.
    > > > +        */
    > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > > > +
    > > >         /* ------
    > > >          * Wakeup walsenders:
    > > >          *
    > > >          * On the standby, the WAL is flushed first (which will only wake up
    > > >          * physical walsenders) and then applied, which will only wake up logical
    > > >          * walsenders.
    > >
    > > It becomes clear that you don't actually need the memory barrier inside
    > > WaitLSNWakeup(). Not sure if they're needed for other callers, but here
    > > we have just released a spinlock, which acts as a memory barrier. It
    > > might not be worth relaxing, but it does seem a little silly.
    >
    > If we made the move here, I think the memory barrier could be relaxed
    > since other callers are guarded by either the spinlock or full-barrier
    > atomic write already.  We might also want to make the contract of
    
    OK, the 'if' here is redundant...
    
    > WaitLSNWakeup() explicit: callers should not publish the LSN with an
    > unsynchronized plain store and then immediately probe minWaitedLSN.
    > Another motivation for doing this might be slightly better performance
    > though untested.
    >
    > > If nothing else, I'd like to move those calls into ApplyWalRecord() for
    > > clarity (point 3 above). What do you think?
    >
    > Personally + 1.
    
    
    -- 
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
    
    
    
  152. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-07-07T15:25:28Z

    Hi, Heikki!
    
    On Mon, Jul 6, 2026 at 2:04 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
    > On 01/05/2026 05:44, Xuneng Zhou wrote:
    > > On Wed, Apr 29, 2026 at 5:01 AM Alexander Korotkov <aekorotkov@gmail.com
    > > <mailto:aekorotkov@gmail.com>> wrote:
    > >
    > >> LGTM, I've added some comments for new functions in 0006.  I propose
    > >> to push this patchset.  Probably something is still missing and we
    > >> will have to go back to this.  But it seems to make a lot of aspects
    > >> much better.
    > >
    > > I reviewed the patchset and found a potential issue in the test for
    > > patch 5, similar to the log-checking problem in the cascading timeline-
    > > switch test. I've applied a minor fix to address it. Other parts LGTM.
    > I happened to look around this code now. To recap, the code in the main
    > WAL redo loop now looks like this:
    >
    > >
    > >                       /*
    > >                        * Apply the record
    > >                        */
    > >                       ApplyWalRecord(xlogreader, record, &replayTLI);
    > >
    > >                       /*
    > >                        * Wake up processes waiting for standby replay, write, or flush
    > >                        * LSN to reach current replay position.  Replay implies that the
    > >                        * WAL was already written and flushed to disk, so write and flush
    > >                        * waiters can be woken at the replay position too.
    > >                        */
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    >
    > That's not wrong, but I've got some comments:
    >
    > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or
    > atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but
    > it feels a little dirty.
    >
    > 2. We're now doing three extra function calls on every WAL record. This
    > is a very hot path, and most of the time, we'll just take the fast path
    > in WaitLSNWakeup to return without doing anything. Andres and others
    > assumed up-thread that it's negligible (we used to have pre-checks here
    > in the caller), but I wonder if you did any performance testing?
    >
    > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up
    > walsenders and walreceivers. They could perhaps use the same wait-lsn
    > machinery now, but that's v20 material. However, I think these
    > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so
    > that we'd have all the wakeup actions in one place.
    >
    > 4. Once you move those calls inside ApplyWalRecord(), like this:
    >
    > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    > >         /*
    > >          * Update lastReplayedEndRecPtr after this record has been successfully
    > >          * replayed.
    > >          */
    > >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    > >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    > >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    > >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    > >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    > >
    > > +       /*
    > > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > > +        * reach current replay position.  Replay implies that the WAL was already
    > > +        * written and flushed to disk, so write and flush waiters can be woken at
    > > +        * the replay position too.
    > > +        */
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > > +
    > >         /* ------
    > >          * Wakeup walsenders:
    > >          *
    > >          * On the standby, the WAL is flushed first (which will only wake up
    > >          * physical walsenders) and then applied, which will only wake up logical
    > >          * walsenders.
    >
    > It becomes clear that you don't actually need the memory barrier inside
    > WaitLSNWakeup(). Not sure if they're needed for other callers, but here
    > we have just released a spinlock, which acts as a memory barrier. It
    > might not be worth relaxing, but it does seem a little silly.
    >
    >
    > If nothing else, I'd like to move those calls into ApplyWalRecord() for
    > clarity (point 3 above). What do you think?
    
    Thank you for bringing this up.
    
    +1 for moving calls into ApplyWalRecord()
    
    Regarding the overhead.  I've tried to apply 158.6 MB of pgbench
    generated WAL on my local Macbook M3 Pro.
    
     * With calls, sec: 1.95/1.86/1.91/1.85/1.90
     * Without calls, sec:  1.83/1.87/1.96/1.90/1.85
    
    So, the difference is less than the measurement error.
    
    Do you prefer to commit the movement of the calls yourself, or do you
    prefer me to do it?
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
    
    
    
  153. Re: Implement waiting for wal lsn replay: reloaded

    Xuneng Zhou <xunengzhou@gmail.com> — 2026-07-08T12:08:01Z

    On Mon, Jul 6, 2026 at 10:17 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    >
    > On Mon, Jul 6, 2026 at 9:49 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > Hi Heikki,
    > >
    > > Thanks for looking into this!
    > >
    > > On Mon, Jul 6, 2026 at 7:04 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
    > >                       /*
    > > > >                        * Apply the record
    > > > >                        */
    > > > >                       ApplyWalRecord(xlogreader, record, &replayTLI);
    > > > >
    > > > >                       /*
    > > > >                        * Wake up processes waiting for standby replay, write, or flush
    > > > >                        * LSN to reach current replay position.  Replay implies that the
    > > > >                        * WAL was already written and flushed to disk, so write and flush
    > > > >                        * waiters can be woken at the replay position too.
    > > > >                        */
    > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > >
    > > > That's not wrong, but I've got some comments:
    > > >
    > > > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or
    > > > atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but
    > > > it feels a little dirty.
    > > >
    > > > 2. We're now doing three extra function calls on every WAL record. This
    > > > is a very hot path, and most of the time, we'll just take the fast path
    > > > in WaitLSNWakeup to return without doing anything. Andres and others
    > > > assumed up-thread that it's negligible (we used to have pre-checks here
    > > > in the caller), but I wonder if you did any performance testing?
    > >
    > > Agreed, this is a hot path. The performance impact of these extra
    > > calls doing real work hasn't been measured yet. I'll do some testing.
    > >
    > > > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up
    > > > walsenders and walreceivers. They could perhaps use the same wait-lsn
    > > > machinery now, but that's v20 material. However, I think these
    > > > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so
    > > > that we'd have all the wakeup actions in one place.
    > >
    > > + 1. This makes the code safer and more readable.
    > >
    > > > 4. Once you move those calls inside ApplyWalRecord(), like this:
    > > >
    > > > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    > > > >         /*
    > > > >          * Update lastReplayedEndRecPtr after this record has been successfully
    > > > >          * replayed.
    > > > >          */
    > > > >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    > > > >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    > > > >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    > > > >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    > > > >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    > > > >
    > > > > +       /*
    > > > > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > > > > +        * reach current replay position.  Replay implies that the WAL was already
    > > > > +        * written and flushed to disk, so write and flush waiters can be woken at
    > > > > +        * the replay position too.
    > > > > +        */
    > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > > > > +
    > > > >         /* ------
    > > > >          * Wakeup walsenders:
    > > > >          *
    > > > >          * On the standby, the WAL is flushed first (which will only wake up
    > > > >          * physical walsenders) and then applied, which will only wake up logical
    > > > >          * walsenders.
    > > >
    > > > It becomes clear that you don't actually need the memory barrier inside
    > > > WaitLSNWakeup(). Not sure if they're needed for other callers, but here
    > > > we have just released a spinlock, which acts as a memory barrier. It
    > > > might not be worth relaxing, but it does seem a little silly.
    > >
    > > If we made the move here, I think the memory barrier could be relaxed
    > > since other callers are guarded by either the spinlock or full-barrier
    > > atomic write already.  We might also want to make the contract of
    >
    > OK, the 'if' here is redundant...
    
    After revisiting the memory barrier in WaitLSNWakeup and why it is
    introduced there in a80a593ab63 rather than recalling it from memory,
    I think relaxing it here could be unsafe.
    
    In WaitLSNWakeup(), use pg_atomic_read_membarrier_u64() in the
    fast-path check so the waker's preceding position store is globally
    visible before minWaitedLSN is read.
    
    Without the barrier in WaitLSNWakeup(), this interleaving is possible:
    
    Initial:
      minWaitedLSN = PG_UINT64_MAX
      replayLSN = 90
    
    Waiter:
      stores minWaitedLSN = 100
      reads replayLSN before the waker publishes the new replay position
      sees replayLSN = 90
      decides it should sleep
    
    Waker:
      publishes replayLSN = 100
      reads old minWaitedLSN = PG_UINT64_MAX
      skips the wakeup
    
    Then the waiter goes to sleep even though replay has reached its
    target LSN. To avoid this, we still need to make sure that the
    publication of replayLSN precedes the read of minWaitedLSN, so that
    the waker cannot decide "nobody is waiting" before its own progress is
    still not visible to the waiter.
    
    --
    Regards,
    Xuneng Zhou
    HighGo Software Co., Ltd.
    
    
    
    
  154. Re: Implement waiting for wal lsn replay: reloaded

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-07-08T17:38:28Z

    On Wed, Jul 8, 2026 at 3:08 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > On Mon, Jul 6, 2026 at 10:17 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > >
    > > On Mon, Jul 6, 2026 at 9:49 PM Xuneng Zhou <xunengzhou@gmail.com> wrote:
    > > >
    > > > Hi Heikki,
    > > >
    > > > Thanks for looking into this!
    > > >
    > > > On Mon, Jul 6, 2026 at 7:04 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:
    > > >                       /*
    > > > > >                        * Apply the record
    > > > > >                        */
    > > > > >                       ApplyWalRecord(xlogreader, record, &replayTLI);
    > > > > >
    > > > > >                       /*
    > > > > >                        * Wake up processes waiting for standby replay, write, or flush
    > > > > >                        * LSN to reach current replay position.  Replay implies that the
    > > > > >                        * WAL was already written and flushed to disk, so write and flush
    > > > > >                        * waiters can be woken at the replay position too.
    > > > > >                        */
    > > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY,
    > > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE,
    > > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > > > >                       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH,
    > > > > >                                                 XLogRecoveryCtl->lastReplayedEndRecPtr);
    > > > >
    > > > > That's not wrong, but I've got some comments:
    > > > >
    > > > > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock or
    > > > > atomics. That's ok, no other process modifies lastReplayedEndRecPtr, but
    > > > > it feels a little dirty.
    > > > >
    > > > > 2. We're now doing three extra function calls on every WAL record. This
    > > > > is a very hot path, and most of the time, we'll just take the fast path
    > > > > in WaitLSNWakeup to return without doing anything. Andres and others
    > > > > assumed up-thread that it's negligible (we used to have pre-checks here
    > > > > in the caller), but I wonder if you did any performance testing?
    > > >
    > > > Agreed, this is a hot path. The performance impact of these extra
    > > > calls doing real work hasn't been measured yet. I'll do some testing.
    > > >
    > > > > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake up
    > > > > walsenders and walreceivers. They could perhaps use the same wait-lsn
    > > > > machinery now, but that's v20 material. However, I think these
    > > > > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), so
    > > > > that we'd have all the wakeup actions in one place.
    > > >
    > > > + 1. This makes the code safer and more readable.
    > > >
    > > > > 4. Once you move those calls inside ApplyWalRecord(), like this:
    > > > >
    > > > > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
    > > > > >         /*
    > > > > >          * Update lastReplayedEndRecPtr after this record has been successfully
    > > > > >          * replayed.
    > > > > >          */
    > > > > >         SpinLockAcquire(&XLogRecoveryCtl->info_lck);
    > > > > >         XLogRecoveryCtl->lastReplayedReadRecPtr = xlogreader->ReadRecPtr;
    > > > > >         XLogRecoveryCtl->lastReplayedEndRecPtr = xlogreader->EndRecPtr;
    > > > > >         XLogRecoveryCtl->lastReplayedTLI = *replayTLI;
    > > > > >         SpinLockRelease(&XLogRecoveryCtl->info_lck);
    > > > > >
    > > > > > +       /*
    > > > > > +        * Wake up processes waiting for standby replay, write, or flush LSN to
    > > > > > +        * reach current replay position.  Replay implies that the WAL was already
    > > > > > +        * written and flushed to disk, so write and flush waiters can be woken at
    > > > > > +        * the replay position too.
    > > > > > +        */
    > > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->EndRecPtr);
    > > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndRecPtr);
    > > > > > +       WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndRecPtr);
    > > > > > +
    > > > > >         /* ------
    > > > > >          * Wakeup walsenders:
    > > > > >          *
    > > > > >          * On the standby, the WAL is flushed first (which will only wake up
    > > > > >          * physical walsenders) and then applied, which will only wake up logical
    > > > > >          * walsenders.
    > > > >
    > > > > It becomes clear that you don't actually need the memory barrier inside
    > > > > WaitLSNWakeup(). Not sure if they're needed for other callers, but here
    > > > > we have just released a spinlock, which acts as a memory barrier. It
    > > > > might not be worth relaxing, but it does seem a little silly.
    > > >
    > > > If we made the move here, I think the memory barrier could be relaxed
    > > > since other callers are guarded by either the spinlock or full-barrier
    > > > atomic write already.  We might also want to make the contract of
    > >
    > > OK, the 'if' here is redundant...
    >
    > After revisiting the memory barrier in WaitLSNWakeup and why it is
    > introduced there in a80a593ab63 rather than recalling it from memory,
    > I think relaxing it here could be unsafe.
    >
    > In WaitLSNWakeup(), use pg_atomic_read_membarrier_u64() in the
    > fast-path check so the waker's preceding position store is globally
    > visible before minWaitedLSN is read.
    >
    > Without the barrier in WaitLSNWakeup(), this interleaving is possible:
    >
    > Initial:
    >   minWaitedLSN = PG_UINT64_MAX
    >   replayLSN = 90
    >
    > Waiter:
    >   stores minWaitedLSN = 100
    >   reads replayLSN before the waker publishes the new replay position
    >   sees replayLSN = 90
    >   decides it should sleep
    >
    > Waker:
    >   publishes replayLSN = 100
    >   reads old minWaitedLSN = PG_UINT64_MAX
    >   skips the wakeup
    >
    > Then the waiter goes to sleep even though replay has reached its
    > target LSN. To avoid this, we still need to make sure that the
    > publication of replayLSN precedes the read of minWaitedLSN, so that
    > the waker cannot decide "nobody is waiting" before its own progress is
    > still not visible to the waiter.
    
    Yes, I also think the memory barrier for waker between publishing
    replayLSN and reading minWaitedLSN is required.  However, sequence of
    three WaitLSNWakeup() calls makes 3 memory barriers while only one is
    required.  We could introduce a hierarchy for WAIT_LSN_TYPE_STANDBY_*:
    WAIT_LSN_TYPE_STANDBY_FLUSH implies WAIT_LSN_TYPE_STANDBY_WRITE,
    WAIT_LSN_TYPE_STANDBY_REPLAY implies WAIT_LSN_TYPE_STANDBY_WRITE and
    WAIT_LSN_TYPE_STANDBY_FLUSH.  Then ApplyWalRecord() can call
    WaitLSNWakeup() only once and make only 1 memory barrier.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase