Subject: Re: Replication slot WAL reservation

From: Phillip Diffley <[email-masked]>
Date: Tue, 25 Mar 2025 22:56:38 -0500

> You shouldn't need to manually advance the replication slot.
> The client is also expected to send back regular messages letting the
publisher / primary know that it has successfully consumed up to a
particular point

I was thinking of these as the same thing, but it sounds like they are
different. At the moment, the only method I know for letting the
publisher/primary know what has been successfully consumed is
pg_replication_slot_advance. I looked at the message formats
<https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS>
and logical replication message formats
<https://www.postgresql.org/docs/current/protocol-logicalrep-message-formats.html>
pages, but I did not see a message type for updating confirmed_flush_lsn or
otherwise letting the publisher/primary know what logs have been
successfully consumed. There is the flush
<https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-FLUSH>
message, but it looks like it only passes a 4 byte int instead of the 8
bytes required for an LSN. Is there a message type that is used to confirm
what logs have been successfully consumed?

> You know that any WAL generated after `confirmed_flush_lsn` is available
for replay.

The part I am uncertain about is what "after" means here, since LSNs are
not presented in order, and the order of data streamed over the replication
slot does not match the order of the data in the WAL.

I initially (and incorrectly) thought the confirmation order was based on
LSN. So if you confirmed an LSN "x" then all logs with LSN less than "x"
could be released by the publisher/primary. That can't work though since
LSNs are not presented in order by the replication slot. Is there a
monotonically increasing identifier that can be used to identify which logs
come "after" another? Or do you just keep track of the order the
replication slot delivers logs in and not confirm a log until it and all
the logs received before it are processed to the point of being crash-proof?

On Tue, Mar 25, 2025 at 4:32 PM Christophe Pettus <[email-masked]> wrote:

>
>
> > On Mar 25, 2025, at 13:58, Phillip Diffley <[email-masked]>
> wrote:
> >
> > Oh I see! I was conflating the data I see coming out of a replication
> slot with the internal organization of the WAL. I think the more specific
> question I am trying to answer is, as a consumer of a replication slot, how
> do I reason about what replication records will be made unavailable when I
> confirm an LSN? Here I am worried about situations where the replication
> connection is interrupted or the program processing the records crashes,
> and we need to replay records that may have been previously sent but were
> not fully processed.
>
> It's up to the consuming client to keep track of where it is in the WAL
> (using an LSN).  When the client connects, it specifies what LSN to start
> streaming at.  If that LSN is no longer available, the publisher / primary
> returns an error.
>
> The client shouldn't confirm the flush of an LSN unless it is crash-proof
> to that point, since any WAL before that should be assumed to be
> unavailable.
>
> > For example, are the records sent by a replication slot always sent in
> the same order such that if I advance the confirmed_flush_lsn of a slot to
> the LSN of record "A", I will know that any records that had been streamed
> after record "A" will be replayable?
>
> You know that any WAL generated after `confirmed_flush_lsn` is available
> for replay.  That's the oldest LSN that the client can specify on
> connection (although it can specify a later one, if it exists).  You
> shouldn't need to manually advance the replication slot.  Instead, the
> client specifies where it wants to start when it connects.  The client is
> also expected to send back regular messages letting the publisher / primary
> know that it has successfully consumed up to a particular point in the WAL,
> so the publisher / primary knows it can release that WAL information.

---

From: Christophe Pettus <[email-masked]>
Date: Tue, 25 Mar 2025 21:32:26 -0700

> On Mar 25, 2025, at 20:56, Phillip Diffley <[email-masked]> wrote:
> 
> Is there a message type that is used to confirm what logs have been successfully consumed?

You're looking for Standby Status Update:

	https://www.postgresql.org/docs/current/protocol-replication.html#PROTOCOL-REPLICATION-STANDBY-STATUS-UPDATE

The logical replication protocol is a superset of the binary replication protocol, so many of the same messages are used.

