Thread: Logical Replication of sequences

Logical Replication of sequences

From
Amit Kapila
Date:
In the past, we have discussed various approaches to replicate
sequences by decoding the sequence changes from WAL. However, we faced
several challenges to achieve the same, some of which are due to the
non-transactional nature of sequences. The major ones were: (a)
correctness of the decoding part, some of the problems were discussed
at [1][2][3] (b) handling of sequences especially adding certain
sequences automatically (e.g. sequences backing SERIAL/BIGSERIAL
columns) for built-in logical replication is not considered in the
proposed work [1] (c) there were some performance concerns in not so
frequent scenarios [4] (see performance issues), we can probably deal
with this by making sequences optional for builtin logical replication

It could be possible that we can deal with these and any other issues
with more work but as the use case for this feature is primarily major
version upgrades it is not clear that we want to make such a big
change to the code or are there better alternatives to achieve the
same.

This time at pgconf.dev (https://2024.pgconf.dev/), we discussed
alternative approaches for this work which I would like to summarize.
The various methods we discussed are as follows:

1. Provide a tool to copy all the sequences from publisher to
subscriber. The major drawback is that users need to perform this as
an additional step during the upgrade which would be inconvenient and
probably not as useful as some built-in mechanism.
2. Provide a command say Alter Subscription ...  Replicate Sequences
(or something like that) which users can perform before shutdown of
the publisher node during upgrade. This will allow copying all the
sequences from the publisher node to the subscriber node directly.
Similar to previous approach, this could also be inconvenient for
users.
3. Replicate published sequences via walsender at the time of shutdown
or incrementally while decoding checkpoint record. The two ways to
achieve this are: (a) WAL log a special NOOP record just before
shutting down checkpointer. Then allow the WALsender to read the
sequence data and send it to the subscriber while decoding the new
NOOP record. (b) Similar to the previous idea but instead of WAL
logging a new record directly invokes a decoding callback after
walsender receives a request to shutdown which will allow pgoutput to
read and send required sequences. This approach has a drawback that we
are adding more work at the time of shutdown but note that we already
waits for all the WAL records to be decoded and sent before shutting
down the walsender during shutdown of the node.

Any other ideas?

I have added the members I remember that were part of the discussion
in the email. Please feel free to correct me if I have misunderstood
or missed any point we talked about.

Thoughts?

[1] - https://www.postgresql.org/message-id/e4145f77-6f37-40e0-a770-aba359c50b93%40enterprisedb.com
[2] - https://www.postgresql.org/message-id/CAA4eK1Lxt%2B5a9fA-B7FRzfd1vns%3DEwZTF5z9_xO9Ms4wsqD88Q%40mail.gmail.com
[3] - https://www.postgresql.org/message-id/CAA4eK1KR4%3DyALKP0pOdVkqUwoUqD_v7oU3HzY-w0R_EBvgHL2w%40mail.gmail.com
[4] - https://www.postgresql.org/message-id/12822961-b7de-9d59-dd27-2e3dc3980c7e%40enterprisedb.com

-- 
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Bharath Rupireddy
Date:
Hi,

On Tue, Jun 4, 2024 at 4:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> 3. Replicate published sequences via walsender at the time of shutdown
> or incrementally while decoding checkpoint record. The two ways to
> achieve this are: (a) WAL log a special NOOP record just before
> shutting down checkpointer. Then allow the WALsender to read the
> sequence data and send it to the subscriber while decoding the new
> NOOP record. (b) Similar to the previous idea but instead of WAL
> logging a new record directly invokes a decoding callback after
> walsender receives a request to shutdown which will allow pgoutput to
> read and send required sequences. This approach has a drawback that we
> are adding more work at the time of shutdown but note that we already
> waits for all the WAL records to be decoded and sent before shutting
> down the walsender during shutdown of the node.

Thanks. IIUC, both of the above approaches decode the sequences during
only shutdown. I'm wondering, why not periodically decode and
replicate the published sequences so that the decoding at the shutdown
will not take that longer? I can imagine a case where there are tens
of thousands of sequences in a production server, and surely decoding
and sending them just during the shutdown can take a lot of time
hampering the overall server uptime.

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



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Jun 4, 2024 at 4:53 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 4:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > 3. Replicate published sequences via walsender at the time of shutdown
> > or incrementally while decoding checkpoint record. The two ways to
> > achieve this are: (a) WAL log a special NOOP record just before
> > shutting down checkpointer. Then allow the WALsender to read the
> > sequence data and send it to the subscriber while decoding the new
> > NOOP record. (b) Similar to the previous idea but instead of WAL
> > logging a new record directly invokes a decoding callback after
> > walsender receives a request to shutdown which will allow pgoutput to
> > read and send required sequences. This approach has a drawback that we
> > are adding more work at the time of shutdown but note that we already
> > waits for all the WAL records to be decoded and sent before shutting
> > down the walsender during shutdown of the node.
>
> Thanks. IIUC, both of the above approaches decode the sequences during
> only shutdown. I'm wondering, why not periodically decode and
> replicate the published sequences so that the decoding at the shutdown
> will not take that longer?
>

Even if we decode it periodically (say each time we decode the
checkpoint record) then also we need to send the entire set of
sequences at shutdown. This is because the sequences may have changed
from the last time we sent them.

>
> I can imagine a case where there are tens
> of thousands of sequences in a production server, and surely decoding
> and sending them just during the shutdown can take a lot of time
> hampering the overall server uptime.
>

It is possible but we will send only the sequences that belong to
publications for which walsender is supposed to send the required
data. Now, we can also imagine providing option 2 (Alter Subscription
... Replicate Sequences) so that users can replicate sequences before
shutdown and then disable the subscriptions so that there won't be a
corresponding walsender.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Ashutosh Bapat
Date:


On Tue, Jun 4, 2024 at 4:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

3. Replicate published sequences via walsender at the time of shutdown
or incrementally while decoding checkpoint record. The two ways to
achieve this are: (a) WAL log a special NOOP record just before
shutting down checkpointer. Then allow the WALsender to read the
sequence data and send it to the subscriber while decoding the new
NOOP record. (b) Similar to the previous idea but instead of WAL
logging a new record directly invokes a decoding callback after
walsender receives a request to shutdown which will allow pgoutput to
read and send required sequences. This approach has a drawback that we
are adding more work at the time of shutdown but note that we already
waits for all the WAL records to be decoded and sent before shutting
down the walsender during shutdown of the node.

Any other ideas?


In case of primary crash the sequence won't get replicated. That is true even with the previous approach in case walsender is shut down because of a crash, but it is more serious with this approach. How about periodically sending this information? 

--
Best Wishes,
Ashutosh Bapat

Re: Logical Replication of sequences

From
Yogesh Sharma
Date:
On 6/4/24 06:57, Amit Kapila wrote:
> 1. Provide a tool to copy all the sequences from publisher to
> subscriber. The major drawback is that users need to perform this as
> an additional step during the upgrade which would be inconvenient and
> probably not as useful as some built-in mechanism.

Agree, this requires additional steps. Not a preferred approach in my
opinion. When a large set of sequences are present, it will add
additional downtime for upgrade process.

> 2. Provide a command say Alter Subscription ...  Replicate Sequences
> (or something like that) which users can perform before shutdown of
> the publisher node during upgrade. This will allow copying all the
> sequences from the publisher node to the subscriber node directly.
> Similar to previous approach, this could also be inconvenient for
> users.

This is similar to option 1 except that it is a SQL command now. Still
not a preferred approach in my opinion. When a large set of sequences are
present, it will add additional downtime for upgrade process.


> 3. Replicate published sequences via walsender at the time of shutdown
> or incrementally while decoding checkpoint record. The two ways to
> achieve this are: (a) WAL log a special NOOP record just before
> shutting down checkpointer. Then allow the WALsender to read the
> sequence data and send it to the subscriber while decoding the new
> NOOP record. (b) Similar to the previous idea but instead of WAL
> logging a new record directly invokes a decoding callback after
> walsender receives a request to shutdown which will allow pgoutput to
> read and send required sequences. This approach has a drawback that we
> are adding more work at the time of shutdown but note that we already
> waits for all the WAL records to be decoded and sent before shutting
> down the walsender during shutdown of the node.

At the time of shutdown a) most logical upgrades don't necessarily call
for shutdown b) it will still add to total downtime with large set of
sequences. Incremental option is better as it will not require a shutdown.

I do see a scenario where sequence of events can lead to loss of sequence
and generate duplicate sequence values, if subscriber starts consuming
sequences while publisher is also consuming them. In such cases, subscriber
shall not be allowed sequence consumption.



-- 
Kind Regards,
Yogesh Sharma
Open Source Enthusiast and Advocate
PostgreSQL Contributors Team @ RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com




Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Jun 4, 2024 at 7:40 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 4:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>>
>>
>> 3. Replicate published sequences via walsender at the time of shutdown
>> or incrementally while decoding checkpoint record. The two ways to
>> achieve this are: (a) WAL log a special NOOP record just before
>> shutting down checkpointer. Then allow the WALsender to read the
>> sequence data and send it to the subscriber while decoding the new
>> NOOP record. (b) Similar to the previous idea but instead of WAL
>> logging a new record directly invokes a decoding callback after
>> walsender receives a request to shutdown which will allow pgoutput to
>> read and send required sequences. This approach has a drawback that we
>> are adding more work at the time of shutdown but note that we already
>> waits for all the WAL records to be decoded and sent before shutting
>> down the walsender during shutdown of the node.
>>
>> Any other ideas?
>>
>
> In case of primary crash the sequence won't get replicated. That is true even with the previous approach in case
walsenderis shut down because of a crash, but it is more serious with this approach. 
>

Right, but if we just want to support a major version upgrade scenario
then this should be fine because upgrades require a clean shutdown.

>
 How about periodically sending this information?
>

Now, if we want to support some sort of failover then probably this
will help. Do you have that use case in mind? If we want to send
periodically then we can do it when decoding checkpoint
(XLOG_CHECKPOINT_ONLINE) or some other periodic WAL record like
running_xacts (XLOG_RUNNING_XACTS).

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Jun 4, 2024 at 8:56 PM Yogesh Sharma
<yogesh.sharma@catprosystems.com> wrote:
>
> On 6/4/24 06:57, Amit Kapila wrote:
>
> > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > (or something like that) which users can perform before shutdown of
> > the publisher node during upgrade. This will allow copying all the
> > sequences from the publisher node to the subscriber node directly.
> > Similar to previous approach, this could also be inconvenient for
> > users.
>
> This is similar to option 1 except that it is a SQL command now.
>

Right, but I would still prefer a command as it provides clear steps
for the upgrade. Users need to perform (a) Replicate Sequences for a
particular subscription (b) Disable that subscription (c) Perform (a)
and (b) for all the subscriptions corresponding to the publisher we
want to shut down for upgrade.

I agree there are some manual steps involved here but it is advisable
for users to ensure that they have received the required data on the
subscriber before the upgrade of the publisher node, otherwise, they
may not be able to continue replication after the upgrade. For
example, see the "Prepare for publisher upgrades" step in pg_upgrade
docs [1].

>
> > 3. Replicate published sequences via walsender at the time of shutdown
> > or incrementally while decoding checkpoint record. The two ways to
> > achieve this are: (a) WAL log a special NOOP record just before
> > shutting down checkpointer. Then allow the WALsender to read the
> > sequence data and send it to the subscriber while decoding the new
> > NOOP record. (b) Similar to the previous idea but instead of WAL
> > logging a new record directly invokes a decoding callback after
> > walsender receives a request to shutdown which will allow pgoutput to
> > read and send required sequences. This approach has a drawback that we
> > are adding more work at the time of shutdown but note that we already
> > waits for all the WAL records to be decoded and sent before shutting
> > down the walsender during shutdown of the node.
>
> At the time of shutdown a) most logical upgrades don't necessarily call
> for shutdown
>

Won't the major version upgrade expect that the node is down? Refer to
step "Stop both servers" in [1].

>
 b) it will still add to total downtime with large set of
> sequences. Incremental option is better as it will not require a shutdown.
>
> I do see a scenario where sequence of events can lead to loss of sequence
> and generate duplicate sequence values, if subscriber starts consuming
> sequences while publisher is also consuming them. In such cases, subscriber
> shall not be allowed sequence consumption.
>

It would be fine to not allow subscribers to consume sequences that
are being logically replicated but what about the cases where we
haven't sent the latest values of sequences before the shutdown of the
publisher? In such a case, the publisher would have already consumed
some values that wouldn't have been sent to the subscriber and now
when the publisher is down then even if we re-allow the sequence
values to be consumed from the subscriber, it can lead to duplicate
values.

[1] - https://www.postgresql.org/docs/devel/pgupgrade.html

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Peter Eisentraut
Date:
On 04.06.24 12:57, Amit Kapila wrote:
> 2. Provide a command say Alter Subscription ...  Replicate Sequences
> (or something like that) which users can perform before shutdown of
> the publisher node during upgrade. This will allow copying all the
> sequences from the publisher node to the subscriber node directly.
> Similar to previous approach, this could also be inconvenient for
> users.

I would start with this.  In any case, you're going to need to write 
code to collect all the sequence values, send them over some protocol, 
apply them on the subscriber.  The easiest way to start is to trigger 
that manually.  Then later you can add other ways to trigger it, either 
by timer or around shutdown, or whatever other ideas there might be.



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 5, 2024 at 9:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 8:56 PM Yogesh Sharma
> <yogesh.sharma@catprosystems.com> wrote:
> >
> > On 6/4/24 06:57, Amit Kapila wrote:
> >
> > > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > > (or something like that) which users can perform before shutdown of
> > > the publisher node during upgrade. This will allow copying all the
> > > sequences from the publisher node to the subscriber node directly.
> > > Similar to previous approach, this could also be inconvenient for
> > > users.
> >
> > This is similar to option 1 except that it is a SQL command now.
> >
>
> Right, but I would still prefer a command as it provides clear steps
> for the upgrade. Users need to perform (a) Replicate Sequences for a
> particular subscription (b) Disable that subscription (c) Perform (a)
> and (b) for all the subscriptions corresponding to the publisher we
> want to shut down for upgrade.
>

Another advantage of this approach over just a plain tool to copy all
sequences before upgrade is that here we can have the facility to copy
just the required sequences. I mean the set sequences that the user
has specified as part of the publication.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Bharath Rupireddy
Date:
Hi,

On Tue, Jun 4, 2024 at 5:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> Even if we decode it periodically (say each time we decode the
> checkpoint record) then also we need to send the entire set of
> sequences at shutdown. This is because the sequences may have changed
> from the last time we sent them.

Agree. How about decoding and sending only the sequences that are
changed from the last time when they were sent? I know it requires a
bit of tracking and more work, but all I'm looking for is to reduce
the amount of work that walsenders need to do during the shutdown.

Having said that, I like the idea of letting the user sync the
sequences via ALTER SUBSCRIPTION command and not weave the logic into
the shutdown checkpoint path. As Peter Eisentraut said here
https://www.postgresql.org/message-id/42e5cb35-4aeb-4f58-8091-90619c7c3ecc%40eisentraut.org,
this can be a good starting point to get going.

> > I can imagine a case where there are tens
> > of thousands of sequences in a production server, and surely decoding
> > and sending them just during the shutdown can take a lot of time
> > hampering the overall server uptime.
>
> It is possible but we will send only the sequences that belong to
> publications for which walsender is supposed to send the required
> data.

Right, but what if all the publication tables can have tens of
thousands of sequences.

> Now, we can also imagine providing option 2 (Alter Subscription
> ... Replicate Sequences) so that users can replicate sequences before
> shutdown and then disable the subscriptions so that there won't be a
> corresponding walsender.

As stated above, I like this idea to start with.

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



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 5, 2024 at 12:51 PM Peter Eisentraut <peter@eisentraut.org> wrote:
>
> On 04.06.24 12:57, Amit Kapila wrote:
> > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > (or something like that) which users can perform before shutdown of
> > the publisher node during upgrade. This will allow copying all the
> > sequences from the publisher node to the subscriber node directly.
> > Similar to previous approach, this could also be inconvenient for
> > users.
>
> I would start with this.  In any case, you're going to need to write
> code to collect all the sequence values, send them over some protocol,
> apply them on the subscriber.  The easiest way to start is to trigger
> that manually.  Then later you can add other ways to trigger it, either
> by timer or around shutdown, or whatever other ideas there might be.
>

Agreed. To achieve this, we can allow sequences to be copied during
the initial CREATE SUBSCRIPTION command similar to what we do for
tables. And then later by new/existing command, we re-copy the already
existing sequences on the subscriber.

The options for the new command could be:
Alter Subscription ... Refresh Sequences
Alter Subscription ... Replicate Sequences

In the second option, we need to introduce a new keyword Replicate.
Can you think of any better option?

In addition to the above, the command Alter Subscription .. Refresh
Publication will fetch any missing sequences similar to what it does
for tables.

Thoughts?

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Ashutosh Bapat
Date:


On Wed, Jun 5, 2024 at 8:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Tue, Jun 4, 2024 at 7:40 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 4:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>>
>>
>> 3. Replicate published sequences via walsender at the time of shutdown
>> or incrementally while decoding checkpoint record. The two ways to
>> achieve this are: (a) WAL log a special NOOP record just before
>> shutting down checkpointer. Then allow the WALsender to read the
>> sequence data and send it to the subscriber while decoding the new
>> NOOP record. (b) Similar to the previous idea but instead of WAL
>> logging a new record directly invokes a decoding callback after
>> walsender receives a request to shutdown which will allow pgoutput to
>> read and send required sequences. This approach has a drawback that we
>> are adding more work at the time of shutdown but note that we already
>> waits for all the WAL records to be decoded and sent before shutting
>> down the walsender during shutdown of the node.
>>
>> Any other ideas?
>>
>
> In case of primary crash the sequence won't get replicated. That is true even with the previous approach in case walsender is shut down because of a crash, but it is more serious with this approach.
>

Right, but if we just want to support a major version upgrade scenario
then this should be fine because upgrades require a clean shutdown.

>
 How about periodically sending this information?
>

Now, if we want to support some sort of failover then probably this
will help. Do you have that use case in mind?

Regular failover was a goal for supporting logical replication of sequences. That might be more common than major upgrade scenario.
 
If we want to send
periodically then we can do it when decoding checkpoint
(XLOG_CHECKPOINT_ONLINE) or some other periodic WAL record like
running_xacts (XLOG_RUNNING_XACTS).


Yeah. I am thinking along those lines.

It must be noted, however, that none of those optional make sure that the replicated sequence's states are consistent with the replicated object state which use those sequences. E.g. table t1 uses sequence s1. By last sequence replication, as of time T1, let's say t1 had consumed values upto vl1 from s1. But later, by time T2, it consumed values upto vl2 which were not replicated but the changes to t1 by T2 were replicated. If failover happens at that point, INSERTs on t1 would fail because of duplicate keys (values between vl1 and vl2). Previous attempt to support logical sequence replication solved this problem by replicating a future state of sequences (current value +/- log count). Similarly, if the sequence was ALTERed between T1 and T2, the state of sequence on replica would be inconsistent with the state of t1. Failing over at this stage, might end t1 in an inconsistent state.

--
Best Wishes,
Ashutosh Bapat

Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 5, 2024 at 6:01 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 8:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>>
>>  How about periodically sending this information?
>> >
>>
>> Now, if we want to support some sort of failover then probably this
>> will help. Do you have that use case in mind?
>
>
> Regular failover was a goal for supporting logical replication of sequences. That might be more common than major
upgradescenario. 
>

We can't support regular failovers to subscribers unless we can
replicate/copy slots because the existing nodes connected to the
current publisher/primary would expect that. It should be primarily
useful for major version upgrades at this stage.

>>
>> If we want to send
>> periodically then we can do it when decoding checkpoint
>> (XLOG_CHECKPOINT_ONLINE) or some other periodic WAL record like
>> running_xacts (XLOG_RUNNING_XACTS).
>>
>
> Yeah. I am thinking along those lines.
>
> It must be noted, however, that none of those optional make sure that the replicated sequence's states are consistent
withthe replicated object state which use those sequences. 
>

Right, I feel as others are advocating, it seems better to support it
manually via command and then later we can extend it to do at shutdown
or at some regular intervals. If we do that then we should be able to
support major version upgrades and planned switchover.


--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Wed, Jun 5, 2024 at 12:43 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 8:56 PM Yogesh Sharma
> <yogesh.sharma@catprosystems.com> wrote:
> >
> > On 6/4/24 06:57, Amit Kapila wrote:
> >
> > > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > > (or something like that) which users can perform before shutdown of
> > > the publisher node during upgrade. This will allow copying all the
> > > sequences from the publisher node to the subscriber node directly.
> > > Similar to previous approach, this could also be inconvenient for
> > > users.
> >
> > This is similar to option 1 except that it is a SQL command now.
> >
>
> Right, but I would still prefer a command as it provides clear steps
> for the upgrade. Users need to perform (a) Replicate Sequences for a
> particular subscription (b) Disable that subscription (c) Perform (a)
> and (b) for all the subscriptions corresponding to the publisher we
> want to shut down for upgrade.
>
> I agree there are some manual steps involved here but it is advisable
> for users to ensure that they have received the required data on the
> subscriber before the upgrade of the publisher node, otherwise, they
> may not be able to continue replication after the upgrade. For
> example, see the "Prepare for publisher upgrades" step in pg_upgrade
> docs [1].
>
> >
> > > 3. Replicate published sequences via walsender at the time of shutdown
> > > or incrementally while decoding checkpoint record. The two ways to
> > > achieve this are: (a) WAL log a special NOOP record just before
> > > shutting down checkpointer. Then allow the WALsender to read the
> > > sequence data and send it to the subscriber while decoding the new
> > > NOOP record. (b) Similar to the previous idea but instead of WAL
> > > logging a new record directly invokes a decoding callback after
> > > walsender receives a request to shutdown which will allow pgoutput to
> > > read and send required sequences. This approach has a drawback that we
> > > are adding more work at the time of shutdown but note that we already
> > > waits for all the WAL records to be decoded and sent before shutting
> > > down the walsender during shutdown of the node.
> >
> > At the time of shutdown a) most logical upgrades don't necessarily call
> > for shutdown
> >
>
> Won't the major version upgrade expect that the node is down? Refer to
> step "Stop both servers" in [1].

I think the idea is that the publisher is the old version and the
subscriber is the new version, and changes generated on the publisher
are replicated to the subscriber via logical replication. And at some
point, we change the application (or a router) settings so that no
more transactions come to the publisher, do the last upgrade
preparation work (e.g. copying the latest sequence values if
requried), and then change the application so that new transactions
come to the subscriber.

I remember the blog post about Knock doing a similar process to
upgrade the clusters with minimal downtime[1].

Regards,

[1] https://knock.app/blog/zero-downtime-postgres-upgrades


--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 5, 2024 at 3:17 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
>
> On Tue, Jun 4, 2024 at 5:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > Even if we decode it periodically (say each time we decode the
> > checkpoint record) then also we need to send the entire set of
> > sequences at shutdown. This is because the sequences may have changed
> > from the last time we sent them.
>
> Agree. How about decoding and sending only the sequences that are
> changed from the last time when they were sent? I know it requires a
> bit of tracking and more work, but all I'm looking for is to reduce
> the amount of work that walsenders need to do during the shutdown.
>

I see your point but going towards tracking the changed sequences
sounds like moving towards what we do for incremental backups unless
we can invent some other smart way.

> Having said that, I like the idea of letting the user sync the
> sequences via ALTER SUBSCRIPTION command and not weave the logic into
> the shutdown checkpoint path. As Peter Eisentraut said here
> https://www.postgresql.org/message-id/42e5cb35-4aeb-4f58-8091-90619c7c3ecc%40eisentraut.org,
> this can be a good starting point to get going.
>

Agreed.

> > > I can imagine a case where there are tens
> > > of thousands of sequences in a production server, and surely decoding
> > > and sending them just during the shutdown can take a lot of time
> > > hampering the overall server uptime.
> >
> > It is possible but we will send only the sequences that belong to
> > publications for which walsender is supposed to send the required
> > data.
>
> Right, but what if all the publication tables can have tens of
> thousands of sequences.
>

In such cases we have no option but to send all the sequences.

> > Now, we can also imagine providing option 2 (Alter Subscription
> > ... Replicate Sequences) so that users can replicate sequences before
> > shutdown and then disable the subscriptions so that there won't be a
> > corresponding walsender.
>
> As stated above, I like this idea to start with.
>

+1.


--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 12:51 PM Peter Eisentraut <peter@eisentraut.org> wrote:
> >
> > On 04.06.24 12:57, Amit Kapila wrote:
> > > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > > (or something like that) which users can perform before shutdown of
> > > the publisher node during upgrade. This will allow copying all the
> > > sequences from the publisher node to the subscriber node directly.
> > > Similar to previous approach, this could also be inconvenient for
> > > users.
> >
> > I would start with this.  In any case, you're going to need to write
> > code to collect all the sequence values, send them over some protocol,
> > apply them on the subscriber.  The easiest way to start is to trigger
> > that manually.  Then later you can add other ways to trigger it, either
> > by timer or around shutdown, or whatever other ideas there might be.
> >
>
> Agreed.

+1

> To achieve this, we can allow sequences to be copied during
> the initial CREATE SUBSCRIPTION command similar to what we do for
> tables. And then later by new/existing command, we re-copy the already
> existing sequences on the subscriber.
>
> The options for the new command could be:
> Alter Subscription ... Refresh Sequences
> Alter Subscription ... Replicate Sequences
>
> In the second option, we need to introduce a new keyword Replicate.
> Can you think of any better option?

Another idea is doing that using options. For example,

For initial sequences synchronization:

CREATE SUBSCRIPTION ... WITH (copy_sequence = true);

For re-copy (or update) sequences:

ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);

>
> In addition to the above, the command Alter Subscription .. Refresh
> Publication will fetch any missing sequences similar to what it does
> for tables.

On the subscriber side, do we need to track which sequences are
created via CREATE/ALTER SUBSCRIPTION?

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Jun 6, 2024 at 9:32 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 12:43 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Tue, Jun 4, 2024 at 8:56 PM Yogesh Sharma
> > <yogesh.sharma@catprosystems.com> wrote:
> > >
> > > On 6/4/24 06:57, Amit Kapila wrote:
> > >
> > > > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > > > (or something like that) which users can perform before shutdown of
> > > > the publisher node during upgrade. This will allow copying all the
> > > > sequences from the publisher node to the subscriber node directly.
> > > > Similar to previous approach, this could also be inconvenient for
> > > > users.
> > >
> > > This is similar to option 1 except that it is a SQL command now.
> > >
> >
> > Right, but I would still prefer a command as it provides clear steps
> > for the upgrade. Users need to perform (a) Replicate Sequences for a
> > particular subscription (b) Disable that subscription (c) Perform (a)
> > and (b) for all the subscriptions corresponding to the publisher we
> > want to shut down for upgrade.
> >
> > I agree there are some manual steps involved here but it is advisable
> > for users to ensure that they have received the required data on the
> > subscriber before the upgrade of the publisher node, otherwise, they
> > may not be able to continue replication after the upgrade. For
> > example, see the "Prepare for publisher upgrades" step in pg_upgrade
> > docs [1].
> >
> > >
> > > > 3. Replicate published sequences via walsender at the time of shutdown
> > > > or incrementally while decoding checkpoint record. The two ways to
> > > > achieve this are: (a) WAL log a special NOOP record just before
> > > > shutting down checkpointer. Then allow the WALsender to read the
> > > > sequence data and send it to the subscriber while decoding the new
> > > > NOOP record. (b) Similar to the previous idea but instead of WAL
> > > > logging a new record directly invokes a decoding callback after
> > > > walsender receives a request to shutdown which will allow pgoutput to
> > > > read and send required sequences. This approach has a drawback that we
> > > > are adding more work at the time of shutdown but note that we already
> > > > waits for all the WAL records to be decoded and sent before shutting
> > > > down the walsender during shutdown of the node.
> > >
> > > At the time of shutdown a) most logical upgrades don't necessarily call
> > > for shutdown
> > >
> >
> > Won't the major version upgrade expect that the node is down? Refer to
> > step "Stop both servers" in [1].
>
> I think the idea is that the publisher is the old version and the
> subscriber is the new version, and changes generated on the publisher
> are replicated to the subscriber via logical replication. And at some
> point, we change the application (or a router) settings so that no
> more transactions come to the publisher, do the last upgrade
> preparation work (e.g. copying the latest sequence values if
> requried), and then change the application so that new transactions
> come to the subscriber.
>

Okay, thanks for sharing the exact steps. If one has to follow that
path then sending incrementally (at checkpoint WAL or other times)
won't work because we want to ensure that the sequences are up-to-date
before the application starts using the new database. To do that in a
bullet-proof way, one has to copy/replicate sequences during the
requests to the new database are paused (Reference from the blog you
shared: For the first second after flipping the flag, our application
artificially paused any new database requests for one second.).
Currently, they are using some guesswork to replicate sequences that
require manual verification and more manual work for each sequence.
The new command (Alter Subscription ... Replicate Sequence) should
ease their procedure and can do things where they would require no or
very less verification.

> I remember the blog post about Knock doing a similar process to
> upgrade the clusters with minimal downtime[1].
>

Thanks for sharing the blog post.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Jun 6, 2024 at 11:10 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
>
> > To achieve this, we can allow sequences to be copied during
> > the initial CREATE SUBSCRIPTION command similar to what we do for
> > tables. And then later by new/existing command, we re-copy the already
> > existing sequences on the subscriber.
> >
> > The options for the new command could be:
> > Alter Subscription ... Refresh Sequences
> > Alter Subscription ... Replicate Sequences
> >
> > In the second option, we need to introduce a new keyword Replicate.
> > Can you think of any better option?
>
> Another idea is doing that using options. For example,
>
> For initial sequences synchronization:
>
> CREATE SUBSCRIPTION ... WITH (copy_sequence = true);
>

How will it interact with the existing copy_data option? So copy_data
will become equivalent to copy_table_data, right?

> For re-copy (or update) sequences:
>
> ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);
>

Similar to the previous point it can be slightly confusing w.r.t
copy_data. And would copy_sequence here mean that it would copy
sequence values of both pre-existing and newly added sequences, if so,
that would make it behave differently than copy_data? The other
possibility in this direction would be to introduce an option like
replicate_all_sequences/copy_all_sequences which indicates a copy of
both pre-existing and new sequences, if any.

If we want to go in the direction of having an option such as
copy_(all)_sequences then do you think specifying that copy_data is
just for tables in the docs would be sufficient? I am afraid that it
would be confusing for users.

> >
> > In addition to the above, the command Alter Subscription .. Refresh
> > Publication will fetch any missing sequences similar to what it does
> > for tables.
>
> On the subscriber side, do we need to track which sequences are
> created via CREATE/ALTER SUBSCRIPTION?
>

I think so unless we find some other way to know at refresh
publication time which all new sequences need to be part of the
subscription. What should be the behavior w.r.t sequences when the
user performs ALTER SUBSCRIPTION ... REFRESH PUBLICATION? I was
thinking similar to tables, it should fetch any missing sequence
information from the publisher.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Ashutosh Bapat
Date:


On Thu, Jun 6, 2024 at 9:22 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Wed, Jun 5, 2024 at 6:01 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 8:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>>
>>  How about periodically sending this information?
>> >
>>
>> Now, if we want to support some sort of failover then probably this
>> will help. Do you have that use case in mind?
>
>
> Regular failover was a goal for supporting logical replication of sequences. That might be more common than major upgrade scenario.
>

We can't support regular failovers to subscribers unless we can
replicate/copy slots because the existing nodes connected to the
current publisher/primary would expect that. It should be primarily
useful for major version upgrades at this stage.

We don't want to design it in a way that requires major rework when we are able to copy slots and then support regular failovers. That's when the consistency between a sequence and the table using it would be a must. So it's better that we take that into consideration now.

--
Best Wishes,
Ashutosh Bapat

Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Jun 6, 2024 at 3:44 PM Ashutosh Bapat
<ashutosh.bapat.oss@gmail.com> wrote:
>
> On Thu, Jun 6, 2024 at 9:22 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>>
>> On Wed, Jun 5, 2024 at 6:01 PM Ashutosh Bapat
>> <ashutosh.bapat.oss@gmail.com> wrote:
>> >
>> > On Wed, Jun 5, 2024 at 8:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>> >>
>> >>  How about periodically sending this information?
>> >> >
>> >>
>> >> Now, if we want to support some sort of failover then probably this
>> >> will help. Do you have that use case in mind?
>> >
>> >
>> > Regular failover was a goal for supporting logical replication of sequences. That might be more common than major
upgradescenario. 
>> >
>>
>> We can't support regular failovers to subscribers unless we can
>> replicate/copy slots because the existing nodes connected to the
>> current publisher/primary would expect that. It should be primarily
>> useful for major version upgrades at this stage.
>
>
> We don't want to design it in a way that requires major rework when we are able to copy slots and then support
regularfailover. 
>

I don't think we can just copy slots like we do for standbys. The
slots would require WAL locations to continue, so not sure if we can
make it work for failover for subscribers.

>
 That's when the consistency between a sequence and the table using it
would be a must. So it's better that we take that into consideration
now.
>

With the ideas being discussed here, I could only see the use case of
a major version upgrade or planned switchover to work. If we come up
with any other agreeable way that is better than this then we can
consider the same.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Dilip Kumar
Date:
On Thu, Jun 6, 2024 at 9:34 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 3:17 PM Bharath Rupireddy
> <bharath.rupireddyforpostgres@gmail.com> wrote:
> >
> > On Tue, Jun 4, 2024 at 5:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > > Even if we decode it periodically (say each time we decode the
> > > checkpoint record) then also we need to send the entire set of
> > > sequences at shutdown. This is because the sequences may have changed
> > > from the last time we sent them.
> >
> > Agree. How about decoding and sending only the sequences that are
> > changed from the last time when they were sent? I know it requires a
> > bit of tracking and more work, but all I'm looking for is to reduce
> > the amount of work that walsenders need to do during the shutdown.
> >
>
> I see your point but going towards tracking the changed sequences
> sounds like moving towards what we do for incremental backups unless
> we can invent some other smart way.

Yes, we would need an entirely new infrastructure to track the
sequence change since the last sync. We can only determine this from
WAL, and relying on it would somehow bring us back to the approach we
were trying to achieve with logical decoding of sequences patch.

> > Having said that, I like the idea of letting the user sync the
> > sequences via ALTER SUBSCRIPTION command and not weave the logic into
> > the shutdown checkpoint path. As Peter Eisentraut said here
> > https://www.postgresql.org/message-id/42e5cb35-4aeb-4f58-8091-90619c7c3ecc%40eisentraut.org,
> > this can be a good starting point to get going.
> >
>
> Agreed.

+1

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Thu, Jun 6, 2024 at 6:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Thu, Jun 6, 2024 at 11:10 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> >
> > > To achieve this, we can allow sequences to be copied during
> > > the initial CREATE SUBSCRIPTION command similar to what we do for
> > > tables. And then later by new/existing command, we re-copy the already
> > > existing sequences on the subscriber.
> > >
> > > The options for the new command could be:
> > > Alter Subscription ... Refresh Sequences
> > > Alter Subscription ... Replicate Sequences
> > >
> > > In the second option, we need to introduce a new keyword Replicate.
> > > Can you think of any better option?
> >
> > Another idea is doing that using options. For example,
> >
> > For initial sequences synchronization:
> >
> > CREATE SUBSCRIPTION ... WITH (copy_sequence = true);
> >
>
> How will it interact with the existing copy_data option? So copy_data
> will become equivalent to copy_table_data, right?

Right.

>
> > For re-copy (or update) sequences:
> >
> > ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);
> >
>
> Similar to the previous point it can be slightly confusing w.r.t
> copy_data. And would copy_sequence here mean that it would copy
> sequence values of both pre-existing and newly added sequences, if so,
> that would make it behave differently than copy_data?  The other
> possibility in this direction would be to introduce an option like
> replicate_all_sequences/copy_all_sequences which indicates a copy of
> both pre-existing and new sequences, if any.

Copying sequence data works differently than replicating table data
(initial data copy and logical replication). So I thought the
copy_sequence option (or whatever better name) always does both
updating pre-existing sequences and adding new sequences. REFRESH
PUBLICATION updates the tables to be subscribed, so we also update or
add sequences associated to these tables.

>
> If we want to go in the direction of having an option such as
> copy_(all)_sequences then do you think specifying that copy_data is
> just for tables in the docs would be sufficient? I am afraid that it
> would be confusing for users.

I see your point. But I guess it would not be very problematic as it
doesn't break the current behavior and copy_(all)_sequences is
primarily for upgrade use cases.

>
> > >
> > > In addition to the above, the command Alter Subscription .. Refresh
> > > Publication will fetch any missing sequences similar to what it does
> > > for tables.
> >
> > On the subscriber side, do we need to track which sequences are
> > created via CREATE/ALTER SUBSCRIPTION?
> >
>
> I think so unless we find some other way to know at refresh
> publication time which all new sequences need to be part of the
> subscription. What should be the behavior w.r.t sequences when the
> user performs ALTER SUBSCRIPTION ... REFRESH PUBLICATION? I was
> thinking similar to tables, it should fetch any missing sequence
> information from the publisher.

It seems to make sense to me. But I have one question: do we want to
support replicating sequences that are not associated with any tables?
if yes, what if we refresh two different subscriptions that subscribe
to different tables on the same database? On the other hand, if no
(i.e. replicating only sequences owned by tables), can we know which
sequences to replicate by checking the subscribed tables?

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Fri, Jun 7, 2024 at 7:55 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Thu, Jun 6, 2024 at 6:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Thu, Jun 6, 2024 at 11:10 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> > > On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > >
> > > > To achieve this, we can allow sequences to be copied during
> > > > the initial CREATE SUBSCRIPTION command similar to what we do for
> > > > tables. And then later by new/existing command, we re-copy the already
> > > > existing sequences on the subscriber.
> > > >
> > > > The options for the new command could be:
> > > > Alter Subscription ... Refresh Sequences
> > > > Alter Subscription ... Replicate Sequences
> > > >
> > > > In the second option, we need to introduce a new keyword Replicate.
> > > > Can you think of any better option?
> > >
> > > Another idea is doing that using options. For example,
> > >
> > > For initial sequences synchronization:
> > >
> > > CREATE SUBSCRIPTION ... WITH (copy_sequence = true);
> > >
> >
> > How will it interact with the existing copy_data option? So copy_data
> > will become equivalent to copy_table_data, right?
>
> Right.
>
> >
> > > For re-copy (or update) sequences:
> > >
> > > ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);
> > >
> >
> > Similar to the previous point it can be slightly confusing w.r.t
> > copy_data. And would copy_sequence here mean that it would copy
> > sequence values of both pre-existing and newly added sequences, if so,
> > that would make it behave differently than copy_data?  The other
> > possibility in this direction would be to introduce an option like
> > replicate_all_sequences/copy_all_sequences which indicates a copy of
> > both pre-existing and new sequences, if any.
>
> Copying sequence data works differently than replicating table data
> (initial data copy and logical replication). So I thought the
> copy_sequence option (or whatever better name) always does both
> updating pre-existing sequences and adding new sequences. REFRESH
> PUBLICATION updates the tables to be subscribed, so we also update or
> add sequences associated to these tables.
>

Are you imagining the behavior for sequences associated with tables
differently than the ones defined by the CREATE SEQUENCE .. command? I
was thinking that users would associate sequences with publications
similar to what we do for tables for both cases. For example, they
need to explicitly mention the sequences they want to replicate by
commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
SEQUENCES IN SCHEMA sch1;

In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
should copy both the explicitly defined sequences and sequences
defined with the tables. Do you think a different variant for just
copying sequences implicitly associated with tables (say for identity
columns)?

>
> >
> > > >
> > > > In addition to the above, the command Alter Subscription .. Refresh
> > > > Publication will fetch any missing sequences similar to what it does
> > > > for tables.
> > >
> > > On the subscriber side, do we need to track which sequences are
> > > created via CREATE/ALTER SUBSCRIPTION?
> > >
> >
> > I think so unless we find some other way to know at refresh
> > publication time which all new sequences need to be part of the
> > subscription. What should be the behavior w.r.t sequences when the
> > user performs ALTER SUBSCRIPTION ... REFRESH PUBLICATION? I was
> > thinking similar to tables, it should fetch any missing sequence
> > information from the publisher.
>
> It seems to make sense to me. But I have one question: do we want to
> support replicating sequences that are not associated with any tables?
>

Yes, unless we see a problem with it.

> if yes, what if we refresh two different subscriptions that subscribe
> to different tables on the same database?

What problem do you see with it?

>
 On the other hand, if no
> (i.e. replicating only sequences owned by tables), can we know which
> sequences to replicate by checking the subscribed tables?
>

Sorry, I didn't understand your question. Can you please try to
explain in more words or use some examples?

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Jun 5, 2024 at 9:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Tue, Jun 4, 2024 at 8:56 PM Yogesh Sharma
> > <yogesh.sharma@catprosystems.com> wrote:
> > >
> > > On 6/4/24 06:57, Amit Kapila wrote:
> > >
> > > > 2. Provide a command say Alter Subscription ...  Replicate Sequences
> > > > (or something like that) which users can perform before shutdown of
> > > > the publisher node during upgrade. This will allow copying all the
> > > > sequences from the publisher node to the subscriber node directly.
> > > > Similar to previous approach, this could also be inconvenient for
> > > > users.
> > >
> > > This is similar to option 1 except that it is a SQL command now.
> > >
> >
> > Right, but I would still prefer a command as it provides clear steps
> > for the upgrade. Users need to perform (a) Replicate Sequences for a
> > particular subscription (b) Disable that subscription (c) Perform (a)
> > and (b) for all the subscriptions corresponding to the publisher we
> > want to shut down for upgrade.
> >
>
> Another advantage of this approach over just a plain tool to copy all
> sequences before upgrade is that here we can have the facility to copy
> just the required sequences. I mean the set sequences that the user
> has specified as part of the publication.

Here is a WIP patch to handle synchronizing the sequence during
create/alter subscription. The following changes were made for it:
Subscriber modifications:
Enable sequence synchronization during subscription creation or
alteration using the following syntax:
CREATE SUBSCRIPTION ... WITH (sequences=true);
When a subscription is created with the sequence option enabled, the
sequence list from the specified publications in the subscription will
be retrieved from the publisher. Each sequence's data will then be
copied from the remote publisher sequence to the local subscriber
sequence by using a wal receiver connection. Since all of the sequence
updating is done within a single transaction, if any errors occur
during the copying process, the entire transaction will be rolled
back.

To refresh sequences, use the syntax:
ALTER SUBSCRIPTION REFRESH SEQUENCES;
During sequence refresh, the sequence list is updated by removing
stale sequences and adding any missing sequences. The updated sequence
list is then re-synchronized.

A new catalog table, pg_subscription_seq, has been introduced for
mapping subscriptions to sequences. Additionally, the sequence LSN
(Log Sequence Number) is stored, facilitating determination of
sequence changes occurring before or after the returned sequence
state.

I have taken some code changes from Tomas's patch at [1].
I'll adjust the syntax as needed based on the ongoing discussion at  [2].

[1] - https://www.postgresql.org/message-id/09613730-5ee9-4cc3-82d8-f089be90aa64%40enterprisedb.com
[2] - https://www.postgresql.org/message-id/CAA4eK1K2X%2BPaErtGVQPD0k_5XqxjV_Cwg37%2B-pWsmKFncwc7Wg%40mail.gmail.com

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Fri, Jun 7, 2024 at 7:55 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Thu, Jun 6, 2024 at 6:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > > On Thu, Jun 6, 2024 at 11:10 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > >
> > > > On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > >
> > > >
> > > > > To achieve this, we can allow sequences to be copied during
> > > > > the initial CREATE SUBSCRIPTION command similar to what we do for
> > > > > tables. And then later by new/existing command, we re-copy the already
> > > > > existing sequences on the subscriber.
> > > > >
> > > > > The options for the new command could be:
> > > > > Alter Subscription ... Refresh Sequences
> > > > > Alter Subscription ... Replicate Sequences
> > > > >
> > > > > In the second option, we need to introduce a new keyword Replicate.
> > > > > Can you think of any better option?
> > > >
> > > > Another idea is doing that using options. For example,
> > > >
> > > > For initial sequences synchronization:
> > > >
> > > > CREATE SUBSCRIPTION ... WITH (copy_sequence = true);
> > > >
> > >
> > > How will it interact with the existing copy_data option? So copy_data
> > > will become equivalent to copy_table_data, right?
> >
> > Right.
> >
> > >
> > > > For re-copy (or update) sequences:
> > > >
> > > > ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);
> > > >
> > >
> > > Similar to the previous point it can be slightly confusing w.r.t
> > > copy_data. And would copy_sequence here mean that it would copy
> > > sequence values of both pre-existing and newly added sequences, if so,
> > > that would make it behave differently than copy_data?  The other
> > > possibility in this direction would be to introduce an option like
> > > replicate_all_sequences/copy_all_sequences which indicates a copy of
> > > both pre-existing and new sequences, if any.
> >
> > Copying sequence data works differently than replicating table data
> > (initial data copy and logical replication). So I thought the
> > copy_sequence option (or whatever better name) always does both
> > updating pre-existing sequences and adding new sequences. REFRESH
> > PUBLICATION updates the tables to be subscribed, so we also update or
> > add sequences associated to these tables.
> >
>
> Are you imagining the behavior for sequences associated with tables
> differently than the ones defined by the CREATE SEQUENCE .. command? I
> was thinking that users would associate sequences with publications
> similar to what we do for tables for both cases. For example, they
> need to explicitly mention the sequences they want to replicate by
> commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> SEQUENCES IN SCHEMA sch1;
>
> In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> should copy both the explicitly defined sequences and sequences
> defined with the tables. Do you think a different variant for just
> copying sequences implicitly associated with tables (say for identity
> columns)?

Oh, I was thinking that your proposal was to copy literally all
sequences by REPLICA/REFRESH SEQUENCE command. But it seems to make
sense to explicitly specify the sequences they want to replicate. It
also means that they can create a publication that has only sequences.
In this case, even if they create a subscription for that publication,
we don't launch any apply workers for that subscription. Right?

Also, given that the main use case (at least as the first step) is
version upgrade, do we really need to support SEQUENCES IN SCHEMA and
even FOR SEQUENCE? The WIP patch Vignesh recently submitted is more
than 6k lines. I think we can cut the scope for the first
implementation so as to make the review easy.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amul Sul
Date:


On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
[...]
A new catalog table, pg_subscription_seq, has been introduced for
mapping subscriptions to sequences. Additionally, the sequence LSN
(Log Sequence Number) is stored, facilitating determination of
sequence changes occurring before or after the returned sequence
state.
 
Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
something. How do you track sequence mapping with the publication?

Regards,
Amul

Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Fri, Jun 7, 2024 at 7:55 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> > > On Thu, Jun 6, 2024 at 6:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > > > On Thu, Jun 6, 2024 at 11:10 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > > >
> > > > > On Wed, Jun 5, 2024 at 9:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > >
> > > > >
> > > > > > To achieve this, we can allow sequences to be copied during
> > > > > > the initial CREATE SUBSCRIPTION command similar to what we do for
> > > > > > tables. And then later by new/existing command, we re-copy the already
> > > > > > existing sequences on the subscriber.
> > > > > >
> > > > > > The options for the new command could be:
> > > > > > Alter Subscription ... Refresh Sequences
> > > > > > Alter Subscription ... Replicate Sequences
> > > > > >
> > > > > > In the second option, we need to introduce a new keyword Replicate.
> > > > > > Can you think of any better option?
> > > > >
> > > > > Another idea is doing that using options. For example,
> > > > >
> > > > > For initial sequences synchronization:
> > > > >
> > > > > CREATE SUBSCRIPTION ... WITH (copy_sequence = true);
> > > > >
> > > >
> > > > How will it interact with the existing copy_data option? So copy_data
> > > > will become equivalent to copy_table_data, right?
> > >
> > > Right.
> > >
> > > >
> > > > > For re-copy (or update) sequences:
> > > > >
> > > > > ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (copy_sequence = true);
> > > > >
> > > >
> > > > Similar to the previous point it can be slightly confusing w.r.t
> > > > copy_data. And would copy_sequence here mean that it would copy
> > > > sequence values of both pre-existing and newly added sequences, if so,
> > > > that would make it behave differently than copy_data?  The other
> > > > possibility in this direction would be to introduce an option like
> > > > replicate_all_sequences/copy_all_sequences which indicates a copy of
> > > > both pre-existing and new sequences, if any.
> > >
> > > Copying sequence data works differently than replicating table data
> > > (initial data copy and logical replication). So I thought the
> > > copy_sequence option (or whatever better name) always does both
> > > updating pre-existing sequences and adding new sequences. REFRESH
> > > PUBLICATION updates the tables to be subscribed, so we also update or
> > > add sequences associated to these tables.
> > >
> >
> > Are you imagining the behavior for sequences associated with tables
> > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > was thinking that users would associate sequences with publications
> > similar to what we do for tables for both cases. For example, they
> > need to explicitly mention the sequences they want to replicate by
> > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > SEQUENCES IN SCHEMA sch1;
> >
> > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > should copy both the explicitly defined sequences and sequences
> > defined with the tables. Do you think a different variant for just
> > copying sequences implicitly associated with tables (say for identity
> > columns)?
>
> Oh, I was thinking that your proposal was to copy literally all
> sequences by REPLICA/REFRESH SEQUENCE command. But it seems to make
> sense to explicitly specify the sequences they want to replicate. It
> also means that they can create a publication that has only sequences.
> In this case, even if they create a subscription for that publication,
> we don't launch any apply workers for that subscription. Right?
>
> Also, given that the main use case (at least as the first step) is
> version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> even FOR SEQUENCE?

Also, I guess that specifying individual sequences might not be easy
to use for users in some cases. For sequences owned by a column of a
table, users might want to specify them altogether, rather than
separately. For example, CREATE PUBLICATION ... FOR TABLE tab1 WITH
SEQUENCES means to add the table tab1 and its sequences to the
publication. For other sequences (i.e., not owned by any tables),
users might want to specify them individually.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Mon, Jun 10, 2024 at 12:43 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > >
> > > Are you imagining the behavior for sequences associated with tables
> > > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > > was thinking that users would associate sequences with publications
> > > similar to what we do for tables for both cases. For example, they
> > > need to explicitly mention the sequences they want to replicate by
> > > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > > SEQUENCES IN SCHEMA sch1;
> > >
> > > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > > should copy both the explicitly defined sequences and sequences
> > > defined with the tables. Do you think a different variant for just
> > > copying sequences implicitly associated with tables (say for identity
> > > columns)?
> >
> > Oh, I was thinking that your proposal was to copy literally all
> > sequences by REPLICA/REFRESH SEQUENCE command.
> >

I am trying to keep the behavior as close to tables as possible.

> > But it seems to make
> > sense to explicitly specify the sequences they want to replicate. It
> > also means that they can create a publication that has only sequences.
> > In this case, even if they create a subscription for that publication,
> > we don't launch any apply workers for that subscription. Right?
> >

Right, good point. I had not thought about this.

> > Also, given that the main use case (at least as the first step) is
> > version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> > even FOR SEQUENCE?
>

At the very least, we can split the patch to move these variants to a
separate patch. Once the main patch is finalized, we can try to
evaluate the remaining separately.

> Also, I guess that specifying individual sequences might not be easy
> to use for users in some cases. For sequences owned by a column of a
> table, users might want to specify them altogether, rather than
> separately. For example, CREATE PUBLICATION ... FOR TABLE tab1 WITH
> SEQUENCES means to add the table tab1 and its sequences to the
> publication. For other sequences (i.e., not owned by any tables),
> users might want to specify them individually.
>

Yeah, or we can have a syntax like CREATE PUBLICATION ... FOR TABLE
tab1 INCLUDE SEQUENCES.  Normally, we use the WITH clause for options
(For example, CREATE SUBSCRIPTION ... WITH (streaming=...)).

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
>
>
>
> On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
>>
>> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
>> [...]
>> A new catalog table, pg_subscription_seq, has been introduced for
>> mapping subscriptions to sequences. Additionally, the sequence LSN
>> (Log Sequence Number) is stored, facilitating determination of
>> sequence changes occurring before or after the returned sequence
>> state.
>
>
> Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> something.

We'll require the lsn because the sequence LSN informs the user that
it has been synchronized up to the LSN in pg_subscription_seq. Since
we are not supporting incremental sync, the user will be able to
identify if he should run refresh sequences or not by checking the lsn
of the pg_subscription_seq and the lsn of the sequence(using
pg_sequence_state added) in the publisher.  Also, this parallels our
implementation for pg_subscription_seq and will aid in expanding for
a) incremental synchronization and b) utilizing workers for
synchronization using sequence states if necessary.

How do you track sequence mapping with the publication?

In the publisher we use pg_publication_rel and
pg_publication_namespace for mapping the sequences with the
publication.

Regards,
Vignesh
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 10 Jun 2024 at 14:48, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Mon, Jun 10, 2024 at 12:43 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> > > On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > > >
> > > > Are you imagining the behavior for sequences associated with tables
> > > > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > > > was thinking that users would associate sequences with publications
> > > > similar to what we do for tables for both cases. For example, they
> > > > need to explicitly mention the sequences they want to replicate by
> > > > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > > > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > > > SEQUENCES IN SCHEMA sch1;
> > > >
> > > > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > > > should copy both the explicitly defined sequences and sequences
> > > > defined with the tables. Do you think a different variant for just
> > > > copying sequences implicitly associated with tables (say for identity
> > > > columns)?
> > >
> > > Oh, I was thinking that your proposal was to copy literally all
> > > sequences by REPLICA/REFRESH SEQUENCE command.
> > >
>
> I am trying to keep the behavior as close to tables as possible.
>
> > > But it seems to make
> > > sense to explicitly specify the sequences they want to replicate. It
> > > also means that they can create a publication that has only sequences.
> > > In this case, even if they create a subscription for that publication,
> > > we don't launch any apply workers for that subscription. Right?
> > >
>
> Right, good point. I had not thought about this.
>
> > > Also, given that the main use case (at least as the first step) is
> > > version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> > > even FOR SEQUENCE?
> >
>
> At the very least, we can split the patch to move these variants to a
> separate patch. Once the main patch is finalized, we can try to
> evaluate the remaining separately.

I engaged in an offline discussion with Amit about strategizing the
division of patches to facilitate the review process. We agreed on the
following split: The first patch will encompass the setting and
getting of sequence values (core sequence changes). The second patch
will cover all changes on the publisher side related to "FOR ALL
SEQUENCES." The third patch will address subscriber side changes aimed
at synchronizing "FOR ALL SEQUENCES" publications. The fourth patch
will focus on supporting "FOR SEQUENCE" publication. Lastly, the fifth
patch will introduce support for  "FOR ALL SEQUENCES IN SCHEMA"
publication.

I will work on this and share an updated patch for the same soon.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Amul Sul
Date:
On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
>
>
>
> On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
>>
>> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
>> [...]
>> A new catalog table, pg_subscription_seq, has been introduced for
>> mapping subscriptions to sequences. Additionally, the sequence LSN
>> (Log Sequence Number) is stored, facilitating determination of
>> sequence changes occurring before or after the returned sequence
>> state.
>
>
> Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> something.

We'll require the lsn because the sequence LSN informs the user that
it has been synchronized up to the LSN in pg_subscription_seq. Since
we are not supporting incremental sync, the user will be able to
identify if he should run refresh sequences or not by checking the lsn
of the pg_subscription_seq and the lsn of the sequence(using
pg_sequence_state added) in the publisher.  Also, this parallels our
implementation for pg_subscription_seq and will aid in expanding for
a) incremental synchronization and b) utilizing workers for
synchronization using sequence states if necessary.

How do you track sequence mapping with the publication?

In the publisher we use pg_publication_rel and
pg_publication_namespace for mapping the sequences with the
publication.

Thanks for the explanation. I'm wondering what the complexity would be, if we
wanted to do something similar on the subscriber side, i.e., tracking via
pg_subscription_rel.

Regards,
Amul

Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 11 Jun 2024 at 09:41, Amul Sul <sulamul@gmail.com> wrote:
>
> On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
>>
>> On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
>> >
>> >
>> >
>> > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
>> >>
>> >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
>> >> [...]
>> >> A new catalog table, pg_subscription_seq, has been introduced for
>> >> mapping subscriptions to sequences. Additionally, the sequence LSN
>> >> (Log Sequence Number) is stored, facilitating determination of
>> >> sequence changes occurring before or after the returned sequence
>> >> state.
>> >
>> >
>> > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
>> > something.
>>
>> We'll require the lsn because the sequence LSN informs the user that
>> it has been synchronized up to the LSN in pg_subscription_seq. Since
>> we are not supporting incremental sync, the user will be able to
>> identify if he should run refresh sequences or not by checking the lsn
>> of the pg_subscription_seq and the lsn of the sequence(using
>> pg_sequence_state added) in the publisher.  Also, this parallels our
>> implementation for pg_subscription_seq and will aid in expanding for
>> a) incremental synchronization and b) utilizing workers for
>> synchronization using sequence states if necessary.
>>
>> How do you track sequence mapping with the publication?
>>
>> In the publisher we use pg_publication_rel and
>> pg_publication_namespace for mapping the sequences with the
>> publication.
>
>
> Thanks for the explanation. I'm wondering what the complexity would be, if we
> wanted to do something similar on the subscriber side, i.e., tracking via
> pg_subscription_rel.

Because we won't utilize sync workers to synchronize the sequence, and
the sequence won't necessitate sync states like init, sync,
finishedcopy, syncdone, ready, etc., initially, I considered keeping
the sequences separate. However, I'm ok with using pg_subscription_rel
as it could potentially help in enhancing incremental synchronization
and parallelizing later on.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Tue, Jun 11, 2024 at 12:25 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Mon, 10 Jun 2024 at 14:48, Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Mon, Jun 10, 2024 at 12:43 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> > > On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > >
> > > > On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > >
> > > > >
> > > > > Are you imagining the behavior for sequences associated with tables
> > > > > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > > > > was thinking that users would associate sequences with publications
> > > > > similar to what we do for tables for both cases. For example, they
> > > > > need to explicitly mention the sequences they want to replicate by
> > > > > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > > > > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > > > > SEQUENCES IN SCHEMA sch1;
> > > > >
> > > > > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > > > > should copy both the explicitly defined sequences and sequences
> > > > > defined with the tables. Do you think a different variant for just
> > > > > copying sequences implicitly associated with tables (say for identity
> > > > > columns)?
> > > >
> > > > Oh, I was thinking that your proposal was to copy literally all
> > > > sequences by REPLICA/REFRESH SEQUENCE command.
> > > >
> >
> > I am trying to keep the behavior as close to tables as possible.
> >
> > > > But it seems to make
> > > > sense to explicitly specify the sequences they want to replicate. It
> > > > also means that they can create a publication that has only sequences.
> > > > In this case, even if they create a subscription for that publication,
> > > > we don't launch any apply workers for that subscription. Right?
> > > >
> >
> > Right, good point. I had not thought about this.
> >
> > > > Also, given that the main use case (at least as the first step) is
> > > > version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> > > > even FOR SEQUENCE?
> > >
> >
> > At the very least, we can split the patch to move these variants to a
> > separate patch. Once the main patch is finalized, we can try to
> > evaluate the remaining separately.
>
> I engaged in an offline discussion with Amit about strategizing the
> division of patches to facilitate the review process. We agreed on the
> following split: The first patch will encompass the setting and
> getting of sequence values (core sequence changes). The second patch
> will cover all changes on the publisher side related to "FOR ALL
> SEQUENCES." The third patch will address subscriber side changes aimed
> at synchronizing "FOR ALL SEQUENCES" publications. The fourth patch
> will focus on supporting "FOR SEQUENCE" publication. Lastly, the fifth
> patch will introduce support for  "FOR ALL SEQUENCES IN SCHEMA"
> publication.
>
> I will work on this and share an updated patch for the same soon.

+1. Sounds like a good plan.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 11 Jun 2024 at 12:38, Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Tue, Jun 11, 2024 at 12:25 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Mon, 10 Jun 2024 at 14:48, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > > On Mon, Jun 10, 2024 at 12:43 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > >
> > > > On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > > >
> > > > > On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > >
> > > > > >
> > > > > > Are you imagining the behavior for sequences associated with tables
> > > > > > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > > > > > was thinking that users would associate sequences with publications
> > > > > > similar to what we do for tables for both cases. For example, they
> > > > > > need to explicitly mention the sequences they want to replicate by
> > > > > > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > > > > > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > > > > > SEQUENCES IN SCHEMA sch1;
> > > > > >
> > > > > > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > > > > > should copy both the explicitly defined sequences and sequences
> > > > > > defined with the tables. Do you think a different variant for just
> > > > > > copying sequences implicitly associated with tables (say for identity
> > > > > > columns)?
> > > > >
> > > > > Oh, I was thinking that your proposal was to copy literally all
> > > > > sequences by REPLICA/REFRESH SEQUENCE command.
> > > > >
> > >
> > > I am trying to keep the behavior as close to tables as possible.
> > >
> > > > > But it seems to make
> > > > > sense to explicitly specify the sequences they want to replicate. It
> > > > > also means that they can create a publication that has only sequences.
> > > > > In this case, even if they create a subscription for that publication,
> > > > > we don't launch any apply workers for that subscription. Right?
> > > > >
> > >
> > > Right, good point. I had not thought about this.
> > >
> > > > > Also, given that the main use case (at least as the first step) is
> > > > > version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> > > > > even FOR SEQUENCE?
> > > >
> > >
> > > At the very least, we can split the patch to move these variants to a
> > > separate patch. Once the main patch is finalized, we can try to
> > > evaluate the remaining separately.
> >
> > I engaged in an offline discussion with Amit about strategizing the
> > division of patches to facilitate the review process. We agreed on the
> > following split: The first patch will encompass the setting and
> > getting of sequence values (core sequence changes). The second patch
> > will cover all changes on the publisher side related to "FOR ALL
> > SEQUENCES." The third patch will address subscriber side changes aimed
> > at synchronizing "FOR ALL SEQUENCES" publications. The fourth patch
> > will focus on supporting "FOR SEQUENCE" publication. Lastly, the fifth
> > patch will introduce support for  "FOR ALL SEQUENCES IN SCHEMA"
> > publication.
> >
> > I will work on this and share an updated patch for the same soon.
>
> +1. Sounds like a good plan.

Amit and I engaged in an offline discussion regarding the design and
contemplated that it could be like below:
1) CREATE PUBLICATION syntax enhancement:
CREATE PUBLICATION ... FOR ALL SEQUENCES;
The addition of a new column titled "all sequences" in the
pg_publication system table will signify whether the publication is
designated as all sequences publication or not.

2)  CREATE SUBSCRIPTION -- no syntax change.
Upon creation of a subscription, the following additional steps will
be managed by the subscriber:
i) The subscriber will retrieve the list of sequences associated with
the subscription's publications.
ii) For each sequence: a) Retrieve the sequence value from the
publisher by invoking the pg_sequence_state function. b) Set the
sequence with the value obtained from the publisher. iv) Once the
subscription creation is completed, all sequence values will become
visible at the subscriber's end.

An alternative design approach could involve retrieving the sequence
list from the publisher during subscription creation and inserting the
sequences with an "init" state into the pg_subscription_rel system
table. These tasks could be executed by a single sequence sync worker,
which would:
i) Retrieve the list of sequences in the "init" state from the
pg_subscription_rel system table.
ii) Initiate a transaction.
iii) For each sequence: a) Obtain the sequence value from the
publisher by utilizing the pg_sequence_state function. b) Update the
sequence with the value obtained from the publisher.
iv) Commit the transaction.

The benefit with the second approach is that if there are large number
of sequences, the sequence sync can be enhanced to happen in parallel
and also if there are any locks held on the sequences in the
publisher, the sequence worker can wait to acquire the lock instead of
blocking the whole create subscription command which will delay the
initial copy of the tables too.

3) Refreshing the sequence can be achieved through the existing
command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
here).
The subscriber identifies stale sequences, meaning sequences present
in pg_subscription_rel but absent from the publication, and removes
them from the pg_subscription_rel system table. The subscriber also
checks for newly added sequences in the publisher and synchronizes
their values from the publisher using the steps outlined in the
subscription creation process. It's worth noting that previously
synchronized sequences won't be synchronized again; the sequence sync
will occur solely for the newly added sequences.

4) Introducing a new command for refreshing all sequences: ALTER
SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
The subscriber will remove stale sequences and add newly added
sequences from the publisher. Following this, it will re-synchronize
the sequence values for all sequences in the updated list from the
publisher, following the steps outlined in the subscription creation
process.

5) Incorporate the pg_sequence_state function to fetch the sequence
value from the publisher, along with the page LSN. Incorporate
SetSequence function, which will procure a new relfilenode for the
sequence and set the new relfilenode with the specified value. This
will facilitate rollback in case of any failures.

Thoughts?

Regards,
Vignesh



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Tue, Jun 11, 2024 at 7:36 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Tue, 11 Jun 2024 at 12:38, Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Tue, Jun 11, 2024 at 12:25 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Mon, 10 Jun 2024 at 14:48, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > > > On Mon, Jun 10, 2024 at 12:43 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > > >
> > > > > On Mon, Jun 10, 2024 at 3:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > > > >
> > > > > > On Fri, Jun 7, 2024 at 7:30 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > > >
> > > > > > >
> > > > > > > Are you imagining the behavior for sequences associated with tables
> > > > > > > differently than the ones defined by the CREATE SEQUENCE .. command? I
> > > > > > > was thinking that users would associate sequences with publications
> > > > > > > similar to what we do for tables for both cases. For example, they
> > > > > > > need to explicitly mention the sequences they want to replicate by
> > > > > > > commands like CREATE PUBLICATION ... FOR SEQUENCE s1, s2, ...; CREATE
> > > > > > > PUBLICATION ... FOR ALL SEQUENCES, or CREATE PUBLICATION ... FOR
> > > > > > > SEQUENCES IN SCHEMA sch1;
> > > > > > >
> > > > > > > In this, variants FOR ALL SEQUENCES and SEQUENCES IN SCHEMA sch1
> > > > > > > should copy both the explicitly defined sequences and sequences
> > > > > > > defined with the tables. Do you think a different variant for just
> > > > > > > copying sequences implicitly associated with tables (say for identity
> > > > > > > columns)?
> > > > > >
> > > > > > Oh, I was thinking that your proposal was to copy literally all
> > > > > > sequences by REPLICA/REFRESH SEQUENCE command.
> > > > > >
> > > >
> > > > I am trying to keep the behavior as close to tables as possible.
> > > >
> > > > > > But it seems to make
> > > > > > sense to explicitly specify the sequences they want to replicate. It
> > > > > > also means that they can create a publication that has only sequences.
> > > > > > In this case, even if they create a subscription for that publication,
> > > > > > we don't launch any apply workers for that subscription. Right?
> > > > > >
> > > >
> > > > Right, good point. I had not thought about this.
> > > >
> > > > > > Also, given that the main use case (at least as the first step) is
> > > > > > version upgrade, do we really need to support SEQUENCES IN SCHEMA and
> > > > > > even FOR SEQUENCE?
> > > > >
> > > >
> > > > At the very least, we can split the patch to move these variants to a
> > > > separate patch. Once the main patch is finalized, we can try to
> > > > evaluate the remaining separately.
> > >
> > > I engaged in an offline discussion with Amit about strategizing the
> > > division of patches to facilitate the review process. We agreed on the
> > > following split: The first patch will encompass the setting and
> > > getting of sequence values (core sequence changes). The second patch
> > > will cover all changes on the publisher side related to "FOR ALL
> > > SEQUENCES." The third patch will address subscriber side changes aimed
> > > at synchronizing "FOR ALL SEQUENCES" publications. The fourth patch
> > > will focus on supporting "FOR SEQUENCE" publication. Lastly, the fifth
> > > patch will introduce support for  "FOR ALL SEQUENCES IN SCHEMA"
> > > publication.
> > >
> > > I will work on this and share an updated patch for the same soon.
> >
> > +1. Sounds like a good plan.
>
> Amit and I engaged in an offline discussion regarding the design and
> contemplated that it could be like below:
> 1) CREATE PUBLICATION syntax enhancement:
> CREATE PUBLICATION ... FOR ALL SEQUENCES;
> The addition of a new column titled "all sequences" in the
> pg_publication system table will signify whether the publication is
> designated as all sequences publication or not.
>

The first approach sounds like we don't create entries for sequences
in pg_subscription_rel. In this case, how do we know all sequences
that we need to refresh when executing the REFRESH PUBLICATION
SEQUENCES command you mentioned below?

> 2)  CREATE SUBSCRIPTION -- no syntax change.
> Upon creation of a subscription, the following additional steps will
> be managed by the subscriber:
> i) The subscriber will retrieve the list of sequences associated with
> the subscription's publications.
> ii) For each sequence: a) Retrieve the sequence value from the
> publisher by invoking the pg_sequence_state function. b) Set the
> sequence with the value obtained from the publisher. iv) Once the
> subscription creation is completed, all sequence values will become
> visible at the subscriber's end.

Sequence values are always copied from the publisher? or does it
happen only when copy_data = true?

>
> An alternative design approach could involve retrieving the sequence
> list from the publisher during subscription creation and inserting the
> sequences with an "init" state into the pg_subscription_rel system
> table. These tasks could be executed by a single sequence sync worker,
> which would:
> i) Retrieve the list of sequences in the "init" state from the
> pg_subscription_rel system table.
> ii) Initiate a transaction.
> iii) For each sequence: a) Obtain the sequence value from the
> publisher by utilizing the pg_sequence_state function. b) Update the
> sequence with the value obtained from the publisher.
> iv) Commit the transaction.
>
> The benefit with the second approach is that if there are large number
> of sequences, the sequence sync can be enhanced to happen in parallel
> and also if there are any locks held on the sequences in the
> publisher, the sequence worker can wait to acquire the lock instead of
> blocking the whole create subscription command which will delay the
> initial copy of the tables too.

I prefer to have separate workers to sync sequences. Probably we can
start with a single worker and extend it to have multiple workers. BTW
the sequence-sync worker will be taken from
max_sync_workers_per_subscription pool?

Or yet another idea I came up with is that a tablesync worker will
synchronize both the table and sequences owned by the table. That is,
after the tablesync worker caught up with the apply worker, the
tablesync worker synchronizes sequences associated with the target
table as well. One benefit would be that at the time of initial table
sync being completed, the table and its sequence data are consistent.
As soon as new changes come to the table, it would become inconsistent
so it might not be helpful much, though. Also, sequences that are not
owned by any table will still need to be synchronized by someone.

>
> 3) Refreshing the sequence can be achieved through the existing
> command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> here).
> The subscriber identifies stale sequences, meaning sequences present
> in pg_subscription_rel but absent from the publication, and removes
> them from the pg_subscription_rel system table. The subscriber also
> checks for newly added sequences in the publisher and synchronizes
> their values from the publisher using the steps outlined in the
> subscription creation process. It's worth noting that previously
> synchronized sequences won't be synchronized again; the sequence sync
> will occur solely for the newly added sequences.
>
> 4) Introducing a new command for refreshing all sequences: ALTER
> SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> The subscriber will remove stale sequences and add newly added
> sequences from the publisher. Following this, it will re-synchronize
> the sequence values for all sequences in the updated list from the
> publisher, following the steps outlined in the subscription creation
> process.

The difference between 3) and 4) is whether or not to re-synchronize
the previously synchronized sequences. Do we really want to introduce
a new command for 4)? I felt that we can invent an option say
copy_all_sequence for the REFRESH PUBLICATION command to cover the 4)
case.

>
> 5) Incorporate the pg_sequence_state function to fetch the sequence
> value from the publisher, along with the page LSN. Incorporate
> SetSequence function, which will procure a new relfilenode for the
> sequence and set the new relfilenode with the specified value. This
> will facilitate rollback in case of any failures.

Does it mean that we create a new relfilenode for every update of the value?

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Dilip Kumar
Date:
On Tue, Jun 11, 2024 at 4:06 PM vignesh C <vignesh21@gmail.com> wrote:
>
> Amit and I engaged in an offline discussion regarding the design and
> contemplated that it could be like below:

If I understand correctly, does this require the sequences to already
exist on the subscribing node before creating the subscription, or
will it also copy any non-existing sequences?

> 1) CREATE PUBLICATION syntax enhancement:
> CREATE PUBLICATION ... FOR ALL SEQUENCES;
> The addition of a new column titled "all sequences" in the
> pg_publication system table will signify whether the publication is
> designated as all sequences publication or not.
>
> 2)  CREATE SUBSCRIPTION -- no syntax change.
> Upon creation of a subscription, the following additional steps will
> be managed by the subscriber:
> i) The subscriber will retrieve the list of sequences associated with
> the subscription's publications.
> ii) For each sequence: a) Retrieve the sequence value from the
> publisher by invoking the pg_sequence_state function. b) Set the
> sequence with the value obtained from the publisher. iv) Once the
> subscription creation is completed, all sequence values will become
> visible at the subscriber's end.
>
> An alternative design approach could involve retrieving the sequence
> list from the publisher during subscription creation and inserting the
> sequences with an "init" state into the pg_subscription_rel system
> table. These tasks could be executed by a single sequence sync worker,
> which would:
> i) Retrieve the list of sequences in the "init" state from the
> pg_subscription_rel system table.
> ii) Initiate a transaction.
> iii) For each sequence: a) Obtain the sequence value from the
> publisher by utilizing the pg_sequence_state function. b) Update the
> sequence with the value obtained from the publisher.
> iv) Commit the transaction.
>
> The benefit with the second approach is that if there are large number
> of sequences, the sequence sync can be enhanced to happen in parallel
> and also if there are any locks held on the sequences in the
> publisher, the sequence worker can wait to acquire the lock instead of
> blocking the whole create subscription command which will delay the
> initial copy of the tables too.

Yeah w.r.t. this point second approach seems better.

> 3) Refreshing the sequence can be achieved through the existing
> command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> here).
> The subscriber identifies stale sequences, meaning sequences present
> in pg_subscription_rel but absent from the publication, and removes
> them from the pg_subscription_rel system table. The subscriber also
> checks for newly added sequences in the publisher and synchronizes
> their values from the publisher using the steps outlined in the
> subscription creation process. It's worth noting that previously
> synchronized sequences won't be synchronized again; the sequence sync
> will occur solely for the newly added sequences.

> 4) Introducing a new command for refreshing all sequences: ALTER
> SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> The subscriber will remove stale sequences and add newly added
> sequences from the publisher. Following this, it will re-synchronize
> the sequence values for all sequences in the updated list from the
> publisher, following the steps outlined in the subscription creation
> process.

Okay, this answers my first question: we will remove the sequences
that are removed from the publisher and add the new sequences. I don't
see any problem with this, but doesn't it seem like we are effectively
doing DDL replication only for sequences without having a
comprehensive plan for overall DDL replication?

> 5) Incorporate the pg_sequence_state function to fetch the sequence
> value from the publisher, along with the page LSN. Incorporate
> SetSequence function, which will procure a new relfilenode for the
> sequence and set the new relfilenode with the specified value. This
> will facilitate rollback in case of any failures.

I do not understand this point, you mean whenever we are fetching the
sequence value from the publisher we need to create a new relfilenode
on the subscriber?  Why not just update the catalog tuple is
sufficient?  Or this is for handling the ALTER SEQUENCE case?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 12, 2024 at 10:44 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Tue, Jun 11, 2024 at 7:36 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > 1) CREATE PUBLICATION syntax enhancement:
> > CREATE PUBLICATION ... FOR ALL SEQUENCES;
> > The addition of a new column titled "all sequences" in the
> > pg_publication system table will signify whether the publication is
> > designated as all sequences publication or not.
> >
>
> The first approach sounds like we don't create entries for sequences
> in pg_subscription_rel. In this case, how do we know all sequences
> that we need to refresh when executing the REFRESH PUBLICATION
> SEQUENCES command you mentioned below?
>

As per my understanding, we should be creating entries for sequences
in pg_subscription_rel similar to tables. The difference would be that
we won't need all the sync_states (i = initialize, d = data is being
copied, f = finished table copy, s = synchronized, r = ready) as we
don't need any synchronization with apply workers.

> > 2)  CREATE SUBSCRIPTION -- no syntax change.
> > Upon creation of a subscription, the following additional steps will
> > be managed by the subscriber:
> > i) The subscriber will retrieve the list of sequences associated with
> > the subscription's publications.
> > ii) For each sequence: a) Retrieve the sequence value from the
> > publisher by invoking the pg_sequence_state function. b) Set the
> > sequence with the value obtained from the publisher. iv) Once the
> > subscription creation is completed, all sequence values will become
> > visible at the subscriber's end.
>
> Sequence values are always copied from the publisher? or does it
> happen only when copy_data = true?
>

It is better to do it when "copy_data = true" to keep it compatible
with the table's behavior.

> >
> > An alternative design approach could involve retrieving the sequence
> > list from the publisher during subscription creation and inserting the
> > sequences with an "init" state into the pg_subscription_rel system
> > table. These tasks could be executed by a single sequence sync worker,
> > which would:
> > i) Retrieve the list of sequences in the "init" state from the
> > pg_subscription_rel system table.
> > ii) Initiate a transaction.
> > iii) For each sequence: a) Obtain the sequence value from the
> > publisher by utilizing the pg_sequence_state function. b) Update the
> > sequence with the value obtained from the publisher.
> > iv) Commit the transaction.
> >
> > The benefit with the second approach is that if there are large number
> > of sequences, the sequence sync can be enhanced to happen in parallel
> > and also if there are any locks held on the sequences in the
> > publisher, the sequence worker can wait to acquire the lock instead of
> > blocking the whole create subscription command which will delay the
> > initial copy of the tables too.
>
> I prefer to have separate workers to sync sequences.
>

+1.

> Probably we can
> start with a single worker and extend it to have multiple workers.

Yeah, starting with a single worker sounds good for now. Do you think
we should sync all the sequences in a single transaction or have some
threshold value above which a different transaction would be required
or maybe a different sequence sync worker altogether? Now, having
multiple sequence-sync workers requires some synchronization so that
only a single worker is allocated for one sequence.

The simplest thing is to use a single sequence sync worker that syncs
all sequences in one transaction but with a large number of sequences,
it could be inefficient. OTOH, I am not sure if it would be a problem
in reality.

>
> BTW
> the sequence-sync worker will be taken from
> max_sync_workers_per_subscription pool?
>

I think so.

> Or yet another idea I came up with is that a tablesync worker will
> synchronize both the table and sequences owned by the table. That is,
> after the tablesync worker caught up with the apply worker, the
> tablesync worker synchronizes sequences associated with the target
> table as well. One benefit would be that at the time of initial table
> sync being completed, the table and its sequence data are consistent.
> As soon as new changes come to the table, it would become inconsistent
> so it might not be helpful much, though. Also, sequences that are not
> owned by any table will still need to be synchronized by someone.
>

The other thing to consider in this idea is that we somehow need to
distinguish the sequences owned by the table.

> >
> > 3) Refreshing the sequence can be achieved through the existing
> > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > here).
> > The subscriber identifies stale sequences, meaning sequences present
> > in pg_subscription_rel but absent from the publication, and removes
> > them from the pg_subscription_rel system table. The subscriber also
> > checks for newly added sequences in the publisher and synchronizes
> > their values from the publisher using the steps outlined in the
> > subscription creation process. It's worth noting that previously
> > synchronized sequences won't be synchronized again; the sequence sync
> > will occur solely for the newly added sequences.
> >
> > 4) Introducing a new command for refreshing all sequences: ALTER
> > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > The subscriber will remove stale sequences and add newly added
> > sequences from the publisher. Following this, it will re-synchronize
> > the sequence values for all sequences in the updated list from the
> > publisher, following the steps outlined in the subscription creation
> > process.
>
> The difference between 3) and 4) is whether or not to re-synchronize
> the previously synchronized sequences. Do we really want to introduce
> a new command for 4)? I felt that we can invent an option say
> copy_all_sequence for the REFRESH PUBLICATION command to cover the 4)
> case.
>

Yeah, that is also an option but it could confuse along with copy_data
option. Say the user has selected copy_data = false but
copy_all_sequences = true then the first option indicates to *not*
copy the data of table and sequences and the second option indicates
to copy the sequences data which sounds contradictory. The other idea
is to have an option copy_existing_sequences (which indicates to copy
existing sequence values) but that also has somewhat the same drawback
as copy_all_sequences but to a lesser degree.

> >
> > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > value from the publisher, along with the page LSN. Incorporate
> > SetSequence function, which will procure a new relfilenode for the
> > sequence and set the new relfilenode with the specified value. This
> > will facilitate rollback in case of any failures.
>
> Does it mean that we create a new relfilenode for every update of the value?
>

We need it for initial sync so that if there is an error both the
sequence state in pg_subscription_rel and sequence values can be
rolled back together. However, it is unclear whether we need to create
a new relfilenode while copying existing sequences (say during ALTER
SUBSCRIPTION .. REFRESH PUBLICATION SEQUENCES, or whatever command we
decide)? Probably the answer lies in how we want to implement this
command. If we want to copy all sequence values during the command
itself then it is probably okay but if we want to handover this task
to the sequence-sync worker then we need some state management and a
new relfilenode so that on error both state and sequence values are
rolled back.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 12 Jun 2024 at 10:51, Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Tue, Jun 11, 2024 at 4:06 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > Amit and I engaged in an offline discussion regarding the design and
> > contemplated that it could be like below:
>
> If I understand correctly, does this require the sequences to already
> exist on the subscribing node before creating the subscription, or
> will it also copy any non-existing sequences?

Sequences must exist in the subscriber; we'll synchronize only their
values. Any sequences that are not present in the subscriber will
trigger an error.

> > 1) CREATE PUBLICATION syntax enhancement:
> > CREATE PUBLICATION ... FOR ALL SEQUENCES;
> > The addition of a new column titled "all sequences" in the
> > pg_publication system table will signify whether the publication is
> > designated as all sequences publication or not.
> >
> > 2)  CREATE SUBSCRIPTION -- no syntax change.
> > Upon creation of a subscription, the following additional steps will
> > be managed by the subscriber:
> > i) The subscriber will retrieve the list of sequences associated with
> > the subscription's publications.
> > ii) For each sequence: a) Retrieve the sequence value from the
> > publisher by invoking the pg_sequence_state function. b) Set the
> > sequence with the value obtained from the publisher. iv) Once the
> > subscription creation is completed, all sequence values will become
> > visible at the subscriber's end.
> >
> > An alternative design approach could involve retrieving the sequence
> > list from the publisher during subscription creation and inserting the
> > sequences with an "init" state into the pg_subscription_rel system
> > table. These tasks could be executed by a single sequence sync worker,
> > which would:
> > i) Retrieve the list of sequences in the "init" state from the
> > pg_subscription_rel system table.
> > ii) Initiate a transaction.
> > iii) For each sequence: a) Obtain the sequence value from the
> > publisher by utilizing the pg_sequence_state function. b) Update the
> > sequence with the value obtained from the publisher.
> > iv) Commit the transaction.
> >
> > The benefit with the second approach is that if there are large number
> > of sequences, the sequence sync can be enhanced to happen in parallel
> > and also if there are any locks held on the sequences in the
> > publisher, the sequence worker can wait to acquire the lock instead of
> > blocking the whole create subscription command which will delay the
> > initial copy of the tables too.
>
> Yeah w.r.t. this point second approach seems better.

ok

> > 3) Refreshing the sequence can be achieved through the existing
> > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > here).
> > The subscriber identifies stale sequences, meaning sequences present
> > in pg_subscription_rel but absent from the publication, and removes
> > them from the pg_subscription_rel system table. The subscriber also
> > checks for newly added sequences in the publisher and synchronizes
> > their values from the publisher using the steps outlined in the
> > subscription creation process. It's worth noting that previously
> > synchronized sequences won't be synchronized again; the sequence sync
> > will occur solely for the newly added sequences.
>
> > 4) Introducing a new command for refreshing all sequences: ALTER
> > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > The subscriber will remove stale sequences and add newly added
> > sequences from the publisher. Following this, it will re-synchronize
> > the sequence values for all sequences in the updated list from the
> > publisher, following the steps outlined in the subscription creation
> > process.
>
> Okay, this answers my first question: we will remove the sequences
> that are removed from the publisher and add the new sequences. I don't
> see any problem with this, but doesn't it seem like we are effectively
> doing DDL replication only for sequences without having a
> comprehensive plan for overall DDL replication?

What I intended to convey is that we'll eliminate the sequences from
pg_subscription_rel. We won't facilitate the DDL replication of
sequences; instead, we anticipate users to create the sequences
themselves.

> > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > value from the publisher, along with the page LSN. Incorporate
> > SetSequence function, which will procure a new relfilenode for the
> > sequence and set the new relfilenode with the specified value. This
> > will facilitate rollback in case of any failures.
>
> I do not understand this point, you mean whenever we are fetching the
> sequence value from the publisher we need to create a new relfilenode
> on the subscriber?  Why not just update the catalog tuple is
> sufficient?  Or this is for handling the ALTER SEQUENCE case?

Sequences operate distinctively from tables. Alterations to sequences
reflect instantly in another session, even before committing the
transaction. To ensure the synchronization of sequence value and state
updates in pg_subscription_rel, we assign it a new relfilenode. This
strategy ensures that any potential errors allow for the rollback of
both the sequence state in pg_subscription_rel and the sequence values
simultaneously.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Dilip Kumar
Date:
On Wed, Jun 12, 2024 at 4:08 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Wed, 12 Jun 2024 at 10:51, Dilip Kumar <dilipbalaut@gmail.com> wrote:
> >
> > On Tue, Jun 11, 2024 at 4:06 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > Amit and I engaged in an offline discussion regarding the design and
> > > contemplated that it could be like below:
> >
> > If I understand correctly, does this require the sequences to already
> > exist on the subscribing node before creating the subscription, or
> > will it also copy any non-existing sequences?
>
> Sequences must exist in the subscriber; we'll synchronize only their
> values. Any sequences that are not present in the subscriber will
> trigger an error.

Okay, that makes sense.

>
> > > 3) Refreshing the sequence can be achieved through the existing
> > > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > > here).
> > > The subscriber identifies stale sequences, meaning sequences present
> > > in pg_subscription_rel but absent from the publication, and removes
> > > them from the pg_subscription_rel system table. The subscriber also
> > > checks for newly added sequences in the publisher and synchronizes
> > > their values from the publisher using the steps outlined in the
> > > subscription creation process. It's worth noting that previously
> > > synchronized sequences won't be synchronized again; the sequence sync
> > > will occur solely for the newly added sequences.
> >
> > > 4) Introducing a new command for refreshing all sequences: ALTER
> > > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > > The subscriber will remove stale sequences and add newly added
> > > sequences from the publisher. Following this, it will re-synchronize
> > > the sequence values for all sequences in the updated list from the
> > > publisher, following the steps outlined in the subscription creation
> > > process.
> >
> > Okay, this answers my first question: we will remove the sequences
> > that are removed from the publisher and add the new sequences. I don't
> > see any problem with this, but doesn't it seem like we are effectively
> > doing DDL replication only for sequences without having a
> > comprehensive plan for overall DDL replication?
>
> What I intended to convey is that we'll eliminate the sequences from
> pg_subscription_rel. We won't facilitate the DDL replication of
> sequences; instead, we anticipate users to create the sequences
> themselves.

hmm okay.

> > > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > > value from the publisher, along with the page LSN. Incorporate
> > > SetSequence function, which will procure a new relfilenode for the
> > > sequence and set the new relfilenode with the specified value. This
> > > will facilitate rollback in case of any failures.
> >
> > I do not understand this point, you mean whenever we are fetching the
> > sequence value from the publisher we need to create a new relfilenode
> > on the subscriber?  Why not just update the catalog tuple is
> > sufficient?  Or this is for handling the ALTER SEQUENCE case?
>
> Sequences operate distinctively from tables. Alterations to sequences
> reflect instantly in another session, even before committing the
> transaction. To ensure the synchronization of sequence value and state
> updates in pg_subscription_rel, we assign it a new relfilenode. This
> strategy ensures that any potential errors allow for the rollback of
> both the sequence state in pg_subscription_rel and the sequence values
> simultaneously.

So, you're saying that when we synchronize the sequence values on the
subscriber side, we will create a new relfilenode to allow reverting
to the old state of the sequence in case of an error or transaction
rollback? But why would we want to do that? Generally, even if you
call nextval() on a sequence and then roll back the transaction, the
sequence value doesn't revert to the old value. So, what specific
problem on the subscriber side are we trying to avoid by operating on
a new relfilenode?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 12 Jun 2024 at 17:09, Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Wed, Jun 12, 2024 at 4:08 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 12 Jun 2024 at 10:51, Dilip Kumar <dilipbalaut@gmail.com> wrote:
> > >
> > > On Tue, Jun 11, 2024 at 4:06 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > Amit and I engaged in an offline discussion regarding the design and
> > > > contemplated that it could be like below:
> > >
> > > If I understand correctly, does this require the sequences to already
> > > exist on the subscribing node before creating the subscription, or
> > > will it also copy any non-existing sequences?
> >
> > Sequences must exist in the subscriber; we'll synchronize only their
> > values. Any sequences that are not present in the subscriber will
> > trigger an error.
>
> Okay, that makes sense.
>
> >
> > > > 3) Refreshing the sequence can be achieved through the existing
> > > > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > > > here).
> > > > The subscriber identifies stale sequences, meaning sequences present
> > > > in pg_subscription_rel but absent from the publication, and removes
> > > > them from the pg_subscription_rel system table. The subscriber also
> > > > checks for newly added sequences in the publisher and synchronizes
> > > > their values from the publisher using the steps outlined in the
> > > > subscription creation process. It's worth noting that previously
> > > > synchronized sequences won't be synchronized again; the sequence sync
> > > > will occur solely for the newly added sequences.
> > >
> > > > 4) Introducing a new command for refreshing all sequences: ALTER
> > > > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > > > The subscriber will remove stale sequences and add newly added
> > > > sequences from the publisher. Following this, it will re-synchronize
> > > > the sequence values for all sequences in the updated list from the
> > > > publisher, following the steps outlined in the subscription creation
> > > > process.
> > >
> > > Okay, this answers my first question: we will remove the sequences
> > > that are removed from the publisher and add the new sequences. I don't
> > > see any problem with this, but doesn't it seem like we are effectively
> > > doing DDL replication only for sequences without having a
> > > comprehensive plan for overall DDL replication?
> >
> > What I intended to convey is that we'll eliminate the sequences from
> > pg_subscription_rel. We won't facilitate the DDL replication of
> > sequences; instead, we anticipate users to create the sequences
> > themselves.
>
> hmm okay.
>
> > > > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > > > value from the publisher, along with the page LSN. Incorporate
> > > > SetSequence function, which will procure a new relfilenode for the
> > > > sequence and set the new relfilenode with the specified value. This
> > > > will facilitate rollback in case of any failures.
> > >
> > > I do not understand this point, you mean whenever we are fetching the
> > > sequence value from the publisher we need to create a new relfilenode
> > > on the subscriber?  Why not just update the catalog tuple is
> > > sufficient?  Or this is for handling the ALTER SEQUENCE case?
> >
> > Sequences operate distinctively from tables. Alterations to sequences
> > reflect instantly in another session, even before committing the
> > transaction. To ensure the synchronization of sequence value and state
> > updates in pg_subscription_rel, we assign it a new relfilenode. This
> > strategy ensures that any potential errors allow for the rollback of
> > both the sequence state in pg_subscription_rel and the sequence values
> > simultaneously.
>
> So, you're saying that when we synchronize the sequence values on the
> subscriber side, we will create a new relfilenode to allow reverting
> to the old state of the sequence in case of an error or transaction
> rollback? But why would we want to do that? Generally, even if you
> call nextval() on a sequence and then roll back the transaction, the
> sequence value doesn't revert to the old value. So, what specific
> problem on the subscriber side are we trying to avoid by operating on
> a new relfilenode?

Let's consider a situation where we have two sequences: seq1 with a
value of 100 and seq2 with a value of 200. Now, let's say seq1 is
synced and updated to 100, then we attempt to synchronize seq2,
there's a failure due to the sequence not existing or encountering
some other issue. In this scenario, we don't want to halt operations
where seq1 is synchronized, but the sequence state for sequence isn't
changed to "ready" in pg_subscription_rel.
Updating the sequence data directly reflects the sequence change
immediately. However, if we assign a new relfile node for the sequence
and update the sequence value for the new relfile node, until the
transaction is committed, other concurrent users will still be
utilizing the old relfile node for the sequence, and only the old data
will be visible. Once all sequences are synchronized, and the sequence
state is updated in pg_subscription_rel, the transaction will either
be committed or aborted. If committed, users will be able to observe
the new sequence values because the sequences will be updated with the
new relfile node containing the updated sequence value.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Dilip Kumar
Date:
On Thu, Jun 13, 2024 at 10:10 AM vignesh C <vignesh21@gmail.com> wrote:
>
> > So, you're saying that when we synchronize the sequence values on the
> > subscriber side, we will create a new relfilenode to allow reverting
> > to the old state of the sequence in case of an error or transaction
> > rollback? But why would we want to do that? Generally, even if you
> > call nextval() on a sequence and then roll back the transaction, the
> > sequence value doesn't revert to the old value. So, what specific
> > problem on the subscriber side are we trying to avoid by operating on
> > a new relfilenode?
>
> Let's consider a situation where we have two sequences: seq1 with a
> value of 100 and seq2 with a value of 200. Now, let's say seq1 is
> synced and updated to 100, then we attempt to synchronize seq2,
> there's a failure due to the sequence not existing or encountering
> some other issue. In this scenario, we don't want to halt operations
> where seq1 is synchronized, but the sequence state for sequence isn't
> changed to "ready" in pg_subscription_rel.

Thanks for the explanation, but I am still not getting it completely,
do you mean to say unless all the sequences are not synced any of the
sequences would not be marked "ready" in pg_subscription_rel? Is that
necessary? I mean why we can not sync the sequences one by one and
mark them ready?  Why it is necessary to either have all the sequences
synced or none of them?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 13 Jun 2024 at 10:27, Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Thu, Jun 13, 2024 at 10:10 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > > So, you're saying that when we synchronize the sequence values on the
> > > subscriber side, we will create a new relfilenode to allow reverting
> > > to the old state of the sequence in case of an error or transaction
> > > rollback? But why would we want to do that? Generally, even if you
> > > call nextval() on a sequence and then roll back the transaction, the
> > > sequence value doesn't revert to the old value. So, what specific
> > > problem on the subscriber side are we trying to avoid by operating on
> > > a new relfilenode?
> >
> > Let's consider a situation where we have two sequences: seq1 with a
> > value of 100 and seq2 with a value of 200. Now, let's say seq1 is
> > synced and updated to 100, then we attempt to synchronize seq2,
> > there's a failure due to the sequence not existing or encountering
> > some other issue. In this scenario, we don't want to halt operations
> > where seq1 is synchronized, but the sequence state for sequence isn't
> > changed to "ready" in pg_subscription_rel.
>
> Thanks for the explanation, but I am still not getting it completely,
> do you mean to say unless all the sequences are not synced any of the
> sequences would not be marked "ready" in pg_subscription_rel? Is that
> necessary? I mean why we can not sync the sequences one by one and
> mark them ready?  Why it is necessary to either have all the sequences
> synced or none of them?

Since updating the sequence is one operation and setting
pg_subscription_rel is another, I was trying to avoid a situation
where the sequence is updated but its state is not reflected in
pg_subscription_rel. It seems you are suggesting that it's acceptable
for the sequence to be updated even if its state isn't updated in
pg_subscription_rel, and in such cases, the sequence value does not
need to be reverted.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Dilip Kumar
Date:
On Thu, Jun 13, 2024 at 11:53 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 13 Jun 2024 at 10:27, Dilip Kumar <dilipbalaut@gmail.com> wrote:

> > Thanks for the explanation, but I am still not getting it completely,
> > do you mean to say unless all the sequences are not synced any of the
> > sequences would not be marked "ready" in pg_subscription_rel? Is that
> > necessary? I mean why we can not sync the sequences one by one and
> > mark them ready?  Why it is necessary to either have all the sequences
> > synced or none of them?
>
> Since updating the sequence is one operation and setting
> pg_subscription_rel is another, I was trying to avoid a situation
> where the sequence is updated but its state is not reflected in
> pg_subscription_rel. It seems you are suggesting that it's acceptable
> for the sequence to be updated even if its state isn't updated in
> pg_subscription_rel, and in such cases, the sequence value does not
> need to be reverted.

Right, the complexity we're adding to achieve a behavior that may not
be truly desirable is a concern. For instance, if we mark the status
as ready but do not sync the sequences, it could lead to issues.
However, if we have synced some sequences but encounter a failure
without marking the status as ready, I don't consider it inconsistent
in any way.  But anyway, now I understand your thinking behind that so
it's a good idea to leave this design behavior for a later decision.
Gathering more opinions and insights during later stages will provide
a clearer perspective on how to proceed with this aspect.  Thanks.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Wed, Jun 12, 2024 at 6:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Jun 12, 2024 at 10:44 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Tue, Jun 11, 2024 at 7:36 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > 1) CREATE PUBLICATION syntax enhancement:
> > > CREATE PUBLICATION ... FOR ALL SEQUENCES;
> > > The addition of a new column titled "all sequences" in the
> > > pg_publication system table will signify whether the publication is
> > > designated as all sequences publication or not.
> > >
> >
> > The first approach sounds like we don't create entries for sequences
> > in pg_subscription_rel. In this case, how do we know all sequences
> > that we need to refresh when executing the REFRESH PUBLICATION
> > SEQUENCES command you mentioned below?
> >
>
> As per my understanding, we should be creating entries for sequences
> in pg_subscription_rel similar to tables. The difference would be that
> we won't need all the sync_states (i = initialize, d = data is being
> copied, f = finished table copy, s = synchronized, r = ready) as we
> don't need any synchronization with apply workers.

Agreed.

>
> > > 2)  CREATE SUBSCRIPTION -- no syntax change.
> > > Upon creation of a subscription, the following additional steps will
> > > be managed by the subscriber:
> > > i) The subscriber will retrieve the list of sequences associated with
> > > the subscription's publications.
> > > ii) For each sequence: a) Retrieve the sequence value from the
> > > publisher by invoking the pg_sequence_state function. b) Set the
> > > sequence with the value obtained from the publisher. iv) Once the
> > > subscription creation is completed, all sequence values will become
> > > visible at the subscriber's end.
> >
> > Sequence values are always copied from the publisher? or does it
> > happen only when copy_data = true?
> >
>
> It is better to do it when "copy_data = true" to keep it compatible
> with the table's behavior.

+1

>
> > Probably we can
> > start with a single worker and extend it to have multiple workers.
>
> Yeah, starting with a single worker sounds good for now. Do you think
> we should sync all the sequences in a single transaction or have some
> threshold value above which a different transaction would be required
> or maybe a different sequence sync worker altogether? Now, having
> multiple sequence-sync workers requires some synchronization so that
> only a single worker is allocated for one sequence.
>
> The simplest thing is to use a single sequence sync worker that syncs
> all sequences in one transaction but with a large number of sequences,
> it could be inefficient. OTOH, I am not sure if it would be a problem
> in reality.

I think that we can start with using a single worker and one
transaction, and measure the performance with a large number of
sequences.

> > Or yet another idea I came up with is that a tablesync worker will
> > synchronize both the table and sequences owned by the table. That is,
> > after the tablesync worker caught up with the apply worker, the
> > tablesync worker synchronizes sequences associated with the target
> > table as well. One benefit would be that at the time of initial table
> > sync being completed, the table and its sequence data are consistent.

Correction; it's not guaranteed that the sequence data and table data
are consistent even in this case since the tablesync worker could get
on-disk sequence data that might have already been updated.

> > As soon as new changes come to the table, it would become inconsistent
> > so it might not be helpful much, though. Also, sequences that are not
> > owned by any table will still need to be synchronized by someone.
> >
>
> The other thing to consider in this idea is that we somehow need to
> distinguish the sequences owned by the table.

I think we can check pg_depend. The owned sequences reference to the table.

>
> > >
> > > 3) Refreshing the sequence can be achieved through the existing
> > > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > > here).
> > > The subscriber identifies stale sequences, meaning sequences present
> > > in pg_subscription_rel but absent from the publication, and removes
> > > them from the pg_subscription_rel system table. The subscriber also
> > > checks for newly added sequences in the publisher and synchronizes
> > > their values from the publisher using the steps outlined in the
> > > subscription creation process. It's worth noting that previously
> > > synchronized sequences won't be synchronized again; the sequence sync
> > > will occur solely for the newly added sequences.
> > >
> > > 4) Introducing a new command for refreshing all sequences: ALTER
> > > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > > The subscriber will remove stale sequences and add newly added
> > > sequences from the publisher. Following this, it will re-synchronize
> > > the sequence values for all sequences in the updated list from the
> > > publisher, following the steps outlined in the subscription creation
> > > process.
> >
> > The difference between 3) and 4) is whether or not to re-synchronize
> > the previously synchronized sequences. Do we really want to introduce
> > a new command for 4)? I felt that we can invent an option say
> > copy_all_sequence for the REFRESH PUBLICATION command to cover the 4)
> > case.
> >
>
> Yeah, that is also an option but it could confuse along with copy_data
> option. Say the user has selected copy_data = false but
> copy_all_sequences = true then the first option indicates to *not*
> copy the data of table and sequences and the second option indicates
> to copy the sequences data which sounds contradictory. The other idea
> is to have an option copy_existing_sequences (which indicates to copy
> existing sequence values) but that also has somewhat the same drawback
> as copy_all_sequences but to a lesser degree.

Good point. And I understood that the REFRESH PUBLICATION SEQUENCES
command would be helpful when users want to synchronize sequences
between two nodes before upgrading.

>
> > >
> > > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > > value from the publisher, along with the page LSN. Incorporate
> > > SetSequence function, which will procure a new relfilenode for the
> > > sequence and set the new relfilenode with the specified value. This
> > > will facilitate rollback in case of any failures.
> >
> > Does it mean that we create a new relfilenode for every update of the value?
> >
>
> We need it for initial sync so that if there is an error both the
> sequence state in pg_subscription_rel and sequence values can be
> rolled back together.

Agreed.

> However, it is unclear whether we need to create
> a new relfilenode while copying existing sequences (say during ALTER
> SUBSCRIPTION .. REFRESH PUBLICATION SEQUENCES, or whatever command we
> decide)? Probably the answer lies in how we want to implement this
> command. If we want to copy all sequence values during the command
> itself then it is probably okay but if we want to handover this task
> to the sequence-sync worker then we need some state management and a
> new relfilenode so that on error both state and sequence values are
> rolled back.

What state transition of pg_subscription_rel entries for sequences do
we need while copying sequences values? For example, we insert an
entry with 'init' state at CREATE SUBSCRIPTION and then the
sequence-sync worker updates to 'ready' and copies the sequence data.
And at REFRESH PUBLICATION SEQUENCES, we update the state back to
'init' again so that the sequence-sync worker can process it? Given
REFRESH PUBLICATION SEQUENCES won't be executed very frequently, it
might be acceptable to transactionally update sequence values.


Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Jun 13, 2024 at 1:09 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Wed, Jun 12, 2024 at 6:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> >
> > Yeah, starting with a single worker sounds good for now. Do you think
> > we should sync all the sequences in a single transaction or have some
> > threshold value above which a different transaction would be required
> > or maybe a different sequence sync worker altogether? Now, having
> > multiple sequence-sync workers requires some synchronization so that
> > only a single worker is allocated for one sequence.
> >
> > The simplest thing is to use a single sequence sync worker that syncs
> > all sequences in one transaction but with a large number of sequences,
> > it could be inefficient. OTOH, I am not sure if it would be a problem
> > in reality.
>
> I think that we can start with using a single worker and one
> transaction, and measure the performance with a large number of
> sequences.
>

Fair enough. However, this raises the question Dilip and Vignesh are
discussing whether we need a new relfilenode for sequence update even
during initial sync? As per my understanding, the idea is that similar
to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
will create the new sequence entries in pg_subscription_rel with the
state as 'i'. Then the sequence-sync worker would start a transaction
and one-by-one copy the latest sequence values for each sequence (that
has state as 'i' in pg_subscription_rel) and mark its state as ready
'r' and commit the transaction. Now if there is an error during this
operation it will restart the entire operation. The idea of creating a
new relfilenode is to handle the error so that if there is a rollback,
the sequence state will be rolled back to 'i' and the sequence value
will also be rolled back. The other option could be that we update the
sequence value without a new relfilenode and if the transaction rolled
back then only the sequence's state will be rolled back to 'i'. This
would work with a minor inconsistency that sequence values will be
up-to-date even when the sequence state is 'i' in pg_subscription_rel.
I am not sure if that matters because anyway, they can quickly be
out-of-sync with the publisher again.

Now, say we don't want to maintain the state of sequences for initial
sync at all then after the error how will we detect if there are any
pending sequences to be synced? One possibility is that we maintain a
subscription level flag 'subsequencesync' in 'pg_subscription' to
indicate whether sequences need sync. This flag would indicate whether
to sync all the sequences in pg_susbcription_rel. This would mean that
if there is an error while syncing the sequences we will resync all
the sequences again. This could be acceptable considering the chances
of error during sequence sync are low. The benefit is that both the
REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
idea and sync all sequences without needing a new relfilenode. Users
can always refer 'subsequencesync' flag in 'pg_subscription' to see if
all the sequences are synced after executing the command.

> > > Or yet another idea I came up with is that a tablesync worker will
> > > synchronize both the table and sequences owned by the table. That is,
> > > after the tablesync worker caught up with the apply worker, the
> > > tablesync worker synchronizes sequences associated with the target
> > > table as well. One benefit would be that at the time of initial table
> > > sync being completed, the table and its sequence data are consistent.
>
> Correction; it's not guaranteed that the sequence data and table data
> are consistent even in this case since the tablesync worker could get
> on-disk sequence data that might have already been updated.
>

The benefit of this approach is not clear to me. Our aim is to sync
all sequences before the upgrade, so not sure if this helps because
anyway both table values and corresponding sequences can again be
out-of-sync very quickly.

> >
> > > >
> > > > 3) Refreshing the sequence can be achieved through the existing
> > > > command: ALTER SUBSCRIPTION ... REFRESH PUBLICATION(no syntax change
> > > > here).
> > > > The subscriber identifies stale sequences, meaning sequences present
> > > > in pg_subscription_rel but absent from the publication, and removes
> > > > them from the pg_subscription_rel system table. The subscriber also
> > > > checks for newly added sequences in the publisher and synchronizes
> > > > their values from the publisher using the steps outlined in the
> > > > subscription creation process. It's worth noting that previously
> > > > synchronized sequences won't be synchronized again; the sequence sync
> > > > will occur solely for the newly added sequences.
> > > >
> > > > 4) Introducing a new command for refreshing all sequences: ALTER
> > > > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES.
> > > > The subscriber will remove stale sequences and add newly added
> > > > sequences from the publisher. Following this, it will re-synchronize
> > > > the sequence values for all sequences in the updated list from the
> > > > publisher, following the steps outlined in the subscription creation
> > > > process.
> > >
> > > The difference between 3) and 4) is whether or not to re-synchronize
> > > the previously synchronized sequences. Do we really want to introduce
> > > a new command for 4)? I felt that we can invent an option say
> > > copy_all_sequence for the REFRESH PUBLICATION command to cover the 4)
> > > case.
> > >
> >
> > Yeah, that is also an option but it could confuse along with copy_data
> > option. Say the user has selected copy_data = false but
> > copy_all_sequences = true then the first option indicates to *not*
> > copy the data of table and sequences and the second option indicates
> > to copy the sequences data which sounds contradictory. The other idea
> > is to have an option copy_existing_sequences (which indicates to copy
> > existing sequence values) but that also has somewhat the same drawback
> > as copy_all_sequences but to a lesser degree.
>
> Good point. And I understood that the REFRESH PUBLICATION SEQUENCES
> command would be helpful when users want to synchronize sequences
> between two nodes before upgrading.
>

Right.

> >
> > > >
> > > > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > > > value from the publisher, along with the page LSN. Incorporate
> > > > SetSequence function, which will procure a new relfilenode for the
> > > > sequence and set the new relfilenode with the specified value. This
> > > > will facilitate rollback in case of any failures.
> > >
> > > Does it mean that we create a new relfilenode for every update of the value?
> > >
> >
> > We need it for initial sync so that if there is an error both the
> > sequence state in pg_subscription_rel and sequence values can be
> > rolled back together.
>
> Agreed.
>
> > However, it is unclear whether we need to create
> > a new relfilenode while copying existing sequences (say during ALTER
> > SUBSCRIPTION .. REFRESH PUBLICATION SEQUENCES, or whatever command we
> > decide)? Probably the answer lies in how we want to implement this
> > command. If we want to copy all sequence values during the command
> > itself then it is probably okay but if we want to handover this task
> > to the sequence-sync worker then we need some state management and a
> > new relfilenode so that on error both state and sequence values are
> > rolled back.
>
> What state transition of pg_subscription_rel entries for sequences do
> we need while copying sequences values? For example, we insert an
> entry with 'init' state at CREATE SUBSCRIPTION and then the
> sequence-sync worker updates to 'ready' and copies the sequence data.
> And at REFRESH PUBLICATION SEQUENCES, we update the state back to
> 'init' again so that the sequence-sync worker can process it? Given
> REFRESH PUBLICATION SEQUENCES won't be executed very frequently, it
> might be acceptable to transactionally update sequence values.
>

Do you mean that sync the sequences during the REFRESH PUBLICATION
SEQUENCES command itself? If so, there is an argument that we can do
the same during CREATE SUBSCRIPTION. It would be beneficial to keep
the method to sync the sequences same for both the CREATE and REFRESH
commands. I have speculated on one idea above and would be happy to
see your thoughts.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Thu, Jun 13, 2024 at 7:06 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Thu, Jun 13, 2024 at 1:09 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Wed, Jun 12, 2024 at 6:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > >
> > > Yeah, starting with a single worker sounds good for now. Do you think
> > > we should sync all the sequences in a single transaction or have some
> > > threshold value above which a different transaction would be required
> > > or maybe a different sequence sync worker altogether? Now, having
> > > multiple sequence-sync workers requires some synchronization so that
> > > only a single worker is allocated for one sequence.
> > >
> > > The simplest thing is to use a single sequence sync worker that syncs
> > > all sequences in one transaction but with a large number of sequences,
> > > it could be inefficient. OTOH, I am not sure if it would be a problem
> > > in reality.
> >
> > I think that we can start with using a single worker and one
> > transaction, and measure the performance with a large number of
> > sequences.
> >
>
> Fair enough. However, this raises the question Dilip and Vignesh are
> discussing whether we need a new relfilenode for sequence update even
> during initial sync? As per my understanding, the idea is that similar
> to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
> will create the new sequence entries in pg_subscription_rel with the
> state as 'i'. Then the sequence-sync worker would start a transaction
> and one-by-one copy the latest sequence values for each sequence (that
> has state as 'i' in pg_subscription_rel) and mark its state as ready
> 'r' and commit the transaction. Now if there is an error during this
> operation it will restart the entire operation. The idea of creating a
> new relfilenode is to handle the error so that if there is a rollback,
> the sequence state will be rolled back to 'i' and the sequence value
> will also be rolled back. The other option could be that we update the
> sequence value without a new relfilenode and if the transaction rolled
> back then only the sequence's state will be rolled back to 'i'. This
> would work with a minor inconsistency that sequence values will be
> up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> I am not sure if that matters because anyway, they can quickly be
> out-of-sync with the publisher again.

I think it would be fine in many cases even if the sequence value is
up-to-date even when the sequence state is 'i' in pg_subscription_rel.
But the case we would like to avoid is where suppose the sequence-sync
worker does both synchronizing sequence values and updating the
sequence states for all sequences in one transaction, and if there is
an error we end up retrying the synchronization for all sequences.

>
> Now, say we don't want to maintain the state of sequences for initial
> sync at all then after the error how will we detect if there are any
> pending sequences to be synced? One possibility is that we maintain a
> subscription level flag 'subsequencesync' in 'pg_subscription' to
> indicate whether sequences need sync. This flag would indicate whether
> to sync all the sequences in pg_susbcription_rel. This would mean that
> if there is an error while syncing the sequences we will resync all
> the sequences again. This could be acceptable considering the chances
> of error during sequence sync are low. The benefit is that both the
> REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> idea and sync all sequences without needing a new relfilenode. Users
> can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> all the sequences are synced after executing the command.

I think that REFRESH PUBLICATION {SEQUENCES} can be executed even
while the sequence-sync worker is synchronizing sequences. In this
case, the worker might not see new sequences added by the concurrent
REFRESH PUBLICATION {SEQUENCES} command since it's already running.
The worker could end up marking the subsequencesync as completed while
not synchronizing these new sequences.

>
> > > > Or yet another idea I came up with is that a tablesync worker will
> > > > synchronize both the table and sequences owned by the table. That is,
> > > > after the tablesync worker caught up with the apply worker, the
> > > > tablesync worker synchronizes sequences associated with the target
> > > > table as well. One benefit would be that at the time of initial table
> > > > sync being completed, the table and its sequence data are consistent.
> >
> > Correction; it's not guaranteed that the sequence data and table data
> > are consistent even in this case since the tablesync worker could get
> > on-disk sequence data that might have already been updated.
> >
>
> The benefit of this approach is not clear to me. Our aim is to sync
> all sequences before the upgrade, so not sure if this helps because
> anyway both table values and corresponding sequences can again be
> out-of-sync very quickly.

Right.

Given that our aim is to sync all sequences before the upgrade, do we
need to synchronize sequences even at CREATE SUBSCRIPTION time? In
cases where there are a large number of sequences, synchronizing
sequences in addition to tables could be overhead and make less sense,
because sequences can again be out-of-sync quickly and typically
CREATE SUBSCRIPTION is not created just before the upgrade.

>
> > >
> > > > >
> > > > > 5) Incorporate the pg_sequence_state function to fetch the sequence
> > > > > value from the publisher, along with the page LSN. Incorporate
> > > > > SetSequence function, which will procure a new relfilenode for the
> > > > > sequence and set the new relfilenode with the specified value. This
> > > > > will facilitate rollback in case of any failures.
> > > >
> > > > Does it mean that we create a new relfilenode for every update of the value?
> > > >
> > >
> > > We need it for initial sync so that if there is an error both the
> > > sequence state in pg_subscription_rel and sequence values can be
> > > rolled back together.
> >
> > Agreed.
> >
> > > However, it is unclear whether we need to create
> > > a new relfilenode while copying existing sequences (say during ALTER
> > > SUBSCRIPTION .. REFRESH PUBLICATION SEQUENCES, or whatever command we
> > > decide)? Probably the answer lies in how we want to implement this
> > > command. If we want to copy all sequence values during the command
> > > itself then it is probably okay but if we want to handover this task
> > > to the sequence-sync worker then we need some state management and a
> > > new relfilenode so that on error both state and sequence values are
> > > rolled back.
> >
> > What state transition of pg_subscription_rel entries for sequences do
> > we need while copying sequences values? For example, we insert an
> > entry with 'init' state at CREATE SUBSCRIPTION and then the
> > sequence-sync worker updates to 'ready' and copies the sequence data.
> > And at REFRESH PUBLICATION SEQUENCES, we update the state back to
> > 'init' again so that the sequence-sync worker can process it? Given
> > REFRESH PUBLICATION SEQUENCES won't be executed very frequently, it
> > might be acceptable to transactionally update sequence values.
> >
>
> Do you mean that sync the sequences during the REFRESH PUBLICATION
> SEQUENCES command itself? If so, there is an argument that we can do
> the same during CREATE SUBSCRIPTION. It would be beneficial to keep
> the method to sync the sequences same for both the CREATE and REFRESH
> commands. I have speculated on one idea above and would be happy to
> see your thoughts.

I meant that the REFRESH PUBLICATION SEQUENCES command updates all
sequence states in pg_subscription_rel to 'init' state, and the
sequence-sync worker can do the synchronization work. We use the same
method for both the CREATE SUBSCRIPTION and REFRESH PUBLICATION
{SEQUENCES} commands.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Michael Paquier
Date:
On Thu, Jun 13, 2024 at 03:36:05PM +0530, Amit Kapila wrote:
> Fair enough. However, this raises the question Dilip and Vignesh are
> discussing whether we need a new relfilenode for sequence update even
> during initial sync? As per my understanding, the idea is that similar
> to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
> will create the new sequence entries in pg_subscription_rel with the
> state as 'i'. Then the sequence-sync worker would start a transaction
> and one-by-one copy the latest sequence values for each sequence (that
> has state as 'i' in pg_subscription_rel) and mark its state as ready
> 'r' and commit the transaction. Now if there is an error during this
> operation it will restart the entire operation.

Hmm.  You mean to use only one transaction for all the sequences?
I've heard about deployments with a lot of them.  Could it be a
problem to process them in batches, as well?  If you maintain a state
for each one of them in pg_subscription_rel, it does not strike me as
an issue, while being more flexible than an all-or-nothing.

> The idea of creating a
> new relfilenode is to handle the error so that if there is a rollback,
> the sequence state will be rolled back to 'i' and the sequence value
> will also be rolled back. The other option could be that we update the
> sequence value without a new relfilenode and if the transaction rolled
> back then only the sequence's state will be rolled back to 'i'. This
> would work with a minor inconsistency that sequence values will be
> up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> I am not sure if that matters because anyway, they can quickly be
> out-of-sync with the publisher again.

Seeing a mention to relfilenodes specifically for sequences freaks me
out a bit, because there's some work I have been doing in this area
and sequences may not have a need for a physical relfilenode at all.
But I guess that you refer to the fact that like tables, relfilenodes
would only be created as required because anything you'd do in the
apply worker path would just call some of the routines of sequence.h,
right?

> Now, say we don't want to maintain the state of sequences for initial
> sync at all then after the error how will we detect if there are any
> pending sequences to be synced? One possibility is that we maintain a
> subscription level flag 'subsequencesync' in 'pg_subscription' to
> indicate whether sequences need sync. This flag would indicate whether
> to sync all the sequences in pg_susbcription_rel. This would mean that
> if there is an error while syncing the sequences we will resync all
> the sequences again. This could be acceptable considering the chances
> of error during sequence sync are low.

There could be multiple subscriptions to a single database that point
to the same set of sequences.  Is there any conflict issue to worry
about here?

> The benefit is that both the
> REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> idea and sync all sequences without needing a new relfilenode. Users
> can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> all the sequences are synced after executing the command.

That would be cheaper, indeed.  Isn't a boolean too limiting?
Isn't that something you'd want to track with a LSN as "the point in
WAL where all the sequences have been synced"?

The approach of doing all the sync work from the subscriber, while
having a command that can be kicked from the subscriber side is a good
user experience.
--
Michael

Attachment

Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Jun 13, 2024 at 6:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Thu, Jun 13, 2024 at 7:06 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Thu, Jun 13, 2024 at 1:09 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> > > On Wed, Jun 12, 2024 at 6:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > > >
> > > > Yeah, starting with a single worker sounds good for now. Do you think
> > > > we should sync all the sequences in a single transaction or have some
> > > > threshold value above which a different transaction would be required
> > > > or maybe a different sequence sync worker altogether? Now, having
> > > > multiple sequence-sync workers requires some synchronization so that
> > > > only a single worker is allocated for one sequence.
> > > >
> > > > The simplest thing is to use a single sequence sync worker that syncs
> > > > all sequences in one transaction but with a large number of sequences,
> > > > it could be inefficient. OTOH, I am not sure if it would be a problem
> > > > in reality.
> > >
> > > I think that we can start with using a single worker and one
> > > transaction, and measure the performance with a large number of
> > > sequences.
> > >
> >
> > Fair enough. However, this raises the question Dilip and Vignesh are
> > discussing whether we need a new relfilenode for sequence update even
> > during initial sync? As per my understanding, the idea is that similar
> > to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
> > will create the new sequence entries in pg_subscription_rel with the
> > state as 'i'. Then the sequence-sync worker would start a transaction
> > and one-by-one copy the latest sequence values for each sequence (that
> > has state as 'i' in pg_subscription_rel) and mark its state as ready
> > 'r' and commit the transaction. Now if there is an error during this
> > operation it will restart the entire operation. The idea of creating a
> > new relfilenode is to handle the error so that if there is a rollback,
> > the sequence state will be rolled back to 'i' and the sequence value
> > will also be rolled back. The other option could be that we update the
> > sequence value without a new relfilenode and if the transaction rolled
> > back then only the sequence's state will be rolled back to 'i'. This
> > would work with a minor inconsistency that sequence values will be
> > up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> > I am not sure if that matters because anyway, they can quickly be
> > out-of-sync with the publisher again.
>
> I think it would be fine in many cases even if the sequence value is
> up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> But the case we would like to avoid is where suppose the sequence-sync
> worker does both synchronizing sequence values and updating the
> sequence states for all sequences in one transaction, and if there is
> an error we end up retrying the synchronization for all sequences.
>

The one idea to avoid this is to update sequences in chunks (say 100
or some threshold number of sequences in one transaction). Then we
would only redo the sync for the last and pending set of sequences.

> >
> > Now, say we don't want to maintain the state of sequences for initial
> > sync at all then after the error how will we detect if there are any
> > pending sequences to be synced? One possibility is that we maintain a
> > subscription level flag 'subsequencesync' in 'pg_subscription' to
> > indicate whether sequences need sync. This flag would indicate whether
> > to sync all the sequences in pg_susbcription_rel. This would mean that
> > if there is an error while syncing the sequences we will resync all
> > the sequences again. This could be acceptable considering the chances
> > of error during sequence sync are low. The benefit is that both the
> > REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> > idea and sync all sequences without needing a new relfilenode. Users
> > can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> > all the sequences are synced after executing the command.
>
> I think that REFRESH PUBLICATION {SEQUENCES} can be executed even
> while the sequence-sync worker is synchronizing sequences. In this
> case, the worker might not see new sequences added by the concurrent
> REFRESH PUBLICATION {SEQUENCES} command since it's already running.
> The worker could end up marking the subsequencesync as completed while
> not synchronizing these new sequences.
>

This is possible but we could avoid REFRESH PUBLICATION {SEQUENCES} by
not allowing to change the subsequencestate during the time
sequence-worker is syncing the sequences. This could be restrictive
but there doesn't seem to be cases where user would like to
immediately refresh sequences after creating the subscription.

> >
> > > > > Or yet another idea I came up with is that a tablesync worker will
> > > > > synchronize both the table and sequences owned by the table. That is,
> > > > > after the tablesync worker caught up with the apply worker, the
> > > > > tablesync worker synchronizes sequences associated with the target
> > > > > table as well. One benefit would be that at the time of initial table
> > > > > sync being completed, the table and its sequence data are consistent.
> > >
> > > Correction; it's not guaranteed that the sequence data and table data
> > > are consistent even in this case since the tablesync worker could get
> > > on-disk sequence data that might have already been updated.
> > >
> >
> > The benefit of this approach is not clear to me. Our aim is to sync
> > all sequences before the upgrade, so not sure if this helps because
> > anyway both table values and corresponding sequences can again be
> > out-of-sync very quickly.
>
> Right.
>
> Given that our aim is to sync all sequences before the upgrade, do we
> need to synchronize sequences even at CREATE SUBSCRIPTION time? In
> cases where there are a large number of sequences, synchronizing
> sequences in addition to tables could be overhead and make less sense,
> because sequences can again be out-of-sync quickly and typically
> CREATE SUBSCRIPTION is not created just before the upgrade.
>

I think for the upgrade one should be creating a subscription just
before the upgrade. Isn't something similar is done even in the
upgrade steps you shared once [1]? Typically users should get all the
data from the publisher before the upgrade of the publisher via
creating a subscription. Also, it would be better to keep the
implementation of sequences close to tables wherever possible. Having
said that, I understand your point as well and if you strongly feel
that we don't need to sync sequences at the time of CREATE
SUBSCRIPTION and others also don't see any problem with it then we can
consider that as well.

> > >
> >
> > Do you mean that sync the sequences during the REFRESH PUBLICATION
> > SEQUENCES command itself? If so, there is an argument that we can do
> > the same during CREATE SUBSCRIPTION. It would be beneficial to keep
> > the method to sync the sequences same for both the CREATE and REFRESH
> > commands. I have speculated on one idea above and would be happy to
> > see your thoughts.
>
> I meant that the REFRESH PUBLICATION SEQUENCES command updates all
> sequence states in pg_subscription_rel to 'init' state, and the
> sequence-sync worker can do the synchronization work. We use the same
> method for both the CREATE SUBSCRIPTION and REFRESH PUBLICATION
> {SEQUENCES} commands.
>

Marking the state as 'init' when we would have already synced the
sequences sounds a bit odd but otherwise, this could also work if we
accept that even if the sequences are synced and value could remain in
'init' state (on rollbacks).


[1] - https://knock.app/blog/zero-downtime-postgres-upgrades

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Fri, Jun 14, 2024 at 5:16 AM Michael Paquier <michael@paquier.xyz> wrote:
>
> On Thu, Jun 13, 2024 at 03:36:05PM +0530, Amit Kapila wrote:
> > Fair enough. However, this raises the question Dilip and Vignesh are
> > discussing whether we need a new relfilenode for sequence update even
> > during initial sync? As per my understanding, the idea is that similar
> > to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
> > will create the new sequence entries in pg_subscription_rel with the
> > state as 'i'. Then the sequence-sync worker would start a transaction
> > and one-by-one copy the latest sequence values for each sequence (that
> > has state as 'i' in pg_subscription_rel) and mark its state as ready
> > 'r' and commit the transaction. Now if there is an error during this
> > operation it will restart the entire operation.
>
> Hmm.  You mean to use only one transaction for all the sequences?
> I've heard about deployments with a lot of them.  Could it be a
> problem to process them in batches, as well?

I don't think so. We can even sync one sequence per transaction but
then it would be resource and time consuming without much gain. As
mentioned in a previous email, we might want to sync 100 or some other
threshold number of sequences per transaction. The other possibility
is to make a subscription-level option for this batch size but I don't
see much advantage in doing so as it won't be convenient for users to
set it. I feel we should pick some threshold number that is neither
too low nor too high and if we later see any problem with it, we can
make it a configurable knob.

>
> > The idea of creating a
> > new relfilenode is to handle the error so that if there is a rollback,
> > the sequence state will be rolled back to 'i' and the sequence value
> > will also be rolled back. The other option could be that we update the
> > sequence value without a new relfilenode and if the transaction rolled
> > back then only the sequence's state will be rolled back to 'i'. This
> > would work with a minor inconsistency that sequence values will be
> > up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> > I am not sure if that matters because anyway, they can quickly be
> > out-of-sync with the publisher again.
>
> Seeing a mention to relfilenodes specifically for sequences freaks me
> out a bit, because there's some work I have been doing in this area
> and sequences may not have a need for a physical relfilenode at all.
> But I guess that you refer to the fact that like tables, relfilenodes
> would only be created as required because anything you'd do in the
> apply worker path would just call some of the routines of sequence.h,
> right?
>

Yes, I think so. The only thing the patch expects is a way to rollback
the sequence changes if the transaction rolls back during the initial
sync. But I am not sure if we need such a behavior. The discussion for
the same is in progress. Let's wait for the outcome.

> > Now, say we don't want to maintain the state of sequences for initial
> > sync at all then after the error how will we detect if there are any
> > pending sequences to be synced? One possibility is that we maintain a
> > subscription level flag 'subsequencesync' in 'pg_subscription' to
> > indicate whether sequences need sync. This flag would indicate whether
> > to sync all the sequences in pg_susbcription_rel. This would mean that
> > if there is an error while syncing the sequences we will resync all
> > the sequences again. This could be acceptable considering the chances
> > of error during sequence sync are low.
>
> There could be multiple subscriptions to a single database that point
> to the same set of sequences.  Is there any conflict issue to worry
> about here?
>

I don't think so. In the worst case, the same value would be copied
twice. The same scenario in case of tables could lead to duplicate
data or unique key violation ERRORs which is much worse. So, I expect
users to be careful about the same.

> > The benefit is that both the
> > REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> > idea and sync all sequences without needing a new relfilenode. Users
> > can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> > all the sequences are synced after executing the command.
>
> That would be cheaper, indeed.  Isn't a boolean too limiting?
>

In this idea, we only need a flag to say whether the sequence sync is
required or not.

> Isn't that something you'd want to track with a LSN as "the point in
> WAL where all the sequences have been synced"?
>

It won't be any better for the required purpose because after CREATE
SUBSCRIPTION, if REFERESH wants to toggle the flag to indicate the
sequences need sync again then using LSN would mean we need to set it
to Invalid value.

> The approach of doing all the sync work from the subscriber, while
> having a command that can be kicked from the subscriber side is a good
> user experience.
>

Thank you for endorsing the idea.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Fri, Jun 14, 2024 at 4:04 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Thu, Jun 13, 2024 at 6:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> >
> > On Thu, Jun 13, 2024 at 7:06 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > > On Thu, Jun 13, 2024 at 1:09 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > > >
> > > > On Wed, Jun 12, 2024 at 6:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > >
> > > > >
> > > > > Yeah, starting with a single worker sounds good for now. Do you think
> > > > > we should sync all the sequences in a single transaction or have some
> > > > > threshold value above which a different transaction would be required
> > > > > or maybe a different sequence sync worker altogether? Now, having
> > > > > multiple sequence-sync workers requires some synchronization so that
> > > > > only a single worker is allocated for one sequence.
> > > > >
> > > > > The simplest thing is to use a single sequence sync worker that syncs
> > > > > all sequences in one transaction but with a large number of sequences,
> > > > > it could be inefficient. OTOH, I am not sure if it would be a problem
> > > > > in reality.
> > > >
> > > > I think that we can start with using a single worker and one
> > > > transaction, and measure the performance with a large number of
> > > > sequences.
> > > >
> > >
> > > Fair enough. However, this raises the question Dilip and Vignesh are
> > > discussing whether we need a new relfilenode for sequence update even
> > > during initial sync? As per my understanding, the idea is that similar
> > > to tables, the CREATE SUBSCRIPTION command (with copy_data = true)
> > > will create the new sequence entries in pg_subscription_rel with the
> > > state as 'i'. Then the sequence-sync worker would start a transaction
> > > and one-by-one copy the latest sequence values for each sequence (that
> > > has state as 'i' in pg_subscription_rel) and mark its state as ready
> > > 'r' and commit the transaction. Now if there is an error during this
> > > operation it will restart the entire operation. The idea of creating a
> > > new relfilenode is to handle the error so that if there is a rollback,
> > > the sequence state will be rolled back to 'i' and the sequence value
> > > will also be rolled back. The other option could be that we update the
> > > sequence value without a new relfilenode and if the transaction rolled
> > > back then only the sequence's state will be rolled back to 'i'. This
> > > would work with a minor inconsistency that sequence values will be
> > > up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> > > I am not sure if that matters because anyway, they can quickly be
> > > out-of-sync with the publisher again.
> >
> > I think it would be fine in many cases even if the sequence value is
> > up-to-date even when the sequence state is 'i' in pg_subscription_rel.
> > But the case we would like to avoid is where suppose the sequence-sync
> > worker does both synchronizing sequence values and updating the
> > sequence states for all sequences in one transaction, and if there is
> > an error we end up retrying the synchronization for all sequences.
> >
>
> The one idea to avoid this is to update sequences in chunks (say 100
> or some threshold number of sequences in one transaction). Then we
> would only redo the sync for the last and pending set of sequences.

That could be one idea.

>
> > >
> > > Now, say we don't want to maintain the state of sequences for initial
> > > sync at all then after the error how will we detect if there are any
> > > pending sequences to be synced? One possibility is that we maintain a
> > > subscription level flag 'subsequencesync' in 'pg_subscription' to
> > > indicate whether sequences need sync. This flag would indicate whether
> > > to sync all the sequences in pg_susbcription_rel. This would mean that
> > > if there is an error while syncing the sequences we will resync all
> > > the sequences again. This could be acceptable considering the chances
> > > of error during sequence sync are low. The benefit is that both the
> > > REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> > > idea and sync all sequences without needing a new relfilenode. Users
> > > can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> > > all the sequences are synced after executing the command.
> >
> > I think that REFRESH PUBLICATION {SEQUENCES} can be executed even
> > while the sequence-sync worker is synchronizing sequences. In this
> > case, the worker might not see new sequences added by the concurrent
> > REFRESH PUBLICATION {SEQUENCES} command since it's already running.
> > The worker could end up marking the subsequencesync as completed while
> > not synchronizing these new sequences.
> >
>
> This is possible but we could avoid REFRESH PUBLICATION {SEQUENCES} by
> not allowing to change the subsequencestate during the time
> sequence-worker is syncing the sequences. This could be restrictive
> but there doesn't seem to be cases where user would like to
> immediately refresh sequences after creating the subscription.

I'm concerned that users would not be able to add sequences during the
time the sequence-worker is syncing the sequences. For example,
suppose we have 10000 sequences and execute REFRESH PUBLICATION
{SEQUENCES} to synchronize 10000 sequences. Now if we add one sequence
to the publication and want to synchronize it to the subscriber, we
have to wait for the current REFRESH PUBLICATION {SEQUENCES} to
complete, and then execute it again, synchronizing 10001 sequences,
instead of synchronizing only the new one.

>
> > >
> > > > > > Or yet another idea I came up with is that a tablesync worker will
> > > > > > synchronize both the table and sequences owned by the table. That is,
> > > > > > after the tablesync worker caught up with the apply worker, the
> > > > > > tablesync worker synchronizes sequences associated with the target
> > > > > > table as well. One benefit would be that at the time of initial table
> > > > > > sync being completed, the table and its sequence data are consistent.
> > > >
> > > > Correction; it's not guaranteed that the sequence data and table data
> > > > are consistent even in this case since the tablesync worker could get
> > > > on-disk sequence data that might have already been updated.
> > > >
> > >
> > > The benefit of this approach is not clear to me. Our aim is to sync
> > > all sequences before the upgrade, so not sure if this helps because
> > > anyway both table values and corresponding sequences can again be
> > > out-of-sync very quickly.
> >
> > Right.
> >
> > Given that our aim is to sync all sequences before the upgrade, do we
> > need to synchronize sequences even at CREATE SUBSCRIPTION time? In
> > cases where there are a large number of sequences, synchronizing
> > sequences in addition to tables could be overhead and make less sense,
> > because sequences can again be out-of-sync quickly and typically
> > CREATE SUBSCRIPTION is not created just before the upgrade.
> >
>
> I think for the upgrade one should be creating a subscription just
> before the upgrade. Isn't something similar is done even in the
> upgrade steps you shared once [1]?

I might be missing something but in the blog post they created
subscriptions in various ways, waited for the initial table data sync
to complete, and then set the sequence values with a buffer based on
the old cluster. What I imagined with this sequence synchronization
feature is that after the initial table sync completes, we stop to
execute further transactions on the publisher, synchronize sequences
using REFRESH PUBLICATION {SEQUENCES}, and resume the application to
execute transactions on the subscriber. So a subscription would be
created just before the upgrade, but sequence synchronization would
not necessarily happen at the same time of the initial table data
synchronization.

> Typically users should get all the
> data from the publisher before the upgrade of the publisher via
> creating a subscription. Also, it would be better to keep the
> implementation of sequences close to tables wherever possible. Having
> said that, I understand your point as well and if you strongly feel
> that we don't need to sync sequences at the time of CREATE
> SUBSCRIPTION and others also don't see any problem with it then we can
> consider that as well.

I see your point that it's better to keep the implementation of
sequences close to the table one. So I agree that we can start with
this approach, and we will see how it works in practice and consider
other options later.

>
> > > >
> > >
> > > Do you mean that sync the sequences during the REFRESH PUBLICATION
> > > SEQUENCES command itself? If so, there is an argument that we can do
> > > the same during CREATE SUBSCRIPTION. It would be beneficial to keep
> > > the method to sync the sequences same for both the CREATE and REFRESH
> > > commands. I have speculated on one idea above and would be happy to
> > > see your thoughts.
> >
> > I meant that the REFRESH PUBLICATION SEQUENCES command updates all
> > sequence states in pg_subscription_rel to 'init' state, and the
> > sequence-sync worker can do the synchronization work. We use the same
> > method for both the CREATE SUBSCRIPTION and REFRESH PUBLICATION
> > {SEQUENCES} commands.
> >
>
> Marking the state as 'init' when we would have already synced the
> sequences sounds a bit odd but otherwise, this could also work if we
> accept that even if the sequences are synced and value could remain in
> 'init' state (on rollbacks).

I mean that it's just for identifying sequences that need to be
synced. With the idea of using sequence states in pg_subscription_rel,
the REFRESH PUBLICATION SEQUENCES command needs to change states to
something so that the sequence-sync worker can identify which sequence
needs to be synced. If 'init' sounds odd, we can invent a new state
for sequences, say 'needs-to-be-syned'.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Jun 18, 2024 at 7:30 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
>
> On Fri, Jun 14, 2024 at 4:04 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Thu, Jun 13, 2024 at 6:14 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
> > >
> >
> > > >
> > > > Now, say we don't want to maintain the state of sequences for initial
> > > > sync at all then after the error how will we detect if there are any
> > > > pending sequences to be synced? One possibility is that we maintain a
> > > > subscription level flag 'subsequencesync' in 'pg_subscription' to
> > > > indicate whether sequences need sync. This flag would indicate whether
> > > > to sync all the sequences in pg_susbcription_rel. This would mean that
> > > > if there is an error while syncing the sequences we will resync all
> > > > the sequences again. This could be acceptable considering the chances
> > > > of error during sequence sync are low. The benefit is that both the
> > > > REFRESH PUBLICATION SEQUENCES and CREATE SUBSCRIPTION can use the same
> > > > idea and sync all sequences without needing a new relfilenode. Users
> > > > can always refer 'subsequencesync' flag in 'pg_subscription' to see if
> > > > all the sequences are synced after executing the command.
> > >
> > > I think that REFRESH PUBLICATION {SEQUENCES} can be executed even
> > > while the sequence-sync worker is synchronizing sequences. In this
> > > case, the worker might not see new sequences added by the concurrent
> > > REFRESH PUBLICATION {SEQUENCES} command since it's already running.
> > > The worker could end up marking the subsequencesync as completed while
> > > not synchronizing these new sequences.
> > >
> >
> > This is possible but we could avoid REFRESH PUBLICATION {SEQUENCES} by
> > not allowing to change the subsequencestate during the time
> > sequence-worker is syncing the sequences. This could be restrictive
> > but there doesn't seem to be cases where user would like to
> > immediately refresh sequences after creating the subscription.
>
> I'm concerned that users would not be able to add sequences during the
> time the sequence-worker is syncing the sequences. For example,
> suppose we have 10000 sequences and execute REFRESH PUBLICATION
> {SEQUENCES} to synchronize 10000 sequences. Now if we add one sequence
> to the publication and want to synchronize it to the subscriber, we
> have to wait for the current REFRESH PUBLICATION {SEQUENCES} to
> complete, and then execute it again, synchronizing 10001 sequences,
> instead of synchronizing only the new one.
>

I see your point and it could hurt such scenarios even though they
won't be frequent. So, let's focus on our other approach of
maintaining the flag at a per-sequence level in pg_subscription_rel.

> >
> > > >
> > > > > > > Or yet another idea I came up with is that a tablesync worker will
> > > > > > > synchronize both the table and sequences owned by the table. That is,
> > > > > > > after the tablesync worker caught up with the apply worker, the
> > > > > > > tablesync worker synchronizes sequences associated with the target
> > > > > > > table as well. One benefit would be that at the time of initial table
> > > > > > > sync being completed, the table and its sequence data are consistent.
> > > > >
> > > > > Correction; it's not guaranteed that the sequence data and table data
> > > > > are consistent even in this case since the tablesync worker could get
> > > > > on-disk sequence data that might have already been updated.
> > > > >
> > > >
> > > > The benefit of this approach is not clear to me. Our aim is to sync
> > > > all sequences before the upgrade, so not sure if this helps because
> > > > anyway both table values and corresponding sequences can again be
> > > > out-of-sync very quickly.
> > >
> > > Right.
> > >
> > > Given that our aim is to sync all sequences before the upgrade, do we
> > > need to synchronize sequences even at CREATE SUBSCRIPTION time? In
> > > cases where there are a large number of sequences, synchronizing
> > > sequences in addition to tables could be overhead and make less sense,
> > > because sequences can again be out-of-sync quickly and typically
> > > CREATE SUBSCRIPTION is not created just before the upgrade.
> > >
> >
> > I think for the upgrade one should be creating a subscription just
> > before the upgrade. Isn't something similar is done even in the
> > upgrade steps you shared once [1]?
>
> I might be missing something but in the blog post they created
> subscriptions in various ways, waited for the initial table data sync
> to complete, and then set the sequence values with a buffer based on
> the old cluster. What I imagined with this sequence synchronization
> feature is that after the initial table sync completes, we stop to
> execute further transactions on the publisher, synchronize sequences
> using REFRESH PUBLICATION {SEQUENCES}, and resume the application to
> execute transactions on the subscriber. So a subscription would be
> created just before the upgrade, but sequence synchronization would
> not necessarily happen at the same time of the initial table data
> synchronization.
>

It depends on the exact steps of the upgrade. For example, if one
stops the publisher before adding sequences to a subscription either
via create subscription or alter subscription add/set command then
there won't be a need for a separate refresh but OTOH, if one follows
the steps you mentioned then the refresh would be required. As you are
okay, with syncing the sequences while creating a subscription in the
below part of the email, there is not much point in arguing about this
further.

> > Typically users should get all the
> > data from the publisher before the upgrade of the publisher via
> > creating a subscription. Also, it would be better to keep the
> > implementation of sequences close to tables wherever possible. Having
> > said that, I understand your point as well and if you strongly feel
> > that we don't need to sync sequences at the time of CREATE
> > SUBSCRIPTION and others also don't see any problem with it then we can
> > consider that as well.
>
> I see your point that it's better to keep the implementation of
> sequences close to the table one. So I agree that we can start with
> this approach, and we will see how it works in practice and consider
> other options later.
>

makes sense.

> >
> > Marking the state as 'init' when we would have already synced the
> > sequences sounds a bit odd but otherwise, this could also work if we
> > accept that even if the sequences are synced and value could remain in
> > 'init' state (on rollbacks).
>
> I mean that it's just for identifying sequences that need to be
> synced. With the idea of using sequence states in pg_subscription_rel,
> the REFRESH PUBLICATION SEQUENCES command needs to change states to
> something so that the sequence-sync worker can identify which sequence
> needs to be synced. If 'init' sounds odd, we can invent a new state
> for sequences, say 'needs-to-be-syned'.
>

Agreed and I am not sure which is better because there is a value in
keeping the state name the same for both sequences and tables. We
probably need more comments in code and doc updates to make the
behavior clear. We can start with the sequence state as 'init' for
'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
feel so during the review.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
>
> Agreed and I am not sure which is better because there is a value in
> keeping the state name the same for both sequences and tables. We
> probably need more comments in code and doc updates to make the
> behavior clear. We can start with the sequence state as 'init' for
> 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> feel so during the review.

Here is a patch which does the sequence synchronization in the
following lines from the above discussion:
This commit introduces sequence synchronization during 1) creation of
subscription for initial sync of sequences 2) refresh publication to
synchronize the sequences for the newly created sequences 3) refresh
publication sequences for synchronizing all the sequences.
1) During subscription creation with CREATE SUBSCRIPTION (no syntax change):
   - The subscriber retrieves sequences associated with publications.
   - Sequences  are added in the 'init' state to the pg_subscription_rel table.
   - Sequence synchronization worker will be started if there are any
sequences to be synchronized
   - A new sequence synchronization worker handles synchronization in
batches of 100 sequences:
     a) Retrieves sequence values using pg_sequence_state from the publisher.
     b) Sets sequence values accordingly.
     c) Updates sequence state to 'READY' in pg_susbcripion_rel
     d) Commits batches of 100 synchronized sequences.
2) Refreshing sequences with ALTER SUBSCRIPTION ... REFRESH
PUBLICATION (no syntax change):
   - Stale sequences are removed from pg_subscription_rel.
   - Newly added sequences in the publisher are added in 'init' state
to pg_subscription_rel.
   - Sequence synchronization will be done by sequence sync worker as
listed in subscription creation process.
   - Sequence synchronization occurs for newly added sequences only.
3) Introduce new command ALTER SUBSCRIPTION ... REFRESH PUBLICATION
SEQUENCES  for refreshing all sequences:
   - Removes stale sequences and adds newly added sequences from the
publisher to pg_subscription_rel.
   - Resets all sequences in pg_subscription_rel to 'init' state.
   - Initiates sequence synchronization for all sequences by sequence
sync worker as listed in subscription creation process.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 19 Jun 2024 at 20:33, vignesh C <vignesh21@gmail.com> wrote:
>
> On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> >
> > Agreed and I am not sure which is better because there is a value in
> > keeping the state name the same for both sequences and tables. We
> > probably need more comments in code and doc updates to make the
> > behavior clear. We can start with the sequence state as 'init' for
> > 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> > feel so during the review.
>
> Here is a patch which does the sequence synchronization in the
> following lines from the above discussion:
> This commit introduces sequence synchronization during 1) creation of
> subscription for initial sync of sequences 2) refresh publication to
> synchronize the sequences for the newly created sequences 3) refresh
> publication sequences for synchronizing all the sequences.
> 1) During subscription creation with CREATE SUBSCRIPTION (no syntax change):
>    - The subscriber retrieves sequences associated with publications.
>    - Sequences  are added in the 'init' state to the pg_subscription_rel table.
>    - Sequence synchronization worker will be started if there are any
> sequences to be synchronized
>    - A new sequence synchronization worker handles synchronization in
> batches of 100 sequences:
>      a) Retrieves sequence values using pg_sequence_state from the publisher.
>      b) Sets sequence values accordingly.
>      c) Updates sequence state to 'READY' in pg_susbcripion_rel
>      d) Commits batches of 100 synchronized sequences.
> 2) Refreshing sequences with ALTER SUBSCRIPTION ... REFRESH
> PUBLICATION (no syntax change):
>    - Stale sequences are removed from pg_subscription_rel.
>    - Newly added sequences in the publisher are added in 'init' state
> to pg_subscription_rel.
>    - Sequence synchronization will be done by sequence sync worker as
> listed in subscription creation process.
>    - Sequence synchronization occurs for newly added sequences only.
> 3) Introduce new command ALTER SUBSCRIPTION ... REFRESH PUBLICATION
> SEQUENCES  for refreshing all sequences:
>    - Removes stale sequences and adds newly added sequences from the
> publisher to pg_subscription_rel.
>    - Resets all sequences in pg_subscription_rel to 'init' state.
>    - Initiates sequence synchronization for all sequences by sequence
> sync worker as listed in subscription creation process.

Here is an updated patch with a few fixes to remove an unused
function, changed a few references of table to sequence and added one
CHECK_FOR_INTERRUPTS in the sequence sync worker loop.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Jun 19, 2024 at 8:33 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> >
> > Agreed and I am not sure which is better because there is a value in
> > keeping the state name the same for both sequences and tables. We
> > probably need more comments in code and doc updates to make the
> > behavior clear. We can start with the sequence state as 'init' for
> > 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> > feel so during the review.
>
> Here is a patch which does the sequence synchronization in the
> following lines from the above discussion:
>

Thanks for summarizing the points discussed. I would like to confirm
whether the patch replicates new sequences that are created
implicitly/explicitly for a publication defined as ALL SEQUENCES.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 20 Jun 2024 at 18:45, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Jun 19, 2024 at 8:33 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > >
> > > Agreed and I am not sure which is better because there is a value in
> > > keeping the state name the same for both sequences and tables. We
> > > probably need more comments in code and doc updates to make the
> > > behavior clear. We can start with the sequence state as 'init' for
> > > 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> > > feel so during the review.
> >
> > Here is a patch which does the sequence synchronization in the
> > following lines from the above discussion:
> >
>
> Thanks for summarizing the points discussed. I would like to confirm
> whether the patch replicates new sequences that are created
> implicitly/explicitly for a publication defined as ALL SEQUENCES.

Currently, FOR ALL SEQUENCES publication both explicitly created
sequences and implicitly created sequences will be synchronized during
the creation of subscriptions (using CREATE SUBSCRIPTION) and
refreshing publication sequences(using ALTER SUBSCRIPTION ... REFRESH
PUBLICATION SEQUENCES).
Therefore, the explicitly created sequence seq1:
CREATE SEQUENCE seq1;
and the implicitly created sequence seq_test2_c2_seq for seq_test2 table:
CREATE TABLE seq_test2 (c1 int, c2 SERIAL);
will both be synchronized.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Shlok Kyal
Date:
On Thu, 20 Jun 2024 at 18:24, vignesh C <vignesh21@gmail.com> wrote:
>
> On Wed, 19 Jun 2024 at 20:33, vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > >
> > > Agreed and I am not sure which is better because there is a value in
> > > keeping the state name the same for both sequences and tables. We
> > > probably need more comments in code and doc updates to make the
> > > behavior clear. We can start with the sequence state as 'init' for
> > > 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> > > feel so during the review.
> >
> > Here is a patch which does the sequence synchronization in the
> > following lines from the above discussion:
> > This commit introduces sequence synchronization during 1) creation of
> > subscription for initial sync of sequences 2) refresh publication to
> > synchronize the sequences for the newly created sequences 3) refresh
> > publication sequences for synchronizing all the sequences.
> > 1) During subscription creation with CREATE SUBSCRIPTION (no syntax change):
> >    - The subscriber retrieves sequences associated with publications.
> >    - Sequences  are added in the 'init' state to the pg_subscription_rel table.
> >    - Sequence synchronization worker will be started if there are any
> > sequences to be synchronized
> >    - A new sequence synchronization worker handles synchronization in
> > batches of 100 sequences:
> >      a) Retrieves sequence values using pg_sequence_state from the publisher.
> >      b) Sets sequence values accordingly.
> >      c) Updates sequence state to 'READY' in pg_susbcripion_rel
> >      d) Commits batches of 100 synchronized sequences.
> > 2) Refreshing sequences with ALTER SUBSCRIPTION ... REFRESH
> > PUBLICATION (no syntax change):
> >    - Stale sequences are removed from pg_subscription_rel.
> >    - Newly added sequences in the publisher are added in 'init' state
> > to pg_subscription_rel.
> >    - Sequence synchronization will be done by sequence sync worker as
> > listed in subscription creation process.
> >    - Sequence synchronization occurs for newly added sequences only.
> > 3) Introduce new command ALTER SUBSCRIPTION ... REFRESH PUBLICATION
> > SEQUENCES  for refreshing all sequences:
> >    - Removes stale sequences and adds newly added sequences from the
> > publisher to pg_subscription_rel.
> >    - Resets all sequences in pg_subscription_rel to 'init' state.
> >    - Initiates sequence synchronization for all sequences by sequence
> > sync worker as listed in subscription creation process.
>
> Here is an updated patch with a few fixes to remove an unused
> function, changed a few references of table to sequence and added one
> CHECK_FOR_INTERRUPTS in the sequence sync worker loop.

Hi Vignesh,

I have reviewed the patches and I have following comments:

===== tablesync.c ======
1. process_syncing_sequences_for_apply can crash with:
2024-06-21 15:25:17.208 IST [3681269] LOG:  logical replication apply
worker for subscription "test1" has started
2024-06-21 15:28:10.127 IST [3682329] LOG:  logical replication
sequences synchronization worker for subscription "test1" has started
2024-06-21 15:28:10.146 IST [3682329] LOG:  logical replication
synchronization for subscription "test1", sequence "s1" has finished
2024-06-21 15:28:10.149 IST [3682329] LOG:  logical replication
synchronization for subscription "test1", sequence "s2" has finished
2024-06-21 15:28:10.149 IST [3682329] LOG:  logical replication
sequences synchronization worker for subscription "test1" has finished
2024-06-21 15:29:53.535 IST [3682767] LOG:  logical replication
sequences synchronization worker for subscription "test1" has started
TRAP: failed Assert("nestLevel > 0 && (nestLevel <= GUCNestLevel ||
(nestLevel == GUCNestLevel + 1 && !isCommit))"), File: "guc.c", Line:
2273, PID: 3682767
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (ExceptionalCondition+0xbb)[0x5b2a61861c99]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (AtEOXact_GUC+0x7b)[0x5b2a618bddfa]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (RestoreUserContext+0xc7)[0x5b2a618a6937]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1ff7dfa)[0x5b2a61115dfa]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1ff7eb4)[0x5b2a61115eb4]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (SequencesyncWorkerMain+0x33)[0x5b2a61115fe7]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (BackgroundWorkerMain+0x4ad)[0x5b2a61029cae]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (postmaster_child_launch+0x236)[0x5b2a6102fb36]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1f1d12a)[0x5b2a6103b12a]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1f1df0f)[0x5b2a6103bf0f]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1f1bf71)[0x5b2a61039f71]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1f16f73)[0x5b2a61034f73]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (PostmasterMain+0x18fb)[0x5b2a61034445]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (+0x1ab1ab8)[0x5b2a60bcfab8]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7b76bc629d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7b76bc629e40]
postgres: logical replication sequencesync worker for subscription
16389 sync 0 (_start+0x25)[0x5b2a601491a5]

Analysis:
Suppose there are two sequences (s1, s2) on publisher.
SO, during initial sync.
in loop,
+ foreach(lc, table_states_not_ready)

table_states_not_ready -> it contains both s1 and s2.
So, for s1 a sequence sync will be started. It will sync all sequences
and the sequence sync worker will exit.
Now, for s2 again a sequence sync will start. It will give the above error.

Is this loop required? Instead we can just use a bool like
'is_any_sequence_not_ready'. Thoughts?

===== sequencesync.c =====
2. function name should be 'LogicalRepSyncSequences' instead of
'LogicalRepSyncSeqeunces'

3. In function 'LogicalRepSyncSeqeunces'
    sequencerel = table_open(seqinfo->relid, RowExclusiveLock);\
    There is a extra '\' symbol

4. In function LogicalRepSyncSeqeunces:
+ ereport(LOG,
+ errmsg("logical replication synchronization for subscription \"%s\",
sequence \"%s\" has finished",
+   get_subscription_name(subid, false), RelationGetRelationName(sequencerel)));
+ table_close(sequencerel, NoLock);
+
+ currseq++;
+
+ if (currseq % MAX_SEQUENCES_SYNC_PER_BATCH == 0 || currseq ==
list_length(sequences))
+ CommitTransactionCommand();


The above message gets logged even if the changes are not committed.
Suppose the sequence worker exits before commit due to some reason.
Thought the log will show that sequence is synced, the sequence will
be in 'init' state. I think this is not desirable.
Maybe we should log the synced sequences at commit time? Thoughts?

===== General ====
5. We can use other macros like 'foreach_ptr' instead of 'foreach'

Thanks and Regards,
Shlok Kyal



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 25 Jun 2024 at 17:53, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
>
> On Thu, 20 Jun 2024 at 18:24, vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 19 Jun 2024 at 20:33, vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Tue, 18 Jun 2024 at 16:10, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >
> > > >
> > > > Agreed and I am not sure which is better because there is a value in
> > > > keeping the state name the same for both sequences and tables. We
> > > > probably need more comments in code and doc updates to make the
> > > > behavior clear. We can start with the sequence state as 'init' for
> > > > 'needs-to-be-sycned' and 'ready' for 'synced' and can change if others
> > > > feel so during the review.
> > >
> > > Here is a patch which does the sequence synchronization in the
> > > following lines from the above discussion:
> > > This commit introduces sequence synchronization during 1) creation of
> > > subscription for initial sync of sequences 2) refresh publication to
> > > synchronize the sequences for the newly created sequences 3) refresh
> > > publication sequences for synchronizing all the sequences.
> > > 1) During subscription creation with CREATE SUBSCRIPTION (no syntax change):
> > >    - The subscriber retrieves sequences associated with publications.
> > >    - Sequences  are added in the 'init' state to the pg_subscription_rel table.
> > >    - Sequence synchronization worker will be started if there are any
> > > sequences to be synchronized
> > >    - A new sequence synchronization worker handles synchronization in
> > > batches of 100 sequences:
> > >      a) Retrieves sequence values using pg_sequence_state from the publisher.
> > >      b) Sets sequence values accordingly.
> > >      c) Updates sequence state to 'READY' in pg_susbcripion_rel
> > >      d) Commits batches of 100 synchronized sequences.
> > > 2) Refreshing sequences with ALTER SUBSCRIPTION ... REFRESH
> > > PUBLICATION (no syntax change):
> > >    - Stale sequences are removed from pg_subscription_rel.
> > >    - Newly added sequences in the publisher are added in 'init' state
> > > to pg_subscription_rel.
> > >    - Sequence synchronization will be done by sequence sync worker as
> > > listed in subscription creation process.
> > >    - Sequence synchronization occurs for newly added sequences only.
> > > 3) Introduce new command ALTER SUBSCRIPTION ... REFRESH PUBLICATION
> > > SEQUENCES  for refreshing all sequences:
> > >    - Removes stale sequences and adds newly added sequences from the
> > > publisher to pg_subscription_rel.
> > >    - Resets all sequences in pg_subscription_rel to 'init' state.
> > >    - Initiates sequence synchronization for all sequences by sequence
> > > sync worker as listed in subscription creation process.
> >
> > Here is an updated patch with a few fixes to remove an unused
> > function, changed a few references of table to sequence and added one
> > CHECK_FOR_INTERRUPTS in the sequence sync worker loop.
>
> Hi Vignesh,
>
> I have reviewed the patches and I have following comments:
>
> ===== tablesync.c ======
> 1. process_syncing_sequences_for_apply can crash with:
> 2024-06-21 15:25:17.208 IST [3681269] LOG:  logical replication apply
> worker for subscription "test1" has started
> 2024-06-21 15:28:10.127 IST [3682329] LOG:  logical replication
> sequences synchronization worker for subscription "test1" has started
> 2024-06-21 15:28:10.146 IST [3682329] LOG:  logical replication
> synchronization for subscription "test1", sequence "s1" has finished
> 2024-06-21 15:28:10.149 IST [3682329] LOG:  logical replication
> synchronization for subscription "test1", sequence "s2" has finished
> 2024-06-21 15:28:10.149 IST [3682329] LOG:  logical replication
> sequences synchronization worker for subscription "test1" has finished
> 2024-06-21 15:29:53.535 IST [3682767] LOG:  logical replication
> sequences synchronization worker for subscription "test1" has started
> TRAP: failed Assert("nestLevel > 0 && (nestLevel <= GUCNestLevel ||
> (nestLevel == GUCNestLevel + 1 && !isCommit))"), File: "guc.c", Line:
> 2273, PID: 3682767
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (ExceptionalCondition+0xbb)[0x5b2a61861c99]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (AtEOXact_GUC+0x7b)[0x5b2a618bddfa]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (RestoreUserContext+0xc7)[0x5b2a618a6937]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1ff7dfa)[0x5b2a61115dfa]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1ff7eb4)[0x5b2a61115eb4]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (SequencesyncWorkerMain+0x33)[0x5b2a61115fe7]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (BackgroundWorkerMain+0x4ad)[0x5b2a61029cae]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (postmaster_child_launch+0x236)[0x5b2a6102fb36]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1f1d12a)[0x5b2a6103b12a]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1f1df0f)[0x5b2a6103bf0f]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1f1bf71)[0x5b2a61039f71]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1f16f73)[0x5b2a61034f73]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (PostmasterMain+0x18fb)[0x5b2a61034445]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (+0x1ab1ab8)[0x5b2a60bcfab8]
> /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7b76bc629d90]
> /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7b76bc629e40]
> postgres: logical replication sequencesync worker for subscription
> 16389 sync 0 (_start+0x25)[0x5b2a601491a5]
>
> Analysis:
> Suppose there are two sequences (s1, s2) on publisher.
> SO, during initial sync.
> in loop,
> + foreach(lc, table_states_not_ready)
>
> table_states_not_ready -> it contains both s1 and s2.
> So, for s1 a sequence sync will be started. It will sync all sequences
> and the sequence sync worker will exit.
> Now, for s2 again a sequence sync will start. It will give the above error.
>
> Is this loop required? Instead we can just use a bool like
> 'is_any_sequence_not_ready'. Thoughts?
>
> ===== sequencesync.c =====
> 2. function name should be 'LogicalRepSyncSequences' instead of
> 'LogicalRepSyncSeqeunces'
>
> 3. In function 'LogicalRepSyncSeqeunces'
>     sequencerel = table_open(seqinfo->relid, RowExclusiveLock);\
>     There is a extra '\' symbol
>
> 4. In function LogicalRepSyncSeqeunces:
> + ereport(LOG,
> + errmsg("logical replication synchronization for subscription \"%s\",
> sequence \"%s\" has finished",
> +   get_subscription_name(subid, false), RelationGetRelationName(sequencerel)));
> + table_close(sequencerel, NoLock);
> +
> + currseq++;
> +
> + if (currseq % MAX_SEQUENCES_SYNC_PER_BATCH == 0 || currseq ==
> list_length(sequences))
> + CommitTransactionCommand();
>
>
> The above message gets logged even if the changes are not committed.
> Suppose the sequence worker exits before commit due to some reason.
> Thought the log will show that sequence is synced, the sequence will
> be in 'init' state. I think this is not desirable.
> Maybe we should log the synced sequences at commit time? Thoughts?
>
> ===== General ====
> 5. We can use other macros like 'foreach_ptr' instead of 'foreach'

Thanks for the comments, the attached patch has the fixes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are my initial review comments for the first patch v20240625-0001.

======
General

1. Missing docs?

Section 9.17. "Sequence Manipulation Functions" [1] describes some
functions. Shouldn't your new function be documented here also?

~~~

2. Missing tests?

Shouldn't there be some test code that at least executes your new
pg_sequence_state function to verify that sane values are returned?

======
Commit Message

3.
This patch introduces new functionalities to PostgreSQL:
- pg_sequence_state allows retrieval of sequence values using LSN.
- SetSequence enables updating sequences with user-specified values.

~

3a.
I didn't understand why this says "using LSN" because IIUC 'lsn' is an
output parameter of that function. Don't you mean "... retrieval of
sequence values including LSN"?

~

3b.
Does "user-specified" make sense? Is this going to be exposed to a
user? How about just "specified"?

======
src/backend/commands/sequence.c

4. SetSequence:

+void
+SetSequence(Oid seq_relid, int64 value)

Would 'new_last_value' be a better parameter name here?

~~~

5.
This new function logic looks pretty similar to the do_setval()
function. Can you explain (maybe in the function comment) some info
about how and why it differs from that other function?

~~~

6.
I saw that RelationNeedsWAL() is called 2 times. It may make no sense,
but is it possible to assign that to a variable 1st time so you don't
need to call it 2nd time within the critical section?

~~~

NITPICK - remove junk (') char in comment

NITPICK - missing periods (.) in multi-sentence comment

~~~

7.
-read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
+read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple,
+    XLogRecPtr *lsn)

7a.
The existing parameters were described in the function comment. So,
the new 'lsn' parameter should be described here also.

~

7b.
Maybe the new parameter name should be 'lsn_res' or 'lsn_out' or
similar to emphasise that this is a returned value.

~~

NITPICK - tweaked comment. YMMV.

~~~

8. pg_sequence_state:

Should you give descriptions of the output parameters in the function
header comment? Otherwise, where are they described so called knows
what they mean?

~~~

NITPICK - /relid/seq_relid/

NITPICK - declare the variables in the same order as the output parameters

NITPICK - An alternative to the memset for nulls is just to use static
initialisation
"bool nulls[4] = {false, false, false, false};"

======
+extern void SetSequence(Oid seq_relid, int64 value);

9.
Would 'SetSequenceLastValue' be a better name for what this function is doing?

======

99.
See also my attached diff which is a top-up patch implementing those
nitpicks mentioned above. Please apply any of these that you agree
with.

======
[1] https://www.postgresql.org/docs/devel/functions-sequence.html

Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are some review comments for the patch v20240625-0002

======
Commit Message

1.
This commit enhances logical replication by enabling the inclusion of all
sequences in publications. This improvement facilitates seamless
synchronization of sequence data during operations such as
CREATE SUBSCRIPTION, REFRESH PUBLICATION, and REFRESH PUBLICATION SEQUENCES.

~

Isn't this description getting ahead of the functionality a bit? For
example, it talks about operations like REFRESH PUBLICATION SEQUENCES
but AFAIK that syntax does not exist just yet.

~~~

2.
The commit message should mention that you are only introducing new
syntax for "FOR ALL SEQUENCES" here, but syntax for "FOR SEQUENCE" is
being deferred to some later patch. Without such a note it is not
clear why the gram.y syntax and docs seemed only half done.

======
doc/src/sgml/ref/create_publication.sgml

3.
    <varlistentry id="sql-createpublication-params-for-all-tables">
     <term><literal>FOR ALL TABLES</literal></term>
+    <term><literal>FOR ALL SEQUENCES</literal></term>
     <listitem>
      <para>
-      Marks the publication as one that replicates changes for all tables in
-      the database, including tables created in the future.
+      Marks the publication as one that replicates changes for all tables or
+      sequences in the database, including tables created in the future.

It might be better here to keep descriptions for "ALL TABLES" and "ALL
SEQUENCES" separated, otherwise the wording does not quite seem
appropriate for sequences (e.g. where it says "including tables
created in the future").

~~~

NITPICK - missing spaces
NITPICK - removed Oxford commas since previously there were none

~~~

4.
+   If <literal>FOR TABLE</literal>, <literal>FOR ALL TABLES</literal>,
+   <literal>FOR ALL SEQUENCES</literal>,or <literal>FOR TABLES IN
SCHEMA</literal>
+   are not specified, then the publication starts out with an empty set of
+   tables.  That is useful if tables or schemas are to be added later.

It seems like "FOR ALL SEQUENCES" is out of place since it is jammed
between other clauses referring to TABLES. Would it be better to
mention SEQUENCES last in the list?

~~~

5.
+   rights on the table.  The <command>FOR ALL TABLES</command>,
+   <command>FOR ALL SEQUENCES</command>, and
    <command>FOR TABLES IN SCHEMA</command> clauses require the invoking

ditto of #4 above.

======
src/backend/catalog/pg_publication.c

GetAllSequencesPublicationRelations:

NITPICK - typo /relation/relations/

======
src/backend/commands/publicationcmds.c

6.
+ foreach(lc, stmt->for_all_objects)
+ {
+ char    *val = strVal(lfirst(lc));
+
+ if (strcmp(val, "tables") == 0)
+ for_all_tables = true;
+ else if (strcmp(val, "sequences") == 0)
+ for_all_sequences = true;
+ }

Consider the foreach_ptr macro to slightly simplify this code.
Actually, this whole logic seems cumbersome -- can’t the parser assign
flags automatically. Please see my more detailed comment #10 below
about this in gram.y

~~~

7.
  /* FOR ALL TABLES requires superuser */
- if (stmt->for_all_tables && !superuser())
+ if (for_all_tables && !superuser())
  ereport(ERROR,
  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  errmsg("must be superuser to create FOR ALL TABLES publication")));

+ /* FOR ALL SEQUENCES requires superuser */
+ if (for_all_sequences && !superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create FOR ALL SEQUENCES publication")));
+

The current code is easy to read, but I wonder if it should try harder
to share common code, or at least a common translatable message like
"must be superuser to create %s publication".

~~~

8.
- else
+
+ /*
+ * If the publication might have either tables or sequences (directly or
+ * through a schema), process that.
+ */
+ if (!for_all_tables || !for_all_sequences)

I did not understand why this code cannot just say "else" like before,
because the direct or through-schema syntax cannot be specified at the
same time as "FOR ALL ...", so why is the more complicated condition
necessary? Also, the similar code in AlterPublicationOptions() was not
changed to be like this.

======
src/backend/parser/gram.y

9. comment

  *
  * CREATE PUBLICATION FOR ALL TABLES [WITH options]
  *
+ * CREATE PUBLICATION FOR ALL SEQUENCES [WITH options]
+ *
  * CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]

The comment is not quite correct because actually you are allowing
simultaneous FOR ALL TABLES, SEQUENCES. It should be more like:

CREATE PUBLICATION FOR ALL pub_obj_type [,...] [WITH options]

pub_obj_type is one of:
TABLES
SEQUENCES

~~~

10.
+pub_obj_type: TABLES
+ { $$ = (Node *) makeString("tables"); }
+ | SEQUENCES
+ { $$ = (Node *) makeString("sequences"); }
+ ;
+
+pub_obj_type_list: pub_obj_type
+ { $$ = list_make1($1); }
+ | pub_obj_type_list ',' pub_obj_type
+ { $$ = lappend($1, $3); }
+ ;

IIUC the only thing you need is a flag to say if FOR ALL TABLE is in
effect and another flag to say if FOR ALL SEQUENCES is in effect. So,
It seemed clunky to build up a temporary list of "tables" or
"sequences" strings here, which is subsequently scanned by
CreatePublication to be turned back into booleans.

Can't we just change the CreatePublicationStmt field to have:

A) a 'for_all_types' bitmask instead of a list:
0x0000 means FOR ALL is not specified
0x0001 means ALL TABLES
0x0010 means ALL SEQUENCES

Or, B) have 2 boolean fields ('for_all_tables' and 'for_all_sequences')

...where the gram.y code can be written to assign the flag/s values directly?

======
src/bin/pg_dump/pg_dump.c

11.
  if (pubinfo->puballtables)
  appendPQExpBufferStr(query, " FOR ALL TABLES");

+ if (pubinfo->puballsequences)
+ appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
+

Hmm. Is that correct? It looks like a possible bug, because if both
flags are true it will give invalid syntax like "FOR ALL TABLES FOR
ALL SEQUENCES" instead of "FOR ALL TABLES, SEQUENCES"

======
src/bin/pg_dump/t/002_pg_dump.pl

12.
This could also try the test scenario of both FOR ALL being
simultaneously set ("FOR ALL TABLES, SEQUENCES") to check for bugs
like the suspected one in dump.c review comment #11 above.

======
src/bin/psql/describe.c

13.
+ if (pset.sversion >= 170000)
+ printfPQExpBuffer(&buf,
+   "SELECT pubname AS \"%s\",\n"
+   "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
+   "  puballtables AS \"%s\",\n"
+   "  puballsequences AS \"%s\",\n"
+   "  pubinsert AS \"%s\",\n"
+   "  pubupdate AS \"%s\",\n"
+   "  pubdelete AS \"%s\"",
+   gettext_noop("Name"),
+   gettext_noop("Owner"),
+   gettext_noop("All tables"),
+   gettext_noop("All sequences"),
+   gettext_noop("Inserts"),
+   gettext_noop("Updates"),
+   gettext_noop("Deletes"));
+ else
+ printfPQExpBuffer(&buf,
+   "SELECT pubname AS \"%s\",\n"
+   "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
+   "  puballtables AS \"%s\",\n"
+   "  pubinsert AS \"%s\",\n"
+   "  pubupdate AS \"%s\",\n"
+   "  pubdelete AS \"%s\"",
+   gettext_noop("Name"),
+   gettext_noop("Owner"),
+   gettext_noop("All tables"),
+   gettext_noop("Inserts"),
+   gettext_noop("Updates"),
+   gettext_noop("Deletes"));
+

IMO this should be coded differently so that only the
"puballsequences" column is guarded by the (pset.sversion >= 170000),
and everything else is the same as before. This suggested way would
also be consistent with the existing code version checks (e.g. for
"pubtruncate" or for "pubviaroot").

~~~

NITPICK - Add blank lines
NITPICK - space in "ncols ++"

======
src/bin/psql/tab-complete.c

14.
Hmm. When I tried this, it didn't seem to be working properly.

For example "CREATE PUBLICATION pub1 FOR ALL" only completes with
"TABLES" but not "SEQUENCES".
For example "CREATE PUBLICATION pub1 FOR ALL SEQ" doesn't complete
"SEQUENCES" properly

======
src/include/catalog/pg_publication.h

NITPICK - move the extern to be adjacent to others like it.

======
src/include/nodes/parsenodes.h

15.
- bool for_all_tables; /* Special publication for all tables in db */
+ List    *for_all_objects; /* Special publication for all objects in
+ * db */
 } CreatePublicationStmt;

I felt this List logic is a bit strange. See my comment #10 in gram.y
for more details.

~~~

16.
- bool for_all_tables; /* Special publication for all tables in db */
+ List    *for_all_objects; /* Special publication for all objects in
+ * db */

Ditto comment #15 in AlterPublicationStmt

======
src/test/regress/sql/publication.sql

17.
+CREATE SEQUENCE testpub_seq0;
+CREATE SEQUENCE pub_test.testpub_seq1;
+
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES;
+RESET client_min_messages;
+
+SELECT pubname, puballtables, puballsequences FROM pg_publication
WHERE pubname = 'testpub_forallsequences';
+\d+ pub_test.testpub_seq1

Should you also do "\d+ tespub_seq0" here? Otherwise what was the
point of defining the seq0 sequence being in this test?

~~~

18.
Maybe there are missing test cases for different syntax combinations like:

FOR ALL TABLES, SEQUENCES
FOR ALL SEQUENCES, TABLES

Note that the current list logic of this patch even considers my
following bogus statement syntax is OK.

test_pub=# CREATE PUBLICATION pub_silly FOR ALL TABLES, SEQUENCES,
TABLES, TABLES, TABLES, SEQUENCES;
CREATE PUBLICATION
test_pub=#

======
99.
Please also refer to the attached nitpicks patch which implements all
the cosmetic issues identified above as NITPICKS.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 26 Jun 2024 at 14:41, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are my initial review comments for the first patch v20240625-0001.
>
> ======
> General
>
> 6.
> I saw that RelationNeedsWAL() is called 2 times. It may make no sense,
> but is it possible to assign that to a variable 1st time so you don't
> need to call it 2nd time within the critical section?
>

I felt this is ok, we do similarly in other places also like
fill_seq_fork_with_data function in the same file.

I have fixed the other comments and merged the nitpicks changes. The
attached patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are my comments for patch v20240702-0001

They are all cosmetic and/or typos. Apart from these the 0001 patch LGTM.

======
doc/src/sgml/func.sgml

Section 9.17. Sequence Manipulation Functions

pg_sequence_state:
nitpick - typo /whethere/whether/
nitpick - reworded slightly using a ChatGPT suggestion. (YMMV, so it
is fine also if you prefer the current wording)

======
src/backend/commands/sequence.c

SetSequenceLastValue:
nitpick - typo in function comment /diffrent/different/

pg_sequence_state:
nitpick - function comment wording: /page LSN/the page LSN/
nitpick - moved some comment details about 'lsn_ret' into the function header
nitpick - rearranged variable assignments to have consistent order
with the values
nitpick - tweaked comments
nitpick - typo /whethere/whether/

======
99.
Please see the attached diffs patch which implements all those
nitpicks mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 1 Jul 2024 at 12:57, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are some review comments for the patch v20240625-0002
>
> ======
> Commit Message
>
> 1.
> This commit enhances logical replication by enabling the inclusion of all
> sequences in publications. This improvement facilitates seamless
> synchronization of sequence data during operations such as
> CREATE SUBSCRIPTION, REFRESH PUBLICATION, and REFRESH PUBLICATION SEQUENCES.
>
> ~
>
> Isn't this description getting ahead of the functionality a bit? For
> example, it talks about operations like REFRESH PUBLICATION SEQUENCES
> but AFAIK that syntax does not exist just yet.
>
> ~~~
>
> 2.
> The commit message should mention that you are only introducing new
> syntax for "FOR ALL SEQUENCES" here, but syntax for "FOR SEQUENCE" is
> being deferred to some later patch. Without such a note it is not
> clear why the gram.y syntax and docs seemed only half done.
>
> ======
> doc/src/sgml/ref/create_publication.sgml
>
> 3.
>     <varlistentry id="sql-createpublication-params-for-all-tables">
>      <term><literal>FOR ALL TABLES</literal></term>
> +    <term><literal>FOR ALL SEQUENCES</literal></term>
>      <listitem>
>       <para>
> -      Marks the publication as one that replicates changes for all tables in
> -      the database, including tables created in the future.
> +      Marks the publication as one that replicates changes for all tables or
> +      sequences in the database, including tables created in the future.
>
> It might be better here to keep descriptions for "ALL TABLES" and "ALL
> SEQUENCES" separated, otherwise the wording does not quite seem
> appropriate for sequences (e.g. where it says "including tables
> created in the future").
>
> ~~~
>
> NITPICK - missing spaces
> NITPICK - removed Oxford commas since previously there were none
>
> ~~~
>
> 4.
> +   If <literal>FOR TABLE</literal>, <literal>FOR ALL TABLES</literal>,
> +   <literal>FOR ALL SEQUENCES</literal>,or <literal>FOR TABLES IN
> SCHEMA</literal>
> +   are not specified, then the publication starts out with an empty set of
> +   tables.  That is useful if tables or schemas are to be added later.
>
> It seems like "FOR ALL SEQUENCES" is out of place since it is jammed
> between other clauses referring to TABLES. Would it be better to
> mention SEQUENCES last in the list?
>
> ~~~
>
> 5.
> +   rights on the table.  The <command>FOR ALL TABLES</command>,
> +   <command>FOR ALL SEQUENCES</command>, and
>     <command>FOR TABLES IN SCHEMA</command> clauses require the invoking
>
> ditto of #4 above.
>
> ======
> src/backend/catalog/pg_publication.c
>
> GetAllSequencesPublicationRelations:
>
> NITPICK - typo /relation/relations/
>
> ======
> src/backend/commands/publicationcmds.c
>
> 6.
> + foreach(lc, stmt->for_all_objects)
> + {
> + char    *val = strVal(lfirst(lc));
> +
> + if (strcmp(val, "tables") == 0)
> + for_all_tables = true;
> + else if (strcmp(val, "sequences") == 0)
> + for_all_sequences = true;
> + }
>
> Consider the foreach_ptr macro to slightly simplify this code.
> Actually, this whole logic seems cumbersome -- can’t the parser assign
> flags automatically. Please see my more detailed comment #10 below
> about this in gram.y
>
> ~~~
>
> 7.
>   /* FOR ALL TABLES requires superuser */
> - if (stmt->for_all_tables && !superuser())
> + if (for_all_tables && !superuser())
>   ereport(ERROR,
>   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
>   errmsg("must be superuser to create FOR ALL TABLES publication")));
>
> + /* FOR ALL SEQUENCES requires superuser */
> + if (for_all_sequences && !superuser())
> + ereport(ERROR,
> + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
> + errmsg("must be superuser to create FOR ALL SEQUENCES publication")));
> +
>
> The current code is easy to read, but I wonder if it should try harder
> to share common code, or at least a common translatable message like
> "must be superuser to create %s publication".
>
> ~~~
>
> 8.
> - else
> +
> + /*
> + * If the publication might have either tables or sequences (directly or
> + * through a schema), process that.
> + */
> + if (!for_all_tables || !for_all_sequences)
>
> I did not understand why this code cannot just say "else" like before,
> because the direct or through-schema syntax cannot be specified at the
> same time as "FOR ALL ...", so why is the more complicated condition
> necessary? Also, the similar code in AlterPublicationOptions() was not
> changed to be like this.
>
> ======
> src/backend/parser/gram.y
>
> 9. comment
>
>   *
>   * CREATE PUBLICATION FOR ALL TABLES [WITH options]
>   *
> + * CREATE PUBLICATION FOR ALL SEQUENCES [WITH options]
> + *
>   * CREATE PUBLICATION FOR pub_obj [, ...] [WITH options]
>
> The comment is not quite correct because actually you are allowing
> simultaneous FOR ALL TABLES, SEQUENCES. It should be more like:
>
> CREATE PUBLICATION FOR ALL pub_obj_type [,...] [WITH options]
>
> pub_obj_type is one of:
> TABLES
> SEQUENCES
>
> ~~~
>
> 10.
> +pub_obj_type: TABLES
> + { $$ = (Node *) makeString("tables"); }
> + | SEQUENCES
> + { $$ = (Node *) makeString("sequences"); }
> + ;
> +
> +pub_obj_type_list: pub_obj_type
> + { $$ = list_make1($1); }
> + | pub_obj_type_list ',' pub_obj_type
> + { $$ = lappend($1, $3); }
> + ;
>
> IIUC the only thing you need is a flag to say if FOR ALL TABLE is in
> effect and another flag to say if FOR ALL SEQUENCES is in effect. So,
> It seemed clunky to build up a temporary list of "tables" or
> "sequences" strings here, which is subsequently scanned by
> CreatePublication to be turned back into booleans.
>
> Can't we just change the CreatePublicationStmt field to have:
>
> A) a 'for_all_types' bitmask instead of a list:
> 0x0000 means FOR ALL is not specified
> 0x0001 means ALL TABLES
> 0x0010 means ALL SEQUENCES
>
> Or, B) have 2 boolean fields ('for_all_tables' and 'for_all_sequences')
>
> ...where the gram.y code can be written to assign the flag/s values directly?
>
> ======
> src/bin/pg_dump/pg_dump.c
>
> 11.
>   if (pubinfo->puballtables)
>   appendPQExpBufferStr(query, " FOR ALL TABLES");
>
> + if (pubinfo->puballsequences)
> + appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
> +
>
> Hmm. Is that correct? It looks like a possible bug, because if both
> flags are true it will give invalid syntax like "FOR ALL TABLES FOR
> ALL SEQUENCES" instead of "FOR ALL TABLES, SEQUENCES"
>
> ======
> src/bin/pg_dump/t/002_pg_dump.pl
>
> 12.
> This could also try the test scenario of both FOR ALL being
> simultaneously set ("FOR ALL TABLES, SEQUENCES") to check for bugs
> like the suspected one in dump.c review comment #11 above.
>
> ======
> src/bin/psql/describe.c
>
> 13.
> + if (pset.sversion >= 170000)
> + printfPQExpBuffer(&buf,
> +   "SELECT pubname AS \"%s\",\n"
> +   "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
> +   "  puballtables AS \"%s\",\n"
> +   "  puballsequences AS \"%s\",\n"
> +   "  pubinsert AS \"%s\",\n"
> +   "  pubupdate AS \"%s\",\n"
> +   "  pubdelete AS \"%s\"",
> +   gettext_noop("Name"),
> +   gettext_noop("Owner"),
> +   gettext_noop("All tables"),
> +   gettext_noop("All sequences"),
> +   gettext_noop("Inserts"),
> +   gettext_noop("Updates"),
> +   gettext_noop("Deletes"));
> + else
> + printfPQExpBuffer(&buf,
> +   "SELECT pubname AS \"%s\",\n"
> +   "  pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
> +   "  puballtables AS \"%s\",\n"
> +   "  pubinsert AS \"%s\",\n"
> +   "  pubupdate AS \"%s\",\n"
> +   "  pubdelete AS \"%s\"",
> +   gettext_noop("Name"),
> +   gettext_noop("Owner"),
> +   gettext_noop("All tables"),
> +   gettext_noop("Inserts"),
> +   gettext_noop("Updates"),
> +   gettext_noop("Deletes"));
> +
>
> IMO this should be coded differently so that only the
> "puballsequences" column is guarded by the (pset.sversion >= 170000),
> and everything else is the same as before. This suggested way would
> also be consistent with the existing code version checks (e.g. for
> "pubtruncate" or for "pubviaroot").
>
> ~~~
>
> NITPICK - Add blank lines
> NITPICK - space in "ncols ++"
>
> ======
> src/bin/psql/tab-complete.c
>
> 14.
> Hmm. When I tried this, it didn't seem to be working properly.
>
> For example "CREATE PUBLICATION pub1 FOR ALL" only completes with
> "TABLES" but not "SEQUENCES".
> For example "CREATE PUBLICATION pub1 FOR ALL SEQ" doesn't complete
> "SEQUENCES" properly
>
> ======
> src/include/catalog/pg_publication.h
>
> NITPICK - move the extern to be adjacent to others like it.
>
> ======
> src/include/nodes/parsenodes.h
>
> 15.
> - bool for_all_tables; /* Special publication for all tables in db */
> + List    *for_all_objects; /* Special publication for all objects in
> + * db */
>  } CreatePublicationStmt;
>
> I felt this List logic is a bit strange. See my comment #10 in gram.y
> for more details.
>
> ~~~
>
> 16.
> - bool for_all_tables; /* Special publication for all tables in db */
> + List    *for_all_objects; /* Special publication for all objects in
> + * db */
>
> Ditto comment #15 in AlterPublicationStmt
>
> ======
> src/test/regress/sql/publication.sql
>
> 17.
> +CREATE SEQUENCE testpub_seq0;
> +CREATE SEQUENCE pub_test.testpub_seq1;
> +
> +SET client_min_messages = 'ERROR';
> +CREATE PUBLICATION testpub_forallsequences FOR ALL SEQUENCES;
> +RESET client_min_messages;
> +
> +SELECT pubname, puballtables, puballsequences FROM pg_publication
> WHERE pubname = 'testpub_forallsequences';
> +\d+ pub_test.testpub_seq1
>
> Should you also do "\d+ tespub_seq0" here? Otherwise what was the
> point of defining the seq0 sequence being in this test?
>
> ~~~
>
> 18.
> Maybe there are missing test cases for different syntax combinations like:
>
> FOR ALL TABLES, SEQUENCES
> FOR ALL SEQUENCES, TABLES
>
> Note that the current list logic of this patch even considers my
> following bogus statement syntax is OK.
>
> test_pub=# CREATE PUBLICATION pub_silly FOR ALL TABLES, SEQUENCES,
> TABLES, TABLES, TABLES, SEQUENCES;
> CREATE PUBLICATION
> test_pub=#
>
> ======
> 99.
> Please also refer to the attached nitpicks patch which implements all
> the cosmetic issues identified above as NITPICKS.

Thank you for your feedback. I have addressed all the comments in the
attached patch.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 3 Jul 2024 at 08:24, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are my comments for patch v20240702-0001
>
> They are all cosmetic and/or typos. Apart from these the 0001 patch LGTM.
>
> ======
> doc/src/sgml/func.sgml
>
> Section 9.17. Sequence Manipulation Functions
>
> pg_sequence_state:
> nitpick - typo /whethere/whether/
> nitpick - reworded slightly using a ChatGPT suggestion. (YMMV, so it
> is fine also if you prefer the current wording)
>
> ======
> src/backend/commands/sequence.c
>
> SetSequenceLastValue:
> nitpick - typo in function comment /diffrent/different/
>
> pg_sequence_state:
> nitpick - function comment wording: /page LSN/the page LSN/
> nitpick - moved some comment details about 'lsn_ret' into the function header
> nitpick - rearranged variable assignments to have consistent order
> with the values
> nitpick - tweaked comments
> nitpick - typo /whethere/whether/
>
> ======
> 99.
> Please see the attached diffs patch which implements all those
> nitpicks mentioned above.

Thank you for your feedback. I have addressed all the comments in the
v20240703 version patch attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm0mSSrvHNRnC67f0HWMpoLW9UzxGVXimhwbRtKjE7Aa-Q%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh. Here are my comments for the latest patch v20240703-0001.

======
doc/src/sgml/func.sgml

nitpick - /lsn/LSN/ (all other doc pages I found use uppercase for this acronym)

======
src/backend/commands/sequence.c

nitpick - /lsn/LSN/

======
Please see attached nitpicks diff.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 4 Jul 2024 at 06:40, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh. Here are my comments for the latest patch v20240703-0001.
>
> ======
> doc/src/sgml/func.sgml
>
> nitpick - /lsn/LSN/ (all other doc pages I found use uppercase for this acronym)
>
> ======
> src/backend/commands/sequence.c
>
> nitpick - /lsn/LSN/

Thanks for the comments, the attached patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are my review comments for the patch v20240703-0002

======
doc/src/sgml/ref/create_publication.sgml

nitpick - consider putting the "FOR ALL SEQUENCES" para last, because
eventually when more sequence syntax is added IMO it will be better to
describe all the TABLES together, and then describe all the SEQUENCES
together.

nitpick - /synchronizing changes/synchronizes changes/

Question: Was there a reason you chose wording "synchronizes changes"
instead of having same "replicates changes" wording of FOR ALL TABLES?

======
src/backend/catalog/system_views.sql

1.
Should there be some new test for the view? Otherwise, AFAICT this
patch has no tests that will exercise the new function
pg_get_publication_sequences.

======
src/backend/commands/publicationcmds.c

2.
+ errmsg("must be superuser to create FOR ALL %s publication",
+ stmt->for_all_tables ? "TABLES" : "SEQUENCES")));

nitpick - the combined error message may be fine, but I think
translators will prefer the substitution to be the full "FOR ALL
TABLES" and "FOR ALL SEQUENCES" instead of just the keywords that are
different.

======
src/backend/parser/gram.y

3.
Some of these new things maybe could be named better?

'preprocess_allpubobjtype_list' => 'preprocess_pub_all_objtype_list'

'AllPublicationObjSpec *allpublicationobjectspec;' =>
'PublicationAllObjSpec *publicationallobjectspec;'

(I didn't include these in nitpicks diffs because you probably have
better ideas than I do for good names)

~~~

nitpick - typo in comment /SCHEMAS/SEQUENCES/

preprocess_allpubobjtype_list:
nitpick - typo /allbjects_list/all_objects_list/
nitpick - simplify /allpubob/obj/
nitpick - add underscores in the enums

======
src/bin/pg_dump/pg_dump.c

4.
+ if (pubinfo->puballtables || pubinfo->puballsequences)
+ {
+ appendPQExpBufferStr(query, " FOR ALL");
+ if (pubinfo->puballtables &&  pubinfo->puballsequences)
+ appendPQExpBufferStr(query, " TABLES, SEQUENCES");
+ else if (pubinfo->puballtables)
+ appendPQExpBufferStr(query, " TABLES");
+ else
+ appendPQExpBufferStr(query, " SEQUENCES");
+ }

nitpick - it seems over-complicated; See nitpicks diff for my suggestion.

======
src/include/nodes/parsenodes.h

nitpick - put underscores in the enum values

~~

5.
- bool for_all_tables; /* Special publication for all tables in db */
+ List    *for_all_objects; /* Special publication for all objects in
+ * db */

Is this OK? Saying "for all objects" seemed misleading.

======
src/test/regress/sql/publication.sql

nitpick - some small changes to comments, e.g. writing keywords in uppercase

~~~

6.
I asked this before in a previous review [1-#17] -- I didn't
understand the point of the sequence 'testpub_seq0' since nobody seems
to be doing anything with it. Should it just be removed? Or is there a
missing test case to use it?

~~~

7.
Other things to consider:

(I didn't include these in my attached diff)

* could use a single CREATE SEQUENCE stmt instead of multiple

* could use a single DROP PUBLICATION stmt instead of multiple

* shouldn't all publication names ideally have a 'regress_' prefix?

======
99.
Please refer to the attached nitpicks diff which has implementation
for the nitpicks cited above.

======
[1] https://www.postgresql.org/message-id/CAHut%2BPvrk75vSDkaXJVmhhZuuqQSY98btWJV%3DBMZAnyTtKRB4g%40mail.gmail.com

Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
The latest (v20240704) patch 0001 LGTM

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh.

After applying the v20240703-0003 patch, I was always getting errors
when running the subscription TAP tests.

# +++ tap check in src/test/subscription +++
t/001_rep_changes.pl ............... ok
t/002_types.pl ..................... ok
t/003_constraints.pl ............... ok
t/004_sync.pl ...................... ok
t/005_encoding.pl .................. ok
t/006_rewrite.pl ................... ok
t/007_ddl.pl ....................... 3/?
#   Failed test 'Alter subscription set publication throws warning for
non-existent publication'
#   at t/007_ddl.pl line 67.
Bailout called.  Further testing stopped:  pg_ctl stop failed
# Tests were run but no plan was declared and done_testing() was not seen.
FAILED--Further testing stopped: pg_ctl stop failed
make: *** [check] Error 255

~~~

The publisher log shows an Assert TRAP occurred:

2024-07-04 18:15:40.089 AEST [745] mysub1 LOG:  statement: SELECT
DISTINCT s.schemaname, s.sequencename
  FROM pg_catalog.pg_publication_sequences s
WHERE s.pubname IN ('mypub', 'non_existent_pub', 'non_existent_pub1',
'non_existent_pub2')
TRAP: failed Assert("IsA(list, OidList)"), File:
"../../../src/include/nodes/pg_list.h", Line: 323, PID: 745

~~~

A debugging backtrace looks like below:

Core was generated by `postgres: publisher: walsender postgres
postgres [local] SELECT               '.
Program terminated with signal 6, Aborted.
#0  0x00007f36f44f02c7 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install
glibc-2.17-260.el7_6.6.x86_64 pcre-8.32-17.el7.x86_64
(gdb) bt
#0  0x00007f36f44f02c7 in raise () from /lib64/libc.so.6
#1  0x00007f36f44f19b8 in abort () from /lib64/libc.so.6
#2  0x0000000000bb8be1 in ExceptionalCondition (conditionName=0xc7aa6c
"IsA(list, OidList)",
    fileName=0xc7aa10 "../../../src/include/nodes/pg_list.h",
lineNumber=323) at assert.c:66
#3  0x00000000005f2c57 in list_nth_oid (list=0x27948f0, n=0) at
../../../src/include/nodes/pg_list.h:323
#4  0x00000000005f5491 in pg_get_publication_sequences
(fcinfo=0x2796a00) at pg_publication.c:1334
#5  0x0000000000763d10 in ExecMakeTableFunctionResult
(setexpr=0x27b2fd8, econtext=0x27b2ef8, argContext=0x2796900,
...

Something goes wrong indexing into that 'sequences' list.

1329 funcctx = SRF_PERCALL_SETUP();
1330 sequences = (List *) funcctx->user_fctx;
1331
1332 if (funcctx->call_cntr < list_length(sequences))
1333 {
1334 Oid relid = list_nth_oid(sequences, funcctx->call_cntr);
1335
1336 SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
1337 }

======

Perhaps now it is time to create a CF entry for this thread because
the cfbot could have detected the error earlier.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 4 Jul 2024 at 12:44, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are my review comments for the patch v20240703-0002
>
> ======
> doc/src/sgml/ref/create_publication.sgml
>
>
> Question: Was there a reason you chose wording "synchronizes changes"
> instead of having same "replicates changes" wording of FOR ALL TABLES?

Since at this point we are only supporting sync of sequences, there
are no incremental changes being replicated to subscribers. I thought
synchronization is better suited here.

> ======
> src/backend/catalog/system_views.sql
>
> 1.
> Should there be some new test for the view? Otherwise, AFAICT this
> patch has no tests that will exercise the new function
> pg_get_publication_sequences.

pg_publication_sequences view uses pg_get_publication_sequences which
will be tested with 3rd patch while creating subscription/refreshing
publication sequences. I felt it is ok not to have a test here.

> 5.
> - bool for_all_tables; /* Special publication for all tables in db */
> + List    *for_all_objects; /* Special publication for all objects in
> + * db */
>
> Is this OK? Saying "for all objects" seemed misleading.

This change is not required, reverting it.

> 6.
> I asked this before in a previous review [1-#17] -- I didn't
> understand the point of the sequence 'testpub_seq0' since nobody seems
> to be doing anything with it. Should it just be removed? Or is there a
> missing test case to use it?

Since we are having all sequences published I wanted to have a
sequence in another schema also. Adding describe for it too.

> ~~~
>
> 7.
> Other things to consider:
>
> (I didn't include these in my attached diff)
>
> * could use a single CREATE SEQUENCE stmt instead of multiple

CREATE SEQUENCE does not support specifying multiple sequences in one
statement, skipping this.

The rest of the comments are fixed, the attached v20240705 version
patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 5 Jul 2024 at 09:46, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh.
>
> After applying the v20240703-0003 patch, I was always getting errors
> when running the subscription TAP tests.
>
> # +++ tap check in src/test/subscription +++
> t/001_rep_changes.pl ............... ok
> t/002_types.pl ..................... ok
> t/003_constraints.pl ............... ok
> t/004_sync.pl ...................... ok
> t/005_encoding.pl .................. ok
> t/006_rewrite.pl ................... ok
> t/007_ddl.pl ....................... 3/?
> #   Failed test 'Alter subscription set publication throws warning for
> non-existent publication'
> #   at t/007_ddl.pl line 67.
> Bailout called.  Further testing stopped:  pg_ctl stop failed
> # Tests were run but no plan was declared and done_testing() was not seen.
> FAILED--Further testing stopped: pg_ctl stop failed
> make: *** [check] Error 255
>
> ~~~
>
> The publisher log shows an Assert TRAP occurred:
>
> 2024-07-04 18:15:40.089 AEST [745] mysub1 LOG:  statement: SELECT
> DISTINCT s.schemaname, s.sequencename
>   FROM pg_catalog.pg_publication_sequences s
> WHERE s.pubname IN ('mypub', 'non_existent_pub', 'non_existent_pub1',
> 'non_existent_pub2')
> TRAP: failed Assert("IsA(list, OidList)"), File:
> "../../../src/include/nodes/pg_list.h", Line: 323, PID: 745
>
> ~~~
>
> A debugging backtrace looks like below:
>
> Core was generated by `postgres: publisher: walsender postgres
> postgres [local] SELECT               '.
> Program terminated with signal 6, Aborted.
> #0  0x00007f36f44f02c7 in raise () from /lib64/libc.so.6
> Missing separate debuginfos, use: debuginfo-install
> glibc-2.17-260.el7_6.6.x86_64 pcre-8.32-17.el7.x86_64
> (gdb) bt
> #0  0x00007f36f44f02c7 in raise () from /lib64/libc.so.6
> #1  0x00007f36f44f19b8 in abort () from /lib64/libc.so.6
> #2  0x0000000000bb8be1 in ExceptionalCondition (conditionName=0xc7aa6c
> "IsA(list, OidList)",
>     fileName=0xc7aa10 "../../../src/include/nodes/pg_list.h",
> lineNumber=323) at assert.c:66
> #3  0x00000000005f2c57 in list_nth_oid (list=0x27948f0, n=0) at
> ../../../src/include/nodes/pg_list.h:323
> #4  0x00000000005f5491 in pg_get_publication_sequences
> (fcinfo=0x2796a00) at pg_publication.c:1334
> #5  0x0000000000763d10 in ExecMakeTableFunctionResult
> (setexpr=0x27b2fd8, econtext=0x27b2ef8, argContext=0x2796900,
> ...
>
> Something goes wrong indexing into that 'sequences' list.
>
> 1329 funcctx = SRF_PERCALL_SETUP();
> 1330 sequences = (List *) funcctx->user_fctx;
> 1331
> 1332 if (funcctx->call_cntr < list_length(sequences))
> 1333 {
> 1334 Oid relid = list_nth_oid(sequences, funcctx->call_cntr);
> 1335
> 1336 SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
> 1337 }

I was not able to reproduce this issue after several runs, but looks
like sequences need to be initialized here.

> Perhaps now it is time to create a CF entry for this thread because
> the cfbot could have detected the error earlier.

I have added a commitfest entry for the same at [1].

The v20240705 version patch attached at [2] has the change for the same.

[1] - https://commitfest.postgresql.org/49/5111/
[2] - https://www.postgresql.org/message-id/CALDaNm3WvLUesGq54JagEkbBh4CBfMoT84Rw7HjL8KML_BSzPw%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
On Fri, Jul 5, 2024 at 9:58 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 4 Jul 2024 at 12:44, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > 1.
> > Should there be some new test for the view? Otherwise, AFAICT this
> > patch has no tests that will exercise the new function
> > pg_get_publication_sequences.
>
> pg_publication_sequences view uses pg_get_publication_sequences which
> will be tested with 3rd patch while creating subscription/refreshing
> publication sequences. I felt it is ok not to have a test here.
>

OTOH, if there had been such a test here then the ("sequence = NIL")
bug in patch 0002 code would have been caught earlier in patch 0002
testing instead of later in patch 0003 testing. In general, I think
each patch should be self-contained w.r.t. to testing all of its new
code, but if you think another test here is overkill then I am fine
with that too.

//////////

Meanwhile, here are my review comments for patch v20240705-0002

======
doc/src/sgml/ref/create_publication.sgml

1.
The CREATE PUBLICATION page has many examples showing many different
combinations of syntax. I think it would not hurt to add another one
showing SEQUENCES being used.

======
src/backend/commands/publicationcmds.c

2.
+ if (form->puballsequences && !superuser_arg(newOwnerId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of publication \"%s\"",
+ NameStr(form->pubname)),
+ errhint("The owner of a FOR ALL SEQUENCES publication must be a
superuser.")));

You might consider combining this with the previous error in the same
way that the "FOR ALL TABLES" and "FOR ALL SEQUENCES" errors were
combined in CreatePublication. The result would be less code. But, I
also think your current code is fine, so I am just putting this out as
an idea in case you prefer it.

======
src/backend/parser/gram.y

nitpick - added a space in the comment
nitpick - changed the call order slightly because $6 comes before $7

======
src/bin/pg_dump/pg_dump.c

3. getPublications

- if (fout->remoteVersion >= 130000)
+ if (fout->remoteVersion >= 170000)

This should be 180000.

======
src/bin/psql/describe.c

4. describeOneTableDetails

+ /* print any publications */
+ if (pset.sversion >= 170000)
+ {

This should be 180000.

~~~

describeOneTableDetails:
nitpick - removed a redundant "else"
nitpick - simplified the "Publications:" header logic slightly

~~~

5. listPublications

+ if (pset.sversion >= 170000)
+ appendPQExpBuffer(&buf,
+   ",\n  puballsequences AS \"%s\"",
+   gettext_noop("All sequences"));

This should be 180000.

~~~

6. describePublications

+ has_pubsequence = (pset.sversion >= 170000);

This should be 180000.

~

nitpick - remove some blank lines for consistency with nearby code

======
src/include/nodes/parsenodes.h

nitpick - minor change to comment for PublicationAllObjType
nitpick - the meanings of the enums are self-evident; I didn't think
comments were very useful

======
src/test/regress/sql/publication.sql

7.
I think it will also be helpful to arrange for a SEQUENCE to be
published by *multiple* publications. This would test that they get
listed as expected in the "Publications:" part of the "describe" (\d+)
for the sequence.

======
99.
Please also see the attached diffs patch which implements any nitpicks
mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are a few comments for patch v20240705-0003.

(This is a WIP. I have only looked at the docs so far.)

======
doc/src/sgml/config.sgml

nitpick - max_logical_replication_workers: /and sequence
synchornization worker/and a sequence synchornization worker/

======
doc/src/sgml/logical-replication.sgml

nitpick - max_logical_replication_workers: re-order list of workers to
be consistent with other docs 1-apply,2-parallel,3-tablesync,4-seqsync

======
doc/src/sgml/ref/alter_subscription.sgml

1.
IIUC the existing "REFRESH PUBLICATION" command will fetch and sync
all new sequences, etc., and/or remove old ones no longer in the
publication. But current docs do not say anything at all about
sequences here. It should say something about sequence behaviour.

~~~

2.
For the existing "REFRESH PUBLICATION" there is a sub-option
"copy_data=true/false". Won't this need some explanation about how it
behaves for sequences? Or will there be another option
"copy_sequences=true/false".

~~~

3.
IIUC the main difference between REFRESH PUBLICATION and REFRESH
PUBLICATION SEQUENCES is that the 2nd command will try synchronize
with all the *existing* sequences to bring them to the same point as
on the publisher, but otherwise, they are the same command. If that is
correct understanding I don't think that distinction is made very
clear in the current docs.

~~~

nitpick - the synopsis is misplaced. It should not be between ENABLE
and DISABLE. I moved it. Also, it should say "REFRESH PUBLICATION
SEQUENCES" because that is how the new syntax is defined in gram.y

nitpick - REFRESH SEQUENCES. Renamed to "REFRESH PUBLICATION
SEQUENCES". And, shouldn't "from the publisher" say "with the
publisher"?

nitpick - changed the varlistentry "id".

======
99.
Please also see the attached diffs patch which implements any nitpicks
mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh. Here are the rest of my comments for patch v20240705-0003.

(Apologies for the length of this post; but it was unavoidable due to
this being the 1st review of a very large large 1700-line patch)

======
src/backend/catalog/pg_subscription.c

1. GetSubscriptionSequences

+/*
+ * Get the sequences for the subscription.
+ *
+ * The returned list is palloc'ed in the current memory context.
+ */

Is that comment right? The palloc seems to be done in
CacheMemoryContext, not in the current context.

~

2.
The code is very similar to the other function
GetSubscriptionRelations(). In fact I did not understand how the 2
functions know what they are returning:

E.g. how does GetSubscriptionRelations not return sequences too?
E.g. how does GetSubscriptionSequences not return relations too?

======
src/backend/commands/subscriptioncmds.c

CreateSubscription:
nitpick - put the sequence logic *after* the relations logic because
that is the order that seems used everywhere else.

~~~

3. AlterSubscription_refresh

- logicalrep_worker_stop(sub->oid, relid);
+ /* Stop the worker if relation kind is not sequence*/
+ if (relkind != RELKIND_SEQUENCE)
+ logicalrep_worker_stop(sub->oid, relid);

Can you give more reasons in the comment why skip the stop for sequence worker?

~

nitpick - period and space in the comment

~~~

4.
  for (off = 0; off < remove_rel_len; off++)
  {
  if (sub_remove_rels[off].state != SUBREL_STATE_READY &&
- sub_remove_rels[off].state != SUBREL_STATE_SYNCDONE)
+ sub_remove_rels[off].state != SUBREL_STATE_SYNCDONE &&
+ get_rel_relkind(sub_remove_rels[off].relid) != RELKIND_SEQUENCE)
  {
Would this new logic perhaps be better written as:

if (get_rel_relkind(sub_remove_rels[off].relid) == RELKIND_SEQUENCE)
  continue;

~~~

AlterSubscription_refreshsequences:
nitpick - rename AlterSubscription_refresh_sequences

~
5.
There is significant code overlap between the existing
AlterSubscription_refresh and the new function
AlterSubscription_refreshsequences. I wonder if it is better to try to
combine the logic and just pass another parameter to
AlterSubscription_refresh saying to update the existing sequences if
necessary. Particularly since the AlterSubscription_refresh is already
tweaked to work for sequences. Of course, the resulting combined
function would be large and complex, but maybe that would still be
better than having giant slabs of nearly identical cut/paste code.
Thoughts?

~~~

check_publications_origin:
nitpick - move variable declarations
~~~

fetch_sequence_list:
nitpick - change /tablelist/seqlist/
nitpick - tweak the spaces of the SQL for alignment (similar to
fetch_table_list)

~

6.
+    " WHERE s.pubname IN (");
+ first = true;
+ foreach_ptr(String, pubname, publications)
+ {
+ if (first)
+ first = false;
+ else
+ appendStringInfoString(&cmd, ", ");
+
+ appendStringInfoString(&cmd, quote_literal_cstr(pubname->sval));
+ }
+ appendStringInfoChar(&cmd, ')');

IMO this can be written much better by using get_publications_str()
function to do all this list work.

======
src/backend/replication/logical/launcher.c

7. logicalrep_worker_find

/*
 * Walks the workers array and searches for one that matches given
 * subscription id and relid.
 *
 * We are only interested in the leader apply worker or table sync worker.
 */

The above function comment (not in the patch 0003) is stale because
this AFAICT this is also going to return sequence workers if it finds
one.

~~~

8. logicalrep_sequence_sync_worker_find

+/*
+ * Walks the workers array and searches for one that matches given
+ * subscription id.
+ *
+ * We are only interested in the sequence sync worker.
+ */
+LogicalRepWorker *
+logicalrep_sequence_sync_worker_find(Oid subid, bool only_running)

There are other similar functions for walking the workers array to
search for a worker. Instead of having different functions for
different cases, wouldn't it be cleaner to combine these into a single
function, where you pass a parameter (e.g. a mask of worker types that
you are interested in finding)?

~

nitpick - declare a for loop variable 'i'

~~~

9. logicalrep_apply_worker_find

+static LogicalRepWorker *
+logicalrep_apply_worker_find(Oid subid, bool only_running)

All the other find* functions assume the lock is already held
(Assert(LWLockHeldByMe(LogicalRepWorkerLock));). But this one is
different. IMO it might be better to acquire the lock in the caller to
make all the find* functions look the same. Anyway, that will help to
combine everything into 1 "find" worker as suggested in the previous
review comment #8.

~

nitpick - declare a for loop variable 'i'
nitpick - removed unnecessary parens in condition.

~~~

10. logicalrep_worker_launch

/*----------
* Sanity checks:
* - must be valid worker type
* - tablesync workers are only ones to have relid
* - parallel apply worker is the only kind of subworker
*/

The above code-comment (not in the 0003 patch) seems stale. This
should now also mention sequence sync workers, right?

~~~

11.
- Assert(is_tablesync_worker == OidIsValid(relid));
+ Assert(is_tablesync_worker == OidIsValid(relid) ||
is_sequencesync_worker == OidIsValid(relid));

IIUC there is only a single sequence sync worker for handling all the
sequences. So, what does the 'relid' actually mean here when there are
multiple sequences?

~~~

12. logicalrep_seqsyncworker_failuretime

+/*
+ * Set the sequence sync worker failure time
+ *
+ * Called on sequence sync worker failure exit.
+ */

12a.
The comment should be improved to make it more clear that the failure
time of the sync worker information is stored with the *apply* worker.
See also other review comments in this post about this area -- perhaps
all this can be removed?

~

12b.
Curious if this had to be a separate exit handler or if may this could
have been handled by the existing logicalrep_worker_onexit handler.
See also other review comments int this post about this area --
perhaps all this can be removed?

======
.../replication/logical/sequencesync.c

13. fetch_sequence_data

13a.
The function comment has no explanation of what exactly the returned
value means. It seems like it is what you will assign as 'last_value'
on the subscriber-side.

~

13b.
Some of the table functions like this are called like
'fetch_remote_table_info()'. Maybe it is better to do similar here
(e.g. include the word "remote" in the function name).

~

14.
The reason for the addition logic "(last_value + log_cnt)" is not
obvious. I am guessing it might be related to code from
'nextval_internal' (fetch = log = fetch + SEQ_LOG_VALS;) but it is
complicated. It is unfortunate that the field 'log_cnt' seems hardly
commented anywhere at all.

Also, I am not 100% sure if I trust the logic in the first place. The
caller of this function is doing:
sequence_value = fetch_sequence_data(conn, remoteid, &lsn);
/* sets the sequence with sequence_value */
SetSequenceLastValue(RelationGetRelid(rel), sequence_value);

Won't that mean you can get to a situation where subscriber-side
result of lastval('s') can be *ahead* from lastval('s') on the
publisher? That doesn't seem good.

~~~

copy_sequence:

nitpick - ERROR message. Reword "for table..." to be more like the 2nd
error message immediately below.
nitpick - /RelationGetRelationName(rel)/relname/
nitpick - moved the Assert for 'relkind' to be nearer the assignment.

~

15.
+ /*
+ * Logical replication of sequences is based on decoding WAL records,
+ * describing the "next" state of the sequence the current state in the
+ * relfilenode is yet to reach. But during the initial sync we read the
+ * current state, so we need to reconstruct the WAL record logged when we
+ * started the current batch of sequence values.
+ *
+ * Otherwise we might get duplicate values (on subscriber) if we failed
+ * over right after the sync.
+ */
+ sequence_value = fetch_sequence_data(conn, remoteid, &lsn);
+
+ /* sets the sequence with sequence_value */
+ SetSequenceLastValue(RelationGetRelid(rel), sequence_value);

(This is related to some earlier review comment #14 above). IMO all
this tricky commentary belongs in the function header of
"fetch_sequence_data", where it should be describing that function's
return value.

~~~

LogicalRepSyncSequences:
nitpick - declare void param
nitpick indentation
nitpick - wrapping
nitpick - /sequencerel/sequence_rel/
nitpick - blank lines

~

16.
+ if (check_enable_rls(RelationGetRelid(sequencerel), InvalidOid,
false) == RLS_ENABLED)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("user \"%s\" cannot replicate into relation with row-level
security enabled: \"%s\"",
+ GetUserNameFromId(GetUserId(), true),
+ RelationGetRelationName(sequencerel)));

This should be reworded to refer to sequences instead of relations. Maybe like:
user \"%s\" cannot replicate into sequence \"%s\" with row-level
security enabled"

~

17.
The Calculations involving the BATCH size seem a bit tricky.
e.g. in 1st place it is doing: (curr_seq % MAX_SEQUENCES_SYNC_PER_BATCH == 0)
e.g. in 2nd place it is doing: (next_seq % MAX_SEQUENCES_SYNC_PER_BATCH) == 0)

Maybe this batch logic can be simplified somehow using a bool variable
for the calculation?

Also, where does the number 100 come from? Why not 1000? Why not 10?
Why have batching at all? Maybe there should be some comment to
describe the reason and the chosen value.

~

18.
+ next_seq = curr_seq + 1;
+ if (((next_seq % MAX_SEQUENCES_SYNC_PER_BATCH) == 0) || next_seq == seq_count)
+ {
+ /* LOG all the sequences synchronized during current batch. */
+ int i = curr_seq - (curr_seq % MAX_SEQUENCES_SYNC_PER_BATCH);
+ for (; i <= curr_seq; i++)
+ {
+ SubscriptionRelState *done_seq;
+ done_seq = (SubscriptionRelState *) lfirst(list_nth_cell(sequences, i));
+ ereport(LOG,
+ errmsg("logical replication synchronization for subscription \"%s\",
sequence \"%s\" has finished",
+    get_subscription_name(subid, false), get_rel_name(done_seq->relid)));
+ }
+
+ CommitTransactionCommand();
+ }
+
+ curr_seq++;

I feel this batching logic needs more comments describing what you are
doing here.

~~~

SequencesyncWorkerMain:
nitpick - spaces in the function comment

======
src/backend/replication/logical/tablesync.c

19. finish_sync_worker

-finish_sync_worker(void)
+finish_sync_worker(bool istable)

IMO, for better readability (here and in the callers) the new
parameter should be the enum LogicalRepWorkerType. Since we have that
enum, might as well make good use of it.

~

nitpick - /sequences synchronization worker/sequence synchronization worker/
nitpick - comment tweak

~

20.
+ char relkind;
+
+ if (!started_tx)
+ {
+ StartTransactionCommand();
+ started_tx = true;
+ }
+
+ relkind = get_rel_relkind(rstate->relid);
+ if (relkind == RELKIND_SEQUENCE)
+ continue;

I am wondering is it possible to put the relkind check can come
*before* the TX code here, because in case there are *only* sequences
then maybe every would be skipped and there would have been no need
for any TX at all in the first place.

~~~

process_syncing_sequences_for_apply:

nitpick - fix typo and slight reword function header comment. Also
/last start time/last failure time/
nitpick - tweak comments
nitpick - blank lines

~

21.
+ if (!started_tx)
+ {
+ StartTransactionCommand();
+ started_tx = true;
+ }
+
+ relkind = get_rel_relkind(rstate->relid);
+ if (relkind != RELKIND_SEQUENCE || rstate->state != SUBREL_STATE_INIT)
+ continue;

Wondering (like in review comment #20) if it is possible to swap those
because maybe there was no reason for any TX if the other condition
would always continue.

~~~

22.
+ if (nsyncworkers < max_sync_workers_per_subscription)
+ {
+ TimestampTz now = GetCurrentTimestamp();
+ if (!MyLogicalRepWorker->sequencesync_failure_time ||
+ TimestampDifferenceExceeds(MyLogicalRepWorker->sequencesync_failure_time,
+    now, wal_retrieve_retry_interval))
+ {
+ MyLogicalRepWorker->sequencesync_failure_time = 0;

It seems to me that storing 'sequencesync_failure_time' logic may be
unnecessarily complicated. Can't the same "throttling" be achieved by
storing the synchronization worker 'start time' instead of 'fail
time', in which case then you won't have to mess around with
considering if the sync worker failed or just exited normally etc? You
might also be able to remove all the
logicalrep_seqsyncworker_failuretime() exit handler code.

~~~

process_syncing_tables:
nitpick - let's process tables before sequences (because all other
code is generally in this same order)
nitpick - removed some excessive comments about code that is not
supposed to happen

======
src/backend/replication/logical/worker.c

should_apply_changes_for_rel:
nitpick - IMO there were excessive comments for something that is not
going to happen

~~~

23. InitializeLogRepWorker

/*
 * Common initialization for leader apply worker, parallel apply worker and
 * tablesync worker.
 *
 * Initialize the database connection, in-memory subscription and necessary
 * config options.
 */

That comment (not part of patch 0003) is stale; it should now mention
the sequence sync worker as well, right?

~

nitpick - Tweak plural /sequences sync worker/sequence sync worker/

~~~

24. SetupApplyOrSyncWorker

/* Common function to setup the leader apply or tablesync worker. */

That comment (not part of patch 0003) is stale; it should now mention
the sequence sync worker as well, right?

======
src/include/nodes/parsenodes.h

25.
  ALTER_SUBSCRIPTION_ADD_PUBLICATION,
  ALTER_SUBSCRIPTION_DROP_PUBLICATION,
  ALTER_SUBSCRIPTION_REFRESH,
+ ALTER_SUBSCRIPTION_REFRESH_PUBLICATION_SEQUENCES,

For consistency with your new enum it would be better to also change
the existing enum name ALTER_SUBSCRIPTION_REFRESH ==>
ALTER_SUBSCRIPTION_REFRESH_PUBLICATION.

======
src/include/replication/logicalworker.h

nitpick - IMO should change the function name
/SequencesyncWorkerMain/SequenceSyncWorkerMain/, and in passing make
the same improvement to the TablesyncWorkerMain function name.

======
src/include/replication/worker_internal.h

26.
  WORKERTYPE_PARALLEL_APPLY,
+ WORKERTYPE_SEQUENCESYNC,
 } LogicalRepWorkerType;

AFAIK the enum order should not matter here so it would be better to
put the WORKERTYPE_SEQUENCESYNC directly after the
WORKERTYPE_TABLESYNC to keep the similar things together.

~

nitpick - IMO change the macro name
/isSequencesyncWorker/isSequenceSyncWorker/, and in passing make the
same improvement to the isTablesyncWorker macro name.

======
src/test/subscription/t/034_sequences.pl

nitpick - Copyright year
nitpick - Modify the "Create subscriber node" comment for consistency
nitpick - Modify comments slightly for the setup structure parts
nitpick - Add or remove various blank lines
nitpick - Since you have sequences 's2' and 's3', IMO it makes more
sense to call the original sequence 's1' instead of just 's'
nitpick - Rearrange so the CREATE PUBLICATION/SUBSCRIPTION can stay together
nitpick - Modified some comment styles to clearly delineate all the
main "TEST" scenarios
nitpick - In the REFRESH PUBLICATION test the create new sequence and
update existing can be combined (like you do in a later test).
nitpick - Changed some of the test messages for REFRESH PUBLICATION
which seemed wrong
nitpick - Added another test for 's1' in REFRESH PUBLICATION SEQUENCES
nitpick - Changed some of the test messages for REFRESH PUBLICATION
SEQUENCES which seemed wrong

~

27.
IIUC the preferred practice is to give these test object names a
'regress_' prefix.

~

28.
+# Check the data on subscriber
+$result = $node_subscriber->safe_psql(
+ 'postgres', qq(
+ SELECT * FROM s;
+));
+
+is($result, '132|0|t', 'initial test data replicated');

28a.
Maybe it is better to say "SELECT last_value, log_cnt, is_called"
instead of "SELECT *" ?
Note - this is in a couple of places.

~

28b.
Can you explain why the expected sequence value its 132, because
AFAICT you only called nextval('s') 100 times, so why isn't it 100?
My guess is that it seems to be related to code in "nextval_internal"
(fetch = log = fetch + SEQ_LOG_VALS;) but it kind of defies
expectations of the test, so if it really is correct then it needs
commentary.

Actually, I found other regression test code that deals with this:
-- log_cnt can be higher if there is a checkpoint just at the right
-- time, so just test for the expected range
SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM
foo_seq_new;

Do you have to do something similar? Or is this a bug? See my other
review comments for function fetch_sequence_data in sequencesync.c

======
99.
Please also see the attached diffs patch which implements any nitpicks
mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi, 

I was reading back through this thread to find out how the proposed new command for refreshing sequences,  came about. The patch 0705 introduces a new command syntax for ALTER SUBSCRIPTION ... REFRESH SEQUENCES

So now there are 2 forms of subscription refresh.

#1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( refresh_option [= value] [, ... ] ) ]

#2. ALTER SUBSCRIPTION name REFRESH SEQUENCES

~~~~

IMO, that separation seems complicated. It leaves many questions like:
* It causes a bit of initial confusion. e.g. When I saw the REFRESH SEQUENCES I first assumed that was needed because sequences were not covered by the existing REFRESH PUBLICATION
* Why wasn't command #2 called ALTER SUBSCRIPTION REFRESH PUBLICATION SEQUENCES? E.g. missing keyword PUBLICATION. It seems inconsistent.
* I expect sequence values can become stale pretty much immediately after command #1, so the user will want to use command #2 anyway...
* ... but if command #2 also does add/remove changed sequences same as command #1 then what benefit was there of having the command #1 for sequences?
* There is a separation of sequences (from tables) in command #2 but there is no separation currently possible in command #1. It seemed inconsistent.

~~~

IIUC some of the goals I saw in the thread are to:
* provide a way to fetch and refresh sequences that also keeps behaviors (e.g. copy_data etc.) consistent with the refresh of subscription tables
* provide a way to fetch and refresh *only* sequences

I felt you could just enhance the existing refresh command syntax (command #1), instead of introducing a new one it would be simpler and it would still meet those same objectives. 

Synopsis:
ALTER SUBSCRIPTION name REFRESH PUBLICATION [TABLES | SEQUENCES | ALL] [ WITH ( refresh_option [= value] [, ... ] ) ]

My only change is the introduction of the optional "[TABLES | SEQUENCES | ALL]" clause.

I believe that can do everything your current patch does, plus more:
* Can refresh *only* TABLES if that is what you want (current patch 0705 cannot do this)
* Can refresh *only* SEQUENCES (same as current patch 0705 command #2)
* Has better integration with refresh options like "copy_data" (current patch 0705 command #2 doesn't have options)
* Existing REFRESH PUBLICATION syntax still works as-is. You can decide later what is PG18 default if the "[TABLES | SEQUENCES | ALL]" is omitted.

~~~

More examples using proposed syntax.

ex1.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = false)
- same as PG17 functionality for ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = false)

ex2.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = true)
- same as PG17 functionality for ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = true)

ex3.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = false)
- this adds/removes only sequences to pg_subscription_rel but doesn't update their sequence values

ex4.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = true)
- this adds/removes only sequences to pg_subscription_rel and also updates all sequence values.
- this is equivalent behaviour of what your current 0705 patch is doing for command #2, ALTER SUBSCRIPTION sub REFRESH SEQUENCES

ex5.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION ALL WITH (copy_data = false)
- this is equivalent behaviour of what your current 0705 patch is doing for command #1, ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = false)

ex6.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION ALL WITH (copy_data = true)
- this adds/removes tables and sequences and updates all table initial data sequence values.- I think it is equivalent to your current 0705 patch doing
command #1 ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = true), followed by another command #2 ALTER SUBSCRIPTION sub REFRESH SEQUENCES

ex7.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES
- Because default copy_data is true you do not need to specify options, so this is the same behaviour as your current 0705 patch command #2, ALTER SUBSCRIPTION sub REFRESH SEQUENCES.

~~~

I hope this post was able to demonstrate that by enhancing the existing command:
- it is less tricky to understand the separate command distinctions
- there is more functionality/flexibility possible
- there is better integration with the refresh options like copy_data
- behaviour for tables/sequences is more consistent

Anyway, it is just my opinion. Maybe there are some pitfalls I'm unaware of.

Thoughts?

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 10 Jul 2024 at 09:34, Peter Smith <smithpb2250@gmail.com> wrote:
>
> On Fri, Jul 5, 2024 at 9:58 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Thu, 4 Jul 2024 at 12:44, Peter Smith <smithpb2250@gmail.com> wrote:
> > >
> > > 1.
> > > Should there be some new test for the view? Otherwise, AFAICT this
> > > patch has no tests that will exercise the new function
> > > pg_get_publication_sequences.
> >
> > pg_publication_sequences view uses pg_get_publication_sequences which
> > will be tested with 3rd patch while creating subscription/refreshing
> > publication sequences. I felt it is ok not to have a test here.
> >
>
> OTOH, if there had been such a test here then the ("sequence = NIL")
> bug in patch 0002 code would have been caught earlier in patch 0002
> testing instead of later in patch 0003 testing. In general, I think
> each patch should be self-contained w.r.t. to testing all of its new
> code, but if you think another test here is overkill then I am fine
> with that too.

Moved these changes to 0003 patch where it is actually required.

> //////////
>
> Meanwhile, here are my review comments for patch v20240705-0002

All the comments are fixed and the attached v20240720 version patch
has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 10 Jul 2024 at 13:46, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are a few comments for patch v20240705-0003.
>
> (This is a WIP. I have only looked at the docs so far.)
>
> ======
> doc/src/sgml/config.sgml
>
> nitpick - max_logical_replication_workers: /and sequence
> synchornization worker/and a sequence synchornization worker/
>
> ======
> doc/src/sgml/logical-replication.sgml
>
> nitpick - max_logical_replication_workers: re-order list of workers to
> be consistent with other docs 1-apply,2-parallel,3-tablesync,4-seqsync
>
> ======
> doc/src/sgml/ref/alter_subscription.sgml
>
> 1.
> IIUC the existing "REFRESH PUBLICATION" command will fetch and sync
> all new sequences, etc., and/or remove old ones no longer in the
> publication. But current docs do not say anything at all about
> sequences here. It should say something about sequence behaviour.
>
> ~~~
>
> 2.
> For the existing "REFRESH PUBLICATION" there is a sub-option
> "copy_data=true/false". Won't this need some explanation about how it
> behaves for sequences? Or will there be another option
> "copy_sequences=true/false".
>
> ~~~
>
> 3.
> IIUC the main difference between REFRESH PUBLICATION and REFRESH
> PUBLICATION SEQUENCES is that the 2nd command will try synchronize
> with all the *existing* sequences to bring them to the same point as
> on the publisher, but otherwise, they are the same command. If that is
> correct understanding I don't think that distinction is made very
> clear in the current docs.
>
> ~~~
>
> nitpick - the synopsis is misplaced. It should not be between ENABLE
> and DISABLE. I moved it. Also, it should say "REFRESH PUBLICATION
> SEQUENCES" because that is how the new syntax is defined in gram.y
>
> nitpick - REFRESH SEQUENCES. Renamed to "REFRESH PUBLICATION
> SEQUENCES". And, shouldn't "from the publisher" say "with the
> publisher"?
>
> nitpick - changed the varlistentry "id".
>
> ======
> 99.
> Please also see the attached diffs patch which implements any nitpicks
> mentioned above.

All these comments are handled in the v20240720 version patch attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm2vuO7Ya4QVTZKR9jY_mkFFcE_hKUJiXx4KUknPgGFjSg%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 12 Jul 2024 at 08:22, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh. Here are the rest of my comments for patch v20240705-0003.
> ======
> src/backend/catalog/pg_subscription.c
>
> 1. GetSubscriptionSequences
>
> +/*
> + * Get the sequences for the subscription.
> + *
> + * The returned list is palloc'ed in the current memory context.
> + */
>
> Is that comment right? The palloc seems to be done in
> CacheMemoryContext, not in the current context.

This function is removed and GetSubscriptionRelations is being used instead.

> ~
>
> 2.
> The code is very similar to the other function
> GetSubscriptionRelations(). In fact I did not understand how the 2
> functions know what they are returning:
>
> E.g. how does GetSubscriptionRelations not return sequences too?
> E.g. how does GetSubscriptionSequences not return relations too?

GetSubscriptionRelations can be used, so removed the
GetSubscriptionSequences function.

>
> 3. AlterSubscription_refresh
>
> - logicalrep_worker_stop(sub->oid, relid);
> + /* Stop the worker if relation kind is not sequence*/
> + if (relkind != RELKIND_SEQUENCE)
> + logicalrep_worker_stop(sub->oid, relid);
>
> Can you give more reasons in the comment why skip the stop for sequence worker?
>
> ~
>
> nitpick - period and space in the comment
>
> ~~~
>
> 8. logicalrep_sequence_sync_worker_find
>
> +/*
> + * Walks the workers array and searches for one that matches given
> + * subscription id.
> + *
> + * We are only interested in the sequence sync worker.
> + */
> +LogicalRepWorker *
> +logicalrep_sequence_sync_worker_find(Oid subid, bool only_running)
>
> There are other similar functions for walking the workers array to
> search for a worker. Instead of having different functions for
> different cases, wouldn't it be cleaner to combine these into a single
> function, where you pass a parameter (e.g. a mask of worker types that
> you are interested in finding)?

I will address this in a future version once the patch has become more stable.
> ~~~
>
> 11.
> - Assert(is_tablesync_worker == OidIsValid(relid));
> + Assert(is_tablesync_worker == OidIsValid(relid) ||
> is_sequencesync_worker == OidIsValid(relid));
>
> IIUC there is only a single sequence sync worker for handling all the
> sequences. So, what does the 'relid' actually mean here when there are
> multiple sequences?

Sequence sync workers will not have relid, modified the assert.

> ~~~
>
> 12. logicalrep_seqsyncworker_failuretime
> 12b.
> Curious if this had to be a separate exit handler or if may this could
> have been handled by the existing logicalrep_worker_onexit handler.
> See also other review comments int this post about this area --
> perhaps all this can be removed?

This function cannot be combined with logicalrep_worker_onexit as this
function should be called only in failure case and this exit handler
should be removed in case of success case.

This cannot be removed because of the following reason:
Consider the following situation: a sequence sync worker starts and
then encounters a failure while syncing sequences. At the same time, a
user initiates a "refresh publication sequences" operation. Given only
the start time, it's not possible to distinguish whether the sequence
sync worker failed or completed successfully. This is because the
"refresh publication sequences" operation would have re-added the
sequences, making it unclear whether the sync worker's failure or
success occurred.

>
> 14.
> The reason for the addition logic "(last_value + log_cnt)" is not
> obvious. I am guessing it might be related to code from
> 'nextval_internal' (fetch = log = fetch + SEQ_LOG_VALS;) but it is
> complicated. It is unfortunate that the field 'log_cnt' seems hardly
> commented anywhere at all.
>
> Also, I am not 100% sure if I trust the logic in the first place. The
> caller of this function is doing:
> sequence_value = fetch_sequence_data(conn, remoteid, &lsn);
> /* sets the sequence with sequence_value */
> SetSequenceLastValue(RelationGetRelid(rel), sequence_value);
>
> Won't that mean you can get to a situation where subscriber-side
> result of lastval('s') can be *ahead* from lastval('s') on the
> publisher? That doesn't seem good.

Added comments for "last_value + log_cnt"
Yes it can be ahead in subscribers. This will happen because every
change of the sequence is not wal logged. It is WAL logged once in
SEQ_LOG_VALS. This was discussed earlier and the sequence value being
ahead was ok.
https://www.postgresql.org/message-id/CA%2BTgmoaVLiKDD5vr1bzL-rxhMA37KCS_2xrqjbKVwGyqK%2BPCXQ%40mail.gmail.com

> 15.
> + /*
> + * Logical replication of sequences is based on decoding WAL records,
> + * describing the "next" state of the sequence the current state in the
> + * relfilenode is yet to reach. But during the initial sync we read the
> + * current state, so we need to reconstruct the WAL record logged when we
> + * started the current batch of sequence values.
> + *
> + * Otherwise we might get duplicate values (on subscriber) if we failed
> + * over right after the sync.
> + */
> + sequence_value = fetch_sequence_data(conn, remoteid, &lsn);
> +
> + /* sets the sequence with sequence_value */
> + SetSequenceLastValue(RelationGetRelid(rel), sequence_value);
>
> (This is related to some earlier review comment #14 above). IMO all
> this tricky commentary belongs in the function header of
> "fetch_sequence_data", where it should be describing that function's
> return value.

Moved it to fetch_sequence_data where pg_sequence_state is called to
avoid any confusion

> 17.
> Also, where does the number 100 come from? Why not 1000? Why not 10?
> Why have batching at all? Maybe there should be some comment to
> describe the reason and the chosen value.

Added a comment for this. I will do one round of testing with few
values and see if this value needs to be changed. I will share it
later.

> 20.
> + char relkind;
> +
> + if (!started_tx)
> + {
> + StartTransactionCommand();
> + started_tx = true;
> + }
> +
> + relkind = get_rel_relkind(rstate->relid);
> + if (relkind == RELKIND_SEQUENCE)
> + continue;
>
> I am wondering is it possible to put the relkind check can come
> *before* the TX code here, because in case there are *only* sequences
> then maybe every would be skipped and there would have been no need
> for any TX at all in the first place.

We need to start the transaction before calling get_rel_relkind, else
it will assert in SearchCatCacheInternal. So Skipping this.

> 21.
> + if (!started_tx)
> + {
> + StartTransactionCommand();
> + started_tx = true;
> + }
> +
> + relkind = get_rel_relkind(rstate->relid);
> + if (relkind != RELKIND_SEQUENCE || rstate->state != SUBREL_STATE_INIT)
> + continue;
>
> Wondering (like in review comment #20) if it is possible to swap those
> because maybe there was no reason for any TX if the other condition
> would always continue.

As transaction is required before calling get_rel_relkind, this cannot
be changed. So skipping this.

> ~~~
>
> 22.
> + if (nsyncworkers < max_sync_workers_per_subscription)
> + {
> + TimestampTz now = GetCurrentTimestamp();
> + if (!MyLogicalRepWorker->sequencesync_failure_time ||
> + TimestampDifferenceExceeds(MyLogicalRepWorker->sequencesync_failure_time,
> +    now, wal_retrieve_retry_interval))
> + {
> + MyLogicalRepWorker->sequencesync_failure_time = 0;
>
> It seems to me that storing 'sequencesync_failure_time' logic may be
> unnecessarily complicated. Can't the same "throttling" be achieved by
> storing the synchronization worker 'start time' instead of 'fail
> time', in which case then you won't have to mess around with
> considering if the sync worker failed or just exited normally etc? You
> might also be able to remove all the
> logicalrep_seqsyncworker_failuretime() exit handler code.

Consider the following situation: a sequence sync worker starts and
then encounters a failure while syncing sequences. At the same time, a
user initiates a "refresh publication sequences" operation. Given only
the start time, it's not possible to distinguish whether the sequence
sync worker failed or completed successfully. This is because the
"refresh publication sequences" operation would have re-added the
sequences, making it unclear whether the sync worker's failure or
success occurred.

> 28b.
> Can you explain why the expected sequence value its 132, because
> AFAICT you only called nextval('s') 100 times, so why isn't it 100?
> My guess is that it seems to be related to code in "nextval_internal"
> (fetch = log = fetch + SEQ_LOG_VALS;) but it kind of defies
> expectations of the test, so if it really is correct then it needs
> commentary.

I felt adding comments for one of the tests should be enough, So I did
not add the comment for all of the tests.

> Actually, I found other regression test code that deals with this:
> -- log_cnt can be higher if there is a checkpoint just at the right
> -- time, so just test for the expected range
> SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM
> foo_seq_new;
>
> Do you have to do something similar? Or is this a bug? See my other
> review comments for function fetch_sequence_data in sequencesync.c

The comments in nextval_internal says:
 * If this is the first nextval after a checkpoint, we must force a new
 * WAL record to be written anyway, else replay starting from the
 * checkpoint would fail to advance the sequence past the logged values.
 * In this case we may as well fetch extra values.

I have increased the checkpoint for this test, so this issue will not occur.

All the other comments were fixed and the same is available in the
v20240720 version attached at [1].

[1] - https://www.postgresql.org/message-id/CALDaNm2vuO7Ya4QVTZKR9jY_mkFFcE_hKUJiXx4KUknPgGFjSg%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are some review comments for patch v20240720-0002.

======
1. Commit message:

1a.
The commit message is stale. It is still referring to functions and
views that have been moved to patch 0003.

1b.
"ALL SEQUENCES" is not a command. It is a clause of the CREATE
PUBLICATION command.

======
doc/src/sgml/ref/create_publication.sgml

nitpick - publication name in the example /allsequences/all_sequences/

======
src/bin/psql/describe.c

2. describeOneTableDetails

Although it's not the fault of this patch, this patch propagates the
confusion of 'result' versus 'res'. Basically, I did not understand
the need for the variable 'result'. There is already a "PGResult
*res", and unless I am mistaken we can just keep re-using that instead
of introducing a 2nd variable having almost the same name and purpose.

~

nitpick - comment case
nitpick - rearrange comment

======
src/test/regress/expected/publication.out

(see publication.sql)

======
src/test/regress/sql/publication.sql

nitpick - tweak comment

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 16 Jul 2024 at 06:00, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi,
>
> I was reading back through this thread to find out how the proposed new command for refreshing sequences,  came
about.The patch 0705 introduces a new command syntax for ALTER SUBSCRIPTION ... REFRESH SEQUENCES
 
>
> So now there are 2 forms of subscription refresh.
>
> #1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( refresh_option [= value] [, ... ] ) ]

This is correct.

> #2. ALTER SUBSCRIPTION name REFRESH SEQUENCES

This is not correct, it is actually "ALTER SUBSCRIPTION name REFRESH
PUBLICATION SEQUENCES"

> ~~~~
>
> IMO, that separation seems complicated. It leaves many questions like:
> * It causes a bit of initial confusion. e.g. When I saw the REFRESH SEQUENCES I first assumed that was needed because
sequenceswere not covered by the existing REFRESH PUBLICATION
 
> * Why wasn't command #2 called ALTER SUBSCRIPTION REFRESH PUBLICATION SEQUENCES? E.g. missing keyword PUBLICATION. It
seemsinconsistent.
 

This is not correct, the existing implementation uses the key word
PUBLICATION, the actual syntax is:
"ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES"

> * I expect sequence values can become stale pretty much immediately after command #1, so the user will want to use
command#2 anyway...
 

Yes

> * ... but if command #2 also does add/remove changed sequences same as command #1 then what benefit was there of
havingthe command #1 for sequences?
 
> * There is a separation of sequences (from tables) in command #2 but there is no separation currently possible in
command#1. It seemed inconsistent.
 

This can be enhanced if required. It is not included as of now because
I'm not sure if there is such a use case in case of tables.

> ~~~
>
> IIUC some of the goals I saw in the thread are to:
> * provide a way to fetch and refresh sequences that also keeps behaviors (e.g. copy_data etc.) consistent with the
refreshof subscription tables
 
> * provide a way to fetch and refresh *only* sequences
>
> I felt you could just enhance the existing refresh command syntax (command #1), instead of introducing a new one it
wouldbe simpler and it would still meet those same objectives.
 
>
> Synopsis:
> ALTER SUBSCRIPTION name REFRESH PUBLICATION [TABLES | SEQUENCES | ALL] [ WITH ( refresh_option [= value] [, ... ] )
]
>
> My only change is the introduction of the optional "[TABLES | SEQUENCES | ALL]" clause.
>
> I believe that can do everything your current patch does, plus more:
> * Can refresh *only* TABLES if that is what you want (current patch 0705 cannot do this)
> * Can refresh *only* SEQUENCES (same as current patch 0705 command #2)
> * Has better integration with refresh options like "copy_data" (current patch 0705 command #2 doesn't have options)
> * Existing REFRESH PUBLICATION syntax still works as-is. You can decide later what is PG18 default if the "[TABLES |
SEQUENCES| ALL]" is omitted.
 
>
> ~~~
>
> More examples using proposed syntax.
>
> ex1.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = false)
> - same as PG17 functionality for ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = false)
>
> ex2.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = true)
> - same as PG17 functionality for ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = true)
>
> ex3.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = false)
> - this adds/removes only sequences to pg_subscription_rel but doesn't update their sequence values
>
> ex4.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = true)
> - this adds/removes only sequences to pg_subscription_rel and also updates all sequence values.
> - this is equivalent behaviour of what your current 0705 patch is doing for command #2, ALTER SUBSCRIPTION sub
REFRESHSEQUENCES
 
>
> ex5.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION ALL WITH (copy_data = false)
> - this is equivalent behaviour of what your current 0705 patch is doing for command #1, ALTER SUBSCRIPTION sub
REFRESHPUBLICATION WITH (copy_data = false)
 
>
> ex6.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION ALL WITH (copy_data = true)
> - this adds/removes tables and sequences and updates all table initial data sequence values.- I think it is
equivalentto your current 0705 patch doing
 
> command #1 ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = true), followed by another command #2 ALTER
SUBSCRIPTIONsub REFRESH SEQUENCES
 
>
> ex7.
> ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES
> - Because default copy_data is true you do not need to specify options, so this is the same behaviour as your current
0705patch command #2, ALTER SUBSCRIPTION sub REFRESH SEQUENCES.
 

I felt ex:4 is equivalent to command #2 "ALTER SUBSCRIPTION name
REFRESH PUBLICATION SEQUENCES" and ex:3 just updates the
pg_subscription_rel. But I'm not seeing an equivalent for "ALTER
SUBSCRIPTION name REFRESH PUBLICATION with (copy_data = true)" which
will identify and remove the stale entries and add entries/synchronize
the sequences for the newly added sequences in the publisher.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi, here are some review comments for patch v20240720-0003.

This review is a WIP. This post is only about the docs (*.sgml) of patch 0003.

======
doc/src/sgml/ref/alter_subscription.sgml

1. REFRESH PUBLICATION and copy_data
nitpicks:
- IMO the "synchronize the sequence data" info was misleading because
synchronization should only occur when copy_data=true.
- I also felt it was strange to mention pg_subscription_rel for
sequences, but not for tables. I modified this part too.
- Then I moved the information about re/synchronization of sequences
into the "copy_data" part.
- And added another link to ALTER SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES

Anyway, in summary, I have updated this page quite a lot according to
my understanding. Please take a look at the attached nitpick for my
suggestions.

nitpick - /The supported options are:/The only supported option is:/

~~~

2. REFRESH PUBLICATION SEQUENCES
nitpick - tweaked the wording
nitpicK - typo /syncronizes/synchronizes/

======
3. catalogs.sgml

IMO something is missing in Section "1.55. pg_subscription_rel".

Currently, this page only talks of relations/tables, but I think it
should mention "sequences" here too, particularly since now we are
linking to here from ALTER SUBSCRIPTION when talking about sequences.

======
99.
Please see the attached diffs patch which implements any nitpicks
mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
shveta malik
Date:
On Wed, Jul 24, 2024 at 9:17 AM Peter Smith <smithpb2250@gmail.com> wrote:
>

I had a look at patches v20240720* (considering these as the latest
one) and tried to do some basic testing (WIP). Few comments:

1)
I see 'last_value' is updated wrongly after create-sub.  Steps:

-----------
pub:
CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
SELECT nextval('myseq0');
SELECT nextval('myseq0');
--last_value on pub is 105
select * from pg_sequences;
create publication pub1 for all tables, sequences;

Sub:
CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
create subscription sub1 connection 'dbname=postgres host=localhost
user=shveta port=5433' publication pub1;

--check 'r' state is reached
select pc.relname, pr.srsubstate, pr.srsublsn from pg_subscription_rel
pr, pg_class pc where (pr.srrelid = pc.oid);

--check 'last_value', it shows some random value as 136
select * from pg_sequences;
-----------

2)
I can use 'for all sequences' only with 'for all tables' and can not
use it with the below. Shouldn't it be allowed?

create publication pub2 for tables in schema public, for all sequences;
create publication pub2 for table t1, for all sequences;

3)
preprocess_pub_all_objtype_list():
Do we need 'alltables_specified' and 'allsequences_specified' ? Can't
we make a repetition check using *alltables and *allsequences?

4) patch02's commit msg says : 'Additionally, a new system view,
pg_publication_sequences, has been introduced'
But it is not part of patch002.

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 24 Jul 2024 at 09:17, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi, here are some review comments for patch v20240720-0003.
>
> This review is a WIP. This post is only about the docs (*.sgml) of patch 0003.
>
> 3. catalogs.sgml
>
> IMO something is missing in Section "1.55. pg_subscription_rel".
>
> Currently, this page only talks of relations/tables, but I think it
> should mention "sequences" here too, particularly since now we are
> linking to here from ALTER SUBSCRIPTION when talking about sequences.

Modified it to mention sequences too.

I have merged the rest of the nitpicks suggested by you.
The attached v20240724 version patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 23 Jul 2024 at 11:03, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are some review comments for patch v20240720-0002.
>
> ======
> 1. Commit message:
>
> 1a.
> The commit message is stale. It is still referring to functions and
> views that have been moved to patch 0003.

Modified

> 1b.
> "ALL SEQUENCES" is not a command. It is a clause of the CREATE
> PUBLICATION command.

Modified

> src/bin/psql/describe.c
>
> 2. describeOneTableDetails
>
> Although it's not the fault of this patch, this patch propagates the
> confusion of 'result' versus 'res'. Basically, I did not understand
> the need for the variable 'result'. There is already a "PGResult
> *res", and unless I am mistaken we can just keep re-using that instead
> of introducing a 2nd variable having almost the same name and purpose.

This is intentional, we cannot clear res as it will be used in many
places of printTable like in
printTable->print_aligned_text->pg_wcssize which was earlier stored
from printTableAddCell calls.

The rest of the nitpicks comments were merged.
The v20240724 version patch attached at [1] has the changes for the same.

[1] - https://www.postgresql.org/message-id/CALDaNm1uncevCSMqo5Nk%3DtqqV_o3KNH_jwp8URiGop_nPC8BTg%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
shveta malik
Date:
On Wed, Jul 24, 2024 at 11:52 AM shveta malik <shveta.malik@gmail.com> wrote:
>
> On Wed, Jul 24, 2024 at 9:17 AM Peter Smith <smithpb2250@gmail.com> wrote:
> >
>
> I had a look at patches v20240720* (considering these as the latest
> one) and tried to do some basic testing (WIP). Few comments:
>
> 1)
> I see 'last_value' is updated wrongly after create-sub.  Steps:
>
> -----------
> pub:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> SELECT nextval('myseq0');
> SELECT nextval('myseq0');
> --last_value on pub is 105
> select * from pg_sequences;
> create publication pub1 for all tables, sequences;
>
> Sub:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> create subscription sub1 connection 'dbname=postgres host=localhost
> user=shveta port=5433' publication pub1;
>
> --check 'r' state is reached
> select pc.relname, pr.srsubstate, pr.srsublsn from pg_subscription_rel
> pr, pg_class pc where (pr.srrelid = pc.oid);
>
> --check 'last_value', it shows some random value as 136
> select * from pg_sequences;

Okay, I see that in fetch_remote_sequence_data(), we are inserting
'last_value + log_cnt' fetched from remote as 'last_val' on subscriber
and thus leading to above behaviour. I did not understand why this is
done? This may result into issue when we insert data into a table with
identity column on subscriber (whose internal sequence is replicated);
the identity column in this case will end up having wrong value.

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 24 Jul 2024 at 11:53, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Wed, Jul 24, 2024 at 9:17 AM Peter Smith <smithpb2250@gmail.com> wrote:
> >
>
> I had a look at patches v20240720* (considering these as the latest
> one) and tried to do some basic testing (WIP). Few comments:
>
> 1)
> I see 'last_value' is updated wrongly after create-sub.  Steps:
>
> -----------
> pub:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> SELECT nextval('myseq0');
> SELECT nextval('myseq0');
> --last_value on pub is 105
> select * from pg_sequences;
> create publication pub1 for all tables, sequences;
>
> Sub:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> create subscription sub1 connection 'dbname=postgres host=localhost
> user=shveta port=5433' publication pub1;
>
> --check 'r' state is reached
> select pc.relname, pr.srsubstate, pr.srsublsn from pg_subscription_rel
> pr, pg_class pc where (pr.srrelid = pc.oid);
>
> --check 'last_value', it shows some random value as 136
> select * from pg_sequences;

Earlier I was setting sequence value with the value of publisher +
log_cnt, that is why the difference is there. On further thinking
since we are not supporting incremental replication of sequences, so
no plugin usage is involved which requires the special decoding last
value and log_count. I felt we can use the exact sequence last value
and log count to generate the similar sequence value. So Now I have
changed it to get the last_value and log_count from the publisher and
set it to the same values.

>
> 2)
> I can use 'for all sequences' only with 'for all tables' and can not
> use it with the below. Shouldn't it be allowed?
>
> create publication pub2 for tables in schema public, for all sequences;
> create publication pub2 for table t1, for all sequences;

I feel this can be added as part of a later version while supporting
"add/drop/set sequence and add/drop/set sequences in schema" once the
patch is stable.

> 3)
> preprocess_pub_all_objtype_list():
> Do we need 'alltables_specified' and 'allsequences_specified' ? Can't
> we make a repetition check using *alltables and *allsequences?

Modified

> 4) patch02's commit msg says : 'Additionally, a new system view,
> pg_publication_sequences, has been introduced'
> But it is not part of patch002.

This is removed now

The attached v20240725 version patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
shveta malik
Date:
On Thu, Jul 25, 2024 at 9:06 AM vignesh C <vignesh21@gmail.com> wrote:
>
> The attached v20240725 version patch has the changes for the same.

Thank You for addressing the comments. Please review below issues:

1) Sub ahead of pub due to wrong initial sync of last_value for
non-incremented sequences. Steps at [1]
2) Sequence's min value is not honored on sub during replication. Steps at [2]

[1]:
-----------
on PUB:
CREATE SEQUENCE myseq001 INCREMENT 5 START 100;
SELECT * from pg_sequences; -->shows last_val as NULL

on SUB:
CREATE SEQUENCE myseq001 INCREMENT 5 START 100;
SELECT * from pg_sequences; -->correctly shows last_val as NULL
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
SELECT * from pg_sequences;  -->wrongly updates last_val to 100; it is
still NULL on Pub.

Thus , SELECT nextval('myseq001') on pub gives 100, while on sub gives 105.
-----------


[2]:
-----------
Pub:
CREATE SEQUENCE myseq0 INCREMENT 5 START 10;
SELECT * from pg_sequences;

Sub:
CREATE SEQUENCE myseq0 INCREMENT 5 MINVALUE 100;

Pub:
SELECT nextval('myseq0');
SELECT nextval('myseq0');

Sub:
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
--check 'last_value', it is 15 while min_value is 100
SELECT * from pg_sequences;
-----------

thanks
Shveta



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi, here are more review comments for patch v20240720-0003.

======
src/backend/catalog/pg_subscription.c

(Numbers are starting at #4 because this is a continuation of the docs review)

4. GetSubscriptionRelations

nitpick - rearranged the function header comment

~

5.
TBH, I'm thinking that just passing 2 parameters:
- bool get_tables
- bool get_sequences
where one or both can be true, would have resulted in simpler code,
instead of introducing this new enum SubscriptionRelKind.

~

6.
The 'not_all_relations' parameter/logic feels really awkward. IMO it
needs a better name and reverse the meaning to remove all the "nots".

For example, commenting it and calling it like below could be much simpler.

'all_relations'
If returning sequences, if all_relations=true get all sequences,
otherwise only get sequences that are in 'init' state.
If returning tables, if all_relation=true get all tables, otherwise
only get tables that have not reached 'READY' state.

======
src/backend/commands/subscriptioncmds.c

AlterSubscription_refresh:

nitpick - this function comment is difficult to understand. I've
rearranged it a bit but it could still do with some further
improvement.
nitpick - move some code comments
nitpick - I adjusted the "stop worker" comment slightly. Please check
it is still correct.
nitpick - add a blank line

~

7.
The logic seems over-complicated. For example, why is the sequence
list *always* fetched, but the tables list is only sometimes fetched?
Furthermore, this 'refresh_all_sequences' parameter seems to have a
strange interference with tables (e.g. even though it is possible to
refresh all tables and sequences at the same time). It is as if the
meaning is 'refresh_publication_sequences' yet it is not called that
(???)

These gripes may be related to my other thread [1] about the new ALTER
syntax. (I feel that there should be the ability to refresh ALL TABLES
or ALL SEQUENCES independently if the user wants to). IIUC, it would
simplify this function logic as well as being more flexible. Anyway, I
will leave the discussion about syntax to that other thread.

~

8.
+ if (relkind != RELKIND_SEQUENCE)
+ logicalrep_worker_stop(sub->oid, relid);

  /*
  * For READY state, we would have already dropped the
  * tablesync origin.
  */
- if (state != SUBREL_STATE_READY)
+ if (state != SUBREL_STATE_READY && relkind != RELKIND_SEQUENCE)

It might be better to have a single "if (relkind != RELKIND_SEQUENCE)"
here and combine both of these codes under that.

~

9.
  ereport(DEBUG1,
- (errmsg_internal("table \"%s.%s\" removed from subscription \"%s\"",
+ (errmsg_internal("%s \"%s.%s\" removed from subscription \"%s\"",
+ get_namespace_name(get_rel_namespace(relid)),
+ get_rel_name(relid),
+ sub->name,
+ get_rel_relkind(relid) == RELKIND_SEQUENCE ? "sequence" : "table")));

IIUC prior conDitions mean get_rel_relkind(relid) == RELKIND_SEQUENCE
will be impossible here.

~~~

10. AlterSubscription

+ PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION ...
REFRESH PUBLICATION SEQUENCES");

IIUC the docs page for ALTER SUBSCRIPTION was missing this information
about "REFRESH PUBLICATION SEQUENCES" in transactions. Docs need more
updates.

======
src/backend/replication/logical/launcher.c

logicalrep_worker_find:
nitpick - tweak comment to say "or" instead of "and"

~~~

11.
+/*
+ * Return the pid of the apply worker for one that matches given
+ * subscription id.
+ */
+static LogicalRepWorker *
+logicalrep_apply_worker_find(Oid subid, bool only_running)

The function comment is wrong. This is not returning a PID.

~~~

12.
+ if (is_sequencesync_worker)
+ Assert(!OidIsValid(relid));

Should we the Assert to something more like:
Assert(!is_sequencesync_worker || !OidIsValid(relid));

Otherwise, in NODEBUG current code will compile into an empty
condition statement, which is a bit odd.

~~~

logicalrep_seqsyncworker_failuretime:
nitpick - tweak function comment
nitpick - add blank line

======
.../replication/logical/sequencesync.c

13. fetch_remote_sequence_data

The "current state" mentioned in the function comment is a bit vague.
Can't tell from this comment what it is returning without looking
deeper into the function code.

~

nitpick - typo "scenarios" in comment

~~~

copy_sequence:
nitpick - typo "withe" in function comment
nitpick - typo /retreived/retrieved/
nitpick - add/remove blank lines

~~~

LogicalRepSyncSequences:
nitpick - move a comment.
nitpick - remove blank line

14.
+ /*
+ * Verify whether the current batch of sequences is synchronized or if
+ * there are no remaining sequences to synchronize.
+ */
+ if ((((curr_seq + 1) % MAX_SEQUENCES_SYNC_PER_BATCH) == 0) ||
+ (curr_seq + 1) == seq_count)


All this "curr_seq + 1" maths seems unnecessarily tricky. Can't we
just increment the cur_seq? before this calculation?

~

nitpick - simplify the comment about batching
nitpick - added a comment to the commit

======
src/backend/replication/logical/tablesync.c

finish_sync_worker:
nitpick - added an Assert so the if/else is less risky.
nitpick - modify the comment about failure time when it is a clean exit

~~~

15. process_syncing_sequences_for_apply

+ /* We need up-to-date sync state info for subscription sequences here. */
+ FetchTableStates(&started_tx, SUB_REL_KIND_ALL);

Should that say SUB_REL_KIND_SEQUENCE?

~

16.
+ /*
+ * If there are free sync worker slot(s), start a new sequence
+ * sync worker, and break from the loop.
+ */
+ if (nsyncworkers < max_sync_workers_per_subscription)

Should this "if" have some "else" code to log a warning if we have run
out of free workers? Otherwise, how will the user know that the system
may need tuning?

~~~

17. FetchTableStates

  /* Fetch all non-ready tables. */
- rstates = GetSubscriptionRelations(MySubscription->oid, true);
+ rstates = GetSubscriptionRelations(MySubscription->oid, rel_type, true);

This feels risky. IMO there needs to be some prior Assert about the
rel_type. For example, if it happened to be SUB_REL_KIND_SEQUENCE then
this function code doesn't seem to make sense.

~~~

======
src/backend/replication/logical/worker.c

18. SetupApplyOrSyncWorker

+
+ if (isSequenceSyncWorker(MyLogicalRepWorker))
+ before_shmem_exit(logicalrep_seqsyncworker_failuretime, (Datum) 0);

Probably that should be using macro am_sequencesync_worker(), right?

======
src/include/catalog/pg_subscription_rel.h

19.
+typedef enum
+{
+ SUB_REL_KIND_TABLE,
+ SUB_REL_KIND_SEQUENCE,
+ SUB_REL_KIND_ALL,
+} SubscriptionRelKind;
+

I was not sure how helpful this is; it might not be needed. e.g. see
review comment for GetSubscriptionRelations

~~~

20.
+extern List *GetSubscriptionRelations(Oid subid, SubscriptionRelKind reltype,
+   bool not_ready);

There is a mismatch with the ‘not_ready’ parameter name here and in
the function implementation

======
src/test/subscription/t/034_sequences.pl

nitpick - removed a blank line

======
99.
Please also see the attached diffs patch which implements all the
nitpicks mentioned above.

======
[1] syntax -
https://www.postgresql.org/message-id/CAHut%2BPuFH1OCj-P1UKoRQE2X4-0zMG%2BN1V7jdn%3DtOQV4RNbAbw%40mail.gmail.com

Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
shveta malik
Date:
On Thu, Jul 25, 2024 at 12:08 PM shveta malik <shveta.malik@gmail.com> wrote:
>
> On Thu, Jul 25, 2024 at 9:06 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > The attached v20240725 version patch has the changes for the same.
>
> Thank You for addressing the comments. Please review below issues:
>
> 1) Sub ahead of pub due to wrong initial sync of last_value for
> non-incremented sequences. Steps at [1]
> 2) Sequence's min value is not honored on sub during replication. Steps at [2]

One more issue:
3)  Sequence datatype's range is not honored on sub during
replication, while it is honored for tables.


Behaviour for tables:
---------------------
Pub: create table tab1( i integer);
Sub: create table tab1( i smallint);

Pub: insert into tab1 values(generate_series(1, 32768));

Error on sub:
2024-07-25 10:38:06.446 IST [178680] ERROR:  value "32768" is out of
range for type smallint

---------------------
Behaviour for sequences:
---------------------

Pub:
CREATE SEQUENCE myseq_i as integer INCREMENT 10000 START 1;

Sub:
CREATE SEQUENCE myseq_i as smallint INCREMENT 10000 START 1;

Pub:
SELECT nextval('myseq_i');
SELECT nextval('myseq_i');
SELECT nextval('myseq_i');
SELECT nextval('myseq_i');
SELECT nextval('myseq_i'); -->brings value to 40001

Sub:
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
SELECT * from pg_sequences;  -->last_val reached till 40001, while the
range is till  32767.

thanks
Shveta



Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are some review comments for latest patch v20240725-0002

======
doc/src/sgml/ref/create_publication.sgml

nitpick - tweak to the description of the example.

======
src/backend/parser/gram.y

preprocess_pub_all_objtype_list:
nitpick - typo "allbjects_list"
nitpick - reword function header
nitpick - /alltables/all_tables/
nitpick - /allsequences/all_sequences/
nitpick - I think code is safe as-is because makeNode internally does
palloc0, but OTOH adding Assert would be nicer just to remove any
doubts.

======
src/bin/psql/describe.c

1.
+ /* Print any publications */
+ if (pset.sversion >= 180000)
+ {
+ int tuples = 0;

No need to assign value 0 here, because this will be unconditionally
assigned before use anyway.

~~~~

2. describePublications

  has_pubviaroot = (pset.sversion >= 130000);
+ has_pubsequence = (pset.sversion >= 18000);

That's a bug! Should be 180000, not 18000.

======

And, please see the attached diffs patch, which implements the
nitpicks mentioned above.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

There are still pending changes from my previous review of the
0720-0003 patch [1], but here are some new review comments for your
latest patch v20240525-0003.

======
doc/src/sgml/catalogs.sgml

nitpick - fix plurals and tweak the description.

~~~

1.
   <para>
-   This catalog only contains tables known to the subscription after running
-   either <link linkend="sql-createsubscription"><command>CREATE
SUBSCRIPTION</command></link> or
-   <link linkend="sql-altersubscription"><command>ALTER SUBSCRIPTION
... REFRESH
+   This catalog only contains tables and sequences known to the subscription
+   after running either
+   <link linkend="sql-createsubscription"><command>CREATE
SUBSCRIPTION</command></link>
+   or <link linkend="sql-altersubscription"><command>ALTER
SUBSCRIPTION ... REFRESH
    PUBLICATION</command></link>.
   </para>

Shouldn't this mention "REFRESH PUBLICATION SEQUENCES" too?

======
src/backend/commands/sequence.c

SetSequenceLastValue:
nitpick - maybe change: /log_cnt/new_log_cnt/ for consistency with the
other parameter, and to emphasise the old log_cnt is overwritten

======
src/backend/replication/logical/sequencesync.c

2.
+/*
+ * fetch_remote_sequence_data
+ *
+ * Fetch sequence data (current state) from the remote node, including
+ * the latest sequence value from the publisher and the Page LSN for the
+ * sequence.
+ */
+static int64
+fetch_remote_sequence_data(WalReceiverConn *conn, Oid remoteid,
+    int64 *log_cnt, XLogRecPtr *lsn)

2a.
Now you are also returning the 'log_cnt' but that is not mentioned by
the function comment.

~

2b.
Is it better to name these returned by-ref ptrs like 'ret_log_cnt',
and 'ret_lsn' to emphasise they are output variables? YMMV.

~~~

3.
+ /* Process the sequence. */
+ slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple);
+ while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))

This will have one-and-only-one tuple for the discovered sequence,
won't it? So, why is this a while loop?

======
src/include/commands/sequence.h

nitpick - maybe change: /log_cnt/new_log_cnt/ (same as earlier in this post)

======
src/test/subscription/t/034_sequences.pl

4.
Q. Should we be suspicious that log_cnt changes from '32' to '31', or
is there a valid explanation? It smells like some calculation is
off-by-one, but without debugging I can't tell if it is right or
wrong.

======
Please also see the attached diffs patch, which implements the
nitpicks mentioned above.

======
[1] 0720-0003 review -
https://www.postgresql.org/message-id/CAHut%2BPsfsfzyBrmo8E43qFMp9_bmen2tuCsNYN8sX%3Dfa86SdfA%40mail.gmail.com

Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 25 Jul 2024 at 12:54, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi, here are more review comments for patch v20240720-0003.
> 7.
> The logic seems over-complicated. For example, why is the sequence
> list *always* fetched, but the tables list is only sometimes fetched?
> Furthermore, this 'refresh_all_sequences' parameter seems to have a
> strange interference with tables (e.g. even though it is possible to
> refresh all tables and sequences at the same time). It is as if the
> meaning is 'refresh_publication_sequences' yet it is not called that
> (???)
>
> These gripes may be related to my other thread [1] about the new ALTER
> syntax. (I feel that there should be the ability to refresh ALL TABLES
> or ALL SEQUENCES independently if the user wants to). IIUC, it would
> simplify this function logic as well as being more flexible. Anyway, I
> will leave the discussion about syntax to that other thread.

1) ALTER SUBCRIPTION ... REFRESH PUBLICATION
This command will refresh both tables and sequences. It will remove
stale tables and sequences and include newly added tables and
sequences.
2) ALTER SUBCRIPTION ... REFRESH PUBLICATION SEQUENCES
This command will refresh only sequences. It will remove stale
sequences and synchronize all sequences including the existing
sequences.
So the table will be fetched only for the first command.
I have changed refresh_publication_sequences parameter to tables,
sequences, all_relations with this the function should be easier to
understand and remove any confusions.

> ~
>
> 9.
>   ereport(DEBUG1,
> - (errmsg_internal("table \"%s.%s\" removed from subscription \"%s\"",
> + (errmsg_internal("%s \"%s.%s\" removed from subscription \"%s\"",
> + get_namespace_name(get_rel_namespace(relid)),
> + get_rel_name(relid),
> + sub->name,
> + get_rel_relkind(relid) == RELKIND_SEQUENCE ? "sequence" : "table")));
>
> IIUC prior conDitions mean get_rel_relkind(relid) == RELKIND_SEQUENCE
> will be impossible here.

Consider a scenario where logical replication is setup with sequences
seq1, seq2.
Now drop sequence seq1 and do "ALTER SUBSCRIPTION sub REFRESH PUBLICATION"
It will hit this code to generate the log:
DEBUG:  sequence "public.seq1" removed from subscription "test1"

> ======
> .../replication/logical/sequencesync.c
>
> 13. fetch_remote_sequence_data
>
> The "current state" mentioned in the function comment is a bit vague.
> Can't tell from this comment what it is returning without looking
> deeper into the function code.

Added more comments to  clarify.

> ~~~
>
> 15. process_syncing_sequences_for_apply
>
> + /* We need up-to-date sync state info for subscription sequences here. */
> + FetchTableStates(&started_tx, SUB_REL_KIND_ALL);
>
> Should that say SUB_REL_KIND_SEQUENCE?

We cannot pass SUB_REL_KIND_SEQUENCE here because the
pg_subscription_rel table is shared between sequences and tables. As
changes to either sequences or relations can affect the validity of
relation states, we update both table_states_not_ready and
sequence_states_not_ready simultaneously to ensure consistency, rather
than updating them separately. I have removed the relation kind
parameter now. Fetch tables is called to fetch all tables and
sequences before calling process_syncing_tables_for_apply and
process_syncing_sequences_for_apply now.

> ~
>
> 16.
> + /*
> + * If there are free sync worker slot(s), start a new sequence
> + * sync worker, and break from the loop.
> + */
> + if (nsyncworkers < max_sync_workers_per_subscription)
>
> Should this "if" have some "else" code to log a warning if we have run
> out of free workers? Otherwise, how will the user know that the system
> may need tuning?

I felt no need to log here else we will get a lot of log messages
which might not be required. Similar logic is used for tablesync to in
process_syncing_tables_for_apply.

> ~~~
>
> 17. FetchTableStates
>
>   /* Fetch all non-ready tables. */
> - rstates = GetSubscriptionRelations(MySubscription->oid, true);
> + rstates = GetSubscriptionRelations(MySubscription->oid, rel_type, true);
>
> This feels risky. IMO there needs to be some prior Assert about the
> rel_type. For example, if it happened to be SUB_REL_KIND_SEQUENCE then
> this function code doesn't seem to make sense.

The pg_subscription_rel table is shared between sequences and tables.
As changes to either sequences or relations can affect the validity of
relation states, we update both table_states_not_ready and
sequence_states_not_ready simultaneously to ensure consistency, rather
than updating them separately. This will update both tables and
sequences that should be synced.

The rest of the comments are fixed. The attached v20240729 version
patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 26 Jul 2024 at 08:04, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are some review comments for latest patch v20240725-0002
>
> ======
> doc/src/sgml/ref/create_publication.sgml
>
> nitpick - tweak to the description of the example.
>
> ======
> src/backend/parser/gram.y
>
> preprocess_pub_all_objtype_list:
> nitpick - typo "allbjects_list"
> nitpick - reword function header
> nitpick - /alltables/all_tables/
> nitpick - /allsequences/all_sequences/
> nitpick - I think code is safe as-is because makeNode internally does
> palloc0, but OTOH adding Assert would be nicer just to remove any
> doubts.
>
> ======
> src/bin/psql/describe.c
>
> 1.
> + /* Print any publications */
> + if (pset.sversion >= 180000)
> + {
> + int tuples = 0;
>
> No need to assign value 0 here, because this will be unconditionally
> assigned before use anyway.
>
> ~~~~
>
> 2. describePublications
>
>   has_pubviaroot = (pset.sversion >= 130000);
> + has_pubsequence = (pset.sversion >= 18000);
>
> That's a bug! Should be 180000, not 18000.
>
> ======
>
> And, please see the attached diffs patch, which implements the
> nitpicks mentioned above.

These are handled in the v20240729 version attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm3SucGGLe-B-a_aqWNWQZ-yfxFTiAA0JyP-SwX4jq9Y3A%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 26 Jul 2024 at 11:46, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> There are still pending changes from my previous review of the
> 0720-0003 patch [1], but here are some new review comments for your
> latest patch v20240525-0003.
> 2b.
> Is it better to name these returned by-ref ptrs like 'ret_log_cnt',
> and 'ret_lsn' to emphasise they are output variables? YMMV.

I felt this is ok as we have mentioned in function header too

> ======
> src/test/subscription/t/034_sequences.pl
>
> 4.
> Q. Should we be suspicious that log_cnt changes from '32' to '31', or
> is there a valid explanation? It smells like some calculation is
> off-by-one, but without debugging I can't tell if it is right or
> wrong.

 It works like this: for every 33 nextval we will get log_cnt as 0. So
for 33 * 6(198) log_cnt will be 0, then for 199 log_cnt will be 32 and
for 200 log_cnt will be 31. This pattern repeats, so this is ok.

These are handled in the v20240729 version attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm3SucGGLe-B-a_aqWNWQZ-yfxFTiAA0JyP-SwX4jq9Y3A%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 25 Jul 2024 at 12:08, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Thu, Jul 25, 2024 at 9:06 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > The attached v20240725 version patch has the changes for the same.
>
> Thank You for addressing the comments. Please review below issues:
>
> 1) Sub ahead of pub due to wrong initial sync of last_value for
> non-incremented sequences. Steps at [1]
> 2) Sequence's min value is not honored on sub during replication. Steps at [2]
>
> [1]:
> -----------
> on PUB:
> CREATE SEQUENCE myseq001 INCREMENT 5 START 100;
> SELECT * from pg_sequences; -->shows last_val as NULL
>
> on SUB:
> CREATE SEQUENCE myseq001 INCREMENT 5 START 100;
> SELECT * from pg_sequences; -->correctly shows last_val as NULL
> ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> SELECT * from pg_sequences;  -->wrongly updates last_val to 100; it is
> still NULL on Pub.
>
> Thus , SELECT nextval('myseq001') on pub gives 100, while on sub gives 105.
> -----------
>
>
> [2]:
> -----------
> Pub:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 10;
> SELECT * from pg_sequences;
>
> Sub:
> CREATE SEQUENCE myseq0 INCREMENT 5 MINVALUE 100;
>
> Pub:
> SELECT nextval('myseq0');
> SELECT nextval('myseq0');
>
> Sub:
> ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> --check 'last_value', it is 15 while min_value is 100
> SELECT * from pg_sequences;

Thanks for reporting this, these issues are fixed in the attached
v20240730_2 version patch.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 25 Jul 2024 at 15:41, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Thu, Jul 25, 2024 at 12:08 PM shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Thu, Jul 25, 2024 at 9:06 AM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > The attached v20240725 version patch has the changes for the same.
> >
> > Thank You for addressing the comments. Please review below issues:
> >
> > 1) Sub ahead of pub due to wrong initial sync of last_value for
> > non-incremented sequences. Steps at [1]
> > 2) Sequence's min value is not honored on sub during replication. Steps at [2]
>
> One more issue:
> 3)  Sequence datatype's range is not honored on sub during
> replication, while it is honored for tables.
>
>
> Behaviour for tables:
> ---------------------
> Pub: create table tab1( i integer);
> Sub: create table tab1( i smallint);
>
> Pub: insert into tab1 values(generate_series(1, 32768));
>
> Error on sub:
> 2024-07-25 10:38:06.446 IST [178680] ERROR:  value "32768" is out of
> range for type smallint
>
> ---------------------
> Behaviour for sequences:
> ---------------------
>
> Pub:
> CREATE SEQUENCE myseq_i as integer INCREMENT 10000 START 1;
>
> Sub:
> CREATE SEQUENCE myseq_i as smallint INCREMENT 10000 START 1;
>
> Pub:
> SELECT nextval('myseq_i');
> SELECT nextval('myseq_i');
> SELECT nextval('myseq_i');
> SELECT nextval('myseq_i');
> SELECT nextval('myseq_i'); -->brings value to 40001
>
> Sub:
> ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> SELECT * from pg_sequences;  -->last_val reached till 40001, while the
> range is till  32767.

This issue is addressed in the v20240730_2 version patch attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm3%2BXzHAbgyn8gmbBLK5goyv_uyGgHEsTQxRZ8bVk6nAEg%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

Here are my review comments for your latest 0730_2* patches.

Patch v20240730_2-0001 looks good to me.

Patch v20240730_2-0002 looks good to me.

My comments for the v20240730_2-0003 patch are below:

//////////

GENERAL

1. Inconsistent terms

I've noticed there are many variations of how the sequence sync worker is known:
- "sequencesync worker"
- "sequence sync worker"
- "sequence-sync worker"
- "sequence synchronization worker"
- more?

We must settle on some standardized name.

AFAICT we generally use "table synchronization worker" in the docs,
and "tablesync worker" in the code and comments.  IMO, we should do
same as that for sequences -- e.g. "sequence synchronization worker"
in the docs, and "sequencesync worker" in the code and comments.

======
doc/src/sgml/catalogs.sgml

nitpick - the links should jump directly to REFRESH PUBLICATION or
REFRESH PUBLICATION SEQUENCES. Currently they go to the top of the
ALTER SUBSCRIPTION page which is not as useful.

======
src/backend/commands/sequence.c

do_setval:
nitpick - minor wording in the function header
nitpick - change some param names to more closely resemble the fields
they get assigned to (/logcnt/log_cnt/, /iscalled/is_called/)

~

2.
  seq->is_called = iscalled;
- seq->log_cnt = 0;
+ seq->log_cnt = (logcnt == SEQ_LOG_CNT_INVALID) ? 0: logcnt;

The logic here for SEQ_LOG_CNT_INVALID seemed strange. Why not just
#define SEQ_LOG_CNT_INVALID as 0 in the first place if that is what
you will assign for invalid? Then you won't need to do anything here
except seq->log_cnt = log_cnt;

======
src/backend/catalog/pg_subscription.c

HasSubscriptionRelations:
nitpick - I think the comment "If even a single tuple exists..." is
not quite accurate. e.g. It also has to be the right kind of tuple.

~~

GetSubscriptionRelations:
nitpick - Give more description in the function header about the other
parameters.
nitpick - I felt that a better name for 'all_relations' is all_states.
Because in my mind *all relations* sounds more like when both
'all_tables' and 'all_sequences' are true.
nitpick - IMO add an Assert to be sure something is being fetched.
Assert(get_tables || get_sequences);
nitpick - Rephrase the "skip the tables" and "skip the sequences"
comments to be more aligned with the code condition.

~

3.
- if (not_ready)
+ /* Get the relations that are not in ready state */
+ if (get_tables && !all_relations)
  ScanKeyInit(&skey[nkeys++],
  Anum_pg_subscription_rel_srsubstate,
  BTEqualStrategyNumber, F_CHARNE,
  CharGetDatum(SUBREL_STATE_READY));
+ /* Get the sequences that are in init state */
+ else if (get_sequences && !all_relations)
+ ScanKeyInit(&skey[nkeys++],
+ Anum_pg_subscription_rel_srsubstate,
+ BTEqualStrategyNumber, F_CHAREQ,
+ CharGetDatum(SUBREL_STATE_INIT));

This is quite tricky, using multiple flags (get_tables and
get_sequences) in such a way. It might even be a bug -- e.g. Is the
'else' keyword correct? Otherwise, when both get_tables and
get_sequences are true, and all_relations is false, then the sequence
part wouldn't even get executed (???).

======
src/backend/commands/subscriptioncmds.c

CreateSubscription:
nitpick - let's move the 'tables' declaration to be beside the
'sequences' var for consistency. (in passing move other vars too)
nitpick - it's not strictly required for the patch, but let's change
the 'tables' loop to be consistent with the new sequences loop.

~~~

4. AlterSubscription_refresh

My first impression (from the function comment) is that these function
parameters are a bit awkward. For example,
- It says:  If 'copy_data' parameter is true, the function will set
the state to "init"; otherwise, it will set the state to "ready".
- It also says: "If 'all_relations' is true, mark all objects with
"init" state..."
Those statements seem to clash. e.g. if copy_data is false but
all_relations is true, then what (???)

~

nitpick - tweak function comment wording.
nitpick - introduce a 'relkind' variable to avoid multiple calls of
get_rel_relkind(relid)
nitpick - use an existing 'relkind' variable instead of calling
get_rel_relkind(relid);
nitpick - add another comment about skipping (for dropping tablesync slots)

~

5.
+ /*
+ * If all the relations should be re-synchronized, then set the
+ * state to init for re-synchronization. This is currently
+ * supported only for sequences.
+ */
+ else if (all_relations)
+ {
+ ereport(DEBUG1,
+ (errmsg_internal("sequence \"%s.%s\" of subscription \"%s\" set to
INIT state",
  get_namespace_name(get_rel_namespace(relid)),
  get_rel_name(relid),
  sub->name)));
+ UpdateSubscriptionRelState(sub->oid, relid, SUBREL_STATE_INIT,
+    InvalidXLogRecPtr);

(This is a continuation of my doubts regarding 'all_relations' in the
previous review comment #4 above)

Here are some more questions about it:

~

5a. Why is this an 'else' of the !bsearch? It needs more explanation
what this case means.

~

5b. Along with more description, it might be better to reverse the
!bsearch condition, so this ('else') code is not so distantly
separated from the condition.

~

5c. Saying "only supported for sequences" seems strange: e.g. what
would it even mean to "re-synchronize" tables? They would all have to
be truncated first -- so if re-sync for tables has no meaning maybe
the parameter is misnamed and should just be 'resync_all_sequences' or
similar? In any case, an Assert here might be good.

======
src/backend/replication/logical/launcher.c

logicalrep_worker_find:

nitpick - I feel the function comment "We are only interested in..."
is now redundant since you are passing the exact worker type you want.
nitpick - I added an Assert for the types you are expecting to look for
nitpick - The comment "Search for attached worker..." is stale now
because there are more search criteria
nitpick - IMO the "Skip parallel apply workers." code is no longer
needed now that you are matching the worker type.

~~~

6. logicalrep_worker_launch

  * - must be valid worker type
  * - tablesync workers are only ones to have relid
  * - parallel apply worker is the only kind of subworker
+ * - sequencesync workers will not have relid
  */
  Assert(wtype != WORKERTYPE_UNKNOWN);
  Assert(is_tablesync_worker == OidIsValid(relid));
  Assert(is_parallel_apply_worker == (subworker_dsm != DSM_HANDLE_INVALID));
+ Assert(!is_sequencesync_worker || !OidIsValid(relid));

On further reflection, is that added comment and added Assert even
needed? I think they can be removed because saying "tablesync workers
are only ones to have relid" seems to already cover what we needed to
say/assert.

~~~

logicalrep_worker_stop:
nitpick - /type/wtype/ for readability

~~~

7.
/*
 * Count the number of registered (not necessarily running) sync workers
 * for a subscription.
 */
int
logicalrep_sync_worker_count(Oid subid)

~

I thought this function should count the sequencesync worker as well.

======
.../replication/logical/sequencesync.c

fetch_remote_sequence_data:
nitpick - tweaked function comment
nitpick - /value/last_value/ for readability

~

8.
+ *lsn = DatumGetInt64(slot_getattr(slot, 4, &isnull));
+ Assert(!isnull);

Should that be DatumGetUInt64?

~~~

copy_sequence:
nitpick - tweak function header.
nitpick - renamed the sequence vars for consistency, and declared them
all together.

======
src/backend/replication/logical/tablesync.c

9.
 void
 invalidate_syncing_table_states(Datum arg, int cacheid, uint32 hashvalue)
 {
- table_states_validity = SYNC_TABLE_STATE_NEEDS_REBUILD;
+ relation_states_validity = SYNC_TABLE_STATE_NEEDS_REBUILD;
 }

I assume you changed the 'table_states_validity' name because this is
no longer exclusively for tables. So, should the function name also be
similarly changed?

~~~

process_syncing_sequences_for_apply:
nitpick - tweaked the function comment
nitpick - cannot just say "if there is not one already." a sequence
syn worker might not even be needed.
nitpick - added blank line for readability

~

10.
+ if (syncworker)
+ {
+ /* Now safe to release the LWLock */
+ LWLockRelease(LogicalRepWorkerLock);
+ break;
+ }
+ else
+ {

This 'else' can be removed if you wish to pull back all the indentation.

~~~

11.
process_syncing_tables(XLogRecPtr current_lsn)

Is the function name still OK given that is is now also syncing for sequences?

~~~

FetchTableStates:
nitpick - Reworded some of the function comment
nitpick - Function comment is stale because it is still referring to
the function parameter which this patch removed.
nitpick - tweak a comment

======
src/include/commands/sequence.h

12.
+#define SEQ_LOG_CNT_INVALID (-1)

See a previous review comment (#2 above) where I wondered why not use
value 0 for this.

~~~

13.
 extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
 extern void DeleteSequenceTuple(Oid relid);
 extern void ResetSequence(Oid seq_relid);
+extern void do_setval(Oid relid, int64 next, bool iscalled, int64 logcnt);
 extern void ResetSequenceCaches(void);

do_setval() was an OK function name when it was static, but as an
exposed API it seems like a terrible name. IMO rename it to something
like 'SetSequence' to match the other API functions nearby.

~

nitpick - same change to the parameter names as suggested for the
implementation.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
shveta malik
Date:
On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> >
> >
> >
> > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> >>
> >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> >> [...]
> >> A new catalog table, pg_subscription_seq, has been introduced for
> >> mapping subscriptions to sequences. Additionally, the sequence LSN
> >> (Log Sequence Number) is stored, facilitating determination of
> >> sequence changes occurring before or after the returned sequence
> >> state.
> >
> >
> > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > something.
>
> We'll require the lsn because the sequence LSN informs the user that
> it has been synchronized up to the LSN in pg_subscription_seq. Since
> we are not supporting incremental sync, the user will be able to
> identify if he should run refresh sequences or not by checking the lsn
> of the pg_subscription_seq and the lsn of the sequence(using
> pg_sequence_state added) in the publisher.

How the user will know from seq's lsn that he needs to run refresh.
lsn indicates page_lsn and thus the sequence might advance on pub
without changing lsn and thus lsn may look the same on subscriber even
though a sequence-refresh is needed. Am I missing something here?

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Sat, 20 Jul 2024 at 20:48, vignesh C <vignesh21@gmail.com> wrote:
>
> On Fri, 12 Jul 2024 at 08:22, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > Hi Vignesh. Here are the rest of my comments for patch v20240705-0003.
> > ======
> >
> > 8. logicalrep_sequence_sync_worker_find
> >
> > +/*
> > + * Walks the workers array and searches for one that matches given
> > + * subscription id.
> > + *
> > + * We are only interested in the sequence sync worker.
> > + */
> > +LogicalRepWorker *
> > +logicalrep_sequence_sync_worker_find(Oid subid, bool only_running)
> >
> > There are other similar functions for walking the workers array to
> > search for a worker. Instead of having different functions for
> > different cases, wouldn't it be cleaner to combine these into a single
> > function, where you pass a parameter (e.g. a mask of worker types that
> > you are interested in finding)?

This is fixed in the v20240730_2 version attached at [1].

> > 17.
> > Also, where does the number 100 come from? Why not 1000? Why not 10?
> > Why have batching at all? Maybe there should be some comment to
> > describe the reason and the chosen value.

I had run some tests with 10/100 and 1000 sequences per batch for
10000 sequences. The results for it:
10 per batch   - 4.94 seconds
100 per batch  - 4.87 seconds
1000 per batch - 4.53 seconds

There is not much time difference between each of them. Currently, it
is set to 100, which seems fine since it will not generate a lot of
transactions. Additionally, the locks on the sequences will be
periodically released during the commit transaction.

I had used the test from the attached patch by changing
max_sequences_sync_per_batch to 10/100/100 in 035_sequences.pl to
verify this.

[1] - https://www.postgresql.org/message-id/CALDaNm3%2BXzHAbgyn8gmbBLK5goyv_uyGgHEsTQxRZ8bVk6nAEg%40mail.gmail.com

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

I have a question about the subscriber-side behaviour of currval().

======

AFAIK it is normal for currval() to give error is nextval() has not
yet been called [1]

For example.
test_pub=# create sequence s1;
CREATE SEQUENCE
test_pub=# select * from currval('s1');
2024-08-01 07:42:48.619 AEST [24131] ERROR:  currval of sequence "s1"
is not yet defined in this session
2024-08-01 07:42:48.619 AEST [24131] STATEMENT:  select * from currval('s1');
ERROR:  currval of sequence "s1" is not yet defined in this session
test_pub=# select * from nextval('s1');
 nextval
---------
       1
(1 row)

test_pub=# select * from currval('s1');
 currval
---------
       1
(1 row)

test_pub=#

~~~

OTOH, I was hoping to be able to use currval() at the subscriber=side
to see the current sequence value after issuing ALTER .. REFRESH
PUBLICATION SEQUENCES.

Unfortunately, it has the same behaviour where currval() cannot be
used without nextval(). But, on the subscriber, you probably never
want to do an explicit nextval() independently of the publisher.

Is this currently a bug, or maybe a quirk that should be documented?

For example:

Publisher
==========

test_pub=# create sequence s1;
CREATE SEQUENCE
test_pub=# CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
CREATE PUBLICATION
test_pub=# select * from nextval('s1');
 nextval
---------
       1
(1 row)

test_pub=# select * from nextval('s1');
 nextval
---------
       2
(1 row)

test_pub=# select * from nextval('s1');
 nextval
---------
       3
(1 row)

test_pub=#

Subscriber
==========

(Notice currval() always gives an error unless nextval() is used prior).

test_sub=# create sequence s1;
CREATE SEQUENCE
test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub'
PUBLICATION pub1;
2024-08-01 07:51:06.955 AEST [24325] WARNING:  subscriptions created
by regression test cases should have names starting with "regress_"
WARNING:  subscriptions created by regression test cases should have
names starting with "regress_"
NOTICE:  created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
test_sub=# 2024-08-01 07:51:07.023 AEST [4211] LOG:  logical
replication apply worker for subscription "sub1" has started
2024-08-01 07:51:07.037 AEST [4213] LOG:  logical replication sequence
synchronization worker for subscription "sub1" has started
2024-08-01 07:51:07.063 AEST [4213] LOG:  logical replication
synchronization for subscription "sub1", sequence "s1" has finished
2024-08-01 07:51:07.063 AEST [4213] LOG:  logical replication sequence
synchronization worker for subscription "sub1" has finished

test_sub=# SELECT * FROM currval('s1');
2024-08-01 07:51:19.688 AEST [24325] ERROR:  currval of sequence "s1"
is not yet defined in this session
2024-08-01 07:51:19.688 AEST [24325] STATEMENT:  SELECT * FROM currval('s1');
ERROR:  currval of sequence "s1" is not yet defined in this session
test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
ALTER SUBSCRIPTION
test_sub=# 2024-08-01 07:51:35.298 AEST [4993] LOG:  logical
replication sequence synchronization worker for subscription "sub1"
has started

test_sub=# 2024-08-01 07:51:35.321 AEST [4993] LOG:  logical
replication synchronization for subscription "sub1", sequence "s1" has
finished
2024-08-01 07:51:35.321 AEST [4993] LOG:  logical replication sequence
synchronization worker for subscription "sub1" has finished

test_sub=#
test_sub=# SELECT * FROM currval('s1');
2024-08-01 07:51:41.438 AEST [24325] ERROR:  currval of sequence "s1"
is not yet defined in this session
2024-08-01 07:51:41.438 AEST [24325] STATEMENT:  SELECT * FROM currval('s1');
ERROR:  currval of sequence "s1" is not yet defined in this session
test_sub=#
test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       4
(1 row)

test_sub=# SELECT * FROM currval('s1');
 currval
---------
       4
(1 row)

test_sub=#

======
[1] https://www.postgresql.org/docs/current/functions-sequence.html

Kind Regards,
Peter Smith.
Fujitsu Australia.



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

I noticed that when replicating sequences (using the latest patches
0730_2*)  the subscriber-side checks the *existence* of the sequence,
but apparently it is not checking other sequence attributes.

For example, consider:

Publisher: "CREATE SEQUENCE s1 START 1 INCREMENT 2;" should be a
sequence of only odd numbers.
Subscriber: "CREATE SEQUENCE s1 START 2 INCREMENT 2;" should be a
sequence of only even numbers.

Because the names match, currently the patch allows replication of the
s1 sequence. I think that might lead to unexpected results on the
subscriber. IMO it might be safer to report ERROR unless the sequences
match properly (i.e. not just a name check).

Below is a demonstration the problem:

==========
Publisher:
==========

(publisher sequence is odd numbers)

test_pub=# create sequence s1 start 1 increment 2;
CREATE SEQUENCE
test_pub=# select * from nextval('s1');
 nextval
---------
       1
(1 row)

test_pub=# select * from nextval('s1');
 nextval
---------
       3
(1 row)

test_pub=# select * from nextval('s1');
 nextval
---------
       5
(1 row)

test_pub=# CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
CREATE PUBLICATION
test_pub=#

==========
Subscriber:
==========

(subscriber sequence is even numbers)

test_sub=# create sequence s1 start 2 increment 2;
CREATE SEQUENCE
test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       2
(1 row)

test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       4
(1 row)

test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       6
(1 row)

test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub'
PUBLICATION pub1;
2024-08-01 08:43:04.198 AEST [24325] WARNING:  subscriptions created
by regression test cases should have names starting with "regress_"
WARNING:  subscriptions created by regression test cases should have
names starting with "regress_"
NOTICE:  created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
test_sub=# 2024-08-01 08:43:04.294 AEST [26240] LOG:  logical
replication apply worker for subscription "sub1" has started
2024-08-01 08:43:04.309 AEST [26244] LOG:  logical replication
sequence synchronization worker for subscription "sub1" has started
2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
synchronization for subscription "sub1", sequence "s1" has finished
2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
sequence synchronization worker for subscription "sub1" has finished

(after the CREATE SUBSCRIPTION we are getting replicated odd values
from the publisher, even though the subscriber side sequence was
supposed to be even numbers)

test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       7
(1 row)

test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
       9
(1 row)

test_sub=# SELECT * FROM nextval('s1');
 nextval
---------
      11
(1 row)

(Looking at the description you would expect odd values for this
sequence to be impossible)

test_sub=# \dS+ s1
                             Sequence "public.s1"
  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
 bigint |     2 |       1 | 9223372036854775807 |         2 | no      |     1

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
shveta malik
Date:
On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > Thanks for reporting this, these issues are fixed in the attached
> > v20240730_2 version patch.
> >

I was reviewing the design of patch003, and I have a query. Do we need
to even start an apply worker and create replication slot when
subscription created is for 'sequences only'? IIUC, currently logical
replication apply worker is the one launching sequence-sync worker
whenever needed. I think it should be the launcher doing this job and
thus apply worker may even not be needed for current functionality of
sequence sync? Going forward when we implement incremental sync of
sequences, then we may have apply worker started but now it is not
needed.

thanks
Shveta



Re: Logical Replication of sequences

From
shveta malik
Date:
On Fri, Aug 2, 2024 at 2:24 PM shveta malik <shveta.malik@gmail.com> wrote:
>
> On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > Thanks for reporting this, these issues are fixed in the attached
> > > v20240730_2 version patch.
> > >
>
> I was reviewing the design of patch003, and I have a query. Do we need
> to even start an apply worker and create replication slot when
> subscription created is for 'sequences only'? IIUC, currently logical
> replication apply worker is the one launching sequence-sync worker
> whenever needed. I think it should be the launcher doing this job and
> thus apply worker may even not be needed for current functionality of
> sequence sync? Going forward when we implement incremental sync of
> sequences, then we may have apply worker started but now it is not
> needed.
>

Also, can we please mention the state change and 'who does what' atop
sequencesync.c file similar to what we have atop tablesync.c file
otherwise it is difficult to figure out the flow.

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 31 Jul 2024 at 12:56, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> Here are my review comments for your latest 0730_2* patches.
>
> Patch v20240730_2-0001 looks good to me.
>
> Patch v20240730_2-0002 looks good to me.
>
> My comments for the v20240730_2-0003 patch are below:
> ~~~
>
> 4. AlterSubscription_refresh
>
> My first impression (from the function comment) is that these function
> parameters are a bit awkward. For example,
> - It says:  If 'copy_data' parameter is true, the function will set
> the state to "init"; otherwise, it will set the state to "ready".
> - It also says: "If 'all_relations' is true, mark all objects with
> "init" state..."
> Those statements seem to clash. e.g. if copy_data is false but
> all_relations is true, then what (???)

all_relations will be true only for "ALTER SUBSCRIPTION ... REFRESH
PUBLICATION SEQUENCES".  With option is not supported along with this
command so copy_data with false option is not possible here. Added an
assert for this.
>
> 8.
> + *lsn = DatumGetInt64(slot_getattr(slot, 4, &isnull));
> + Assert(!isnull);
>
> Should that be DatumGetUInt64?

It should be DatumGetLSN here.

The rest of the comments are fixed. The attached v20240805 version
patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 1 Aug 2024 at 03:33, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> I have a question about the subscriber-side behaviour of currval().
>
> ======
>
> AFAIK it is normal for currval() to give error is nextval() has not
> yet been called [1]
>
> For example.
> test_pub=# create sequence s1;
> CREATE SEQUENCE
> test_pub=# select * from currval('s1');
> 2024-08-01 07:42:48.619 AEST [24131] ERROR:  currval of sequence "s1"
> is not yet defined in this session
> 2024-08-01 07:42:48.619 AEST [24131] STATEMENT:  select * from currval('s1');
> ERROR:  currval of sequence "s1" is not yet defined in this session
> test_pub=# select * from nextval('s1');
>  nextval
> ---------
>        1
> (1 row)
>
> test_pub=# select * from currval('s1');
>  currval
> ---------
>        1
> (1 row)
>
> test_pub=#
>
> ~~~
>
> OTOH, I was hoping to be able to use currval() at the subscriber=side
> to see the current sequence value after issuing ALTER .. REFRESH
> PUBLICATION SEQUENCES.
>
> Unfortunately, it has the same behaviour where currval() cannot be
> used without nextval(). But, on the subscriber, you probably never
> want to do an explicit nextval() independently of the publisher.
>
> Is this currently a bug, or maybe a quirk that should be documented?

The currval returns the most recent value obtained from the nextval
function for a given sequence within the current session. This
function is specific to the session, meaning it only provides the last
sequence value retrieved during that session. However, if you call
currval before using nextval in the same session, you'll encounter an
error stating "currval of the sequence is not yet defined in this
session." Meaning even in the publisher this value is only visible in
the current session and not in a different session. Alternatively you
can use the following to get the last_value of the sequence: SELECT
last_value FROM sequence_name. I feel this need not be documented as
the similar issue is present in the publisher and there is an "SELECT
last_value FROM sequence_name" to get the last_value.

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 1 Aug 2024 at 04:25, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> I noticed that when replicating sequences (using the latest patches
> 0730_2*)  the subscriber-side checks the *existence* of the sequence,
> but apparently it is not checking other sequence attributes.
>
> For example, consider:
>
> Publisher: "CREATE SEQUENCE s1 START 1 INCREMENT 2;" should be a
> sequence of only odd numbers.
> Subscriber: "CREATE SEQUENCE s1 START 2 INCREMENT 2;" should be a
> sequence of only even numbers.
>
> Because the names match, currently the patch allows replication of the
> s1 sequence. I think that might lead to unexpected results on the
> subscriber. IMO it might be safer to report ERROR unless the sequences
> match properly (i.e. not just a name check).
>
> Below is a demonstration the problem:
>
> ==========
> Publisher:
> ==========
>
> (publisher sequence is odd numbers)
>
> test_pub=# create sequence s1 start 1 increment 2;
> CREATE SEQUENCE
> test_pub=# select * from nextval('s1');
>  nextval
> ---------
>        1
> (1 row)
>
> test_pub=# select * from nextval('s1');
>  nextval
> ---------
>        3
> (1 row)
>
> test_pub=# select * from nextval('s1');
>  nextval
> ---------
>        5
> (1 row)
>
> test_pub=# CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
> CREATE PUBLICATION
> test_pub=#
>
> ==========
> Subscriber:
> ==========
>
> (subscriber sequence is even numbers)
>
> test_sub=# create sequence s1 start 2 increment 2;
> CREATE SEQUENCE
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>        2
> (1 row)
>
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>        4
> (1 row)
>
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>        6
> (1 row)
>
> test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub'
> PUBLICATION pub1;
> 2024-08-01 08:43:04.198 AEST [24325] WARNING:  subscriptions created
> by regression test cases should have names starting with "regress_"
> WARNING:  subscriptions created by regression test cases should have
> names starting with "regress_"
> NOTICE:  created replication slot "sub1" on publisher
> CREATE SUBSCRIPTION
> test_sub=# 2024-08-01 08:43:04.294 AEST [26240] LOG:  logical
> replication apply worker for subscription "sub1" has started
> 2024-08-01 08:43:04.309 AEST [26244] LOG:  logical replication
> sequence synchronization worker for subscription "sub1" has started
> 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> synchronization for subscription "sub1", sequence "s1" has finished
> 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> sequence synchronization worker for subscription "sub1" has finished
>
> (after the CREATE SUBSCRIPTION we are getting replicated odd values
> from the publisher, even though the subscriber side sequence was
> supposed to be even numbers)
>
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>        7
> (1 row)
>
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>        9
> (1 row)
>
> test_sub=# SELECT * FROM nextval('s1');
>  nextval
> ---------
>       11
> (1 row)
>
> (Looking at the description you would expect odd values for this
> sequence to be impossible)
>
> test_sub=# \dS+ s1
>                              Sequence "public.s1"
>   Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
> --------+-------+---------+---------------------+-----------+---------+-------
>  bigint |     2 |       1 | 9223372036854775807 |         2 | no      |     1

Even if we check the sequence definition during the CREATE
SUBSCRIPTION/ALTER SUBSCRIPTION ... REFRESH PUBLICATION or ALTER
SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES commands, there's still
a chance that the sequence definition might change after the command
has been executed. Currently, there's no mechanism to lock a sequence,
and we also permit replication of table data even if the table
structures differ, such as mismatched data types like int and
smallint. I have modified it to log a warning to inform users that the
sequence options on the publisher and subscriber are not the same and
advise them to ensure that the sequence definitions are consistent
between both.
The v20240805 version patch attached at [1] has the changes for the same.
[1] - https://www.postgresql.org/message-id/CALDaNm1Y_ot-jFRfmtwDuwmFrgSSYHjVuy28RspSopTtwzXy8w%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > >
> > >
> > >
> > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > >>
> > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >> [...]
> > >> A new catalog table, pg_subscription_seq, has been introduced for
> > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > >> (Log Sequence Number) is stored, facilitating determination of
> > >> sequence changes occurring before or after the returned sequence
> > >> state.
> > >
> > >
> > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > something.
> >
> > We'll require the lsn because the sequence LSN informs the user that
> > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > we are not supporting incremental sync, the user will be able to
> > identify if he should run refresh sequences or not by checking the lsn
> > of the pg_subscription_seq and the lsn of the sequence(using
> > pg_sequence_state added) in the publisher.
>
> How the user will know from seq's lsn that he needs to run refresh.
> lsn indicates page_lsn and thus the sequence might advance on pub
> without changing lsn and thus lsn may look the same on subscriber even
> though a sequence-refresh is needed. Am I missing something here?

When a sequence is synchronized to the subscriber, the page LSN of the
sequence from the publisher is also retrieved and stored in
pg_subscriber_rel as shown below:
--- Publisher page lsn
publisher=# select pg_sequence_state('seq1');
 pg_sequence_state
--------------------
 (0/1510E38,65,1,t)
(1 row)

--- Subscriber stores the publisher's page lsn for the sequence
subscriber=# select * from pg_subscription_rel where srrelid = 16384;
 srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+-----------
   16389 |   16384 | r          | 0/1510E38
(1 row)

If changes are made to the sequence, such as performing many nextvals,
the page LSN will be updated. Currently the sequence values are
prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
the prefetched values, once the prefetched values are consumed the lsn
will get updated.
For example:
--- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
publisher=# select pg_sequence_state('seq1');
  pg_sequence_state
----------------------
 (0/1558CA8,143,22,t)
(1 row)

The user can then compare this updated value with the sequence's LSN
in pg_subscription_rel to determine when to re-synchronize the
sequence.

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 1 Aug 2024 at 09:26, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > Thanks for reporting this, these issues are fixed in the attached
> > v20240730_2 version patch.
> >
>
> Thanks for addressing the comments. Please find few comments on patch001 alone:
>
> Potential Bug:
> 1) 'last_value' returned by pg_sequence_state() is wrong initially.
>
> postgres=# create sequence myseq5;
> CREATE SEQUENCE
>
> postgres=# select * from pg_sequence_state('myseq5');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/1579C78 |          1 |       0 | f
>
> postgres=# SELECT nextval('myseq5') ;
>  nextval
> ---------
>        1
>
> postgres=# select * from pg_sequence_state('myseq5');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/1579FD8 |          1 |      32 | t
>
>
> Both calls returned 1. First call should have returned NULL.

I noticed the same behavior for selecting from a sequence:
postgres=# select * from myseq5;
 last_value | log_cnt | is_called
------------+---------+-----------
          1 |       0 | f
(1 row)

postgres=# select nextval('myseq5');
 nextval
---------
       1
(1 row)

postgres=# select * from myseq5;
 last_value | log_cnt | is_called
------------+---------+-----------
          1 |      32 | t
(1 row)

By default it shows the last_value as the start value for the
sequence. So this looks ok to me.

> 2)
> func.sgml:
> a) pg_sequence_state : Don't we need to give input arg as regclass
> like we give in  nextval,setval etc?
> b) Should 'lsn' be changed to 'page_lsn' as returned in output of
> pg_sequence_state()

Modified

>
> 3)
> read_seq_tuple() header says:
>  * lsn_ret will be set to the page LSN if the caller requested it.
>  * This allows the caller to determine which sequence changes are
>  * before/after the returned sequence state.
>
> How, using lsn which is page-lsn and not sequence value/change lsn,
> does the user interpret if sequence changes are before/after the
> returned sequence state? Can you please elaborate or amend the
> comment?

I have added this to pg_sequence_state function header in 003 patch as
the subscriber side changes are present here, I felt that is more apt
to mention. This is also added in sequencesync.c file header.

The attached v20240805_2 version patch has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 2 Aug 2024 at 14:24, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > Thanks for reporting this, these issues are fixed in the attached
> > > v20240730_2 version patch.
> > >
>
> I was reviewing the design of patch003, and I have a query. Do we need
> to even start an apply worker and create replication slot when
> subscription created is for 'sequences only'? IIUC, currently logical
> replication apply worker is the one launching sequence-sync worker
> whenever needed. I think it should be the launcher doing this job and
> thus apply worker may even not be needed for current functionality of
> sequence sync? Going forward when we implement incremental sync of
> sequences, then we may have apply worker started but now it is not
> needed.

I believe the current method of having the apply worker initiate the
sequence sync worker is advantageous for several reasons:
a) Reduces Launcher Load: This approach prevents overloading the
launcher, which must handle various other subscription requests.
b) Facilitates Incremental Sync: It provides a more straightforward
path to extend support for incremental sequence synchronization.
c) Reuses Existing Code: It leverages the existing tablesync worker
code for starting the tablesync process, avoiding the need to
duplicate code in the launcher.
d) Simplified Code Maintenance: Centralizing sequence synchronization
logic within the apply worker can simplify code maintenance and
updates, as changes will only need to be made in one place rather than
across multiple components.
e) Better Monitoring and Debugging: With sequence synchronization
being handled by the apply worker, you can more effectively monitor
and debug synchronization processes since all related operations are
managed by a single component.

Also, I noticed that even when a publication has no tables, we create
replication slot and start apply worker.

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 2 Aug 2024 at 14:33, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Fri, Aug 2, 2024 at 2:24 PM shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > Thanks for reporting this, these issues are fixed in the attached
> > > > v20240730_2 version patch.
> > > >
> >
> > I was reviewing the design of patch003, and I have a query. Do we need
> > to even start an apply worker and create replication slot when
> > subscription created is for 'sequences only'? IIUC, currently logical
> > replication apply worker is the one launching sequence-sync worker
> > whenever needed. I think it should be the launcher doing this job and
> > thus apply worker may even not be needed for current functionality of
> > sequence sync? Going forward when we implement incremental sync of
> > sequences, then we may have apply worker started but now it is not
> > needed.
> >
>
> Also, can we please mention the state change and 'who does what' atop
> sequencesync.c file similar to what we have atop tablesync.c file
> otherwise it is difficult to figure out the flow.

I have added this in sequencesync.c file, the changes for the same are
available at v20240805_2 version patch at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm1kk1MHGk3BU_XTxay%3DdR6sMHnm4TT5cmVz2f_JXkWENQ%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Mon, Aug 5, 2024 at 2:36 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Fri, 2 Aug 2024 at 14:24, shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > Thanks for reporting this, these issues are fixed in the attached
> > > > v20240730_2 version patch.
> > > >
> >
> > I was reviewing the design of patch003, and I have a query. Do we need
> > to even start an apply worker and create replication slot when
> > subscription created is for 'sequences only'? IIUC, currently logical
> > replication apply worker is the one launching sequence-sync worker
> > whenever needed. I think it should be the launcher doing this job and
> > thus apply worker may even not be needed for current functionality of
> > sequence sync?
>

But that would lead to maintaining all sequence-sync of each
subscription by launcher. Say there are 100 sequences per subscription
and some of them from each subscription are failing due to some
reasons then the launcher will be responsible for ensuring all the
sequences are synced. I think it would be better to handle
per-subscription work by the apply worker.

>
> Going forward when we implement incremental sync of
> > sequences, then we may have apply worker started but now it is not
> > needed.
>
> I believe the current method of having the apply worker initiate the
> sequence sync worker is advantageous for several reasons:
> a) Reduces Launcher Load: This approach prevents overloading the
> launcher, which must handle various other subscription requests.
> b) Facilitates Incremental Sync: It provides a more straightforward
> path to extend support for incremental sequence synchronization.
> c) Reuses Existing Code: It leverages the existing tablesync worker
> code for starting the tablesync process, avoiding the need to
> duplicate code in the launcher.
> d) Simplified Code Maintenance: Centralizing sequence synchronization
> logic within the apply worker can simplify code maintenance and
> updates, as changes will only need to be made in one place rather than
> across multiple components.
> e) Better Monitoring and Debugging: With sequence synchronization
> being handled by the apply worker, you can more effectively monitor
> and debug synchronization processes since all related operations are
> managed by a single component.
>
> Also, I noticed that even when a publication has no tables, we create
> replication slot and start apply worker.
>

As far as I understand slots and origins are primarily required for
incremental sync. Would it be used only for sequence-sync cases? If
not then we can avoid creating those. I agree that it would add some
complexity to the code with sequence-specific checks, so we can create
a top-up patch for this if required and evaluate its complexity versus
the benefit it produces.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
shveta malik
Date:
On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > >
> > > >
> > > >
> > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >>
> > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > >> [...]
> > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > >> (Log Sequence Number) is stored, facilitating determination of
> > > >> sequence changes occurring before or after the returned sequence
> > > >> state.
> > > >
> > > >
> > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > something.
> > >
> > > We'll require the lsn because the sequence LSN informs the user that
> > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > we are not supporting incremental sync, the user will be able to
> > > identify if he should run refresh sequences or not by checking the lsn
> > > of the pg_subscription_seq and the lsn of the sequence(using
> > > pg_sequence_state added) in the publisher.
> >
> > How the user will know from seq's lsn that he needs to run refresh.
> > lsn indicates page_lsn and thus the sequence might advance on pub
> > without changing lsn and thus lsn may look the same on subscriber even
> > though a sequence-refresh is needed. Am I missing something here?
>
> When a sequence is synchronized to the subscriber, the page LSN of the
> sequence from the publisher is also retrieved and stored in
> pg_subscriber_rel as shown below:
> --- Publisher page lsn
> publisher=# select pg_sequence_state('seq1');
>  pg_sequence_state
> --------------------
>  (0/1510E38,65,1,t)
> (1 row)
>
> --- Subscriber stores the publisher's page lsn for the sequence
> subscriber=# select * from pg_subscription_rel where srrelid = 16384;
>  srsubid | srrelid | srsubstate | srsublsn
> ---------+---------+------------+-----------
>    16389 |   16384 | r          | 0/1510E38
> (1 row)
>
> If changes are made to the sequence, such as performing many nextvals,
> the page LSN will be updated. Currently the sequence values are
> prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> the prefetched values, once the prefetched values are consumed the lsn
> will get updated.
> For example:
> --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> publisher=# select pg_sequence_state('seq1');
>   pg_sequence_state
> ----------------------
>  (0/1558CA8,143,22,t)
> (1 row)
>
> The user can then compare this updated value with the sequence's LSN
> in pg_subscription_rel to determine when to re-synchronize the
> sequence.

Thanks for the details. But I was referring to the case where we are
in between pre-fetched values on publisher (say at 25th value), while
on subscriber we are slightly behind (say at 15th value), but page-lsn
will be the same on both. Since the subscriber is behind, a
sequence-refresh is needed on sub, but by looking at lsn (which is
same), one can not say that for sure.  Let me know if I have
misunderstood it.

thanks
Shveta



Re: Logical Replication of sequences

From
shveta malik
Date:
On Mon, Aug 5, 2024 at 5:28 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Mon, Aug 5, 2024 at 2:36 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Fri, 2 Aug 2024 at 14:24, shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > >
> > > > > Thanks for reporting this, these issues are fixed in the attached
> > > > > v20240730_2 version patch.
> > > > >
> > >
> > > I was reviewing the design of patch003, and I have a query. Do we need
> > > to even start an apply worker and create replication slot when
> > > subscription created is for 'sequences only'? IIUC, currently logical
> > > replication apply worker is the one launching sequence-sync worker
> > > whenever needed. I think it should be the launcher doing this job and
> > > thus apply worker may even not be needed for current functionality of
> > > sequence sync?
> >
>
> But that would lead to maintaining all sequence-sync of each
> subscription by launcher. Say there are 100 sequences per subscription
> and some of them from each subscription are failing due to some
> reasons then the launcher will be responsible for ensuring all the
> sequences are synced. I think it would be better to handle
> per-subscription work by the apply worker.

I thought we can give that task to sequence-sync worker. Once sequence
sync worker is started by launcher, it keeps on syncing until all the
sequences are synced (even failed ones) and then exits only after all
are synced; instead of apply worker starting it multiple times for
failed sequences. Launcher to start sequence sync worker when signaled
by 'alter-sub refresh seq'.
But after going through details given by Vignesh in [1], I also see
the benefits of using apply worker for this task. Since apply worker
is already looping and doing that for table-sync, we can reuse the
same code for sequence sync and maintenance will be easy. So looks
okay if we go with existing apply worker design.

[1]: https://www.postgresql.org/message-id/CALDaNm1KO8f3Fj%2BRHHXM%3DUSGwOcW242M1jHee%3DX_chn2ToiCpw%40mail.gmail.com

>
> >
> > Going forward when we implement incremental sync of
> > > sequences, then we may have apply worker started but now it is not
> > > needed.
> >
> > I believe the current method of having the apply worker initiate the
> > sequence sync worker is advantageous for several reasons:
> > a) Reduces Launcher Load: This approach prevents overloading the
> > launcher, which must handle various other subscription requests.
> > b) Facilitates Incremental Sync: It provides a more straightforward
> > path to extend support for incremental sequence synchronization.
> > c) Reuses Existing Code: It leverages the existing tablesync worker
> > code for starting the tablesync process, avoiding the need to
> > duplicate code in the launcher.
> > d) Simplified Code Maintenance: Centralizing sequence synchronization
> > logic within the apply worker can simplify code maintenance and
> > updates, as changes will only need to be made in one place rather than
> > across multiple components.
> > e) Better Monitoring and Debugging: With sequence synchronization
> > being handled by the apply worker, you can more effectively monitor
> > and debug synchronization processes since all related operations are
> > managed by a single component.
> >
> > Also, I noticed that even when a publication has no tables, we create
> > replication slot and start apply worker.
> >
>
> As far as I understand slots and origins are primarily required for
> incremental sync. Would it be used only for sequence-sync cases? If
> not then we can avoid creating those. I agree that it would add some
> complexity to the code with sequence-specific checks, so we can create
> a top-up patch for this if required and evaluate its complexity versus
> the benefit it produces.
>
> --
> With Regards,
> Amit Kapila.



Re: Logical Replication of sequences

From
shveta malik
Date:
On Tue, Aug 6, 2024 at 8:49 AM shveta malik <shveta.malik@gmail.com> wrote:
>

Do we need some kind of coordination between table sync and sequence
sync for internally generated sequences? Lets say we have an identity
column with a 'GENERATED ALWAYS' sequence. When the sequence is synced
to subscriber, subscriber can also do an insert to table (extra one)
incrementing the sequence and then when publisher performs an insert,
apply worker will blindly copy that row to sub's table making identity
column's duplicate entries.

CREATE TABLE color ( color_id INT GENERATED ALWAYS AS
IDENTITY,color_name VARCHAR NOT NULL);

Pub: insert into color(color_name) values('red');

Sub: perform sequence refresh and check 'r' state is reached, then do insert:
insert into color(color_name) values('yellow');

Pub: insert into color(color_name) values('blue');

After above, data on Pub: (1, 'red') ;(2, 'blue'),

After above, data on Sub: (1, 'red') ;(2, 'yellow'); (2, 'blue'),

Identity column has duplicate values. Should the apply worker error
out while inserting such a  row to the table? Or it is not in the
scope of this project?

thanks
Shveta



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Aug 6, 2024 at 8:49 AM shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Aug 5, 2024 at 5:28 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Mon, Aug 5, 2024 at 2:36 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Fri, 2 Aug 2024 at 14:24, shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> > > > >
> > > > > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > >
> > > > > > Thanks for reporting this, these issues are fixed in the attached
> > > > > > v20240730_2 version patch.
> > > > > >
> > > >
> > > > I was reviewing the design of patch003, and I have a query. Do we need
> > > > to even start an apply worker and create replication slot when
> > > > subscription created is for 'sequences only'? IIUC, currently logical
> > > > replication apply worker is the one launching sequence-sync worker
> > > > whenever needed. I think it should be the launcher doing this job and
> > > > thus apply worker may even not be needed for current functionality of
> > > > sequence sync?
> > >
> >
> > But that would lead to maintaining all sequence-sync of each
> > subscription by launcher. Say there are 100 sequences per subscription
> > and some of them from each subscription are failing due to some
> > reasons then the launcher will be responsible for ensuring all the
> > sequences are synced. I think it would be better to handle
> > per-subscription work by the apply worker.
>
> I thought we can give that task to sequence-sync worker. Once sequence
> sync worker is started by launcher, it keeps on syncing until all the
> sequences are synced (even failed ones) and then exits only after all
> are synced; instead of apply worker starting it multiple times for
> failed sequences. Launcher to start sequence sync worker when signaled
> by 'alter-sub refresh seq'.
> But after going through details given by Vignesh in [1], I also see
> the benefits of using apply worker for this task. Since apply worker
> is already looping and doing that for table-sync, we can reuse the
> same code for sequence sync and maintenance will be easy. So looks
> okay if we go with existing apply worker design.
>

Fair enough. However, I was wondering whether apply_worker should exit
after syncing all sequences for a sequence-only subscription or should
it be there for future commands that can refresh the subscription and
add additional tables or sequences?

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
shveta malik
Date:
On Tue, Aug 6, 2024 at 9:54 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Tue, Aug 6, 2024 at 8:49 AM shveta malik <shveta.malik@gmail.com> wrote:
> >
> > > > > I was reviewing the design of patch003, and I have a query. Do we need
> > > > > to even start an apply worker and create replication slot when
> > > > > subscription created is for 'sequences only'? IIUC, currently logical
> > > > > replication apply worker is the one launching sequence-sync worker
> > > > > whenever needed. I think it should be the launcher doing this job and
> > > > > thus apply worker may even not be needed for current functionality of
> > > > > sequence sync?
> > > >
> > >
> > > But that would lead to maintaining all sequence-sync of each
> > > subscription by launcher. Say there are 100 sequences per subscription
> > > and some of them from each subscription are failing due to some
> > > reasons then the launcher will be responsible for ensuring all the
> > > sequences are synced. I think it would be better to handle
> > > per-subscription work by the apply worker.
> >
> > I thought we can give that task to sequence-sync worker. Once sequence
> > sync worker is started by launcher, it keeps on syncing until all the
> > sequences are synced (even failed ones) and then exits only after all
> > are synced; instead of apply worker starting it multiple times for
> > failed sequences. Launcher to start sequence sync worker when signaled
> > by 'alter-sub refresh seq'.
> > But after going through details given by Vignesh in [1], I also see
> > the benefits of using apply worker for this task. Since apply worker
> > is already looping and doing that for table-sync, we can reuse the
> > same code for sequence sync and maintenance will be easy. So looks
> > okay if we go with existing apply worker design.
> >
>
> Fair enough. However, I was wondering whether apply_worker should exit
> after syncing all sequences for a sequence-only subscription

If apply worker exits, then on next sequence-refresh, we need a way to
wake-up launcher to start apply worker which then will start
table-sync worker. Instead, won't it be better if the launcher starts
table-sync worker directly without the need of apply worker being
present (which I stated earlier).

>  or should
> it be there for future commands that can refresh the subscription and
> add additional tables or sequences?

If we stick with apply worker starting table sync worker when needed
by continuously checking seq-sync states ('i'/'r'), then IMO, it is
better that apply-worker stays. But if we want  apply-worker to exit
and start only when needed, then why not to start sequence-sync worker
directly for seq-only subscriptions?

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 6 Aug 2024 at 10:24, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Tue, Aug 6, 2024 at 9:54 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Tue, Aug 6, 2024 at 8:49 AM shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > > > > I was reviewing the design of patch003, and I have a query. Do we need
> > > > > > to even start an apply worker and create replication slot when
> > > > > > subscription created is for 'sequences only'? IIUC, currently logical
> > > > > > replication apply worker is the one launching sequence-sync worker
> > > > > > whenever needed. I think it should be the launcher doing this job and
> > > > > > thus apply worker may even not be needed for current functionality of
> > > > > > sequence sync?
> > > > >
> > > >
> > > > But that would lead to maintaining all sequence-sync of each
> > > > subscription by launcher. Say there are 100 sequences per subscription
> > > > and some of them from each subscription are failing due to some
> > > > reasons then the launcher will be responsible for ensuring all the
> > > > sequences are synced. I think it would be better to handle
> > > > per-subscription work by the apply worker.
> > >
> > > I thought we can give that task to sequence-sync worker. Once sequence
> > > sync worker is started by launcher, it keeps on syncing until all the
> > > sequences are synced (even failed ones) and then exits only after all
> > > are synced; instead of apply worker starting it multiple times for
> > > failed sequences. Launcher to start sequence sync worker when signaled
> > > by 'alter-sub refresh seq'.
> > > But after going through details given by Vignesh in [1], I also see
> > > the benefits of using apply worker for this task. Since apply worker
> > > is already looping and doing that for table-sync, we can reuse the
> > > same code for sequence sync and maintenance will be easy. So looks
> > > okay if we go with existing apply worker design.
> > >
> >
> > Fair enough. However, I was wondering whether apply_worker should exit
> > after syncing all sequences for a sequence-only subscription
>
> If apply worker exits, then on next sequence-refresh, we need a way to
> wake-up launcher to start apply worker which then will start
> table-sync worker. Instead, won't it be better if the launcher starts
> table-sync worker directly without the need of apply worker being
> present (which I stated earlier).

I favour the current design because it ensures the system remains
extendable for future incremental sequence synchronization. If the
launcher were responsible for starting the sequence sync worker, it
would add extra load that could hinder its ability to service other
subscriptions and complicate the design for supporting incremental
sync of sequences. Additionally, this approach offers the other
benefits mentioned in [1].

> >  or should
> > it be there for future commands that can refresh the subscription and
> > add additional tables or sequences?
>
> If we stick with apply worker starting table sync worker when needed
> by continuously checking seq-sync states ('i'/'r'), then IMO, it is
> better that apply-worker stays. But if we want  apply-worker to exit
> and start only when needed, then why not to start sequence-sync worker
> directly for seq-only subscriptions?

There is a risk that sequence synchronization might fail if the
sequence value from the publisher falls outside the defined minvalue
or maxvalue range. The apply worker must be active to determine
whether to initiate the sequence sync worker after the
wal_retrieve_retry_interval period. Typically, publications consisting
solely of sequences are uncommon. However, if a user wishes to use
such publications, they can disable the subscription if necessary and
re-enable it when a sequence refresh is needed.

[1] - https://www.postgresql.org/message-id/CALDaNm1KO8f3Fj%2BRHHXM%3DUSGwOcW242M1jHee%3DX_chn2ToiCpw%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are some review comments for the patch v20240805_2-0003.

======
doc/src/sgml/catalogs.sgml

nitpick - removed the word "either"

======
doc/src/sgml/ref/alter_subscription.sgml

I felt the discussions about "how to handle warnings" are a bit scattered:
e.g.1 - ALTER SUBSCRIPTION REFRESH PUBLICATION copy data referred to
CREATE SUBSCRIPTION copy data.
e.g.2 - ALTER SUBSCRIPTION REFRESH explains what to do, but now the
explanation is in 2 places.
e.g.3 - CREATE SUBSCRIPTION copy data explains what to do (again), but
IMO it belongs better in the common "Notes" part

FYI, I've moved all the information to one place (in the CREATE
SUBSCRIPTION "Notes") and others refer to this central place. See the
attached nitpicks diff.

REFRESH PUBLICATION copy_data
nitpick - now refers to  CREATE SUBSCRIPTION "Notes". I also moved it
to be nearer to the other sequence stuff.

REFRESH PUBLICATION SEQUENCES:
nitpick - now refers to CREATE SUBSCRIPTION "Notes".

======
doc/src/sgml/ref/create_subscription.sgml

REFRESH PUBLICATION copy_data
nitpick - now refers to CREATE SUBSCRIPTION "Notes"

Notes:
nitpick - the explanation of, and what to do about sequence WARNINGS,
is moved to here

======
src/backend/commands/sequence.c

pg_sequence_state:
nitpick - I just moved the comment in pg_sequence_state() to below the
NOTE, which talks about "page LSN".

======
src/backend/catalog/pg_subscription.c

1. HasSubscriptionRelations

Should function 'HasSubscriptionRelations' be renamed to
'HasSubscriptionTables'?

~~~

GetSubscriptionRelations:
nitpick - tweak some "skip" comments.

======
src/backend/commands/subscriptioncmds.c

2. CreateSubscription

  tables = fetch_table_list(wrconn, publications);
- foreach(lc, tables)
+ foreach_ptr(RangeVar, rv, tables)
+ {
+ Oid relid;
+
+ relid = RangeVarGetRelid(rv, AccessShareLock, false);
+
+ /* Check for supported relkind. */
+ CheckSubscriptionRelkind(get_rel_relkind(relid),
+ rv->schemaname, rv->relname);
+
+ AddSubscriptionRelState(subid, relid, table_state,
+ InvalidXLogRecPtr, true);
+ }
+
+ /* Add the sequences in init state */
+ sequences = fetch_sequence_list(wrconn, publications);
+ foreach_ptr(RangeVar, rv, sequences)

These 2 loops (first for tables and then for sequences) seem to be
executing the same code. If you wanted you could combine the lists
up-front, and then have one code loop instead of 2. It would mean less
code. OTOH, maybe the current code is more readable? I am not sure
what is best, so just bringing this to your attention.

~~~

AlterSubscription_refresh:
nitpick = typo /indicating tha/indicating that/

~~~

3. fetch_sequence_list

+ appendStringInfoString(&cmd, "SELECT DISTINCT n.nspname, c.relname,
s.seqtypid, s.seqmin, s.seqmax, s.seqstart, s.seqincrement,
s.seqcycle"
+    " FROM pg_publication p, LATERAL
pg_get_publication_sequences(p.pubname::text) gps(relid),"
+    " pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace JOIN
pg_sequence s ON c.oid = s.seqrelid"
+    " WHERE c.oid = gps.relid AND p.pubname IN (");
+ get_publications_str(publications, &cmd, true);
+ appendStringInfoChar(&cmd, ')');

Please wrap this better to make the SQL more readable.

~~

4.
+ if (seqform->seqtypid != seqtypid || seqform->seqmin != seqmin ||
+ seqform->seqmax != seqmax || seqform->seqstart != seqstart ||
+ seqform->seqincrement != seqincrement ||
+ seqform->seqcycle != seqcycle)
+ ereport(WARNING,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("Sequence option in remote and local is not same for \"%s.%s\"",
+    get_namespace_name(get_rel_namespace(relid)), get_rel_name(relid)),
+ errhint("Alter/Re-create the sequence using the same options as in remote."));

4a.
Are these really known as "options"? Or should they be called
"sequence parameters", or something else, like "sequence attributes"?

4a.
Is there a way to give more helpful information by identifying what
was different in the log? OTOH, maybe it would become too messy if
there were multiple differences...

======
src/backend/replication/logical/launcher.c

5. logicalrep_sync_worker_count

- if (isTablesyncWorker(w) && w->subid == subid)
+ if ((isTableSyncWorker(w) || isSequenceSyncWorker(w)) &&
+ w->subid == subid)

You could micro-optimize this -- it may be more efficient to write the
condition the other way around.

SUGGESTION
if (w->subid == subid && (isTableSyncWorker(w) || isSequenceSyncWorker(w)))

======
.../replication/logical/sequencesync.c

File header comment:
nitpick - there seems a large cut/paste mistake (the first 2
paragraphs are almost the same).
nitpick - reworded with the help of Chat-GPT for slightly better
clarity. Also fixed a couple of typos.
nitpick - it mentioned MAX_SEQUENCES_SYNC_PER_BATCH several times so I
changed the wording of one of them

~~~

fetch_remote_sequence_data:
nitpick - all other params have the same name as sequence members, so
change the parameter name /lsn/page_lsn/

~

copy_sequence:
nitpick - rename var /seq_lsn/seq_page_lsn/

======
src/backend/replication/logical/tablesync.c

6. process_syncing_sequences_for_apply

+ * If a sequencesync worker is running already, there is no need to start a new
+ * one; the existing sequencesync worker will synchronize all the sequences. If
+ * there are still any sequences to be synced after the sequencesync worker
+ * exited, then a new sequencesync worker can be started in the next iteration.
+ * To prevent starting the sequencesync worker at a high frequency after a
+ * failure, we store its last failure time. We start the sync worker for the
+ * same relation after waiting at least wal_retrieve_retry_interval.

Why is it talking about "We start the sync worker for the same
relation ...". The sequencesync_failuretime is per sync worker, not
per relation. And, I don't see any 'same relation' check in the code.

======
src/include/catalog/pg_subscription_rel.h

GetSubscriptionRelations:
nitpick - changed parameter name /all_relations/all_states/

======
src/test/subscription/t/034_sequences.pl

nitpick - add some ########## comments to highlight the main test
parts to make it easier to read.
nitpick - fix typo /syned/synced/

7. More test cases?
IIUC you can also get a sequence mismatch warning during "ALTER ...
REFRESH PUBLICATION", and "CREATE SUBSCRIPTION". So, should those be
tested also?

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 5 Aug 2024 at 18:05, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > > >
> > > > >
> > > > >
> > > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > >>
> > > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > >> [...]
> > > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > > >> (Log Sequence Number) is stored, facilitating determination of
> > > > >> sequence changes occurring before or after the returned sequence
> > > > >> state.
> > > > >
> > > > >
> > > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > > something.
> > > >
> > > > We'll require the lsn because the sequence LSN informs the user that
> > > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > > we are not supporting incremental sync, the user will be able to
> > > > identify if he should run refresh sequences or not by checking the lsn
> > > > of the pg_subscription_seq and the lsn of the sequence(using
> > > > pg_sequence_state added) in the publisher.
> > >
> > > How the user will know from seq's lsn that he needs to run refresh.
> > > lsn indicates page_lsn and thus the sequence might advance on pub
> > > without changing lsn and thus lsn may look the same on subscriber even
> > > though a sequence-refresh is needed. Am I missing something here?
> >
> > When a sequence is synchronized to the subscriber, the page LSN of the
> > sequence from the publisher is also retrieved and stored in
> > pg_subscriber_rel as shown below:
> > --- Publisher page lsn
> > publisher=# select pg_sequence_state('seq1');
> >  pg_sequence_state
> > --------------------
> >  (0/1510E38,65,1,t)
> > (1 row)
> >
> > --- Subscriber stores the publisher's page lsn for the sequence
> > subscriber=# select * from pg_subscription_rel where srrelid = 16384;
> >  srsubid | srrelid | srsubstate | srsublsn
> > ---------+---------+------------+-----------
> >    16389 |   16384 | r          | 0/1510E38
> > (1 row)
> >
> > If changes are made to the sequence, such as performing many nextvals,
> > the page LSN will be updated. Currently the sequence values are
> > prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> > the prefetched values, once the prefetched values are consumed the lsn
> > will get updated.
> > For example:
> > --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> > publisher=# select pg_sequence_state('seq1');
> >   pg_sequence_state
> > ----------------------
> >  (0/1558CA8,143,22,t)
> > (1 row)
> >
> > The user can then compare this updated value with the sequence's LSN
> > in pg_subscription_rel to determine when to re-synchronize the
> > sequence.
>
> Thanks for the details. But I was referring to the case where we are
> in between pre-fetched values on publisher (say at 25th value), while
> on subscriber we are slightly behind (say at 15th value), but page-lsn
> will be the same on both. Since the subscriber is behind, a
> sequence-refresh is needed on sub, but by looking at lsn (which is
> same), one can not say that for sure.  Let me know if I have
> misunderstood it.

Yes, at present, if the value is within the pre-fetched range, we
cannot distinguish it solely using the page_lsn. However, the
pg_sequence_state function also provides last_value and log_cnt, which
can be used to handle these specific cases.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Tue, Aug 6, 2024 at 5:13 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Mon, 5 Aug 2024 at 18:05, shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > >
> > > > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > > > >
> > > > > >
> > > > > >
> > > > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > >>
> > > > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > >> [...]
> > > > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > > > >> (Log Sequence Number) is stored, facilitating determination of
> > > > > >> sequence changes occurring before or after the returned sequence
> > > > > >> state.
> > > > > >
> > > > > >
> > > > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > > > something.
> > > > >
> > > > > We'll require the lsn because the sequence LSN informs the user that
> > > > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > > > we are not supporting incremental sync, the user will be able to
> > > > > identify if he should run refresh sequences or not by checking the lsn
> > > > > of the pg_subscription_seq and the lsn of the sequence(using
> > > > > pg_sequence_state added) in the publisher.
> > > >
> > > > How the user will know from seq's lsn that he needs to run refresh.
> > > > lsn indicates page_lsn and thus the sequence might advance on pub
> > > > without changing lsn and thus lsn may look the same on subscriber even
> > > > though a sequence-refresh is needed. Am I missing something here?
> > >
> > > When a sequence is synchronized to the subscriber, the page LSN of the
> > > sequence from the publisher is also retrieved and stored in
> > > pg_subscriber_rel as shown below:
> > > --- Publisher page lsn
> > > publisher=# select pg_sequence_state('seq1');
> > >  pg_sequence_state
> > > --------------------
> > >  (0/1510E38,65,1,t)
> > > (1 row)
> > >
> > > --- Subscriber stores the publisher's page lsn for the sequence
> > > subscriber=# select * from pg_subscription_rel where srrelid = 16384;
> > >  srsubid | srrelid | srsubstate | srsublsn
> > > ---------+---------+------------+-----------
> > >    16389 |   16384 | r          | 0/1510E38
> > > (1 row)
> > >
> > > If changes are made to the sequence, such as performing many nextvals,
> > > the page LSN will be updated. Currently the sequence values are
> > > prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> > > the prefetched values, once the prefetched values are consumed the lsn
> > > will get updated.
> > > For example:
> > > --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> > > publisher=# select pg_sequence_state('seq1');
> > >   pg_sequence_state
> > > ----------------------
> > >  (0/1558CA8,143,22,t)
> > > (1 row)
> > >
> > > The user can then compare this updated value with the sequence's LSN
> > > in pg_subscription_rel to determine when to re-synchronize the
> > > sequence.
> >
> > Thanks for the details. But I was referring to the case where we are
> > in between pre-fetched values on publisher (say at 25th value), while
> > on subscriber we are slightly behind (say at 15th value), but page-lsn
> > will be the same on both. Since the subscriber is behind, a
> > sequence-refresh is needed on sub, but by looking at lsn (which is
> > same), one can not say that for sure.  Let me know if I have
> > misunderstood it.
>
> Yes, at present, if the value is within the pre-fetched range, we
> cannot distinguish it solely using the page_lsn.
>

This makes sense to me.

>
> However, the
> pg_sequence_state function also provides last_value and log_cnt, which
> can be used to handle these specific cases.
>

BTW, can we document all these steps for users to know when to refresh
the sequences, if not already documented?

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

This is mostly a repeat of my previous mail from a while ago [1] but
includes some corrections, answers, and more examples. I'm going to
try to persuade one last time because the current patch is becoming
stable, so I wanted to revisit this syntax proposal before it gets too
late to change anything.

If there is some problem with the proposed idea please let me know
because I can see only the advantages and no disadvantages of doing it
this way.

~~~

The current patchset offers two forms of subscription refresh:
1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( refresh_option
[= value] [, ... ] ) ]
2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES

Since 'copy_data' is the only supported refresh_option, really it is more like:
1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( copy_data [=
true|false] ) ]
2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES

~~~

I proposed previously that instead of having 2 commands for refreshing
subscriptions we should have a single refresh command:

ALTER SUBSCRIPTION name REFRESH PUBLICATION [TABLES|SEQUENCES] [ WITH
( copy_data [= true|false] ) ]

Why?

- IMO it is less confusing than having 2 commands that both refresh
sequences in slightly different ways

- It is more flexible because apart from refreshing everything, a user
can choose to refresh only tables or only sequences if desired; IMO
more flexibility is always good.

- There is no loss of functionality from the current implementation
AFAICT. You can still say "ALTER SUBSCRIPTION sub REFRESH PUBLICATION
SEQUENCES" exactly the same as the patchset allows.

- The implementation code will become simpler. For example, the
current implementation of AlterSubscription_refresh(...) includes the
(hacky?) 'resync_all_sequences' parameter and has an overcomplicated
relationship with other parameters as demonstrated by the assertions
below. IMO using the prosed syntax means this coding will become not
only simpler, but shorter too.
+ /* resync_all_sequences cannot be specified with refresh_tables */
+ Assert(!(resync_all_sequences && refresh_tables));
+
+ /* resync_all_sequences cannot be specified with copy_data as false */
+ Assert(!(resync_all_sequences && !copy_data));

~~~

So, to continue this proposal, let the meaning of 'copy_data' for
SEQUENCES be as follows:

- when copy_data == false: it means don't copy data (i.e. don't
synchronize anything). Add/remove sequences from pg_subscriber_rel as
needed.

- when copy_data == true: it means to copy data (i.e. synchronize) for
all sequences. Add/remove sequences from pg_subscriber_rel as needed)


~~~

EXAMPLES using the proposed syntax:

Refreshing TABLES only...

ex1.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = false)
- same as PG17 functionality for "ALTER SUBSCRIPTION sub REFRESH
PUBLICATION WITH (copy_data = false)"

ex2.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES WITH (copy_data = true)
- same as PG17 functionality for "ALTER SUBSCRIPTION sub REFRESH
PUBLICATION WITH (copy_data = true)"

ex3. (using default copy_data)
ALTER SUBSCRIPTION sub REFRESH PUBLICATION TABLES
- same as ex2.

~

Refreshing SEQUENCES only...

ex4.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = false)
- this adds/removes only sequences to pg_subscription_rel but doesn't
update the sequence values

ex5.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES WITH (copy data = true)
- this adds/removes only sequences to pg_subscription_rel and also
updates (synchronizes) all sequence values.
- same functionality as "ALTER SUBSCRIPTION sub REFRESH PUBLICATION
SEQUENCES" in your current patchset

ex6. (using default copy_data)
ALTER SUBSCRIPTION sub REFRESH PUBLICATION SEQUENCES
- same as ex5.
- note, that this command has the same syntax and functionality as the
current patchset

~~~

When no object_type is specified it has intuitive meaning to refresh
both TABLES and SEQUENCES...

ex7.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = false)
- For tables, it is the same as the PG17 functionality
- For sequences it includes the same behaviour of ex4.

ex8.
ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = true)
- For tables, it is the same as the PG17 functionality
- For sequences it includes the same behaviour of ex5.
- There is one subtle difference from the current patchset because
this proposal will synchronize *all* sequences instead of only new
ones. But, this is a good thing. The current documentation is
complicated by having to explain the differences between REFRESH
PUBLICATION and REFRESH PUBLICATION SEQUENCES. The current patchset
also raises questions like how the user chooses whether to use
"REFRESH PUBLICATION SEQUENCES" versus "REFRESH PUBLICATION WITH
(copy_data=true)". OTHO, the proposed syntax eliminates ambiguity.

ex9. (using default copy_data)
ALTER SUBSCRIPTION sub REFRESH PUBLICATION
- same as ex8

======
[1] https://www.postgresql.org/message-id/CAHut%2BPuFH1OCj-P1UKoRQE2X4-0zMG%2BN1V7jdn%3DtOQV4RNbAbw%40mail.gmail.com

Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
shveta malik
Date:
On Mon, Aug 5, 2024 at 10:26 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 1 Aug 2024 at 04:25, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > Hi Vignesh,
> >
> > I noticed that when replicating sequences (using the latest patches
> > 0730_2*)  the subscriber-side checks the *existence* of the sequence,
> > but apparently it is not checking other sequence attributes.
> >
> > For example, consider:
> >
> > Publisher: "CREATE SEQUENCE s1 START 1 INCREMENT 2;" should be a
> > sequence of only odd numbers.
> > Subscriber: "CREATE SEQUENCE s1 START 2 INCREMENT 2;" should be a
> > sequence of only even numbers.
> >
> > Because the names match, currently the patch allows replication of the
> > s1 sequence. I think that might lead to unexpected results on the
> > subscriber. IMO it might be safer to report ERROR unless the sequences
> > match properly (i.e. not just a name check).
> >
> > Below is a demonstration the problem:
> >
> > ==========
> > Publisher:
> > ==========
> >
> > (publisher sequence is odd numbers)
> >
> > test_pub=# create sequence s1 start 1 increment 2;
> > CREATE SEQUENCE
> > test_pub=# select * from nextval('s1');
> >  nextval
> > ---------
> >        1
> > (1 row)
> >
> > test_pub=# select * from nextval('s1');
> >  nextval
> > ---------
> >        3
> > (1 row)
> >
> > test_pub=# select * from nextval('s1');
> >  nextval
> > ---------
> >        5
> > (1 row)
> >
> > test_pub=# CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
> > CREATE PUBLICATION
> > test_pub=#
> >
> > ==========
> > Subscriber:
> > ==========
> >
> > (subscriber sequence is even numbers)
> >
> > test_sub=# create sequence s1 start 2 increment 2;
> > CREATE SEQUENCE
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >        2
> > (1 row)
> >
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >        4
> > (1 row)
> >
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >        6
> > (1 row)
> >
> > test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub'
> > PUBLICATION pub1;
> > 2024-08-01 08:43:04.198 AEST [24325] WARNING:  subscriptions created
> > by regression test cases should have names starting with "regress_"
> > WARNING:  subscriptions created by regression test cases should have
> > names starting with "regress_"
> > NOTICE:  created replication slot "sub1" on publisher
> > CREATE SUBSCRIPTION
> > test_sub=# 2024-08-01 08:43:04.294 AEST [26240] LOG:  logical
> > replication apply worker for subscription "sub1" has started
> > 2024-08-01 08:43:04.309 AEST [26244] LOG:  logical replication
> > sequence synchronization worker for subscription "sub1" has started
> > 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> > synchronization for subscription "sub1", sequence "s1" has finished
> > 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> > sequence synchronization worker for subscription "sub1" has finished
> >
> > (after the CREATE SUBSCRIPTION we are getting replicated odd values
> > from the publisher, even though the subscriber side sequence was
> > supposed to be even numbers)
> >
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >        7
> > (1 row)
> >
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >        9
> > (1 row)
> >
> > test_sub=# SELECT * FROM nextval('s1');
> >  nextval
> > ---------
> >       11
> > (1 row)
> >
> > (Looking at the description you would expect odd values for this
> > sequence to be impossible)

I see that for such even sequences, user can still do 'setval'  to a
odd number and then nextval will keep on returning odd value.

postgres=# SELECT nextval('s1');
       6

postgres=SELECT setval('s1', 43);
     43

postgres=# SELECT nextval('s1');
     45

> > test_sub=# \dS+ s1
> >                              Sequence "public.s1"
> >   Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
> > --------+-------+---------+---------------------+-----------+---------+-------
> >  bigint |     2 |       1 | 9223372036854775807 |         2 | no      |     1
>
> Even if we check the sequence definition during the CREATE
> SUBSCRIPTION/ALTER SUBSCRIPTION ... REFRESH PUBLICATION or ALTER
> SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES commands, there's still
> a chance that the sequence definition might change after the command
> has been executed. Currently, there's no mechanism to lock a sequence,
> and we also permit replication of table data even if the table
> structures differ, such as mismatched data types like int and
> smallint. I have modified it to log a warning to inform users that the
> sequence options on the publisher and subscriber are not the same and
> advise them to ensure that the sequence definitions are consistent
> between both.
> The v20240805 version patch attached at [1] has the changes for the same.
> [1] - https://www.postgresql.org/message-id/CALDaNm1Y_ot-jFRfmtwDuwmFrgSSYHjVuy28RspSopTtwzXy8w%40mail.gmail.com

The behavior for applying is no different from setval. Having said
that, I agree that sequence definition can change even after the
subscription creation, but earlier we were not syncing sequences and
thus the value of a particular sequence was going to remain in the
range/pattern defined by its attributes unless user sets it manually
using setval. But now, it is being changed in the background without
user's knowledge.
The table case is different. In case of table replication, if we have
CHECK constraint or say primary-key etc, then the value which violates
these constraints will never be  inserted to a table even during
replication on sub. For sequences, parameters (MIN,MAX, START,
INCREMENT) can be considered similar to check-constraints, the only
difference is during apply, we are still overriding these and copying
pub's value. May be such inconsistencies detection can be targeted
later in next project. But for the time being, it will be good to add
a 'caveat' section in doc mentioning all such cases. The scope of this
project should be clearly documented.

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 6 Aug 2024 at 14:38, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here are some review comments for the patch v20240805_2-0003.
>
> 4a.
> Is there a way to give more helpful information by identifying what
> was different in the log? OTOH, maybe it would become too messy if
> there were multiple differences...

I had considered this while implementing and did not implement it to
print each of the parameters because it will be too messy. I felt
existing is better.

>
> 7. More test cases?
> IIUC you can also get a sequence mismatch warning during "ALTER ...
> REFRESH PUBLICATION", and "CREATE SUBSCRIPTION". So, should those be
> tested also?

Since it won't add any extra coverage, I feel no need to add this test.

The remaining comments have been addressed, and the changes are
included in the attached v20240807 version patch.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 6 Aug 2024 at 09:28, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Tue, Aug 6, 2024 at 8:49 AM shveta malik <shveta.malik@gmail.com> wrote:
> >
>
> Do we need some kind of coordination between table sync and sequence
> sync for internally generated sequences? Lets say we have an identity
> column with a 'GENERATED ALWAYS' sequence. When the sequence is synced
> to subscriber, subscriber can also do an insert to table (extra one)
> incrementing the sequence and then when publisher performs an insert,
> apply worker will blindly copy that row to sub's table making identity
> column's duplicate entries.
>
> CREATE TABLE color ( color_id INT GENERATED ALWAYS AS
> IDENTITY,color_name VARCHAR NOT NULL);
>
> Pub: insert into color(color_name) values('red');
>
> Sub: perform sequence refresh and check 'r' state is reached, then do insert:
> insert into color(color_name) values('yellow');
>
> Pub: insert into color(color_name) values('blue');
>
> After above, data on Pub: (1, 'red') ;(2, 'blue'),
>
> After above, data on Sub: (1, 'red') ;(2, 'yellow'); (2, 'blue'),
>
> Identity column has duplicate values. Should the apply worker error
> out while inserting such a  row to the table? Or it is not in the
> scope of this project?

This behavior is documented at [1]:
Sequence data is not replicated. The data in serial or identity
columns backed by sequences will of course be replicated as part of
the table, but the sequence itself would still show the start value on
the subscriber.

This behavior is because of the above logical replication restriction.
So the behavior looks ok to me and I feel this is not part of the
scope of this project. I have updated this documentation section here
to mention sequences can be updated using ALTER SEQUENCE ... REFRESH
PUBLICATION SEQUENCES at v20240807 version patch attached at [2].

[1] - https://www.postgresql.org/docs/devel/logical-replication-restrictions.html
[2] - https://www.postgresql.org/message-id/CALDaNm01Z6Oo9osGMFTOoyTR1kVoyh1rEvZ%2B6uJn-ZymV%3D0dbQ%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 5 Aug 2024 at 17:28, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Mon, Aug 5, 2024 at 2:36 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Fri, 2 Aug 2024 at 14:24, shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Thu, Aug 1, 2024 at 9:26 AM shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Mon, Jul 29, 2024 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > >
> > > > > Thanks for reporting this, these issues are fixed in the attached
> > > > > v20240730_2 version patch.
> > > > >
> > >
> > > I was reviewing the design of patch003, and I have a query. Do we need
> > > to even start an apply worker and create replication slot when
> > > subscription created is for 'sequences only'? IIUC, currently logical
> > > replication apply worker is the one launching sequence-sync worker
> > > whenever needed. I think it should be the launcher doing this job and
> > > thus apply worker may even not be needed for current functionality of
> > > sequence sync?
> >
>
> But that would lead to maintaining all sequence-sync of each
> subscription by launcher. Say there are 100 sequences per subscription
> and some of them from each subscription are failing due to some
> reasons then the launcher will be responsible for ensuring all the
> sequences are synced. I think it would be better to handle
> per-subscription work by the apply worker.
>
> >
> > Going forward when we implement incremental sync of
> > > sequences, then we may have apply worker started but now it is not
> > > needed.
> >
> > I believe the current method of having the apply worker initiate the
> > sequence sync worker is advantageous for several reasons:
> > a) Reduces Launcher Load: This approach prevents overloading the
> > launcher, which must handle various other subscription requests.
> > b) Facilitates Incremental Sync: It provides a more straightforward
> > path to extend support for incremental sequence synchronization.
> > c) Reuses Existing Code: It leverages the existing tablesync worker
> > code for starting the tablesync process, avoiding the need to
> > duplicate code in the launcher.
> > d) Simplified Code Maintenance: Centralizing sequence synchronization
> > logic within the apply worker can simplify code maintenance and
> > updates, as changes will only need to be made in one place rather than
> > across multiple components.
> > e) Better Monitoring and Debugging: With sequence synchronization
> > being handled by the apply worker, you can more effectively monitor
> > and debug synchronization processes since all related operations are
> > managed by a single component.
> >
> > Also, I noticed that even when a publication has no tables, we create
> > replication slot and start apply worker.
> >
>
> As far as I understand slots and origins are primarily required for
> incremental sync. Would it be used only for sequence-sync cases? If
> not then we can avoid creating those. I agree that it would add some
> complexity to the code with sequence-specific checks, so we can create
> a top-up patch for this if required and evaluate its complexity versus
> the benefit it produces.

I have added a XXX todo comments in the v20240807 version patch
attached at [1]. I will handle this as a separate patch once the
current patch is stable.
[1] - https://www.postgresql.org/message-id/CALDaNm01Z6Oo9osGMFTOoyTR1kVoyh1rEvZ%2B6uJn-ZymV%3D0dbQ%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 7 Aug 2024 at 08:09, Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Tue, Aug 6, 2024 at 5:13 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Mon, 5 Aug 2024 at 18:05, shveta malik <shveta.malik@gmail.com> wrote:
> > >
> > > On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> > > > >
> > > > > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > >
> > > > > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > > >>
> > > > > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > > >> [...]
> > > > > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > > > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > > > > >> (Log Sequence Number) is stored, facilitating determination of
> > > > > > >> sequence changes occurring before or after the returned sequence
> > > > > > >> state.
> > > > > > >
> > > > > > >
> > > > > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > > > > something.
> > > > > >
> > > > > > We'll require the lsn because the sequence LSN informs the user that
> > > > > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > > > > we are not supporting incremental sync, the user will be able to
> > > > > > identify if he should run refresh sequences or not by checking the lsn
> > > > > > of the pg_subscription_seq and the lsn of the sequence(using
> > > > > > pg_sequence_state added) in the publisher.
> > > > >
> > > > > How the user will know from seq's lsn that he needs to run refresh.
> > > > > lsn indicates page_lsn and thus the sequence might advance on pub
> > > > > without changing lsn and thus lsn may look the same on subscriber even
> > > > > though a sequence-refresh is needed. Am I missing something here?
> > > >
> > > > When a sequence is synchronized to the subscriber, the page LSN of the
> > > > sequence from the publisher is also retrieved and stored in
> > > > pg_subscriber_rel as shown below:
> > > > --- Publisher page lsn
> > > > publisher=# select pg_sequence_state('seq1');
> > > >  pg_sequence_state
> > > > --------------------
> > > >  (0/1510E38,65,1,t)
> > > > (1 row)
> > > >
> > > > --- Subscriber stores the publisher's page lsn for the sequence
> > > > subscriber=# select * from pg_subscription_rel where srrelid = 16384;
> > > >  srsubid | srrelid | srsubstate | srsublsn
> > > > ---------+---------+------------+-----------
> > > >    16389 |   16384 | r          | 0/1510E38
> > > > (1 row)
> > > >
> > > > If changes are made to the sequence, such as performing many nextvals,
> > > > the page LSN will be updated. Currently the sequence values are
> > > > prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> > > > the prefetched values, once the prefetched values are consumed the lsn
> > > > will get updated.
> > > > For example:
> > > > --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> > > > publisher=# select pg_sequence_state('seq1');
> > > >   pg_sequence_state
> > > > ----------------------
> > > >  (0/1558CA8,143,22,t)
> > > > (1 row)
> > > >
> > > > The user can then compare this updated value with the sequence's LSN
> > > > in pg_subscription_rel to determine when to re-synchronize the
> > > > sequence.
> > >
> > > Thanks for the details. But I was referring to the case where we are
> > > in between pre-fetched values on publisher (say at 25th value), while
> > > on subscriber we are slightly behind (say at 15th value), but page-lsn
> > > will be the same on both. Since the subscriber is behind, a
> > > sequence-refresh is needed on sub, but by looking at lsn (which is
> > > same), one can not say that for sure.  Let me know if I have
> > > misunderstood it.
> >
> > Yes, at present, if the value is within the pre-fetched range, we
> > cannot distinguish it solely using the page_lsn.
> >
>
> This makes sense to me.
>
> >
> > However, the
> > pg_sequence_state function also provides last_value and log_cnt, which
> > can be used to handle these specific cases.
> >
>
> BTW, can we document all these steps for users to know when to refresh
> the sequences, if not already documented?

This has been documented in the v20240807 version attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm01Z6Oo9osGMFTOoyTR1kVoyh1rEvZ%2B6uJn-ZymV%3D0dbQ%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, Here are my v20240807-0003 review comments.

======
1. GENERAL DOCS.

IMO the replication of SEQUENCES is a big enough topic that it
deserves to have its own section in the docs chapter 31 [1].

Some of the create/alter subscription docs content would stay where it
is in, but a new chapter would just tie everything together better. It
could also serve as a better place to describe the other sequence
replication content like:
(a) getting a WARNING for mismatched sequences and how to handle it.
(b) how can the user know when a subscription refresh is required to
(re-)synchronise sequences
(c) pub/sub examples

======
doc/src/sgml/logical-replication.sgml

2. Restrictions

Sequence data is not replicated. The data in serial or identity
columns backed by sequences will of course be replicated as part of
the table, but the sequence itself would still show the start value on
the subscriber. If the subscriber is used as a read-only database,
then this should typically not be a problem. If, however, some kind of
switchover or failover to the subscriber database is intended, then
the sequences would need to be updated to the latest values, either by
executing ALTER SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES or by
copying the current data from the publisher (perhaps using pg_dump) or
by determining a sufficiently high value from the tables themselves.

~

2a.
The paragraph starts by saying "Sequence data is not replicated.". It
seems wrong now. Doesn't that need rewording or removing?

~

2b.
Should the info "If, however, some kind of switchover or failover..."
be mentioned in the "Logical Replication Failover" section [2],
instead of here?

======
doc/src/sgml/ref/alter_subscription.sgml

3.
Sequence values may occasionally become out of sync due to updates in
the publisher. To verify this, compare the
pg_subscription_rel.srsublsn on the subscriber with the page_lsn
obtained from the pg_sequence_state for the sequence on the publisher.
If the sequence is still using prefetched values, the page_lsn will
not be updated. In such cases, you will need to directly compare the
sequences and execute REFRESH PUBLICATION SEQUENCES if required.

~

3a.
This whole paragraph may be better put in the new chapter that was
suggested earlier in review comment #1.

~

3b.
Is it only "Occasionally"? I expected subscriber-side sequences could
become stale quite often.

~

3c.
Is this advice very useful? It's saying if the LSN is different then
the sequence is out of date, but if the LSN is not different then you
cannot tell. Why not ignore LSN altogether and just advise the user to
directly compare the sequences in the first place?

======

Also, there are more minor suggestions in the attached nitpicks diff.

======
[1] https://www.postgresql.org/docs/current/logical-replication.html
[2] file:///usr/local/pg_oss/share/doc/postgresql/html/logical-replication-failover.html

Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Wed, Aug 7, 2024 at 10:12 AM Peter Smith <smithpb2250@gmail.com> wrote:
>
> This is mostly a repeat of my previous mail from a while ago [1] but
> includes some corrections, answers, and more examples. I'm going to
> try to persuade one last time because the current patch is becoming
> stable, so I wanted to revisit this syntax proposal before it gets too
> late to change anything.
>
> If there is some problem with the proposed idea please let me know
> because I can see only the advantages and no disadvantages of doing it
> this way.
>
> ~~~
>
> The current patchset offers two forms of subscription refresh:
> 1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( refresh_option
> [= value] [, ... ] ) ]
> 2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES
>
> Since 'copy_data' is the only supported refresh_option, really it is more like:
> 1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( copy_data [=
> true|false] ) ]
> 2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES
>
> ~~~
>
> I proposed previously that instead of having 2 commands for refreshing
> subscriptions we should have a single refresh command:
>
> ALTER SUBSCRIPTION name REFRESH PUBLICATION [TABLES|SEQUENCES] [ WITH
> ( copy_data [= true|false] ) ]
>
> Why?
>
> - IMO it is less confusing than having 2 commands that both refresh
> sequences in slightly different ways
>
> - It is more flexible because apart from refreshing everything, a user
> can choose to refresh only tables or only sequences if desired; IMO
> more flexibility is always good.
>
> - There is no loss of functionality from the current implementation
> AFAICT. You can still say "ALTER SUBSCRIPTION sub REFRESH PUBLICATION
> SEQUENCES" exactly the same as the patchset allows.
>
> ~~~
>
> So, to continue this proposal, let the meaning of 'copy_data' for
> SEQUENCES be as follows:
>
> - when copy_data == false: it means don't copy data (i.e. don't
> synchronize anything). Add/remove sequences from pg_subscriber_rel as
> needed.
>
> - when copy_data == true: it means to copy data (i.e. synchronize) for
> all sequences. Add/remove sequences from pg_subscriber_rel as needed)
>

I find overloading the copy_data option more confusing than adding a
new variant for REFRESH. To make it clear, we can even think of
extending the command as ALTER SUBSCRIPTION name REFRESH PUBLICATION
ALL SEQUENCES or something like that.  I don't know where there is a
need or not but one can imagine extending it as ALTER SUBSCRIPTION
name REFRESH PUBLICATION SEQUENCES [<seq_name_1>, <seq_name_2>, ..].
This will allow to selectively refresh the sequences.

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
Peter Smith
Date:
On Thu, Aug 8, 2024 at 1:55 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
>
> On Wed, Aug 7, 2024 at 10:12 AM Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > This is mostly a repeat of my previous mail from a while ago [1] but
> > includes some corrections, answers, and more examples. I'm going to
> > try to persuade one last time because the current patch is becoming
> > stable, so I wanted to revisit this syntax proposal before it gets too
> > late to change anything.
> >
> > If there is some problem with the proposed idea please let me know
> > because I can see only the advantages and no disadvantages of doing it
> > this way.
> >
> > ~~~
> >
> > The current patchset offers two forms of subscription refresh:
> > 1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( refresh_option
> > [= value] [, ... ] ) ]
> > 2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES
> >
> > Since 'copy_data' is the only supported refresh_option, really it is more like:
> > 1. ALTER SUBSCRIPTION name REFRESH PUBLICATION [ WITH ( copy_data [=
> > true|false] ) ]
> > 2. ALTER SUBSCRIPTION name REFRESH PUBLICATION SEQUENCES
> >
> > ~~~
> >
> > I proposed previously that instead of having 2 commands for refreshing
> > subscriptions we should have a single refresh command:
> >
> > ALTER SUBSCRIPTION name REFRESH PUBLICATION [TABLES|SEQUENCES] [ WITH
> > ( copy_data [= true|false] ) ]
> >
> > Why?
> >
> > - IMO it is less confusing than having 2 commands that both refresh
> > sequences in slightly different ways
> >
> > - It is more flexible because apart from refreshing everything, a user
> > can choose to refresh only tables or only sequences if desired; IMO
> > more flexibility is always good.
> >
> > - There is no loss of functionality from the current implementation
> > AFAICT. You can still say "ALTER SUBSCRIPTION sub REFRESH PUBLICATION
> > SEQUENCES" exactly the same as the patchset allows.
> >
> > ~~~
> >
> > So, to continue this proposal, let the meaning of 'copy_data' for
> > SEQUENCES be as follows:
> >
> > - when copy_data == false: it means don't copy data (i.e. don't
> > synchronize anything). Add/remove sequences from pg_subscriber_rel as
> > needed.
> >
> > - when copy_data == true: it means to copy data (i.e. synchronize) for
> > all sequences. Add/remove sequences from pg_subscriber_rel as needed)
> >
>
> I find overloading the copy_data option more confusing than adding a
> new variant for REFRESH. To make it clear, we can even think of
> extending the command as ALTER SUBSCRIPTION name REFRESH PUBLICATION
> ALL SEQUENCES or something like that.  I don't know where there is a
> need or not but one can imagine extending it as ALTER SUBSCRIPTION
> name REFRESH PUBLICATION SEQUENCES [<seq_name_1>, <seq_name_2>, ..].
> This will allow to selectively refresh the sequences.
>

But, I haven't invented a new overloading for "copy_data" option
(meaning "synchronize") for sequences. The current patchset already
interprets copy_data exactly this way.

For example, below are patch 0003 results:

ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION WITH (copy_data=false)
- this will add/remove new sequences in pg_subscription_rel, but it
will *not* synchronize the new sequence

ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION WITH (copy_data=true)
- this will add/remove new sequences in pg_subscription_rel, and it
*will* synchronize the new sequence

~

I only proposed that copy_data should apply to *all* sequences, not
just new ones.

======
Kind Regards.
Peter Smith.
Fujitsu Australia.



Re: Logical Replication of sequences

From
shveta malik
Date:
On Wed, Aug 7, 2024 at 1:45 PM vignesh C <vignesh21@gmail.com> wrote:
>
>
> The remaining comments have been addressed, and the changes are
> included in the attached v20240807 version patch.

Thanks for addressing the comment. Please find few comments for v20240807 :

patch002:
1)
create_publication.sgml:

--I think it will be good to add another example for both tables and sequences:
CREATE PUBLICATION all_sequences FOR ALL TABLES, SEQUENCES;
I was trying FOR ALL TABLES, FOR ALL SEQUENCES; but I think it is not
the correct way, so good to have the correct way mentioned in one
example.

patch003:
2)

 * The page_lsn allows the user to determine if the sequence has been updated
 * since the last synchronization with the subscriber. This is done by
 * comparing the current page_lsn with the value stored in pg_subscription_rel
 * from the last synchronization.
 */
Datum
pg_sequence_state(PG_FUNCTION_ARGS)

--This information is still incomplete. Maybe we should mention the
other attribute name as well which helps to determine this.

3)
Shall process_syncing_sequences_for_apply() be moved to sequencesync.c

4)
Would it be better to give a single warning for all unequal sequences
(comma separated list of sequenec names?)

postgres=# create subscription sub1 connection '....' publication pub1;
WARNING:  Sequence parameter in remote and local is not same for "public.myseq2"
HINT:  Alter/Re-create the sequence using the same parameter as in remote.
WARNING:  Sequence parameter in remote and local is not same for "public.myseq0"
HINT:  Alter/Re-create the sequence using the same parameter as in remote.
WARNING:  Sequence parameter in remote and local is not same for "public.myseq4"
HINT:  Alter/Re-create the sequence using the same parameter as in remote.


5)
IIUC, sequencesync_failure_time is changed by multiple processes.
Seq-sync worker sets it before exiting on failure, while apply worker
resets it. Also, the applied worker reads it at a few places. Shall it
be accessed using LogicalRepWorkerLock?

6)
process_syncing_sequences_for_apply():

--I feel MyLogicalRepWorker->sequencesync_failure_time should be reset
to 0 after we are sure that logicalrep_worker_launch() has launched
the worker without any error. But not sure what could be the clean way
to do it? If we move it after logicalrep_worker_launch() call, there
are chances that seq-sync worker has started and failed already and
has set this failure time which will then be mistakenly reset by apply
worker. Also moving it inside logicalrep_worker_launch() does not seem
a good way.

7)
sequencesync.c
PostgreSQL logical replication: initial sequence synchronization

--Since it is called by REFRESH also. So shall we remove 'initial'?

8)
/*
* Process any tables that are being synchronized in parallel and
* any newly added relations.
*/
process_syncing_relations(last_received);

--I did not understand the comment very well. Why are we using 2
separate words 'tables' and 'relations'? I feel we should have
mentioned sequences too in the comment.


9)
logical-replication.sgml: Sequence data is not replicated.

--I feel we should rephrase this line now to indicate that it could be
replicated by the new options.

thanks
Shveta



Re: Logical Replication of sequences

From
Amit Kapila
Date:
On Thu, Aug 8, 2024 at 11:09 AM Peter Smith <smithpb2250@gmail.com> wrote:
>
> But, I haven't invented a new overloading for "copy_data" option
> (meaning "synchronize") for sequences. The current patchset already
> interprets copy_data exactly this way.
>
> For example, below are patch 0003 results:
>
> ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION WITH (copy_data=false)
> - this will add/remove new sequences in pg_subscription_rel, but it
> will *not* synchronize the new sequence
>
> ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION WITH (copy_data=true)
> - this will add/remove new sequences in pg_subscription_rel, and it
> *will* synchronize the new sequence
>
> ~
>
> I only proposed that copy_data should apply to *all* sequences, not
> just new ones.
>

I don't like this difference because for tables, it would *not*
consider syncing already the existing tables whereas for sequences it
would consider syncing existing ones. We previously discussed adding a
new option like copy_all_sequences instead of adding a new variant of
command but that has its own set of problems, so we agreed to proceed
with a new variant. See [1] ( ...Good point. And I understood that the
REFRESH PUBLICATION SEQUENCES command would be helpful when users want
to synchronize sequences between two nodes before upgrading.).

Having said that, if others also prefer to use copy_data for this
purpose with a different meaning of this option w.r.t tables and
sequences then we can still consider it.

[1] - https://www.postgresql.org/message-id/CAD21AoAAszSeHNRha4HND8b9XyzNrx6jbA7t3Mbe%2BfH4hNRj9A%40mail.gmail.com

--
With Regards,
Amit Kapila.



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 8 Aug 2024 at 08:30, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh, Here are my v20240807-0003 review comments.
>
> 2a.
> The paragraph starts by saying "Sequence data is not replicated.". It
> seems wrong now. Doesn't that need rewording or removing?

Changed it to incremental sequence changes.

> ~
>
> 2b.
> Should the info "If, however, some kind of switchover or failover..."
> be mentioned in the "Logical Replication Failover" section [2],
> instead of here?

I think mentioning this here is appropriate. The other section focuses
more on how logical replication can proceed with a new primary. Once
the logical replication setup is complete, sequences can be refreshed
at any time.

Rest of the comments  are fixed,  the attached v20240808 version patch
has the changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Wed, 7 Aug 2024 at 10:27, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Mon, Aug 5, 2024 at 10:26 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Thu, 1 Aug 2024 at 04:25, Peter Smith <smithpb2250@gmail.com> wrote:
> > >
> > > Hi Vignesh,
> > >
> > > I noticed that when replicating sequences (using the latest patches
> > > 0730_2*)  the subscriber-side checks the *existence* of the sequence,
> > > but apparently it is not checking other sequence attributes.
> > >
> > > For example, consider:
> > >
> > > Publisher: "CREATE SEQUENCE s1 START 1 INCREMENT 2;" should be a
> > > sequence of only odd numbers.
> > > Subscriber: "CREATE SEQUENCE s1 START 2 INCREMENT 2;" should be a
> > > sequence of only even numbers.
> > >
> > > Because the names match, currently the patch allows replication of the
> > > s1 sequence. I think that might lead to unexpected results on the
> > > subscriber. IMO it might be safer to report ERROR unless the sequences
> > > match properly (i.e. not just a name check).
> > >
> > > Below is a demonstration the problem:
> > >
> > > ==========
> > > Publisher:
> > > ==========
> > >
> > > (publisher sequence is odd numbers)
> > >
> > > test_pub=# create sequence s1 start 1 increment 2;
> > > CREATE SEQUENCE
> > > test_pub=# select * from nextval('s1');
> > >  nextval
> > > ---------
> > >        1
> > > (1 row)
> > >
> > > test_pub=# select * from nextval('s1');
> > >  nextval
> > > ---------
> > >        3
> > > (1 row)
> > >
> > > test_pub=# select * from nextval('s1');
> > >  nextval
> > > ---------
> > >        5
> > > (1 row)
> > >
> > > test_pub=# CREATE PUBLICATION pub1 FOR ALL SEQUENCES;
> > > CREATE PUBLICATION
> > > test_pub=#
> > >
> > > ==========
> > > Subscriber:
> > > ==========
> > >
> > > (subscriber sequence is even numbers)
> > >
> > > test_sub=# create sequence s1 start 2 increment 2;
> > > CREATE SEQUENCE
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >        2
> > > (1 row)
> > >
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >        4
> > > (1 row)
> > >
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >        6
> > > (1 row)
> > >
> > > test_sub=# CREATE SUBSCRIPTION sub1 CONNECTION 'dbname=test_pub'
> > > PUBLICATION pub1;
> > > 2024-08-01 08:43:04.198 AEST [24325] WARNING:  subscriptions created
> > > by regression test cases should have names starting with "regress_"
> > > WARNING:  subscriptions created by regression test cases should have
> > > names starting with "regress_"
> > > NOTICE:  created replication slot "sub1" on publisher
> > > CREATE SUBSCRIPTION
> > > test_sub=# 2024-08-01 08:43:04.294 AEST [26240] LOG:  logical
> > > replication apply worker for subscription "sub1" has started
> > > 2024-08-01 08:43:04.309 AEST [26244] LOG:  logical replication
> > > sequence synchronization worker for subscription "sub1" has started
> > > 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> > > synchronization for subscription "sub1", sequence "s1" has finished
> > > 2024-08-01 08:43:04.323 AEST [26244] LOG:  logical replication
> > > sequence synchronization worker for subscription "sub1" has finished
> > >
> > > (after the CREATE SUBSCRIPTION we are getting replicated odd values
> > > from the publisher, even though the subscriber side sequence was
> > > supposed to be even numbers)
> > >
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >        7
> > > (1 row)
> > >
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >        9
> > > (1 row)
> > >
> > > test_sub=# SELECT * FROM nextval('s1');
> > >  nextval
> > > ---------
> > >       11
> > > (1 row)
> > >
> > > (Looking at the description you would expect odd values for this
> > > sequence to be impossible)
>
> I see that for such even sequences, user can still do 'setval'  to a
> odd number and then nextval will keep on returning odd value.
>
> postgres=# SELECT nextval('s1');
>        6
>
> postgres=SELECT setval('s1', 43);
>      43
>
> postgres=# SELECT nextval('s1');
>      45
>
> > > test_sub=# \dS+ s1
> > >                              Sequence "public.s1"
> > >   Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
> > > --------+-------+---------+---------------------+-----------+---------+-------
> > >  bigint |     2 |       1 | 9223372036854775807 |         2 | no      |     1
> >
> > Even if we check the sequence definition during the CREATE
> > SUBSCRIPTION/ALTER SUBSCRIPTION ... REFRESH PUBLICATION or ALTER
> > SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES commands, there's still
> > a chance that the sequence definition might change after the command
> > has been executed. Currently, there's no mechanism to lock a sequence,
> > and we also permit replication of table data even if the table
> > structures differ, such as mismatched data types like int and
> > smallint. I have modified it to log a warning to inform users that the
> > sequence options on the publisher and subscriber are not the same and
> > advise them to ensure that the sequence definitions are consistent
> > between both.
> > The v20240805 version patch attached at [1] has the changes for the same.
> > [1] - https://www.postgresql.org/message-id/CALDaNm1Y_ot-jFRfmtwDuwmFrgSSYHjVuy28RspSopTtwzXy8w%40mail.gmail.com
>
> The behavior for applying is no different from setval. Having said
> that, I agree that sequence definition can change even after the
> subscription creation, but earlier we were not syncing sequences and
> thus the value of a particular sequence was going to remain in the
> range/pattern defined by its attributes unless user sets it manually
> using setval. But now, it is being changed in the background without
> user's knowledge.
> The table case is different. In case of table replication, if we have
> CHECK constraint or say primary-key etc, then the value which violates
> these constraints will never be  inserted to a table even during
> replication on sub. For sequences, parameters (MIN,MAX, START,
> INCREMENT) can be considered similar to check-constraints, the only
> difference is during apply, we are still overriding these and copying
> pub's value. May be such inconsistencies detection can be targeted
> later in next project. But for the time being, it will be good to add
> a 'caveat' section in doc mentioning all such cases. The scope of this
> project should be clearly documented.

I have added a Caveats section and mentioned it.
The changes for the same are available at v20240808 version attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm1QQK_Pgx35LrJGuRxBzzYSO8rm1YGJF4w8hYc3Gm%2B5NQ%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, I reviewed the latest v20240808-0003 patch.

Attached are my minor change suggestions.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
shveta malik
Date:
On Wed, Aug 7, 2024 at 2:00 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Wed, 7 Aug 2024 at 08:09, Amit Kapila <amit.kapila16@gmail.com> wrote:
> >
> > On Tue, Aug 6, 2024 at 5:13 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Mon, 5 Aug 2024 at 18:05, shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
> > > > >
> > > > > On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> > > > > >
> > > > > > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > > >
> > > > > > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > > > >>
> > > > > > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > > > >> [...]
> > > > > > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > > > > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > > > > > >> (Log Sequence Number) is stored, facilitating determination of
> > > > > > > >> sequence changes occurring before or after the returned sequence
> > > > > > > >> state.
> > > > > > > >
> > > > > > > >
> > > > > > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > > > > > something.
> > > > > > >
> > > > > > > We'll require the lsn because the sequence LSN informs the user that
> > > > > > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > > > > > we are not supporting incremental sync, the user will be able to
> > > > > > > identify if he should run refresh sequences or not by checking the lsn
> > > > > > > of the pg_subscription_seq and the lsn of the sequence(using
> > > > > > > pg_sequence_state added) in the publisher.
> > > > > >
> > > > > > How the user will know from seq's lsn that he needs to run refresh.
> > > > > > lsn indicates page_lsn and thus the sequence might advance on pub
> > > > > > without changing lsn and thus lsn may look the same on subscriber even
> > > > > > though a sequence-refresh is needed. Am I missing something here?
> > > > >
> > > > > When a sequence is synchronized to the subscriber, the page LSN of the
> > > > > sequence from the publisher is also retrieved and stored in
> > > > > pg_subscriber_rel as shown below:
> > > > > --- Publisher page lsn
> > > > > publisher=# select pg_sequence_state('seq1');
> > > > >  pg_sequence_state
> > > > > --------------------
> > > > >  (0/1510E38,65,1,t)
> > > > > (1 row)
> > > > >
> > > > > --- Subscriber stores the publisher's page lsn for the sequence
> > > > > subscriber=# select * from pg_subscription_rel where srrelid = 16384;
> > > > >  srsubid | srrelid | srsubstate | srsublsn
> > > > > ---------+---------+------------+-----------
> > > > >    16389 |   16384 | r          | 0/1510E38
> > > > > (1 row)
> > > > >
> > > > > If changes are made to the sequence, such as performing many nextvals,
> > > > > the page LSN will be updated. Currently the sequence values are
> > > > > prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> > > > > the prefetched values, once the prefetched values are consumed the lsn
> > > > > will get updated.
> > > > > For example:
> > > > > --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> > > > > publisher=# select pg_sequence_state('seq1');
> > > > >   pg_sequence_state
> > > > > ----------------------
> > > > >  (0/1558CA8,143,22,t)
> > > > > (1 row)
> > > > >
> > > > > The user can then compare this updated value with the sequence's LSN
> > > > > in pg_subscription_rel to determine when to re-synchronize the
> > > > > sequence.
> > > >
> > > > Thanks for the details. But I was referring to the case where we are
> > > > in between pre-fetched values on publisher (say at 25th value), while
> > > > on subscriber we are slightly behind (say at 15th value), but page-lsn
> > > > will be the same on both. Since the subscriber is behind, a
> > > > sequence-refresh is needed on sub, but by looking at lsn (which is
> > > > same), one can not say that for sure.  Let me know if I have
> > > > misunderstood it.
> > >
> > > Yes, at present, if the value is within the pre-fetched range, we
> > > cannot distinguish it solely using the page_lsn.
> > >
> >
> > This makes sense to me.
> >
> > >
> > > However, the
> > > pg_sequence_state function also provides last_value and log_cnt, which
> > > can be used to handle these specific cases.
> > >
> >
> > BTW, can we document all these steps for users to know when to refresh
> > the sequences, if not already documented?
>
> This has been documented in the v20240807 version attached at [1].
> [1] - https://www.postgresql.org/message-id/CALDaNm01Z6Oo9osGMFTOoyTR1kVoyh1rEvZ%2B6uJn-ZymV%3D0dbQ%40mail.gmail.com
>

Vignesh, I looked at the patch dated 240808, but I could not  find
these steps. Are you referring to the section ' Examples:
Synchronizing Sequences Between Publisher and Subscriber' in doc
patch004? If not, please point me to the concerned section.

thanks
Shveta



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, here are my review comments for the sequences docs patch
v20240808-0004.

======
doc/src/sgml/logical-replication.sgml

The new section content looked good.

Just some nitpicks including:
- renamed the section "Replicating Sequences"
- added missing mention about how to publish sequences
- rearranged the subscription commands into a more readable list
- some sect2 titles were very long; I shortened them.
- added <warning> markup for the sequence definition advice
- other minor rewording and typo fixes

~

1.
IMO the "Caveats" section can be removed.
- the advice to avoid changing the sequence definition is already
given earlier in the "Sequence Definition Mismatches" section
- the limitation of "incremental synchronization" is already stated in
the logical replication "Limitations" section
- (FYI, I removed it already in my nitpicks attachment)

======
doc/src/sgml/ref/alter_subscription.sgml

nitpick - I reversed the paragraphs to keep the references in a natural order.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

On Fri, Aug 9, 2024 at 1:52 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 8 Aug 2024 at 08:30, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > Hi Vignesh, Here are my v20240807-0003 review comments.
> >
> > 2a.
> > The paragraph starts by saying "Sequence data is not replicated.". It
> > seems wrong now. Doesn't that need rewording or removing?
>
> Changed it to incremental sequence changes.
>
> > ~
> >
> > 2b.
> > Should the info "If, however, some kind of switchover or failover..."
> > be mentioned in the "Logical Replication Failover" section [2],
> > instead of here?
>
> I think mentioning this here is appropriate. The other section focuses
> more on how logical replication can proceed with a new primary. Once
> the logical replication setup is complete, sequences can be refreshed
> at any time.
>
> Rest of the comments  are fixed,  the attached v20240808 version patch
> has the changes for the same.
>
> Regards,
> Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 8 Aug 2024 at 12:21, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Wed, Aug 7, 2024 at 1:45 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> >
> > The remaining comments have been addressed, and the changes are
> > included in the attached v20240807 version patch.
>
> Thanks for addressing the comment. Please find few comments for v20240807 :
>
> patch003:
> 2)
>
>  * The page_lsn allows the user to determine if the sequence has been updated
>  * since the last synchronization with the subscriber. This is done by
>  * comparing the current page_lsn with the value stored in pg_subscription_rel
>  * from the last synchronization.
>  */
> Datum
> pg_sequence_state(PG_FUNCTION_ARGS)
>
> --This information is still incomplete. Maybe we should mention the
> other attribute name as well which helps to determine this.

I have removed this comment now as suggesting that users use
pg_sequence_state and sequence when page_lsn seems complex, the same
can be achieved by comparing the sequence values from a single
statement instead of a couple of statements. Peter had felt this would
be easier based on comment 3c at [1].

> 5)
> IIUC, sequencesync_failure_time is changed by multiple processes.
> Seq-sync worker sets it before exiting on failure, while apply worker
> resets it. Also, the applied worker reads it at a few places. Shall it
> be accessed using LogicalRepWorkerLock?

If sequenceApply worker is already running, apply worker will not
access sequencesync_failure_time. Only if sequence sync worker is not
running apply worker will access sequencesync_failure_time in the
below code. I feel no need to use LogicalRepWorkerLock in this case.

...
syncworker = logicalrep_worker_find(MyLogicalRepWorker->subid,
InvalidOid, WORKERTYPE_SEQUENCESYNC,
true);
if (syncworker)
{
/* Now safe to release the LWLock */
LWLockRelease(LogicalRepWorkerLock);
break;
}

/*
* Count running sync workers for this subscription, while we have the
* lock.
*/
nsyncworkers = logicalrep_sync_worker_count(MyLogicalRepWorker->subid);

/* Now safe to release the LWLock */
LWLockRelease(LogicalRepWorkerLock);

/*
* If there are free sync worker slot(s), start a new sequence sync
* worker, and break from the loop.
*/
if (nsyncworkers < max_sync_workers_per_subscription)
{
TimestampTz now = GetCurrentTimestamp();

if (!MyLogicalRepWorker->sequencesync_failure_time ||
TimestampDifferenceExceeds(MyLogicalRepWorker->sequencesync_failure_time,
   now, wal_retrieve_retry_interval))
{
MyLogicalRepWorker->sequencesync_failure_time = 0;

logicalrep_worker_launch(WORKERTYPE_SEQUENCESYNC,
MyLogicalRepWorker->dbid,
MySubscription->oid,
MySubscription->name,
MyLogicalRepWorker->userid,
InvalidOid,
DSM_HANDLE_INVALID);
break;
}
}
...

> 6)
> process_syncing_sequences_for_apply():
>
> --I feel MyLogicalRepWorker->sequencesync_failure_time should be reset
> to 0 after we are sure that logicalrep_worker_launch() has launched
> the worker without any error. But not sure what could be the clean way
> to do it? If we move it after logicalrep_worker_launch() call, there
> are chances that seq-sync worker has started and failed already and
> has set this failure time which will then be mistakenly reset by apply
> worker. Also moving it inside logicalrep_worker_launch() does not seem
> a good way.

I felt we can keep it in the existing way to keep it consistent with
table sync worker restart like in process_syncing_tables_for_apply.

The rest of the comments are fixed.  The rest of the comments are
fixed in the v20240809 version patch attached.

[1] - https://www.postgresql.org/message-id/CAHut%2BPvaq%3D0xsDWdVQ-kdjRa8Az%2BvgiMFTvT2E2nR3N-47TO8A%40mail.gmail.com

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 9 Aug 2024 at 12:13, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Wed, Aug 7, 2024 at 2:00 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 7 Aug 2024 at 08:09, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > >
> > > On Tue, Aug 6, 2024 at 5:13 PM vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > On Mon, 5 Aug 2024 at 18:05, shveta malik <shveta.malik@gmail.com> wrote:
> > > > >
> > > > > On Mon, Aug 5, 2024 at 11:04 AM vignesh C <vignesh21@gmail.com> wrote:
> > > > > >
> > > > > > On Wed, 31 Jul 2024 at 14:39, shveta malik <shveta.malik@gmail.com> wrote:
> > > > > > >
> > > > > > > On Mon, Jun 10, 2024 at 5:00 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > > > >
> > > > > > > > On Mon, 10 Jun 2024 at 12:24, Amul Sul <sulamul@gmail.com> wrote:
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > On Sat, Jun 8, 2024 at 6:43 PM vignesh C <vignesh21@gmail.com> wrote:
> > > > > > > > >>
> > > > > > > > >> On Wed, 5 Jun 2024 at 14:11, Amit Kapila <amit.kapila16@gmail.com> wrote:
> > > > > > > > >> [...]
> > > > > > > > >> A new catalog table, pg_subscription_seq, has been introduced for
> > > > > > > > >> mapping subscriptions to sequences. Additionally, the sequence LSN
> > > > > > > > >> (Log Sequence Number) is stored, facilitating determination of
> > > > > > > > >> sequence changes occurring before or after the returned sequence
> > > > > > > > >> state.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Can't it be done using pg_depend? It seems a bit excessive unless I'm missing
> > > > > > > > > something.
> > > > > > > >
> > > > > > > > We'll require the lsn because the sequence LSN informs the user that
> > > > > > > > it has been synchronized up to the LSN in pg_subscription_seq. Since
> > > > > > > > we are not supporting incremental sync, the user will be able to
> > > > > > > > identify if he should run refresh sequences or not by checking the lsn
> > > > > > > > of the pg_subscription_seq and the lsn of the sequence(using
> > > > > > > > pg_sequence_state added) in the publisher.
> > > > > > >
> > > > > > > How the user will know from seq's lsn that he needs to run refresh.
> > > > > > > lsn indicates page_lsn and thus the sequence might advance on pub
> > > > > > > without changing lsn and thus lsn may look the same on subscriber even
> > > > > > > though a sequence-refresh is needed. Am I missing something here?
> > > > > >
> > > > > > When a sequence is synchronized to the subscriber, the page LSN of the
> > > > > > sequence from the publisher is also retrieved and stored in
> > > > > > pg_subscriber_rel as shown below:
> > > > > > --- Publisher page lsn
> > > > > > publisher=# select pg_sequence_state('seq1');
> > > > > >  pg_sequence_state
> > > > > > --------------------
> > > > > >  (0/1510E38,65,1,t)
> > > > > > (1 row)
> > > > > >
> > > > > > --- Subscriber stores the publisher's page lsn for the sequence
> > > > > > subscriber=# select * from pg_subscription_rel where srrelid = 16384;
> > > > > >  srsubid | srrelid | srsubstate | srsublsn
> > > > > > ---------+---------+------------+-----------
> > > > > >    16389 |   16384 | r          | 0/1510E38
> > > > > > (1 row)
> > > > > >
> > > > > > If changes are made to the sequence, such as performing many nextvals,
> > > > > > the page LSN will be updated. Currently the sequence values are
> > > > > > prefetched for SEQ_LOG_VALS 32, so the lsn will not get updated for
> > > > > > the prefetched values, once the prefetched values are consumed the lsn
> > > > > > will get updated.
> > > > > > For example:
> > > > > > --- Updated LSN on the publisher (old lsn - 0/1510E38, new lsn - 0/1558CA8)
> > > > > > publisher=# select pg_sequence_state('seq1');
> > > > > >   pg_sequence_state
> > > > > > ----------------------
> > > > > >  (0/1558CA8,143,22,t)
> > > > > > (1 row)
> > > > > >
> > > > > > The user can then compare this updated value with the sequence's LSN
> > > > > > in pg_subscription_rel to determine when to re-synchronize the
> > > > > > sequence.
> > > > >
> > > > > Thanks for the details. But I was referring to the case where we are
> > > > > in between pre-fetched values on publisher (say at 25th value), while
> > > > > on subscriber we are slightly behind (say at 15th value), but page-lsn
> > > > > will be the same on both. Since the subscriber is behind, a
> > > > > sequence-refresh is needed on sub, but by looking at lsn (which is
> > > > > same), one can not say that for sure.  Let me know if I have
> > > > > misunderstood it.
> > > >
> > > > Yes, at present, if the value is within the pre-fetched range, we
> > > > cannot distinguish it solely using the page_lsn.
> > > >
> > >
> > > This makes sense to me.
> > >
> > > >
> > > > However, the
> > > > pg_sequence_state function also provides last_value and log_cnt, which
> > > > can be used to handle these specific cases.
> > > >
> > >
> > > BTW, can we document all these steps for users to know when to refresh
> > > the sequences, if not already documented?
> >
> > This has been documented in the v20240807 version attached at [1].
> > [1] -
https://www.postgresql.org/message-id/CALDaNm01Z6Oo9osGMFTOoyTR1kVoyh1rEvZ%2B6uJn-ZymV%3D0dbQ%40mail.gmail.com
> >
>
> Vignesh, I looked at the patch dated 240808, but I could not  find
> these steps. Are you referring to the section ' Examples:
> Synchronizing Sequences Between Publisher and Subscriber' in doc
> patch004? If not, please point me to the concerned section.

I'm  referring to the "Refreshing Stale Sequences" part in the
v20240809 version patch attached at [1] which only mentions directly
comparing the sequence values.. I have removed the reference to
pg_sequence_state now as suggesting that users use pg_sequence_state
and sequence when page_lsn seems complex, the same can be achieved by
comparing the sequence values from a single statement instead of a
couple of statements. Peter had felt this would be easier based on
comment 3c at [1].

[1] - https://www.postgresql.org/message-id/CALDaNm0LJCtGoBCO6DFY-RDjR8vxapW3W1f7%3D-LSQx%3DXYjqU%3Dw%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 9 Aug 2024 at 05:51, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh, I reviewed the latest v20240808-0003 patch.
>
> Attached are my minor change suggestions.

Thanks, these changes are merged in the v20240809 version posted at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm0LJCtGoBCO6DFY-RDjR8vxapW3W1f7%3D-LSQx%3DXYjqU%3Dw%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 9 Aug 2024 at 12:40, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh, here are my review comments for the sequences docs patch
> v20240808-0004.
>
> ======
> doc/src/sgml/logical-replication.sgml
>
> The new section content looked good.
>
> Just some nitpicks including:
> - renamed the section "Replicating Sequences"
> - added missing mention about how to publish sequences
> - rearranged the subscription commands into a more readable list
> - some sect2 titles were very long; I shortened them.
> - added <warning> markup for the sequence definition advice
> - other minor rewording and typo fixes

I have retained the caveats section for now, I will think more and
remove it if required in the next version.

The rest of the comments are fixed in the v20240809 version patch
attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm0LJCtGoBCO6DFY-RDjR8vxapW3W1f7%3D-LSQx%3DXYjqU%3Dw%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

v20240809-0001. No comments.
v20240809-0002. See below.
v20240809-0003. See below.
v20240809-0004. No comments.

//////////

Here are my review comments for patch v20240809-0002.

nit - Tweak wording in new docs example, because a publication only
publishes the sequences; it doesn't "synchronize" anything.

//////////

Here are my review comments for patch v20240809-0003.

fetch_sequence_list:
nit - move comment
nit - minor rewording for parameter WARNING message

======
.../replication/logical/sequencesync.c
src/backend/replication/logical/tablesync.c

1.
Currently the declaration 'sequence_states_not_ready' list seems
backwards. IMO it makes more sense for the declaration to be in
sequencesync.c, and the extern in the tablesync.c. (please also see
review comment #3 below which might affect this too).

~~~

2.
 static bool
-FetchTableStates(bool *started_tx)
+FetchTableStates(void)
 {
- static bool has_subrels = false;
-
- *started_tx = false;
+ static bool has_subtables = false;
+ bool started_tx = false;

Maybe give the explanation why 'has_subtables' is declared static here.

~~~

3.
I am not sure that it was an improvement to move the
process_syncing_sequences_for_apply() function into the
sequencesync.c. Calling the sequence code from the tablesync code
still looks strange. OTOH, I see why you don't want to leave it in
tablesync.c.

Perhaps it would be better to refactor/move all following functions
back to the (apply) worker.c instead:
- process_syncing_relations
- process_syncing_sequences_for_apply(void)
- process_syncing_tables_for_apply(void)

Actually, now that there are 2 kinds of 'sync' workers, maybe you
should introduce a new module (e.g. 'commonsync.c' or
'syncworker.c...), where you can put functions such as
process_syncing_relations() plus any other code common to both
tablesync and sequencesync. That might make more sense then having one
call to the other.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

I noticed it is not currently possible (there is no syntax way to do
it) to ALTER an existing publication so that it will publish
SEQUENCES.

Isn't that a limitation? Why?

For example,. Why should users be prevented from changing a FOR ALL
TABLES publication into a FOR ALL TABLES, SEQUENCES one?

Similarly, there are other combinations not possible
DROP ALL SEQUENCES from a publication that is FOR ALL TABLES, SEQUENCES
DROP ALL TABLES from a publication that is FOR ALL TABLES, SEQUENCES
ADD ALL TABLES to a publication that is FOR ALL SEQUENCES
...

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

I found that when 2 subscriptions are both subscribing to a
publication publishing sequences, an ERROR occurs on refresh.

======

Publisher:
----------

test_pub=# create publication pub1 for all sequences;

Subscriber:
-----------

test_sub=# create subscription sub1 connection 'dbname=test_pub'
publication pub1;

test_sub=# create subscription sub2 connection 'dbname=test_pub'
publication pub1;

test_sub=# alter subscription sub1 refresh publication sequences;
2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
subscription "sub1" set to INIT state
2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
sub1 refresh publication sequences;
2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
subscription "sub1" set to INIT state
2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
sub1 refresh publication sequences;
2024-08-12 15:04:04.947 AEST [7306] ERROR:  tuple already updated by self
2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
sub1 refresh publication sequences;
ERROR:  tuple already updated by self

test_sub=# alter subscription sub2 refresh publication sequences;
2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
subscription "sub2" set to INIT state
2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
sub2 refresh publication sequences;
2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
subscription "sub2" set to INIT state
2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
sub2 refresh publication sequences;
2024-08-12 15:04:30.427 AEST [7306] ERROR:  tuple already updated by self
2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
sub2 refresh publication sequences;
ERROR:  tuple already updated by self

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 12 Aug 2024 at 08:50, Peter Smith <smithpb2250@gmail.com> wrote:
>
> ~~~
>
> 3.
> I am not sure that it was an improvement to move the
> process_syncing_sequences_for_apply() function into the
> sequencesync.c. Calling the sequence code from the tablesync code
> still looks strange. OTOH, I see why you don't want to leave it in
> tablesync.c.
>
> Perhaps it would be better to refactor/move all following functions
> back to the (apply) worker.c instead:
> - process_syncing_relations
> - process_syncing_sequences_for_apply(void)
> - process_syncing_tables_for_apply(void)
>
> Actually, now that there are 2 kinds of 'sync' workers, maybe you
> should introduce a new module (e.g. 'commonsync.c' or
> 'syncworker.c...), where you can put functions such as
> process_syncing_relations() plus any other code common to both
> tablesync and sequencesync. That might make more sense then having one
> call to the other.

I created syncutils.c to consolidate code that supports worker
synchronization, table synchronization, and sequence synchronization.
While it may not align exactly with your suggestion, I included
functions like finish_sync_worker, invalidate_syncing_relation_states,
FetchRelationStates, and process_syncing_relations in this new file. I
believe this organization will make the code easier to review.

The rest of the comments are also fixed in the attached v20240812
version patch attached.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 12 Aug 2024 at 10:40, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> I found that when 2 subscriptions are both subscribing to a
> publication publishing sequences, an ERROR occurs on refresh.
>
> ======
>
> Publisher:
> ----------
>
> test_pub=# create publication pub1 for all sequences;
>
> Subscriber:
> -----------
>
> test_sub=# create subscription sub1 connection 'dbname=test_pub'
> publication pub1;
>
> test_sub=# create subscription sub2 connection 'dbname=test_pub'
> publication pub1;
>
> test_sub=# alter subscription sub1 refresh publication sequences;
> 2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
> subscription "sub1" set to INIT state
> 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> sub1 refresh publication sequences;
> 2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
> subscription "sub1" set to INIT state
> 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> sub1 refresh publication sequences;
> 2024-08-12 15:04:04.947 AEST [7306] ERROR:  tuple already updated by self
> 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> sub1 refresh publication sequences;
> ERROR:  tuple already updated by self
>
> test_sub=# alter subscription sub2 refresh publication sequences;
> 2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
> subscription "sub2" set to INIT state
> 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> sub2 refresh publication sequences;
> 2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
> subscription "sub2" set to INIT state
> 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> sub2 refresh publication sequences;
> 2024-08-12 15:04:30.427 AEST [7306] ERROR:  tuple already updated by self
> 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> sub2 refresh publication sequences;
> ERROR:  tuple already updated by self

This issue is fixed in the v20240812 version attached at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm3hS58W0RTbgsMTk-YvXwt956uabA%3DkYfLGUs3uRNC2Qg%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Mon, 12 Aug 2024 at 09:59, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> I noticed it is not currently possible (there is no syntax way to do
> it) to ALTER an existing publication so that it will publish
> SEQUENCES.
>
> Isn't that a limitation? Why?
>
> For example,. Why should users be prevented from changing a FOR ALL
> TABLES publication into a FOR ALL TABLES, SEQUENCES one?
>
> Similarly, there are other combinations not possible
> DROP ALL SEQUENCES from a publication that is FOR ALL TABLES, SEQUENCES
> DROP ALL TABLES from a publication that is FOR ALL TABLES, SEQUENCES
> ADD ALL TABLES to a publication that is FOR ALL SEQUENCES

Yes, this should be addressed. However, I'll defer it until the
current set of patches is finalized and all comments have been
resolved.

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, Here are my review comments for latest v20240812* patchset:

patch v20240812-0001. No comments.
patch v20240812-0002. Fixed docs.LGTM
patch v20240812-0003. This is new refactoring. See below.
patch v20240812-0004. (was 0003). See below.
patch v20240812-0005. (was 0004). No comments.

//////

patch v20240812-0003.

3.1. GENERAL

Hmm. I am guessing this was provided as a separate patch to aid review
by showing that existing functions are moved? OTOH you can't really
judge this patch properly without already knowing details of what will
come next in the sequencesync. i.e. As a *standalone* patch without
the sequencesync.c the refactoring doesn't make much sense.

Maybe it is OK later to combine patches 0003 and 0004. Alternatively,
keep this patch separated but give greater emphasis in the comment
header to say this patch only exists separately in order to help the
review.

======
Commit message

3.2.
Reorganized tablesync code to generate a syncutils file which will
help in sequence synchronization worker code.

~

"generate" ??

======
src/backend/replication/logical/syncutils.c

3.3. "common code" ??

FYI - There are multiple code comments mentioning "common code..."
which, in the absence of the sequencesync worker (which comes in the
next patch), have nothing "common" about them at all. Fixing them and
then fixing them again in the next patch might cause unnecessary code
churn, but OTOH they aren't correct as-is either. I have left them
alone for now.

~

3.4. function names

With the re-shuffling that this patch does, and changing several from
static to not-static, should the function names remain as they are?
They look random to me.
- finish_sync_worker(void)
- invalidate_syncing_table_states(Datum arg, int cacheid, uint32 hashvalue)
- FetchTableStates(bool *started_tx)
- process_syncing_tables(XLogRecPtr current_lsn)

I think using a consistent naming convention would be better. e.g.
SyncFinishWorker
SyncInvalidateTableStates
SyncFetchTableStates
SyncProcessTables

~~~

nit - file header comment

======
src/backend/replication/logical/tablesync.c

3.5.
-static void
+void
 process_syncing_tables_for_sync(XLogRecPtr current_lsn)

-static void
+void
 process_syncing_tables_for_apply(XLogRecPtr current_lsn)

Since these functions are no longer static should those function names
be changed to use the CamelCase convention for non-static API?

//////////

patch v20240812-0004.

======
src/backend/replication/logical/syncutils.c

nit - file header comment (made same as patch 0003)

~

FetchRelationStates:
nit - IIUC sequence states are only INIT -> READY. So the comments in
this function dont need to specifically talk about sequence INIT
state.

======
src/backend/utils/misc/guc_tables.c

4.1.
  {"max_sync_workers_per_subscription",
  PGC_SIGHUP,
  REPLICATION_SUBSCRIBERS,
- gettext_noop("Maximum number of table synchronization workers per
subscription."),
+ gettext_noop("Maximum number of relation synchronization workers per
subscription."),
  NULL,
  },

I was wondering if "relation synchronization workers" is meaningful to
the user because that seems like new terminology.
Maybe it should say "... of table + sequence synchronization workers..."

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
On Mon, Aug 12, 2024 at 11:07 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Mon, 12 Aug 2024 at 10:40, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > Hi Vignesh,
> >
> > I found that when 2 subscriptions are both subscribing to a
> > publication publishing sequences, an ERROR occurs on refresh.
> >
> > ======
> >
> > Publisher:
> > ----------
> >
> > test_pub=# create publication pub1 for all sequences;
> >
> > Subscriber:
> > -----------
> >
> > test_sub=# create subscription sub1 connection 'dbname=test_pub'
> > publication pub1;
> >
> > test_sub=# create subscription sub2 connection 'dbname=test_pub'
> > publication pub1;
> >
> > test_sub=# alter subscription sub1 refresh publication sequences;
> > 2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
> > subscription "sub1" set to INIT state
> > 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> > sub1 refresh publication sequences;
> > 2024-08-12 15:04:04.947 AEST [7306] LOG:  sequence "public.seq1" of
> > subscription "sub1" set to INIT state
> > 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> > sub1 refresh publication sequences;
> > 2024-08-12 15:04:04.947 AEST [7306] ERROR:  tuple already updated by self
> > 2024-08-12 15:04:04.947 AEST [7306] STATEMENT:  alter subscription
> > sub1 refresh publication sequences;
> > ERROR:  tuple already updated by self
> >
> > test_sub=# alter subscription sub2 refresh publication sequences;
> > 2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
> > subscription "sub2" set to INIT state
> > 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> > sub2 refresh publication sequences;
> > 2024-08-12 15:04:30.427 AEST [7306] LOG:  sequence "public.seq1" of
> > subscription "sub2" set to INIT state
> > 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> > sub2 refresh publication sequences;
> > 2024-08-12 15:04:30.427 AEST [7306] ERROR:  tuple already updated by self
> > 2024-08-12 15:04:30.427 AEST [7306] STATEMENT:  alter subscription
> > sub2 refresh publication sequences;
> > ERROR:  tuple already updated by self
>
> This issue is fixed in the v20240812 version attached at [1].
> [1] - https://www.postgresql.org/message-id/CALDaNm3hS58W0RTbgsMTk-YvXwt956uabA%3DkYfLGUs3uRNC2Qg%40mail.gmail.com
>

Yes, I confirmed it is now fixed. Thanks!

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

I have been using the latest patchset, trying a few things using many
(1000) sequences.

Here are some observations, plus some suggestions for consideration.

~~~~~

OBSERVATION #1

When 1000s of sequences are refreshed using REFRESH PUBLICATION
SEQUENCES the logging is excessive. For example, since there is only
one sequencesync worker why does it need to broadcast that it is
"finished" separately for every single sequence. That is giving 1000s
of lines of logs which don't seem to be of much interest to a user.

...
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0918" has
finished
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0919" has
finished
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0920" has
finished
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0921" has
finished
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0922" has
finished
2024-08-13 16:17:04.151 AEST [5002] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0923" has
finished
...

Perhaps just LOG when each "batch" is completed, but the individual
sequence finished logs can just be DEBUG information?

~~~~~

OBSERVATION #2

When 1000s of sequences are refreshed (set to INIT) then there are
1000s of logs like below:

...
2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0698"
of subscription "sub3" set to INIT state
2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
sub3 refresh publication sequences;
2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0699"
of subscription "sub3" set to INIT state
2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
sub3 refresh publication sequences;
2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0700"
of subscription "sub3" set to INIT state
2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
sub3 refresh publication sequences;
2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0701"
of subscription "sub3" set to INIT state
2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
sub3 refresh publication sequences;
2024-08-13 16:13:57.874 AEST [10301] LOG:  sequence "public.seq_0702"
of subscription "sub3" set to INIT state
2024-08-13 16:13:57.874 AEST [10301] STATEMENT:  alter subscription
sub3 refresh publication sequences;
...

I felt that showing the STATEMENT for all of these is overkill. How
about changing that ereport LOG so it does not emit the statement 1000
times? Or, maybe you can implement it as a "dynamic" log that emits
the STATEMENT if there are only a few logs a few times but skips it
for the next 995 logs.

~~~~~

OBSERVATION #3

The WARNING about mismatched sequences currently looks like this:

2024-08-13 16:41:45.496 AEST [10301] WARNING:  Parameters differ for
remote and local sequences "public.seq_0999"
2024-08-13 16:41:45.496 AEST [10301] HINT:  Alter/Re-create the
sequence using the same parameter as in remote.

Although you could probably deduce it from nearby logs, I think it
might be more helpful to also identify the subscription name in this
WARNING message. Otherwise, if there are many publications the user
may have no idea where the mismatched "remote" is coming from.

~~~~

OBSERVATION #4

When 1000s of sequences are refreshed then there are 1000s of
associated logs. But (given there is only one sequencesync worker)
those logs are not always the order that I was expecting to see them.

e.g.
...
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0885" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0887" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0888" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0889" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0890" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0906" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0566" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0568" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0569" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0570" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0571" has
finished
2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
synchronization for subscription "sub3", sequence "seq_0582" has
finished
...

Is there a way to refresh sequences in a more natural (e.g.
alphabetical) order to make these logs more readable?

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 13 Aug 2024 at 09:19, Peter Smith <smithpb2250@gmail.com> wrote:
>
> 3.1. GENERAL
>
> Hmm. I am guessing this was provided as a separate patch to aid review
> by showing that existing functions are moved? OTOH you can't really
> judge this patch properly without already knowing details of what will
> come next in the sequencesync. i.e. As a *standalone* patch without
> the sequencesync.c the refactoring doesn't make much sense.
>
> Maybe it is OK later to combine patches 0003 and 0004. Alternatively,
> keep this patch separated but give greater emphasis in the comment
> header to say this patch only exists separately in order to help the
> review.

I have kept this patch only to show that this patch as such has no
code changes. If we move this to the next patch it will be difficult
for reviewers to know which is new code and which is old code. During
commit we can merge this with the next one. I felt it is better to add
it in the commit message instead of comment header so updated the
commit message.

> ======
> src/backend/replication/logical/syncutils.c
>
> 3.3. "common code" ??
>
> FYI - There are multiple code comments mentioning "common code..."
> which, in the absence of the sequencesync worker (which comes in the
> next patch), have nothing "common" about them at all. Fixing them and
> then fixing them again in the next patch might cause unnecessary code
> churn, but OTOH they aren't correct as-is either. I have left them
> alone for now.

We can ignore this as this will get merged to the next one. If you
have any comments you can give it on top of the next(0004) patch.

> ~
>
> 3.4. function names
>
> With the re-shuffling that this patch does, and changing several from
> static to not-static, should the function names remain as they are?
> They look random to me.
> - finish_sync_worker(void)
> - invalidate_syncing_table_states(Datum arg, int cacheid, uint32 hashvalue)
> - FetchTableStates(bool *started_tx)
> - process_syncing_tables(XLogRecPtr current_lsn)
>
> I think using a consistent naming convention would be better. e.g.
> SyncFinishWorker
> SyncInvalidateTableStates
> SyncFetchTableStates
> SyncProcessTables

One advantage with keeping the existing names the same wherever
possible will help while merging the changes to back-branches. So I'm
not making this change.

> ~~~
>
> nit - file header comment
>
> ======
> src/backend/replication/logical/tablesync.c
>
> 3.5.
> -static void
> +void
>  process_syncing_tables_for_sync(XLogRecPtr current_lsn)
>
> -static void
> +void
>  process_syncing_tables_for_apply(XLogRecPtr current_lsn)
>
> Since these functions are no longer static should those function names
> be changed to use the CamelCase convention for non-static API?

One advantage with keeping the existing names the same wherever
possible will help while merging the changes to back-branches. So I'm
not making this change.

The rest of the comments were fixed, the attached v20240813 has the
changes for the same.

Regards,
Vignesh

Attachment

Re: Logical Replication of sequences

From
vignesh C
Date:
On Tue, 13 Aug 2024 at 12:31, Peter Smith <smithpb2250@gmail.com> wrote:
>
> OBSERVATION #2
>
> When 1000s of sequences are refreshed (set to INIT) then there are
> 1000s of logs like below:
>
> ...
> 2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0698"
> of subscription "sub3" set to INIT state
> 2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
> sub3 refresh publication sequences;
> 2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0699"
> of subscription "sub3" set to INIT state
> 2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
> sub3 refresh publication sequences;
> 2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0700"
> of subscription "sub3" set to INIT state
> 2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
> sub3 refresh publication sequences;
> 2024-08-13 16:13:57.873 AEST [10301] LOG:  sequence "public.seq_0701"
> of subscription "sub3" set to INIT state
> 2024-08-13 16:13:57.873 AEST [10301] STATEMENT:  alter subscription
> sub3 refresh publication sequences;
> 2024-08-13 16:13:57.874 AEST [10301] LOG:  sequence "public.seq_0702"
> of subscription "sub3" set to INIT state
> 2024-08-13 16:13:57.874 AEST [10301] STATEMENT:  alter subscription
> sub3 refresh publication sequences;
> ...
>
> I felt that showing the STATEMENT for all of these is overkill. How
> about changing that ereport LOG so it does not emit the statement 1000
> times? Or, maybe you can implement it as a "dynamic" log that emits
> the STATEMENT if there are only a few logs a few times but skips it
> for the next 995 logs.

I have changed it to debug1 log level how we do for tables, so this
will not appear for  default log level

>
> OBSERVATION #4
>
> When 1000s of sequences are refreshed then there are 1000s of
> associated logs. But (given there is only one sequencesync worker)
> those logs are not always the order that I was expecting to see them.
>
> e.g.
> ...
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0885" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0887" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0888" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0889" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0890" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0906" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0566" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0568" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0569" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0570" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0571" has
> finished
> 2024-08-13 16:41:47.436 AEST [11735] LOG:  logical replication
> synchronization for subscription "sub3", sequence "seq_0582" has
> finished
> ...
>
> Is there a way to refresh sequences in a more natural (e.g.
> alphabetical) order to make these logs more readable?

I felt this is ok, no need to order it as it can easily be done using
some scripts if required from logs.

The rest of the issues were fixed, the v20240813 version patch
attached at [1] has the changes for the same.
[1] - https://www.postgresql.org/message-id/CALDaNm1Nr_n9SBB52L8A10Txyb4nqGJWfHUapwzM5BopvjMhjA%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
Peter Smith
Date:
On Tue, Aug 13, 2024 at 10:00 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Tue, 13 Aug 2024 at 09:19, Peter Smith <smithpb2250@gmail.com> wrote:
> >
> > 3.1. GENERAL
> >
> > Hmm. I am guessing this was provided as a separate patch to aid review
> > by showing that existing functions are moved? OTOH you can't really
> > judge this patch properly without already knowing details of what will
> > come next in the sequencesync. i.e. As a *standalone* patch without
> > the sequencesync.c the refactoring doesn't make much sense.
> >
> > Maybe it is OK later to combine patches 0003 and 0004. Alternatively,
> > keep this patch separated but give greater emphasis in the comment
> > header to say this patch only exists separately in order to help the
> > review.
>
> I have kept this patch only to show that this patch as such has no
> code changes. If we move this to the next patch it will be difficult
> for reviewers to know which is new code and which is old code. During
> commit we can merge this with the next one. I felt it is better to add
> it in the commit message instead of comment header so updated the
> commit message.
>

Yes, I wrote "comment header" but it was a typo; I meant "commit
header". What you did looks good now. Thanks.

> > ~
> >
> > 3.4. function names
> >
> > With the re-shuffling that this patch does, and changing several from
> > static to not-static, should the function names remain as they are?
> > They look random to me.
> > - finish_sync_worker(void)
> > - invalidate_syncing_table_states(Datum arg, int cacheid, uint32 hashvalue)
> > - FetchTableStates(bool *started_tx)
> > - process_syncing_tables(XLogRecPtr current_lsn)
> >
> > I think using a consistent naming convention would be better. e.g.
> > SyncFinishWorker
> > SyncInvalidateTableStates
> > SyncFetchTableStates
> > SyncProcessTables
>
> One advantage with keeping the existing names the same wherever
> possible will help while merging the changes to back-branches. So I'm
> not making this change.
>

According to my understanding, the logical replication code tries to
maintain name conventions for static functions (snake_case) and for
non-static functions (CamelCase) as an aid for code readability. I
think we should either do our best to abide by those conventions, or
we might as well just forget them and have a naming free-for-all.
Since the new syncutils.c module is being introduced by this patch, my
guess is that any future merging to back-branches will be affected
regardless. IMO this is an ideal opportunity to try to nudge the
function names in the right direction. YMMV.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, Here are my review comments for the latest patchset:

Patch v20240813-0001. No comments
Patch v20240813-0002. No comments
Patch v20240813-0003. No comments
Patch v20240813-0004. See below
Patch v20240813-0005. No comments

//////

Patch v20240813-0004

======
src/backend/catalog/pg_subscription.

GetSubscriptionRelations:
nit - modify a condition for readability

======
src/backend/commands/subscriptioncmds.c

fetch_sequence_list:
nit - changed the WARNING message. /parameters differ
between.../parameters differ for.../ (FYI, Chat-GPT agrees that 2nd
way is more correct)
nit - other minor changes to the message and hint

======
.../replication/logical/sequencesync.c

1. LogicalRepSyncSequences

+ ereport(DEBUG1,
+ errmsg("logical replication synchronization for subscription \"%s\",
sequence \"%s\" has finished",
+    get_subscription_name(subid, false), get_rel_name(done_seq->relid)));

DEBUG logs should use errmsg_internal. (fixed also nitpicks attachment).

~

nit - minor change to the log message counting the batched sequences

~~~

process_syncing_sequences_for_apply:
nit - /sequence sync worker/seqeuencesync worker/

======
src/backend/utils/misc/guc_tables.c

nit - /max workers/maximum number of workers/ (for consistency because
all other GUCs are verbose like this; nothing just says "max".)

======
src/test/subscription/t/034_sequences.pl

nit - adjust the expected WARNING message (which was modified above)

======
Kind Regards,
Peter Smith.
Fujitsu Australia

Attachment

Re: Logical Replication of sequences

From
Peter Smith
Date:
Here are my review comments for the latest patchset

v20240817-0001. No changes. No comments.
v20240817-0002. No changes. No comments.
v20240817-0003. See below.
v20240817-0004. See below.
v20240817-0005. No changes. No comments.

//////

v20240817-0003 and 0004.

(This is a repeat of the same comment as in previous reviews, but lots
more functions seem affected now)

IIUC, the LR code tries to follow function naming conventions (e.g.
CamelCase/snake_case for exposed/static functions respectively),
intended to make the code more readable. But, this only works if the
conventions are followed.

Now, patches 0003 and 0004 are shuffling more and more functions
between modules while changing them from static to non-static (or vice
versa). So, the function name conventions are being violated many
times. IMO these functions ought to be renamed according to their new
modifiers to avoid the confusion caused by ignoring the name
conventions.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh, Here are my review comments for the latest patchset

v20240819-0001. No changes. No comments.
v20240819-0002. No changes. No comments.
v20240819-0003. See below.
v20240819-0004. See below.
v20240819-0005. No changes. No comments.

///////////////////////

PATCH v20240819-0003

======
src/backend/replication/logical/syncutils.c

3.1.
+typedef enum
+{
+ SYNC_RELATION_STATE_NEEDS_REBUILD,
+ SYNC_RELATION_STATE_REBUILD_STARTED,
+ SYNC_RELATION_STATE_VALID,
+} SyncingRelationsState;
+
+static SyncingRelationsState relation_states_validity =
SYNC_RELATION_STATE_NEEDS_REBUILD;

There is some muddle of singular/plural names here. The
typedef/values/var should all match:

e.g. It could be like:
SYNC_RELATION_STATE_xxx --> SYNC_RELATION_STATES_xxx
SyncingRelationsState --> SyncRelationStates

But, a more radical change might be better.

typedef enum
{
RELATION_STATES_SYNC_NEEDED,
RELATION_STATES_SYNC_STARTED,
RELATION_STATES_SYNCED,
} SyncRelationStates;

~~~

3.2. GENERAL refactoring

I don't think all of the functions moved into syncutil.c truly belong there.

This new module was introduced to be for common/util functions for
tablesync and sequencesync, but with each patchset, it has been
sucking in more and more functions that maybe do not quite belong
here.

For example, AFAIK these below have logic that is *solely* for TABLES
(not for SEQUENCES). Perhaps it was convenient to dump them here
because they are statically called, but I felt they still logically
belong in tablesync.c:
- process_syncing_tables_for_sync(XLogRecPtr current_lsn)
- process_syncing_tables_for_apply(XLogRecPtr current_lsn)
- AllTablesyncsReady(void)

~~~

3.3.
+static bool
+FetchRelationStates(bool *started_tx)
+{

If this function can remain static then the name should change to be
like fetch_table_states, right?

======
src/include/replication/worker_internal.h

3.4.
+extern bool wait_for_relation_state_change(Oid relid, char expected_state);

If this previously static function will be exposed now (it may not
need to be if some other functions are returned tablesync.c) then the
function name should also be changed, right?

////////////////////////

PATCH v20240819-0004

======
src/backend/replication/logical/syncutils.c

4.1 GENERAL refactoring

(this is similar to review comment #3.2 above)

Functions like below have logic that is *solely* for SEQUENCES (not
for TABLES). I felt they logically belong in sequencesync.c, not here.
- process_syncing_sequences_for_apply(void)

~~~

FetchRelationStates:
nit - the comment change about "not-READY tables" (instead of
relations) should be already in patch 0003.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
shveta malik
Date:
On Fri, Sep 20, 2024 at 9:36 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Wed, 21 Aug 2024 at 11:54, vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 21 Aug 2024 at 08:33, Peter Smith <smithpb2250@gmail.com> wrote:
> > >
> > > Hi Vignesh, Here are my only review comments for the latest patch set.
> >
> > Thanks, these issues have been addressed in the updated version.
> > Additionally, I have fixed the pgindent problems that were reported
> > and included another advantage of this design in the file header of
> > the sequencesync file.
>
> The patch was not applied on top of head, here is a rebased version of
> the patches.
> I have also removed an invalidation which was  not required for
> sequences and a typo.
>

Thank You for the patches. I would like to understand srsublsn and
page_lsn more. Please see the scenario below:

I have a sequence:
CREATE SEQUENCE myseq0 INCREMENT 5 START 100;

After refresh on sub:
postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
ALTER SUBSCRIPTION

postgres=# select * from pg_subscription_rel;
 srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+-----------
   16385 |   16384 | r          | 0/152F380 -->pub's page_lsn


postgres=# select * from pg_sequence_state('myseq0');
 page_lsn  | last_value | log_cnt | is_called
-----------+------------+---------+-----------
 0/152D830 |        105 |      31 | t   -->(I am assuming 0/152D830 is
local page_lsn corresponding to value-=105)

Now I assume that *only* after doing next_wal for 31 times,  page_lsn
shall change. But I observe strange behaviour

After running nextval on sub for 7 times:
postgres=# select * from pg_sequence_state('myseq0');
 page_lsn  | last_value | log_cnt | is_called
-----------+------------+---------+-----------
 0/152D830 |        140 |      24 | t   -->correct

After running nextval on sub for 15 more times:
postgres=# select * from pg_sequence_state('myseq0');
 page_lsn  | last_value | log_cnt | is_called
-----------+------------+---------+-----------
 0/152D830 |        215 |       9 | t -->correct
(1 row)

Now after running it 6 more times:
postgres=# select * from pg_sequence_state('myseq0');
 page_lsn  | last_value | log_cnt | is_called
-----------+------------+---------+-----------
 0/152D990 |        245 |      28 | t --> how??

last_value increased in the expected way (6*5), but page_lsn changed
and log_cnt changed before we could complete the remaining runs as
well. Not sure why??

Now if I do refresh again:

postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
ALTER SUBSCRIPTION

postgres=# select * from pg_subscription_rel;
 srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+-----------
   16385 |   16384 | r          | 0/152F380-->pub's page_lsn, same as old one.

postgres=# select * from pg_sequence_state('myseq0');
 page_lsn  | last_value | log_cnt | is_called
-----------+------------+---------+-----------
 0/152DDB8 |        105 |      31 | t
(1 row)

Now, what is this page_lsn = 0/152DDB8? Should it be the one
corresponding to last_value=105 and thus shouldn't it match the
previous value of  0/152D830?

thanks
Shveta



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 26 Sept 2024 at 11:07, shveta malik <shveta.malik@gmail.com> wrote:
>
> On Fri, Sep 20, 2024 at 9:36 AM vignesh C <vignesh21@gmail.com> wrote:
> >
> > On Wed, 21 Aug 2024 at 11:54, vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Wed, 21 Aug 2024 at 08:33, Peter Smith <smithpb2250@gmail.com> wrote:
> > > >
> > > > Hi Vignesh, Here are my only review comments for the latest patch set.
> > >
> > > Thanks, these issues have been addressed in the updated version.
> > > Additionally, I have fixed the pgindent problems that were reported
> > > and included another advantage of this design in the file header of
> > > the sequencesync file.
> >
> > The patch was not applied on top of head, here is a rebased version of
> > the patches.
> > I have also removed an invalidation which was  not required for
> > sequences and a typo.
> >
>
> Thank You for the patches. I would like to understand srsublsn and
> page_lsn more. Please see the scenario below:
>
> I have a sequence:
> CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
>
> After refresh on sub:
> postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> ALTER SUBSCRIPTION
>
> postgres=# select * from pg_subscription_rel;
>  srsubid | srrelid | srsubstate | srsublsn
> ---------+---------+------------+-----------
>    16385 |   16384 | r          | 0/152F380 -->pub's page_lsn
>
>
> postgres=# select * from pg_sequence_state('myseq0');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/152D830 |        105 |      31 | t   -->(I am assuming 0/152D830 is
> local page_lsn corresponding to value-=105)
>
> Now I assume that *only* after doing next_wal for 31 times,  page_lsn
> shall change. But I observe strange behaviour
>
> After running nextval on sub for 7 times:
> postgres=# select * from pg_sequence_state('myseq0');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/152D830 |        140 |      24 | t   -->correct
>
> After running nextval on sub for 15 more times:
> postgres=# select * from pg_sequence_state('myseq0');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/152D830 |        215 |       9 | t -->correct
> (1 row)
>
> Now after running it 6 more times:
> postgres=# select * from pg_sequence_state('myseq0');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/152D990 |        245 |      28 | t --> how??
>
> last_value increased in the expected way (6*5), but page_lsn changed
> and log_cnt changed before we could complete the remaining runs as
> well. Not sure why??

This can occur if a checkpoint happened at that time. The regression
test also has specific handling for this, as noted in a comment within
the sequence.sql test file:
-- log_cnt can be higher if there is a checkpoint just at the right
-- time

> Now if I do refresh again:
>
> postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> ALTER SUBSCRIPTION
>
> postgres=# select * from pg_subscription_rel;
>  srsubid | srrelid | srsubstate | srsublsn
> ---------+---------+------------+-----------
>    16385 |   16384 | r          | 0/152F380-->pub's page_lsn, same as old one.
>
> postgres=# select * from pg_sequence_state('myseq0');
>  page_lsn  | last_value | log_cnt | is_called
> -----------+------------+---------+-----------
>  0/152DDB8 |        105 |      31 | t
> (1 row)
>
> Now, what is this page_lsn = 0/152DDB8? Should it be the one
> corresponding to last_value=105 and thus shouldn't it match the
> previous value of  0/152D830?

After executing REFRESH PUBLICATION SEQUENCES, the publication value
will be resynchronized, and a new LSN will be generated and updated
for the publisher sequence (using the old value). Therefore, this is
not a concern.

Regards,
Vignesh



Re: Logical Replication of sequences

From
shveta malik
Date:
On Sun, Sep 29, 2024 at 12:34 PM vignesh C <vignesh21@gmail.com> wrote:
>
> On Thu, 26 Sept 2024 at 11:07, shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Fri, Sep 20, 2024 at 9:36 AM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Wed, 21 Aug 2024 at 11:54, vignesh C <vignesh21@gmail.com> wrote:
> > > >
> > > > On Wed, 21 Aug 2024 at 08:33, Peter Smith <smithpb2250@gmail.com> wrote:
> > > > >
> > > > > Hi Vignesh, Here are my only review comments for the latest patch set.
> > > >
> > > > Thanks, these issues have been addressed in the updated version.
> > > > Additionally, I have fixed the pgindent problems that were reported
> > > > and included another advantage of this design in the file header of
> > > > the sequencesync file.
> > >
> > > The patch was not applied on top of head, here is a rebased version of
> > > the patches.
> > > I have also removed an invalidation which was  not required for
> > > sequences and a typo.
> > >
> >
> > Thank You for the patches. I would like to understand srsublsn and
> > page_lsn more. Please see the scenario below:
> >
> > I have a sequence:
> > CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> >
> > After refresh on sub:
> > postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> > ALTER SUBSCRIPTION
> >
> > postgres=# select * from pg_subscription_rel;
> >  srsubid | srrelid | srsubstate | srsublsn
> > ---------+---------+------------+-----------
> >    16385 |   16384 | r          | 0/152F380 -->pub's page_lsn
> >
> >
> > postgres=# select * from pg_sequence_state('myseq0');
> >  page_lsn  | last_value | log_cnt | is_called
> > -----------+------------+---------+-----------
> >  0/152D830 |        105 |      31 | t   -->(I am assuming 0/152D830 is
> > local page_lsn corresponding to value-=105)
> >
> > Now I assume that *only* after doing next_wal for 31 times,  page_lsn
> > shall change. But I observe strange behaviour
> >
> > After running nextval on sub for 7 times:
> > postgres=# select * from pg_sequence_state('myseq0');
> >  page_lsn  | last_value | log_cnt | is_called
> > -----------+------------+---------+-----------
> >  0/152D830 |        140 |      24 | t   -->correct
> >
> > After running nextval on sub for 15 more times:
> > postgres=# select * from pg_sequence_state('myseq0');
> >  page_lsn  | last_value | log_cnt | is_called
> > -----------+------------+---------+-----------
> >  0/152D830 |        215 |       9 | t -->correct
> > (1 row)
> >
> > Now after running it 6 more times:
> > postgres=# select * from pg_sequence_state('myseq0');
> >  page_lsn  | last_value | log_cnt | is_called
> > -----------+------------+---------+-----------
> >  0/152D990 |        245 |      28 | t --> how??
> >
> > last_value increased in the expected way (6*5), but page_lsn changed
> > and log_cnt changed before we could complete the remaining runs as
> > well. Not sure why??
>
> This can occur if a checkpoint happened at that time. The regression
> test also has specific handling for this, as noted in a comment within
> the sequence.sql test file:
> -- log_cnt can be higher if there is a checkpoint just at the right
> -- time

Okay. I see. I tried by executing 'checkpoint' and can see the same behaviour.

>
> > Now if I do refresh again:
> >
> > postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> > ALTER SUBSCRIPTION
> >
> > postgres=# select * from pg_subscription_rel;
> >  srsubid | srrelid | srsubstate | srsublsn
> > ---------+---------+------------+-----------
> >    16385 |   16384 | r          | 0/152F380-->pub's page_lsn, same as old one.
> >
> > postgres=# select * from pg_sequence_state('myseq0');
> >  page_lsn  | last_value | log_cnt | is_called
> > -----------+------------+---------+-----------
> >  0/152DDB8 |        105 |      31 | t
> > (1 row)
> >
> > Now, what is this page_lsn = 0/152DDB8? Should it be the one
> > corresponding to last_value=105 and thus shouldn't it match the
> > previous value of  0/152D830?
>
> After executing REFRESH PUBLICATION SEQUENCES, the publication value
> will be resynchronized, and a new LSN will be generated and updated
> for the publisher sequence (using the old value). Therefore, this is
> not a concern.
>

Okay.

Few comments:

1)
+static List *
+fetch_sequence_list(WalReceiverConn *wrconn, char *subname, List *publications)

--fetch_sequence_list() is not using the argument subanme anywhere.

2)

+ if (resync_all_sequences)
+ {
+ ereport(DEBUG1,
+ errmsg_internal("sequence \"%s.%s\" of subscription \"%s\" set to INIT state",
+ get_namespace_name(get_rel_namespace(relid)),
+ get_rel_name(relid),
+ sub->name));
+ UpdateSubscriptionRelState(sub->oid, relid, SUBREL_STATE_INIT,
+    InvalidXLogRecPtr);
+ }

--Shall we have DEBUG1 after we are done with
UpdateSubscriptionRelState? Otherwise we may end up putting this log
statement, even if the update fails for some reason.

3)
fetch_remote_sequence_data():

Should we have a macro REMOTE_SEQ_COL_COUNT 10 and use it instead of
direct 10. Also instead of  having 1,2,3 etc in slot_getattr, we can
have ++col and at the end we can have:
Assert(col == REMOTE_SEQ_COL_COUNT);

thanks
Shveta



Re: Logical Replication of sequences

From
Masahiko Sawada
Date:
On Tue, Oct 8, 2024 at 2:46 AM vignesh C <vignesh21@gmail.com> wrote:
>
> On Fri, 4 Oct 2024 at 15:39, shveta malik <shveta.malik@gmail.com> wrote:
> >
> > On Sun, Sep 29, 2024 at 12:34 PM vignesh C <vignesh21@gmail.com> wrote:
> > >
> > > On Thu, 26 Sept 2024 at 11:07, shveta malik <shveta.malik@gmail.com> wrote:
> > > >
> > > > On Fri, Sep 20, 2024 at 9:36 AM vignesh C <vignesh21@gmail.com> wrote:
> > > > >
> > > > > On Wed, 21 Aug 2024 at 11:54, vignesh C <vignesh21@gmail.com> wrote:
> > > > > >
> > > > > > On Wed, 21 Aug 2024 at 08:33, Peter Smith <smithpb2250@gmail.com> wrote:
> > > > > > >
> > > > > > > Hi Vignesh, Here are my only review comments for the latest patch set.
> > > > > >
> > > > > > Thanks, these issues have been addressed in the updated version.
> > > > > > Additionally, I have fixed the pgindent problems that were reported
> > > > > > and included another advantage of this design in the file header of
> > > > > > the sequencesync file.
> > > > >
> > > > > The patch was not applied on top of head, here is a rebased version of
> > > > > the patches.
> > > > > I have also removed an invalidation which was  not required for
> > > > > sequences and a typo.
> > > > >
> > > >
> > > > Thank You for the patches. I would like to understand srsublsn and
> > > > page_lsn more. Please see the scenario below:
> > > >
> > > > I have a sequence:
> > > > CREATE SEQUENCE myseq0 INCREMENT 5 START 100;
> > > >
> > > > After refresh on sub:
> > > > postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> > > > ALTER SUBSCRIPTION
> > > >
> > > > postgres=# select * from pg_subscription_rel;
> > > >  srsubid | srrelid | srsubstate | srsublsn
> > > > ---------+---------+------------+-----------
> > > >    16385 |   16384 | r          | 0/152F380 -->pub's page_lsn
> > > >
> > > >
> > > > postgres=# select * from pg_sequence_state('myseq0');
> > > >  page_lsn  | last_value | log_cnt | is_called
> > > > -----------+------------+---------+-----------
> > > >  0/152D830 |        105 |      31 | t   -->(I am assuming 0/152D830 is
> > > > local page_lsn corresponding to value-=105)
> > > >
> > > > Now I assume that *only* after doing next_wal for 31 times,  page_lsn
> > > > shall change. But I observe strange behaviour
> > > >
> > > > After running nextval on sub for 7 times:
> > > > postgres=# select * from pg_sequence_state('myseq0');
> > > >  page_lsn  | last_value | log_cnt | is_called
> > > > -----------+------------+---------+-----------
> > > >  0/152D830 |        140 |      24 | t   -->correct
> > > >
> > > > After running nextval on sub for 15 more times:
> > > > postgres=# select * from pg_sequence_state('myseq0');
> > > >  page_lsn  | last_value | log_cnt | is_called
> > > > -----------+------------+---------+-----------
> > > >  0/152D830 |        215 |       9 | t -->correct
> > > > (1 row)
> > > >
> > > > Now after running it 6 more times:
> > > > postgres=# select * from pg_sequence_state('myseq0');
> > > >  page_lsn  | last_value | log_cnt | is_called
> > > > -----------+------------+---------+-----------
> > > >  0/152D990 |        245 |      28 | t --> how??
> > > >
> > > > last_value increased in the expected way (6*5), but page_lsn changed
> > > > and log_cnt changed before we could complete the remaining runs as
> > > > well. Not sure why??
> > >
> > > This can occur if a checkpoint happened at that time. The regression
> > > test also has specific handling for this, as noted in a comment within
> > > the sequence.sql test file:
> > > -- log_cnt can be higher if there is a checkpoint just at the right
> > > -- time
> >
> > Okay. I see. I tried by executing 'checkpoint' and can see the same behaviour.
> >
> > >
> > > > Now if I do refresh again:
> > > >
> > > > postgres=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION SEQUENCES;
> > > > ALTER SUBSCRIPTION
> > > >
> > > > postgres=# select * from pg_subscription_rel;
> > > >  srsubid | srrelid | srsubstate | srsublsn
> > > > ---------+---------+------------+-----------
> > > >    16385 |   16384 | r          | 0/152F380-->pub's page_lsn, same as old one.
> > > >
> > > > postgres=# select * from pg_sequence_state('myseq0');
> > > >  page_lsn  | last_value | log_cnt | is_called
> > > > -----------+------------+---------+-----------
> > > >  0/152DDB8 |        105 |      31 | t
> > > > (1 row)
> > > >
> > > > Now, what is this page_lsn = 0/152DDB8? Should it be the one
> > > > corresponding to last_value=105 and thus shouldn't it match the
> > > > previous value of  0/152D830?
> > >
> > > After executing REFRESH PUBLICATION SEQUENCES, the publication value
> > > will be resynchronized, and a new LSN will be generated and updated
> > > for the publisher sequence (using the old value). Therefore, this is
> > > not a concern.
> > >
> >
> > Okay.
> >
> > Few comments:
> >
> > 1)
> > +static List *
> > +fetch_sequence_list(WalReceiverConn *wrconn, char *subname, List *publications)
> >
> > --fetch_sequence_list() is not using the argument subanme anywhere.
> >
> > 2)
> >
> > + if (resync_all_sequences)
> > + {
> > + ereport(DEBUG1,
> > + errmsg_internal("sequence \"%s.%s\" of subscription \"%s\" set to INIT state",
> > + get_namespace_name(get_rel_namespace(relid)),
> > + get_rel_name(relid),
> > + sub->name));
> > + UpdateSubscriptionRelState(sub->oid, relid, SUBREL_STATE_INIT,
> > +    InvalidXLogRecPtr);
> > + }
> >
> > --Shall we have DEBUG1 after we are done with
> > UpdateSubscriptionRelState? Otherwise we may end up putting this log
> > statement, even if the update fails for some reason.
> >
> > 3)
> > fetch_remote_sequence_data():
> >
> > Should we have a macro REMOTE_SEQ_COL_COUNT 10 and use it instead of
> > direct 10. Also instead of  having 1,2,3 etc in slot_getattr, we can
> > have ++col and at the end we can have:
> > Assert(col == REMOTE_SEQ_COL_COUNT);
>
> Thanks for the comments, these are addressed in the attached patch.
>

Here are comments on the 0001 and 0002 patches:

0001 patch:

read_seq_tuple() reads a buffer and acquires a lock on it, and the
buffer is returned to the caller while being locked. So I think it's
possible for the caller to get the page LSN even without changes.
Since pg_sequence_state() is the sole caller that requests lsn_ret to
be set, I think the changes of read_seq_tuples() is not necessarily
necessary.

0002 patch:
+        Assert(all_tables && *all_tables == false);
+        Assert(all_sequences && *all_sequences == false);

I think it's better to set both *all_tables and *all_sequence to false
at the beginning of the function to ensure this function works as
expected regardless of their initial values.

---
        appendPQExpBufferStr(query,
                             "SELECT p.tableoid, p.oid, p.pubname, "
                             "p.pubowner, "
-                            "p.puballtables, p.pubinsert,
p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
+                            "p.puballtables, false as
p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete,
p.pubtruncate, p.pubviaroot "
                             "FROM pg_publication p");
    else if (fout->remoteVersion >= 110000)
        appendPQExpBufferStr(query,
                             "SELECT p.tableoid, p.oid, p.pubname, "
                             "p.pubowner, "
-                            "p.puballtables, p.pubinsert,
p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
+                            "p.puballtables, false as
p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete,
p.pubtruncate, false AS pubviaroot "
                             "FROM pg_publication p");
    else
        appendPQExpBufferStr(query,
                             "SELECT p.tableoid, p.oid, p.pubname, "
                             "p.pubowner, "
-                            "p.puballtables, p.pubinsert,
p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
+                            "p.puballtables, false as
p.puballsequences, p.pubinsert, p.pubupdate, p.pubdelete, false AS
pubtruncate, false AS pubviaroot "
                             "FROM pg_publication p");

The column name should be puballsequences, not p.puballsequences.

---
IIUC the changes of describeOneTableDetails() includes two kinds of
changes: refactoring to use printTable() instead of printQuery(), and
adding publications that includes the sequence. Is the first
refactoring necessary for the second change? If not, should it be done
in a separate patch?
fg
Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

Here are some review comments for the patch v20241211-0002.

======
doc/src/sgml/ref/create_publication.sgml

1.
+<phrase>where <replaceable class="parameter">object
type</replaceable> is one of:</phrase>
+
+    TABLES
+    SEQUENCES

The replaceable "object_type" is missing an underscore.

~~~

publish option effect fro SEQUENCE replication?

2.
It's not obvious to me if the SEQUENCE replication stuff is affected
but the setting of pubactions (ie the 'publish' option). I'm thinking
that probably anything to do with SEQUENCEs may no be considered a DML
operation, but if that is true it might be better to explicitly say
so.

Also, we might need to include a test to show even if publish='' that
the SEQUENCE synchronization is not affected.


======
src/backend/commands/publicationcmds.c

CreatePublication:

3.
- /* FOR ALL TABLES requires superuser */
- if (stmt->for_all_tables && !superuser())
+ /* FOR ALL TABLES or FOR ALL SEQUENCES requires superuser */
+ if ((stmt->for_all_tables || stmt->for_all_sequences) && !superuser())
  ereport(ERROR,
  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to create FOR ALL TABLES publication")));
+ errmsg("must be superuser to create a %s publication",
+ stmt->for_all_tables ? "FOR ALL TABLES" :
+ "FOR ALL SEQUENCES")));

It seems a quirk that if FOR ALL TABLES and FOR ALL SEQUENCES are
specified at the same time then you would only get the "FOR ALL
TABLES" error, but maybe that is OK?

~~~

AlterPublicationOwner_internal:

4.
Ditto quirk as commented above, but maybe it is OK.

======
src/bin/psql/describe.c

describePublications:

5.
It seems the ordering of the local variables, and then the attributes
in the SQL, and the headings in the "describe" output are a bit
muddled.

IMO it might be better to always keep things in the same order as the
eventual display headings. So anything to do with puballsequences
should be immediately after anything to do with puballtables.

(There are multiple changes needed in this function to rearrange
things to be this way).

~~~

6.
The following seems wrong because now somehow there are two lots of
index 9 (???)

-----
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
if (has_pubsequence)
  printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false); /*
all sequences */
printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
if (has_pubtruncate)
  printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
if (has_pubgencols)
  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
if (has_pubviaroot)
  printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
-----


======
src/test/regress/expected/psql.out

7.
+\dRp+ regress_pub_forallsequences1
+                                            Publication
regress_pub_forallsequences1
+          Owner           | All tables | All sequences | Inserts |
Updates | Deletes | Truncates | Generated columns | Via root

+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
+ regress_publication_user | f          | f             | t       | t
     | t       | t         | f                 | f
+(1 row)
+

The expected value of 'f' for "All sequences" looks wrong to me. I
think this might be a manifestation of that duplicated '9' index
mentioned in an earlier review comment #6.

~~~

8.
+\dRp+ regress_pub_for_allsequences_alltables
+                                       Publication
regress_pub_for_allsequences_alltables
+          Owner           | All tables | All sequences | Inserts |
Updates | Deletes | Truncates | Generated columns | Via root

+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
+ regress_publication_user | t          | f             | t       | t
     | t       | t         | f                 | f
+(1 row)
+

The expected value of 'f' for "All sequences" looks wrong to me. I
think this might be a manifestation of that duplicated '9' index
mentioned in an earlier review comment #6.

~~~

9.
 \dRp+ testpub_forparted
-                                         Publication testpub_forparted
-          Owner           | All tables | Inserts | Updates | Deletes
| Truncates | Generated columns | Via root
---------------------------+------------+---------+---------+---------+-----------+-------------------+----------
- regress_publication_user | f          | t       | t       | t
| t         | f                 | t
+                                                 Publication testpub_forparted
+          Owner           | All tables | All sequences | Inserts |
Updates | Deletes | Truncates | Generated columns | Via root

+--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
+ regress_publication_user | f          | t             | t       | t
     | t       | t         | f                 | t


AFAIK these partition tests should not be impacting the "All
Sequences" flag value, so the expected value of 't' for "All
sequences" looks wrong to me. I think this might be a manifestation of
that duplicated '9' index mentioned in an earlier review comment #6.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

Here are some review comments for the patch v20241211-0003.

======
src/backend/replication/logical/syncutils.c

1.
+typedef enum
+{
+ SYNC_RELATIONS_STATE_NEEDS_REBUILD,
+ SYNC_RELATIONS_STATE_REBUILD_STARTED,
+ SYNC_RELATIONS_STATE_VALID,
+} SyncingRelationsState;
+

Even though the patch's intent was only to "move" this from
tablsync.c, this enum probably deserved a comment describing its
purpose.

~~~

2.
+List    *table_states_not_ready = NIL;

Maybe it is convenient to move this, but there is something about this
list being exposed out of a "utils" module back to the tablesync.c
module that seems a bit strange. Would it make more sense for this to
be the other way around e.g. declared in the tablesync.c but exposed
to the syncutils.c? (and then similarly in subsequent patch 0004 the
sequence_states_not_ready would belong in the sequencesync.c)

~~~

3.
+/*
+ * Process possible state change(s) of tables that are being synchronized.
+ */
+void
+SyncProcessRelations(XLogRecPtr current_lsn)
+{

IIUC there was a deliberate effort to rename some comments and
functions to say "relations" instead of "tables". AFAICT that was done
to encompass the SEQUENCES which can fit under the umbrella of
"relations". Anyway, it becomes confusing sometimes when there is a
mismatch. For example, here is a function (relations) with a function
comment (tables).

~~~

4.
+/*
+ * Common code to fetch the up-to-date sync state info into the static lists.
+ *
+ * Returns true if subscription has 1 or more tables, else false.
+ *
+ * Note: If this function started the transaction (indicated by the parameter)
+ * then it is the caller's responsibility to commit it.
+ */
+bool
+FetchRelationStates(bool *started_tx)

Here is another place where the function name is "relations", but the
function comment refers to "tables".

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh,

Here are some review comments for the patch v20241211-0004.

======
GENERAL

1.
There are more than a dozen places where the relation (relkind) is
checked to see if it is a SEQUENCE:

e.g. + get_rel_relkind(subrel->srrelid) != RELKIND_SEQUENCE &&
e.g. + if (get_rel_relkind(subrel->srrelid) != RELKIND_SEQUENCE)
e.g. + if (relkind == RELKIND_SEQUENCE && !get_sequences)
e.g. + if (relkind != RELKIND_SEQUENCE && !get_tables)
e.g. + relkind == RELKIND_SEQUENCE ? "sequence" : "table",
e.g. + if (relkind != RELKIND_SEQUENCE)
e.g. + relkind == RELKIND_SEQUENCE ? "sequence" : "table",
e.g. + if (get_rel_relkind(sub_remove_rels[off].relid) == RELKIND_SEQUENCE)
e.g. + if (get_rel_relkind(relid) != RELKIND_SEQUENCE)
e.g. + relkind != RELKIND_SEQUENCE)
e.g. + Assert(get_rel_relkind(rstate->relid) == RELKIND_SEQUENCE);
e.g. + Assert(relkind == RELKIND_SEQUENCE);
e.g. + if (get_rel_relkind(rstate->relid) == RELKIND_SEQUENCE)
e.g. + Assert(get_rel_relkind(rstate->relid) != RELKIND_SEQUENCE);

I am wondering if the code might be improved slightly by adding one new macro:

#define RELKIND_IS_SEQUENCE(relkind) ((relkind) == RELKIND_SEQUENCE)

======
Commit message

2.
1) CREATE SUBSCRIPTION
    - (PG17 command syntax is unchanged)
    - The subscriber retrieves sequences associated with publications.
    - Publisher sequences are added to pg_subscription_rel with INIT state.
    - Initiates the sequencesync worker (see above) to synchronize all
      sequences.

~

Shouldn't this say "Published sequences" instead of "Publisher sequences"?

I guess if the patch currently supports only ALL SEQUENCES then maybe
it amounts to the same thing, but IMO "Published" seems more correct.

~~~

3.
2) ALTER SUBSCRIPTION ... REFRESH PUBLICATION
    - (PG17 command syntax is unchanged)
    - Dropped publisher sequences are removed from pg_subscription_rel.
    - New publisher sequences are added to pg_subscription_rel with INIT state.
    - Initiates the sequencesync worker (see above) to synchronize only
      newly added sequences.

3) ALTER SUBSCRIPTION ... REFRESH PUBLICATION SEQUENCES
    - The patch introduces this new command to refresh all sequences
    - Dropped publisher sequences are removed from pg_subscription_rel.
    - New publisher sequences are added to pg_subscription_rel
    - All sequences in pg_subscription_rel are reset to INIT state.
    - Initiates the sequencesync worker (see above) to synchronize all
      sequences.

~

Ditto previous comment -- maybe those should say "Newly published
sequences" instead of "New publisher sequences"/

~~~

4.
Should there be some mention of the WARNING logged if sequence
parameter differences are detected?

======
src/backend/catalog/pg_subscription.c

GetSubscriptionRelations:

5.
+ * all_states:
+ * If getting tables, if all_states is true get all tables, otherwise
+ * only get tables that have not reached READY state.
+ * If getting sequences, if all_states is true get all sequences,
+ * otherwise only get sequences that are in INIT state.
+ *
+ * The returned list is palloc'ed in the current memory context.

and

- if (not_ready)
+ if (!all_states)
  ScanKeyInit(&skey[nkeys++],
  Anum_pg_subscription_rel_srsubstate,
  BTEqualStrategyNumber, F_CHARNE,
                    CharGetDatum(SUBREL_STATE_READY));
~

It was a bit confusing that the code for (!all_states) is excluding
everything that is not in SUBREL_STATE_READY, but OTOH the function
comment for sequences it said "that are in INIT state". Maybe that
function comment for sequence also should have said "that have not
reached READY state (i.e. are still in INIT state)" to better match
the code.

~~~

6.
+ /* Skip sequences if they were not requested */
+ if (relkind == RELKIND_SEQUENCE && !get_sequences)
+ continue;
+
+ /* Skip tables if they were not requested */
+ if (relkind != RELKIND_SEQUENCE && !get_tables)
+ continue;

Somehow, I feel this logic would seem simpler if expressed differently
to make it more explicit that the relation is either a table or a
sequence. e.g. by adding some variables.

SUGGESTION:

bool is_sequence;
bool is_table;

...

/* Relation is either a sequence or a table */
is_sequence = get_rel_relkind(subrel->srrelid) == RELKIND_SEQUENCE;
is_table = !is_sequence;

/* Skip sequences if they were not requested */
if (!get_sequences && is_sequence)
  continue;

/* Skip tables if they were not requested */
if (!get_tables && is_table)
  continue;

======
src/backend/commands/sequence.c

7.
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 2 arg set sequence procedure.
+ * See SetSequence for discussion.
  */
 Datum
 setval_oid(PG_FUNCTION_ARGS)

~

Not sure this function comment is right. Shouldn't it still say
"Implement the 2 arg setval procedure."

~~~

8.
 /*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg set sequence procedure.
+ * See SetSequence for discussion.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)

~

Not sure this function comment is right. Shouldn't it still say
"Implement the 3 arg setval procedure."

======
src/backend/commands/subscriptioncmds.c

AlterSubscription_refresh:

9.
+ * If 'refresh_tables' is true, update the subscription by adding or removing
+ * tables that have been added or removed since the last subscription creation
+ * or refresh publication.
+ *
+ * If 'refresh_sequences' is true, update the subscription by adding
or removing
+ * sequences that have been added or removed since the last subscription
+ * creation or publication refresh.

The first para says "refresh publication". The second para says
"publication refresh", but I guess it should also be saying "refresh
publication".

~~~

10.
+ if (resync_all_sequences)
+ {
+
+ UpdateSubscriptionRelState(sub->oid, relid, SUBREL_STATE_INIT,
+    InvalidXLogRecPtr);
+ ereport(DEBUG1,
+ errmsg_internal("sequence \"%s.%s\" of subscription \"%s\" set to INIT state",
+ get_namespace_name(get_rel_namespace(relid)),
+ get_rel_name(relid),
+ sub->name));
+ }

Has unnecessary blank line.

~~~

AlterSubscription:

11.
Some of the generic error messages in this function are now
potentially misleading.

e.g. There are multiple places in this function that say "ALTER
SUBSCRIPTION ... REFRESH", meaning ALTER SUBSCRIPTION <subname>
REFRESH PUBLICATION, but not meaning ALTER SUBSCRIPTION <subname>
REFRESH PUBLICATION SEQUENCES, so possibly those need to be modified
to eliminate any ambiguity.

(Actually, maybe it is not only in this function -- the short form
"ALTER SUBSCRIPTION ... REFRESH" seems to be scattered in other
comments in this file also).

~~~

12.
- logicalrep_worker_stop(w->subid, w->relid);
+ /* Worker might have exited because of an error */
+ if (w->type == WORKERTYPE_UNKNOWN)
+ continue;
+
+ logicalrep_worker_stop(w->subid, w->relid, w->type);

It may be better to put that special case WORKERTYPE_UNKNOWN condition
as a quick exit within the logicalrep_worker_stop() function.

======
src/backend/replication/logical/launcher.c

logicalrep_worker_find:

13.
+ Assert(wtype == WORKERTYPE_TABLESYNC ||
+    wtype == WORKERTYPE_SEQUENCESYNC ||
+    wtype == WORKERTYPE_APPLY);
+

The master version of this function checked for
isParallelApplyWorker(w), but now if a WORKERTYPE_PARALLEL_APPLY ever
got to here it would fail the above Assert. So, is the patch code OK,
or do we still need to also account for a possible
WORKERTYPE_PARALLEL_APPLY reaching here?

~~~

logicalrep_worker_stop:

14.
worker = logicalrep_worker_find(subid, relid, wtype, false);

if (worker)
{
  Assert(!isParallelApplyWorker(worker));
  logicalrep_worker_stop_internal(worker, SIGTERM);
}

~

This code is not changed much from before, so it first finds the
worker, but then asserts that it must not be not a parallel apply
worker. But now, since the wtype is known and passed to the function
why not move the Assert up-front based on the wtype and before even
doing the 'find'?

======
.../replication/logical/sequencesync.c

ProcessSyncingSequencesForApply:

15.
+ /*
+ * Check if there is a sequence worker already running?
+ */
+ LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
+
+ syncworker = logicalrep_worker_find(MyLogicalRepWorker->subid,
+ InvalidOid, WORKERTYPE_SEQUENCESYNC,
+ true);
+ if (syncworker)
+ {
+ /* Now safe to release the LWLock */
+ LWLockRelease(LogicalRepWorkerLock);
+ break;
+ }

15a.
Comment: /sequence worker/sequencesync worker/

~

15b.
Maybe it's better to call this variable 'sequencesync_worker' or
similar because sync worker is too generic

~~~

fetch_remote_sequence_data:

16.
+/*
+ * fetch_remote_sequence_data
+ *
+ * Retrieves sequence data (last_value, log_cnt, page_lsn, and is_called)
+ * from a remote node.

The SELECT of this function fetch a lot more columns, so why are only
these few mentioned in the function comment?

~

17.
+ res = walrcv_exec(conn, cmd.data, REMOTE_SEQ_COL_COUNT, tableRow);
+ pfree(cmd.data);
+
+ if (res->status != WALRCV_OK_TUPLES)
+ ereport(ERROR,
+ errmsg("could not receive sequence list from the publisher: %s",
+    res->err));

That error msg does seem quite right. IIUC, this is just the data from
a single sequence; it is not a "sequence list".

~~~

report_mismatched_sequences:

18.
+static void
+report_mismatched_sequences(StringInfo mismatched_seqs)
+{
+ if (mismatched_seqs->len)
+ {
+ ereport(WARNING,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("parameters differ for the remote and local sequences (%s)
for subscription \"%s\"",
+    mismatched_seqs->data, MySubscription->name),
+ errhint("Alter/Re-create local sequences to have the same parameters
as the remote sequences."));
+
+ resetStringInfo(mismatched_seqs);
+ }
+}

I'm confused. The errhint says "Alter/Re-create local sequences to
have the same parameters", but in the function 'copy_sequence' there
was the code (below) that seems to be already setting the sequence
values (SetSequence) regardless of whether it detected
sequence_mismatch true/false.

+ *sequence_mismatch = !fetch_remote_sequence_data(conn, relid, remoteid,
+ nspname, relname,
+ &seq_log_cnt, &seq_is_called,
+ &seq_page_lsn, &seq_last_value);
+
+ SetSequence(RelationGetRelid(rel), seq_last_value, seq_is_called,
+ seq_log_cnt);

Is the setting regardless like that OK, or just going to lead to weird
integrity errors?

~~~

LogicalRepSyncSequences:

19.
+/*
+ * Start syncing the sequences in the sync worker.
+ */
+static void
+LogicalRepSyncSequences(void)

/sync worker/sequencesync worker/

~~~

20.
+ curr_seq++;
+
+ /*
+ * Have we reached the end of the current batch of sequences, or last
+ * remaining sequences to synchronize?
+ */
+ if (((curr_seq % MAX_SEQUENCES_SYNC_PER_BATCH) == 0) ||
+ curr_seq == seq_count)
+ {
+ /* LOG all the sequences synchronized during current batch. */
+ for (int i = (curr_seq - 1) - ((curr_seq - 1) % MAX_SEQUENCES_SYNC_PER_BATCH);
+ i < curr_seq; i++)
+ {

The calculation is quite tricky.

IMO this might all be simplified if you have another variable like
'batch_seq' that just ranges from 0 -- MAX_SEQUENCES_SYNC_PER_BATCH,
then do something like:

SUGGESTION:

cur_seq++;
batch_seq++;

if (batch_seq >= MAX_SEQUENCES_SYNC_PER_BATCH || cur_seq == seq_count)
{
  /* Process the batch. */

  ...

  /* LOG all the sequences synchronized during the current batch. */
  for (int i = 0; i < batch_seq; i++)
  {
    ...
  }

  /* Prepare for next batch */
  batch_seq = 0;
}

======
src/backend/replication/logical/syncutils.c

21.
 List    *table_states_not_ready = NIL;
+List    *sequence_states_not_ready = NIL;

I thought this declaration belonged more in the sequencesync.c file.

~~~

SyncProcessRelations:

22.
 /*
- * Process possible state change(s) of tables that are being synchronized.
+ * Process possible state change(s) of tables that are being synchronized and
+ * start new tablesync workers for the newly added tables and start new
+ * sequencesync worker for the newly added sequences.
  */

/added tables and start new sequencesync worker/added tables. Also,
start a new sequencesync worker/

~~~

FetchRelationStates:

23.
- * Note: If this function started the transaction (indicated by the parameter)
- * then it is the caller's responsibility to commit it.
+ * Returns true if subscription has 1 or more tables, else false.
  */
 bool
-FetchRelationStates(bool *started_tx)
+FetchRelationStates(void)

Partly because of the name (relations), I felt this might be better to
be a void function and the returned value would be passed back by
references (bool *has_tables).

======
src/backend/replication/logical/worker.c

SetupApplyOrSyncWorker:

24.
+
+ if (am_sequencesync_worker())
+ before_shmem_exit(logicalrep_seqsyncworker_failuretime, (Datum) 0);

There should be a comment saying what this callback is for.

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
Peter Smith
Date:
Hi Vignesh.

Here are some review comments for the patch v20241211-0005.

======
doc/src/sgml/logical-replication.sgml

Section "29.6.1. Sequence Definition Mismatches"

1.
+   <warning>
+    <para>
+     If there are differences in sequence definitions between the publisher and
+     subscriber, a WARNING is logged.
+    </para>
+   </warning>

Maybe this should say *when* this happens.

SUGGESTION
During sequence synchronization, the sequence definitions of the
publisher and the subscriber are compared. A WARNING is logged if any
differences are detected.

~~~

Section "29.6.3. Examples"

2.
Should the Examples section also have an example of ALTER SUBSCRIPTION
... REFRESH PUBLICATION to demonstrate (like in the TAP tests) that if
the sequences are already known, then those are not synchronised?

~~~

Section "29.8. Restrictions"

3.
+     Incremental sequence changes are not replicated.  The data in serial or
+     identity columns backed by sequences will of course be replicated as part
+     of the table, but the sequence itself would still show the start value on
+     the subscriber.  If the subscriber is used as a read-only database, then
+     this should typically not be a problem.  If, however, some kind of
+     switchover or failover to the subscriber database is intended, then the
+     sequences would need to be updated to the latest values, either
by executing
+     <link linkend="sql-altersubscription-params-refresh-publication-sequences">
+     <command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION
SEQUENCES</command></link>
+     or by copying the current data from the publisher (perhaps using
+     <command>pg_dump</command>) or by determining a sufficiently high value
+     from the tables themselves.

I don't know if you need to mention it, or maybe it is too obvious,
but the suggestion here to use "ALTER SUBSCRIPTION ... REFRESH
PUBLICATION SEQUENCES" assumed you've already arranged for the
PUBLICATION to be publishing sequences before this.

======
doc/src/sgml/ref/alter_subscription.sgml

4.
          <para>
-          Specifies whether to copy pre-existing data in the publications
-          that are being subscribed to when the replication starts.
-          The default is <literal>true</literal>.
+          Specifies whether to copy pre-existing data for tables and
synchronize
+          sequences in the publications that are being subscribed to
when the replication
+          starts. The default is <literal>true</literal>.
          </para>

This is talking also about "synchronize sequences" when the
replication starts, but it is a bit confusing. IIUC, the .. REFRESH
PUBLICATION only synchronizes *newly added* sequences anyway, so does
it mean even that will not happen if copy_data=false?

I think this option needs more clarification on how it interacts with
sequences. Also, I don't recall seeing any test for sequences and
copy_data in the patch 0004 TAP tests, so maybe something needs to be
added there too.

~~~

5.
+     <para>
+      See <xref linkend="sequences-out-of-sync"/> for recommendations on how
+      to identify sequences and handle out-of-sync sequences.
+     </para>

/on how to identify sequences and handle out-of-sync sequences./on how
to identify and handle out-of-sync sequences./

======
Kind Regards,
Peter Smith.
Fujitsu Australia



Re: Logical Replication of sequences

From
vignesh C
Date:
On Thu, 19 Dec 2024 at 04:58, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> Here are some review comments for the patch v20241211-0003.
>
> ~~~
>
> 4.
> +/*
> + * Common code to fetch the up-to-date sync state info into the static lists.
> + *
> + * Returns true if subscription has 1 or more tables, else false.
> + *
> + * Note: If this function started the transaction (indicated by the parameter)
> + * then it is the caller's responsibility to commit it.
> + */
> +bool
> +FetchRelationStates(bool *started_tx)
>
> Here is another place where the function name is "relations", but the
> function comment refers to "tables".

In this place the use of tables in comment is intentional, as the
return is based on subscription having any tables, and is not
applicable for sequence.

The rest of the comments are fixed and the changes for the same are
available at the v202412123 version patch shared at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm0FqKMqOdm7tNoT5KgK1BAMeeVnOXrSJ2024TscAbf4Og%40mail.gmail.com

Regards,
Vignesh



Re: Logical Replication of sequences

From
vignesh C
Date:
On Fri, 20 Dec 2024 at 08:05, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh.
>
> Here are some review comments for the patch v20241211-0005.
>
> ======
>
> Section "29.6.3. Examples"
>
> 2.
> Should the Examples section also have an example of ALTER SUBSCRIPTION
> ... REFRESH PUBLICATION to demonstrate (like in the TAP tests) that if
> the sequences are already known, then those are not synchronised?

I felt it is not required, let's not add too many examples.

> Section "29.8. Restrictions"
>
> 3.
> +     Incremental sequence changes are not replicated.  The data in serial or
> +     identity columns backed by sequences will of course be replicated as part
> +     of the table, but the sequence itself would still show the start value on
> +     the subscriber.  If the subscriber is used as a read-only database, then
> +     this should typically not be a problem.  If, however, some kind of
> +     switchover or failover to the subscriber database is intended, then the
> +     sequences would need to be updated to the latest values, either
> by executing
> +     <link linkend="sql-altersubscription-params-refresh-publication-sequences">
> +     <command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION
> SEQUENCES</command></link>
> +     or by copying the current data from the publisher (perhaps using
> +     <command>pg_dump</command>) or by determining a sufficiently high value
> +     from the tables themselves.
>
> I don't know if you need to mention it, or maybe it is too obvious,
> but the suggestion here to use "ALTER SUBSCRIPTION ... REFRESH
> PUBLICATION SEQUENCES" assumed you've already arranged for the
> PUBLICATION to be publishing sequences before this.

I felt it is obvious, it need not be mentioned.

> ======
> doc/src/sgml/ref/alter_subscription.sgml
>
> 4.
>           <para>
> -          Specifies whether to copy pre-existing data in the publications
> -          that are being subscribed to when the replication starts.
> -          The default is <literal>true</literal>.
> +          Specifies whether to copy pre-existing data for tables and
> synchronize
> +          sequences in the publications that are being subscribed to
> when the replication
> +          starts. The default is <literal>true</literal>.
>           </para>
>
> This is talking also about "synchronize sequences" when the
> replication starts, but it is a bit confusing. IIUC, the .. REFRESH
> PUBLICATION only synchronizes *newly added* sequences anyway, so does
> it mean even that will not happen if copy_data=false?
>
> I think this option needs more clarification on how it interacts with
> sequences. Also, I don't recall seeing any test for sequences and
> copy_data in the patch 0004 TAP tests, so maybe something needs to be
> added there too.

This case is similar to how tables are handled, that is add the table
in subscription_rel and mark it as ready without updating the data.
Since tables and sequences behave the same way I have kept the
documentation the same. I have added a test for this in the 0004 TAP
test.

The rest of the comments are fixed and the changes for the same are
available in the v202412125 version patch at [1].
[1] - https://www.postgresql.org/message-id/CALDaNm0PbSAQvs34D%2BJ63SgmRUzDQHZ1W4aeW_An9pR_tXJnRA%40mail.gmail.com

Regards,
Vignesh