> The part I am uncertain about is what "after" means here, since LSNs are not presented in order, and the order of data streamed over the replication slot does not match the order of the data in the WAL. 

I think there's a misunderstanding here (possibly my fault).  Transactions are always presented to the output plugin in commit order, and LSNs can be reliably used to determine the time ordering of commits.  LSNs are exactly what is used to determine how far into the WAL the replication slot has gotten.

---

From: Phillip Diffley <[email-masked]>
Date: Wed, 26 Mar 2025 09:55:06 -0500

> You're looking for Standby Status Update

Awesome! I completely missed that.

> Transactions are always presented to the output plugin in commit order

Ok great. I think that is what I needed to know.

Just to confirm, it sounds like the order messages are sent from the output
plugin is what matters here. When you update confirmed_flush_lsn to LSN
"A", any messages that were sent by the output plugin after the message
with LSN "A" will be replayable. Any messages sent by the output plugin
before the message with LSN "A" will most likely not be replayed, since
their data is freed for deletion. Is that correct?

On Tue, Mar 25, 2025 at 11:32 PM Christophe Pettus <[email-masked]> wrote:

> The logical replication protocol is a superset of the binary replication
> protocol, so many of the same messages are used.
>
> I think there's a misunderstanding here (possibly my fault).  Transactions
> are always presented to the output plugin in commit order, and LSNs can be
> reliably used to determine the time ordering of commits.  LSNs are exactly
> what is used to determine how far into the WAL the replication slot has
> gotten.

---

From: Christophe Pettus <[email-masked]>
Date: Wed, 26 Mar 2025 09:18:00 -0700

> On Mar 26, 2025, at 07:55, Phillip Diffley <[email-masked]> wrote:
> Just to confirm, it sounds like the order messages are sent from the output plugin is what matters here. When you update confirmed_flush_lsn to LSN "A", any messages that were sent by the output plugin after the message with LSN "A" will be replayable. Any messages sent by the output plugin before the message with LSN "A" will most likely not be replayed, since their data is freed for deletion. Is that correct?

The terminology is shifting around a bit here, so to be specific: When the primary (or publisher) receives a message from the secondary (or replica) that a particular LSN has been flushed, the primary at that point feels free to recycle any WAL segments that only contain WAL entries whose LSN is less than that flush point (whether or not it actually does depends on a lot of other factors).  The actual horizon that the primary needs to retain can be farther back than that, because there's no requirement that the secondary send an LSN as confirmed_flush_lsn that is at a transaction boundary, so the flush LSN might land in the middle of a transaction.  The actual point before which the primary can recycle WAL is restart_lsn, which the primary determines based on the flush LSN.

When the secondary connects, it provides an LSN from which the primary should start sending WAL (if a binary replica) or decoded WAL via the plugin (if a logical replica).  For a logical replica, that can be confirmed_flush_lsn or any point after, but it can't be before.  (Even if the WAL exists, the primary will return an error if the start point provided in START_REPLICATION is before confirmed_flush_lsn for a logical replication slot.)  Of course, you'll get an error if START_REPLICATION supplies an LSN that doesn't actually exist yet.

The behavior that the primary is expecting from the secondary is that the secondary never sends back a confirmed_flush_lsn until up to that point is crash / disconnection-safe.  What "safe" means in this case depends on the client behavior.  It might be just spooling the incoming stream to disk and processing it later, or it might be processing it completely on the fly as it comes in.

The most important point here is that the client consuming the logical replication messages must keep track of the flush point (defined however the client implements processing the messages), and provide the right one back to the primary when it connects.  (Another option is that that the client is written so that each transaction is idempotent, and even if transactions that it has already processed are sent again, the result is the same.)

One more note is that if the client supplies an LSN (for logical replication) that lands in the middle of a transaction, the primary will send over the complete transaction, so the actual start point may be earlier than the supplied start point.  Generally, this means that the client should respect transaction boundaries, and be able to deal with getting a partial transaction but discarding it if it doesn't get a commit record for it.