[PATCH] Support automatic sequence replication

Started by Ajin Cherianabout 1 month ago72 messages
Jump to latest
#1Ajin Cherian
itsajin@gmail.com

Hello hackers,

I'd like to propose an improvement to the sequence replication feature
that was committed in [1]https://github.com/postgres/postgres/commit/5509055d6956745532e65ab218e15b99d87d66ce.

The current implementation synchronizes sequences during initial
subscription setup, but the sequence sync worker exits after this
initial sync. This means that as sequences advance on the publisher,
they drift from the subscriber values over time. Users must manually
run ALTER SUBSCRIPTION ... REFRESH SEQUENCES to resynchronize, which
requires monitoring and intervention.

Proposed Enhancement:

This patch changes the sequence sync worker to run continuously
throughout the subscription lifetime, automatically detecting and
correcting sequence drift. The key changes are:

1. The sequence sync worker remains running instead of exiting after
initial sync, periodically checking for and synchronizing drifted
sequences.

2. The worker uses an exponential backoff strategy - starting at 2
seconds, doubling up to a maximum of 30 seconds when sequences are in
sync, and resetting to the minimum interval when drift is detected.

3. Since synchronization is now automatic, ALTER SUBSCRIPTION ...
REFRESH SEQUENCES is no longer needed and has been removed.

The patch modifies documentation to reflect the new behavior, removes
the REFRESH SEQUENCES command from the grammar and subscription
commands, and implements the continuous monitoring loop in
sequencesync.c. Tap tests have been updated to verify automatic
synchronization rather than manual refresh.

The attached v2 patch is attached and ready for review.

Thoughts and feedback are welcome!

[1]: https://github.com/postgres/postgres/commit/5509055d6956745532e65ab218e15b99d87d66ce

Best regards,
Ajin Cherian
Fujitsu Australia

Attachments:

v2-0001-Support-automatic-sequence-replication.patchapplication/octet-stream; name=v2-0001-Support-automatic-sequence-replication.patchDownload+235-274
#2shveta malik
shveta.malik@gmail.com
In reply to: Ajin Cherian (#1)
Re: [PATCH] Support automatic sequence replication

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:

Hello hackers,

I'd like to propose an improvement to the sequence replication feature
that was committed in [1].

The current implementation synchronizes sequences during initial
subscription setup, but the sequence sync worker exits after this
initial sync. This means that as sequences advance on the publisher,
they drift from the subscriber values over time. Users must manually
run ALTER SUBSCRIPTION ... REFRESH SEQUENCES to resynchronize, which
requires monitoring and intervention.

Proposed Enhancement:

This patch changes the sequence sync worker to run continuously
throughout the subscription lifetime, automatically detecting and
correcting sequence drift. The key changes are:

1. The sequence sync worker remains running instead of exiting after
initial sync, periodically checking for and synchronizing drifted
sequences.

2. The worker uses an exponential backoff strategy - starting at 2
seconds, doubling up to a maximum of 30 seconds when sequences are in
sync, and resetting to the minimum interval when drift is detected.

3. Since synchronization is now automatic, ALTER SUBSCRIPTION ...
REFRESH SEQUENCES is no longer needed and has been removed.

The patch modifies documentation to reflect the new behavior, removes
the REFRESH SEQUENCES command from the grammar and subscription
commands, and implements the continuous monitoring loop in
sequencesync.c. Tap tests have been updated to verify automatic
synchronization rather than manual refresh.

The attached v2 patch is attached and ready for review.

Thoughts and feedback are welcome!

[1] - https://github.com/postgres/postgres/commit/5509055d6956745532e65ab218e15b99d87d66ce

Thanks for the patch.

+1 for the overall idea of patch that once a subscription is created
which subscribes to sequences, a sequence sync worker is started which
continuously syncs the sequences. This makes usage of REFRESH
SEQUENCES redundant and thus it is removed. I am still reviewing the
design choice here, and will post my comments soon (if any).

By quick validation, few issues in current implementation:

1)
If the sequence sync worker exits due to some issue (or killed or
server restarts), sequence-sync worker is not started again by apply
worker unless there is a sequence in INIT state i.e. synchronization
of sequences in READY state stops. IIUC, the logic of
ProcessSequencesForSync() needs to change to start seq sync worker
irrespective of state of sequences.

2)
There is some issue in how LOGs (DEBUGs) are getting generated.

a) Even if there is no drift, it still keeps on dumping:
"logical replication sequence synchronization for subscription "sub1"
- total unsynchronized: 3"

b)
When there is a drift in say single sequence, it puts rest (which are
in sync) to "missing" section:
"logical replication sequence synchronization for subscription "sub1"
- batch #1 = 3 attempted, 1 succeeded, 0 mismatched, 0 insufficient
permission, 2 missing from publisher, 0 skipped"

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

thanks
Shveta

#3Ajin Cherian
itsajin@gmail.com
In reply to: shveta malik (#2)
Re: [PATCH] Support automatic sequence replication

On Tue, Feb 3, 2026 at 9:22 PM shveta malik <shveta.malik@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:
Thanks for the patch.

+1 for the overall idea of patch that once a subscription is created
which subscribes to sequences, a sequence sync worker is started which
continuously syncs the sequences. This makes usage of REFRESH
SEQUENCES redundant and thus it is removed. I am still reviewing the
design choice here, and will post my comments soon (if any).

Thanks!

By quick validation, few issues in current implementation:

1)
If the sequence sync worker exits due to some issue (or killed or
server restarts), sequence-sync worker is not started again by apply
worker unless there is a sequence in INIT state i.e. synchronization
of sequences in READY state stops. IIUC, the logic of
ProcessSequencesForSync() needs to change to start seq sync worker
irrespective of state of sequences.

Yes, I fixed this. I've changed FetchRelationStates to fetch sequences
in ANY state and not just ones in NON READY state.

2)
There is some issue in how LOGs (DEBUGs) are getting generated.

a) Even if there is no drift, it still keeps on dumping:
"logical replication sequence synchronization for subscription "sub1"
- total unsynchronized: 3"

Removed this.

b)
When there is a drift in say single sequence, it puts rest (which are
in sync) to "missing" section:
"logical replication sequence synchronization for subscription "sub1"
- batch #1 = 3 attempted, 1 succeeded, 0 mismatched, 0 insufficient
permission, 2 missing from publisher, 0 skipped"

Fixed, and added a new section called "no drift". Also now this debug
message is printed every time the worker attempts to synchronize
sequences. also mentioning the state of the sequences being synced.

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

Well, one way is to wait for a debug message that says that all the
attempted sequences are in the "no drift" state. Also remote
sequence's LSN is updated in pg_subscription_rel for each sequence.
Let me know if you have anything more in mind. One option is to leave
the ALTER SUBSCRIPTION..REFRESH SEQUENCE in place, that will change
the state of all the sequences to the INIT state, and the user can
then wait for the sequences to change state to READY.

Attaching patch v3 addressing the above comments.

regards,
Ajin Cherian
Fujitsu Australia

Attachments:

v3-0001-Support-automatic-sequence-replication.patchapplication/octet-stream; name=v3-0001-Support-automatic-sequence-replication.patchDownload+260-311
#4shveta malik
shveta.malik@gmail.com
In reply to: Ajin Cherian (#3)
Re: [PATCH] Support automatic sequence replication

We revisited the design of this patch. Sharing my thoughts and
analysis here. Any feedback is appreciated.

Background:
-----------------------
Previously, sequence synchronization was triggered during CREATE
SUBSCRIPTION, ALTER SUBSCRIPTION REFRESH PUBLICATION, and REFRESH
SEQUENCES. A sequence-sync worker was started whenever a sequence
entered the INIT state, which could also occur if a previous sync
failed.

Therefore, a mechanism was required to continuously scan
pg_subscription_rel and start a sequence-sync worker for a
subscription whenever any sequence was found in INIT. Since the apply
worker already performs this role for table-sync workers, the same
infrastructure was reused for sequence-sync workers. Using the
launcher for this purpose was rejected, as it would have required
overloading the launcher with logic to repeatedly inspect
pg_subscription_rel and decide whether to start a worker for each
sequence (see discussion at [1]/messages/by-id/CAA4eK1+p=M+5NAq5VSxD4_XyE1MBTKwU40RD1cL9PgpbELKBRQ@m%E2%80%A6).

Current scenario:
-----------------------
The requirement is different now: the sequence-sync worker is now
expected to run continuously, independent of sequence state. This
makes us revisit our design choices and re-analyze whether we can do
it in the launcher.

The primary benefit of starting the sequence-sync worker from the
launcher would be avoiding an extra apply worker for sequence-only
subscriptions. However, this approach introduces challenges. The
launcher currently accesses only global pg_subscription and does not
establish a database connection (see [2]/* * Establish connection to nailed catalogs (we only ever access * pg_subscription). */ BackgroundWorkerInitializeConnection(NULL, NULL, 0);). To decide whether to start
an apply worker, a sequence-sync worker, or both, the launcher would
need to access pg_subscription_rel, which requires a database
connection. It is unclear which database the launcher should connect
to, since subscriptions can target different databases. Another option
would be to explicitly feed this information to the launcher during
CREATE SUBSCRIPTION and REFRESH PUBLICATION by having an additional
column in pg_subscription indicating object_type: table_only,
seq_only, both. This would undoubtedly add complexity. Also, I am
unsure if it is a good idea to add an additional field to global
catalog pg_subscription for this purpose.

That said, it is reasonable to expect that users who create a
publication for ALL SEQUENCES will typically have only a single
publication–subscription pair. In such cases, the overhead of an extra
apply worker per subscription, along with a sequence-sync worker, is
likely acceptable.
~~

Considering the above, starting the sequence-sync worker from the
launcher seems feasible ((though it would require a more detailed
analysis), but it comes with its own complexities. OTOH, (potential)
significant extra worker overhead, which could impact the system,
would only occur if a large number of 'sequence-only' subscriptions
were created. It is unclear whether there is ever a need for multiple
ALL-SEQUENCE subscriptions, or whether business requirements would
need subscribing to multiple machines for ALL-SEQUENCES, which would
necessitate multiple such subscriptions.

Given this, it seems reasonable to continue with the current design of
starting the sequence-sync worker from the apply worker. We may think
of other approaches if there is any objection or user-feedback for
this approach.

~~

[1]: /messages/by-id/CAA4eK1+p=M+5NAq5VSxD4_XyE1MBTKwU40RD1cL9PgpbELKBRQ@m%E2%80%A6
[2]: /* * Establish connection to nailed catalogs (we only ever access * pg_subscription). */ BackgroundWorkerInitializeConnection(NULL, NULL, 0);
/*
* Establish connection to nailed catalogs (we only ever access
* pg_subscription).
*/
BackgroundWorkerInitializeConnection(NULL, NULL, 0);

thanks
Shveta

#5Peter Smith
smithpb2250@gmail.com
In reply to: Ajin Cherian (#3)
Re: [PATCH] Support automatic sequence replication

Hi Ajin.

Some review comments for patch v2-0001.

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

copy_sequences:

1.
+ return drift_detected;

This seems a bit strange. And it is not doing quite what the function
comment says it does. I felt you should have another variable like
'sequences_copied', which is set to true only when that
'batch_succeeded_count++' is incremented. This is what you ultimately
want to return. IMO, the variable 'drift_detected' isn't needed at
all.

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

2.
##########
## ALTER SUBSCRIPTION ... REFRESH PUBLICATION should cause sync of new
# sequences of the publisher.
##########

# Create a new sequence 'regress_s2', and update existing sequence 'regress_s1'
$node_publisher->safe_psql(5.
'postgres', qq(
CREATE SEQUENCE regress_s2;
INSERT INTO regress_seq_test SELECT nextval('regress_s2') FROM
generate_series(1,100);

-- Existing sequence
INSERT INTO regress_seq_test SELECT nextval('regress_s1') FROM
generate_series(1,100);
));

~

IIUC, you are no longer sync of testing "existing sequences" in this
test part, so you might also want to remove that comment and INSERT
for 'regress_s1'.

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

#6shveta malik
shveta.malik@gmail.com
In reply to: Ajin Cherian (#3)
Re: [PATCH] Support automatic sequence replication

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:22 PM shveta malik <shveta.malik@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:
Thanks for the patch.

+1 for the overall idea of patch that once a subscription is created
which subscribes to sequences, a sequence sync worker is started which
continuously syncs the sequences. This makes usage of REFRESH
SEQUENCES redundant and thus it is removed. I am still reviewing the
design choice here, and will post my comments soon (if any).

Thanks!

By quick validation, few issues in current implementation:

1)
If the sequence sync worker exits due to some issue (or killed or
server restarts), sequence-sync worker is not started again by apply
worker unless there is a sequence in INIT state i.e. synchronization
of sequences in READY state stops. IIUC, the logic of
ProcessSequencesForSync() needs to change to start seq sync worker
irrespective of state of sequences.

Yes, I fixed this. I've changed FetchRelationStates to fetch sequences
in ANY state and not just ones in NON READY state.

2)
There is some issue in how LOGs (DEBUGs) are getting generated.

a) Even if there is no drift, it still keeps on dumping:
"logical replication sequence synchronization for subscription "sub1"
- total unsynchronized: 3"

Removed this.

b)
When there is a drift in say single sequence, it puts rest (which are
in sync) to "missing" section:
"logical replication sequence synchronization for subscription "sub1"
- batch #1 = 3 attempted, 1 succeeded, 0 mismatched, 0 insufficient
permission, 2 missing from publisher, 0 skipped"

Fixed, and added a new section called "no drift". Also now this debug
message is printed every time the worker attempts to synchronize
sequences. also mentioning the state of the sequences being synced.

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

Well, one way is to wait for a debug message that says that all the
attempted sequences are in the "no drift" state. Also remote
sequence's LSN is updated in pg_subscription_rel for each sequence.
Let me know if you have anything more in mind. One option is to leave
the ALTER SUBSCRIPTION..REFRESH SEQUENCE in place, that will change
the state of all the sequences to the INIT state, and the user can
then wait for the sequences to change state to READY.

I think this point needs more analysis. I am analyzing and discussing
it further.

Attaching patch v3 addressing the above comments.

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

thanks
Shveta

#7shveta malik
shveta.malik@gmail.com
In reply to: shveta malik (#6)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 6, 2026 at 2:45 PM shveta malik <shveta.malik@gmail.com> wrote:

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:22 PM shveta malik <shveta.malik@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:
Thanks for the patch.

+1 for the overall idea of patch that once a subscription is created
which subscribes to sequences, a sequence sync worker is started which
continuously syncs the sequences. This makes usage of REFRESH
SEQUENCES redundant and thus it is removed. I am still reviewing the
design choice here, and will post my comments soon (if any).

Thanks!

By quick validation, few issues in current implementation:

1)
If the sequence sync worker exits due to some issue (or killed or
server restarts), sequence-sync worker is not started again by apply
worker unless there is a sequence in INIT state i.e. synchronization
of sequences in READY state stops. IIUC, the logic of
ProcessSequencesForSync() needs to change to start seq sync worker
irrespective of state of sequences.

Yes, I fixed this. I've changed FetchRelationStates to fetch sequences
in ANY state and not just ones in NON READY state.

2)
There is some issue in how LOGs (DEBUGs) are getting generated.

a) Even if there is no drift, it still keeps on dumping:
"logical replication sequence synchronization for subscription "sub1"
- total unsynchronized: 3"

Removed this.

b)
When there is a drift in say single sequence, it puts rest (which are
in sync) to "missing" section:
"logical replication sequence synchronization for subscription "sub1"
- batch #1 = 3 attempted, 1 succeeded, 0 mismatched, 0 insufficient
permission, 2 missing from publisher, 0 skipped"

Fixed, and added a new section called "no drift". Also now this debug
message is printed every time the worker attempts to synchronize
sequences. also mentioning the state of the sequences being synced.

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

Well, one way is to wait for a debug message that says that all the
attempted sequences are in the "no drift" state. Also remote
sequence's LSN is updated in pg_subscription_rel for each sequence.
Let me know if you have anything more in mind. One option is to leave
the ALTER SUBSCRIPTION..REFRESH SEQUENCE in place, that will change
the state of all the sequences to the INIT state, and the user can
then wait for the sequences to change state to READY.

I think this point needs more analysis. I am analyzing and discussing
it further.

Attaching patch v3 addressing the above comments.

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

Correction to last line:
There, we could check whether *the current susbcription* has any
sequences and, if a worker is not already running,start one.

thanks
Shveta

#8vignesh C
vignesh21@gmail.com
In reply to: shveta malik (#2)
Re: [PATCH] Support automatic sequence replication

On Tue, 3 Feb 2026 at 15:52, shveta malik <shveta.malik@gmail.com> wrote:

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

One way to address the upgrade issue is to record the publisher LSN at
the completion of the most recent SEQUENCE SYNC and persist it in the
subscription metadata.
During an upgrade, the following check should be performed: If the
last recorded sequence-sync LSN is not ahead of the apply worker's
LSN, report a clear error instructing the user to disable the
subscription and explicitly run: ALTER SUBSCRIPTION … REFRESH
SEQUENCES.
Additionally, the existing ALTER SUBSCRIPTION … REFRESH SEQUENCES
command should be enhanced so that it can be executed on disabled
subscriptions and perform sequence synchronization independently,
without relying on the sequence sync worker. Supporting execution on a
disabled subscription is necessary because, if REFRESH SEQUENCES is
run while the subscription is enabled, the apply worker may start
immediately, ingest new transactions, and advance the replication
slot's LSN beyond the point at which sequences were last synchronized
again.

Note: This approach may conservatively report that sequences need to
be synchronized even when no sequence values have actually changed.
This limitation is inherent to the design, as during an upgrade we
don't connect to the publisher and decode WAL between the two LSNs to
determine whether any sequence changes actually occurred.

Regards,
Vignesh

#9Peter Smith
smithpb2250@gmail.com
In reply to: Ajin Cherian (#3)
Re: [PATCH] Support automatic sequence replication

Some review comments for v3-0001.

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

copy_sequences:

1.
- if (sync_status == COPYSEQ_SUCCESS)
+
+ /*
+ * For sequences in READY state, only sync if there's drift
+ */
+ if (relstate == SUBREL_STATE_READY && sync_status == COPYSEQ_SUCCESS)
+ {
+ should_sync = check_sequence_drift(seqinfo);
+ if (should_sync)
+ drift_detected = true;
+ }
+
+ if (sync_status == COPYSEQ_SUCCESS && should_sync)
  sync_status = copy_sequence(seqinfo,
- sequence_rel->rd_rel->relowner);
+ sequence_rel->rd_rel->relowner,
+ relstate);
+ else if (sync_status == COPYSEQ_SUCCESS && !should_sync)
+ sync_status = COPYSEQ_NOWORK;

I'm struggling somewhat to follow this logic.

For example, every condition refers to COPYSEQ_SUCCESS -- is that
really necessary; can't this all be boiled down to something like
below?

SUGGESTION

/*
* For sequences in INIT state, always sync.
* Otherwise, for sequences in READY state, only sync if there's drift.
*/
if (sync_status == COPYSEQ_SUCCESS)
{
if ((relstate == SUBREL_STATE_INIT) || check_sequence_drift(seqinfo))
sync_status = copy_sequence(...);
else
sync_status = COPYSEQ_NOWORK;
}

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

#10shveta malik
shveta.malik@gmail.com
In reply to: shveta malik (#7)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 6, 2026 at 2:47 PM shveta malik <shveta.malik@gmail.com> wrote:

On Fri, Feb 6, 2026 at 2:45 PM shveta malik <shveta.malik@gmail.com> wrote:

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:22 PM shveta malik <shveta.malik@gmail.com> wrote:

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:
Thanks for the patch.

+1 for the overall idea of patch that once a subscription is created
which subscribes to sequences, a sequence sync worker is started which
continuously syncs the sequences. This makes usage of REFRESH
SEQUENCES redundant and thus it is removed. I am still reviewing the
design choice here, and will post my comments soon (if any).

Thanks!

By quick validation, few issues in current implementation:

1)
If the sequence sync worker exits due to some issue (or killed or
server restarts), sequence-sync worker is not started again by apply
worker unless there is a sequence in INIT state i.e. synchronization
of sequences in READY state stops. IIUC, the logic of
ProcessSequencesForSync() needs to change to start seq sync worker
irrespective of state of sequences.

Yes, I fixed this. I've changed FetchRelationStates to fetch sequences
in ANY state and not just ones in NON READY state.

2)
There is some issue in how LOGs (DEBUGs) are getting generated.

a) Even if there is no drift, it still keeps on dumping:
"logical replication sequence synchronization for subscription "sub1"
- total unsynchronized: 3"

Removed this.

b)
When there is a drift in say single sequence, it puts rest (which are
in sync) to "missing" section:
"logical replication sequence synchronization for subscription "sub1"
- batch #1 = 3 attempted, 1 succeeded, 0 mismatched, 0 insufficient
permission, 2 missing from publisher, 0 skipped"

Fixed, and added a new section called "no drift". Also now this debug
message is printed every time the worker attempts to synchronize
sequences. also mentioning the state of the sequences being synced.

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

Well, one way is to wait for a debug message that says that all the
attempted sequences are in the "no drift" state. Also remote
sequence's LSN is updated in pg_subscription_rel for each sequence.
Let me know if you have anything more in mind. One option is to leave
the ALTER SUBSCRIPTION..REFRESH SEQUENCE in place, that will change
the state of all the sequences to the INIT state, and the user can
then wait for the sequences to change state to READY.

I think this point needs more analysis. I am analyzing and discussing
it further.

Attaching patch v3 addressing the above comments.

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

Correction to last line:
There, we could check whether *the current susbcription* has any
sequences and, if a worker is not already running,start one.

Few more comments:

1)
+ /* Run sync for sequences in READY state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_READY);
+
+ /* Call initial sync for sequences in INIT state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_INIT);

Above logic means we ping primary twice for one seq-sync cycle? Is it
possible to do it in below way:

Get all sequences from the publisher in one go.
If local-seq's state is INIT, copy those sequences, move the state to READY.
If local-seq's state is READY, check the drift and proceed accordingly.

i.e, there should be a single call to LogicalRepSyncSequences() and
relstate shouldn't even be an argument.

2)
GetSequence:
+ /* Open and lock the sequence relation */
+ seqrel = table_open(relid, AccessShareLock);

GetSequence is called from check_sequence_drift and
check_sequence_drift() from copy_sequences(). In copy_sequences(), we
already open the seq's (localrelid) table in
get_and_validate_seq_info() and give it as output (see sequence_rel).
Same can be passed to this instead of trying opening the table again.

3)
+ /* Verify it's actually a sequence */
+ if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ RelationGetRelationName(seqrel))));

Do we need this check? IIUC, sequence_rel is opened using
RowExclusiveLock in get_and_validate_seq_info() and thus should not
hit this case so that it becomes non-sequence later on. If required,
we can have Assert.

I can review further once these and previous comments are addressed.

thanks
Shveta

#11shveta malik
shveta.malik@gmail.com
In reply to: vignesh C (#8)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 6, 2026 at 6:07 PM vignesh C <vignesh21@gmail.com> wrote:

On Tue, 3 Feb 2026 at 15:52, shveta malik <shveta.malik@gmail.com> wrote:

3)
If a sequence sync worker is taking a nap, and subscription is
disabled or the server is stopped just before upgrade, how is the user
supposed to know that sequences are synced at the end?

One way to address the upgrade issue is to record the publisher LSN at
the completion of the most recent SEQUENCE SYNC and persist it in the
subscription metadata.
During an upgrade, the following check should be performed: If the
last recorded sequence-sync LSN is not ahead of the apply worker's
LSN, report a clear error instructing the user to disable the
subscription and explicitly run: ALTER SUBSCRIPTION … REFRESH
SEQUENCES.

I guess here we are trying to target the problem where a table is
dependent upon sequence and table data is replicated while seq's is
not. And post upgrade, say if somehow the same sequence is used on the
subscriber to insert data in the table, values can go back.

Additionally, the existing ALTER SUBSCRIPTION … REFRESH SEQUENCES
command should be enhanced so that it can be executed on disabled
subscriptions and perform sequence synchronization independently,
without relying on the sequence sync worker. Supporting execution on a
disabled subscription is necessary because, if REFRESH SEQUENCES is
run while the subscription is enabled, the apply worker may start
immediately, ingest new transactions, and advance the replication
slot's LSN beyond the point at which sequences were last synchronized
again.

Note: This approach may conservatively report that sequences need to
be synchronized even when no sequence values have actually changed.
This limitation is inherent to the design, as during an upgrade we
don't connect to the publisher and decode WAL between the two LSNs to
determine whether any sequence changes actually occurred.

I like the idea. In particular, I like the approach of providing a
REFRESH SEQUENCE command to the user. Even if we don’t implement the
check during the upgrade process, we can clearly document that users
should verify sequence drift before upgrading. If any sequence drift
is detected, they should run REFRESH-SEQ manually.

thanks
Shveta

#12Ajin Cherian
itsajin@gmail.com
In reply to: shveta malik (#10)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 6, 2026 at 8:15 PM shveta malik <shveta.malik@gmail.com> wrote:

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

I've moved this to LogicalRepApplyLoop()

On Mon, Feb 9, 2026 at 10:55 AM Peter Smith <smithpb2250@gmail.com> wrote:

Some review comments for v3-0001.

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

copy_sequences:

1.
- if (sync_status == COPYSEQ_SUCCESS)
+
+ /*
+ * For sequences in READY state, only sync if there's drift
+ */
+ if (relstate == SUBREL_STATE_READY && sync_status == COPYSEQ_SUCCESS)
+ {
+ should_sync = check_sequence_drift(seqinfo);
+ if (should_sync)
+ drift_detected = true;
+ }
+
+ if (sync_status == COPYSEQ_SUCCESS && should_sync)
sync_status = copy_sequence(seqinfo,
- sequence_rel->rd_rel->relowner);
+ sequence_rel->rd_rel->relowner,
+ relstate);
+ else if (sync_status == COPYSEQ_SUCCESS && !should_sync)
+ sync_status = COPYSEQ_NOWORK;

I'm struggling somewhat to follow this logic.

For example, every condition refers to COPYSEQ_SUCCESS -- is that
really necessary; can't this all be boiled down to something like
below?

SUGGESTION

/*
* For sequences in INIT state, always sync.
* Otherwise, for sequences in READY state, only sync if there's drift.
*/
if (sync_status == COPYSEQ_SUCCESS)
{
if ((relstate == SUBREL_STATE_INIT) || check_sequence_drift(seqinfo))
sync_status = copy_sequence(...);
else
sync_status = COPYSEQ_NOWORK;
}

Changed as suggested.

On Wed, Feb 11, 2026 at 2:30 PM shveta malik <shveta.malik@gmail.com> wrote:

On Fri, Feb 6, 2026 at 2:47 PM shveta malik <shveta.malik@gmail.com> wrote:

Few more comments:

1)
+ /* Run sync for sequences in READY state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_READY);
+
+ /* Call initial sync for sequences in INIT state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_INIT);

Above logic means we ping primary twice for one seq-sync cycle? Is it
possible to do it in below way:

Get all sequences from the publisher in one go.
If local-seq's state is INIT, copy those sequences, move the state to READY.
If local-seq's state is READY, check the drift and proceed accordingly.

i.e, there should be a single call to LogicalRepSyncSequences() and
relstate shouldn't even be an argument.

Changed as suggested.

2)
GetSequence:
+ /* Open and lock the sequence relation */
+ seqrel = table_open(relid, AccessShareLock);

GetSequence is called from check_sequence_drift and
check_sequence_drift() from copy_sequences(). In copy_sequences(), we
already open the seq's (localrelid) table in
get_and_validate_seq_info() and give it as output (see sequence_rel).
Same can be passed to this instead of trying opening the table again.

3)
+ /* Verify it's actually a sequence */
+ if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ RelationGetRelationName(seqrel))));

Changed these.

Other than these, I've changed seqinfos from being a global static
list to a list that gets passed around and destroyed in each
iteration.
I haven't addressed the upgrade issue raised by Vignesh and Shveta in
this patch. I will address that in the next patch.
Here's patch v4 addressing the above comments.

regards,
Ajin Cherian
Fujitsu Australia

Attachments:

v4-0001-Support-automatic-sequence-replication.patchapplication/octet-stream; name=v4-0001-Support-automatic-sequence-replication.patchDownload+294-324
#13shveta malik
shveta.malik@gmail.com
In reply to: Ajin Cherian (#12)
Re: [PATCH] Support automatic sequence replication

On Thu, Feb 12, 2026 at 2:54 PM Ajin Cherian <itsajin@gmail.com> wrote:

On Fri, Feb 6, 2026 at 8:15 PM shveta malik <shveta.malik@gmail.com> wrote:

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

I've moved this to LogicalRepApplyLoop()

On Mon, Feb 9, 2026 at 10:55 AM Peter Smith <smithpb2250@gmail.com> wrote:

Some review comments for v3-0001.

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

copy_sequences:

1.
- if (sync_status == COPYSEQ_SUCCESS)
+
+ /*
+ * For sequences in READY state, only sync if there's drift
+ */
+ if (relstate == SUBREL_STATE_READY && sync_status == COPYSEQ_SUCCESS)
+ {
+ should_sync = check_sequence_drift(seqinfo);
+ if (should_sync)
+ drift_detected = true;
+ }
+
+ if (sync_status == COPYSEQ_SUCCESS && should_sync)
sync_status = copy_sequence(seqinfo,
- sequence_rel->rd_rel->relowner);
+ sequence_rel->rd_rel->relowner,
+ relstate);
+ else if (sync_status == COPYSEQ_SUCCESS && !should_sync)
+ sync_status = COPYSEQ_NOWORK;

I'm struggling somewhat to follow this logic.

For example, every condition refers to COPYSEQ_SUCCESS -- is that
really necessary; can't this all be boiled down to something like
below?

SUGGESTION

/*
* For sequences in INIT state, always sync.
* Otherwise, for sequences in READY state, only sync if there's drift.
*/
if (sync_status == COPYSEQ_SUCCESS)
{
if ((relstate == SUBREL_STATE_INIT) || check_sequence_drift(seqinfo))
sync_status = copy_sequence(...);
else
sync_status = COPYSEQ_NOWORK;
}

Changed as suggested.

On Wed, Feb 11, 2026 at 2:30 PM shveta malik <shveta.malik@gmail.com> wrote:

On Fri, Feb 6, 2026 at 2:47 PM shveta malik <shveta.malik@gmail.com> wrote:

Few more comments:

1)
+ /* Run sync for sequences in READY state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_READY);
+
+ /* Call initial sync for sequences in INIT state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_INIT);

Above logic means we ping primary twice for one seq-sync cycle? Is it
possible to do it in below way:

Get all sequences from the publisher in one go.
If local-seq's state is INIT, copy those sequences, move the state to READY.
If local-seq's state is READY, check the drift and proceed accordingly.

i.e, there should be a single call to LogicalRepSyncSequences() and
relstate shouldn't even be an argument.

Changed as suggested.

2)
GetSequence:
+ /* Open and lock the sequence relation */
+ seqrel = table_open(relid, AccessShareLock);

GetSequence is called from check_sequence_drift and
check_sequence_drift() from copy_sequences(). In copy_sequences(), we
already open the seq's (localrelid) table in
get_and_validate_seq_info() and give it as output (see sequence_rel).
Same can be passed to this instead of trying opening the table again.

3)
+ /* Verify it's actually a sequence */
+ if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ RelationGetRelationName(seqrel))));

Changed these.

Other than these, I've changed seqinfos from being a global static
list to a list that gets passed around and destroyed in each
iteration.
I haven't addressed the upgrade issue raised by Vignesh and Shveta in
this patch. I will address that in the next patch.

Thanks Ajin, I will review. I will suggest waiting for others'
feedback before doing anything about the upgrade issue. Meanwhile, we
can try to improve the current patch itself.

thanks
Shveta

#14Peter Smith
smithpb2250@gmail.com
In reply to: Ajin Cherian (#12)
Re: [PATCH] Support automatic sequence replication

Hi Ajin.

Some review comments for patch v4-0001

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

GetSequence:

1.
+/*
+ * Read the current sequence values (last_value and is_called)
+ *
+ * This is a read-only operation that acquires AccessShareLock on the sequence.
+ * Used by logical replication sequence synchronization to detect drift.
+ */

The comment seems stale. e.g. the function is not acquiring a lock
anymore, contrary to what that comment says.

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

2.
-static List *seqinfos = NIL;

The removal of this global was not strictly part of this patch; it is
more like a prerequisite to make everything tidier, so your new code
does not go further down that track of side-affecting a global. From
that POV, I thought this global removal should be implemented as a
first/separate (0001) patch so that it might be quickly reviewed and
committed independently of the new seq-sync logic.

~~~

LogicalRepSyncSequences:

3.
+ /* Error on unexpected states */
+ if (relstate != SUBREL_STATE_INIT && relstate != SUBREL_STATE_READY)
+ {
+ table_close(sequence_rel, NoLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("unexpected relstate '%c' for sequence \"%s.%s\" in
subscription \"%s\"",
+ relstate,
+ get_namespace_name(RelationGetNamespace(sequence_rel)),
+ RelationGetRelationName(sequence_rel),
+ MySubscription->name)));
+ }
+

How is this possible? Should it just be Assert?

~~~

start_sequence_sync:

4.
+ /*
+ * Synchronize all sequences (both READY and INIT states).
+ * The function will process INIT sequences first, then READY sequences.
+ */
+ sequence_copied = LogicalRepSyncSequences(LogRepWorkerWalRcvConn);

Why is talking about the processing order relevant?

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

5.
+ /* Check if any new sequences need syncing */
+ ProcessSequencesForSync();
+

Maybe don't say "new" because IIUC it also handles older sequences
where the values have drifted.

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

6.
-$result = $node_publisher->safe_psql(
- 'postgres', qq(
- SELECT last_value, is_called FROM regress_s1;
-));
-is($result, '200|t', 'Check sequence value in the publisher');
-
-# Check - existing sequence ('regress_s1') is not synced
-$result = $node_subscriber->safe_psql(
- 'postgres', qq(
- SELECT last_value, is_called FROM regress_s1;
-));
-is($result, '100|t', 'REFRESH PUBLICATION will not sync existing sequence');
-

Since you are no longer testing "existing sequences" in this test
part, should you also remove the earlier INSERT for 'regress_s1'?

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

#15Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Ajin Cherian (#1)
Re: [PATCH] Support automatic sequence replication

Hi,

On Tue, Feb 3, 2026 at 9:18 AM Ajin Cherian <itsajin@gmail.com> wrote:

Hello hackers,

I'd like to propose an improvement to the sequence replication feature
that was committed in [1].

The current implementation synchronizes sequences during initial
subscription setup, but the sequence sync worker exits after this
initial sync. This means that as sequences advance on the publisher,
they drift from the subscriber values over time. Users must manually
run ALTER SUBSCRIPTION ... REFRESH SEQUENCES to resynchronize, which
requires monitoring and intervention.

Proposed Enhancement:

This patch changes the sequence sync worker to run continuously
throughout the subscription lifetime, automatically detecting and
correcting sequence drift. The key changes are:

1. The sequence sync worker remains running instead of exiting after
initial sync, periodically checking for and synchronizing drifted
sequences.

2. The worker uses an exponential backoff strategy - starting at 2
seconds, doubling up to a maximum of 30 seconds when sequences are in
sync, and resetting to the minimum interval when drift is detected.

3. Since synchronization is now automatic, ALTER SUBSCRIPTION ...
REFRESH SEQUENCES is no longer needed and has been removed.

The patch modifies documentation to reflect the new behavior, removes
the REFRESH SEQUENCES command from the grammar and subscription
commands, and implements the continuous monitoring loop in
sequencesync.c. Tap tests have been updated to verify automatic
synchronization rather than manual refresh.

The attached v2 patch is attached and ready for review.

Thoughts and feedback are welcome!

[1] -

https://github.com/postgres/postgres/commit/5509055d6956745532e65ab218e15b99d87d66ce

Is this expected behavior?

1) *Publisher:*

*create sequence t1_seq;create table t1 (id int default nextval('t1_seq')
primary key, a int);create publication t1_pub for table t1;create
publication t1_seq_pub for all sequences;*

2) *Subscriber:*

*create sequence t1_seq;create table t1 (id int default nextval('t1_seq')
primary key, a int);create subscription t1_sub connection 'host=127.0.0.1
port=37500 dbname=test user=$USER' publication t1_pub with (create_slot =
false, slot_name = 't1_sub');create subscription t1_seq_sub connection
'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_seq_pub
with (create_slot = false, slot_name = 't1_seq_sub');select * from
pg_subscription_rel;select * from pg_sequences;*

3) *Publisher:*

*insert into t1(a) values(10);select * from pg_sequences;*

4) *Subscriber:*

*select * from pg_sequences;* -- in sync with publisher.
*insert into t1(a) values(20);*
*select * from pg_sequences;* -- the sequence gets deviated from publisher.

After a few minutes, re-running the above shows that the sequence value is
reset to match the publisher. However, any new insert on the subscriber
fails:

*insert into t1(a) values(30);*

*ERROR: 23505: duplicate key value violates unique constraint
"t1_pkey"DETAIL: Key (id)=(2) already exists.SCHEMA NAME: publicTABLE
NAME: t1CONSTRAINT NAME: t1_pkey*

--

Automatic sequence replication resets the last_value on the subscriber to
match the publisher, which leads to duplicate key conflicts and prevents
further inserts on the subscriber.

--
With Regards,
Ashutosh Sharma.

#16Amit Kapila
amit.kapila16@gmail.com
In reply to: Ashutosh Sharma (#15)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 13, 2026 at 11:39 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

Is this expected behavior?

1) Publisher:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create publication t1_pub for table t1;
create publication t1_seq_pub for all sequences;

2) Subscriber:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create subscription t1_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_pub with (create_slot = false, slot_name = 't1_sub');
create subscription t1_seq_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_seq_pub with (create_slot = false, slot_name = 't1_seq_sub');

select * from pg_subscription_rel;
select * from pg_sequences;

3) Publisher:

insert into t1(a) values(10);
select * from pg_sequences;

4) Subscriber:

select * from pg_sequences; -- in sync with publisher.
insert into t1(a) values(20);
select * from pg_sequences; -- the sequence gets deviated from publisher.

After a few minutes, re-running the above shows that the sequence value is reset to match the publisher. However, any new insert on the subscriber fails:

insert into t1(a) values(30);
ERROR: 23505: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (id)=(2) already exists.
SCHEMA NAME: public
TABLE NAME: t1
CONSTRAINT NAME: t1_pkey

--

Automatic sequence replication resets the last_value on the subscriber to match the publisher, which leads to duplicate key conflicts and prevents further inserts on the subscriber.

This is possible even without automatic sequence replication, say when
the user uses REFRESH SEQUENCES command just before values(30). This
is because sequence replication is mainly provided for upgrade
purposes where sequences can be made up-to-date before upgrade. We
should update this information in docs, if not already present.

Having said that, we can possibly detect such synchronization as the
sequence_update type of conflict and don't allow it to update on
subscribers but that will be a separate patch.

--
With Regards,
Amit Kapila.

#17shveta malik
shveta.malik@gmail.com
In reply to: Amit Kapila (#16)
Re: [PATCH] Support automatic sequence replication

On Fri, Feb 13, 2026 at 4:48 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Feb 13, 2026 at 11:39 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

Is this expected behavior?

1) Publisher:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create publication t1_pub for table t1;
create publication t1_seq_pub for all sequences;

2) Subscriber:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create subscription t1_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_pub with (create_slot = false, slot_name = 't1_sub');
create subscription t1_seq_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_seq_pub with (create_slot = false, slot_name = 't1_seq_sub');

select * from pg_subscription_rel;
select * from pg_sequences;

3) Publisher:

insert into t1(a) values(10);
select * from pg_sequences;

4) Subscriber:

select * from pg_sequences; -- in sync with publisher.
insert into t1(a) values(20);
select * from pg_sequences; -- the sequence gets deviated from publisher.

After a few minutes, re-running the above shows that the sequence value is reset to match the publisher. However, any new insert on the subscriber fails:

insert into t1(a) values(30);
ERROR: 23505: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (id)=(2) already exists.
SCHEMA NAME: public
TABLE NAME: t1
CONSTRAINT NAME: t1_pkey

--

Automatic sequence replication resets the last_value on the subscriber to match the publisher, which leads to duplicate key conflicts and prevents further inserts on the subscriber.

This is possible even without automatic sequence replication, say when
the user uses REFRESH SEQUENCES command just before values(30). This
is because sequence replication is mainly provided for upgrade
purposes where sequences can be made up-to-date before upgrade. We
should update this information in docs, if not already present.

Yes. It is not present currently.

Having said that, we can possibly detect such synchronization as the
sequence_update type of conflict and don't allow it to update on
subscribers but that will be a separate patch.

I agree with this. It will not be in scope of the current patch.

thanks
Shveta

#18Amit Kapila
amit.kapila16@gmail.com
In reply to: shveta malik (#11)
Re: [PATCH] Support automatic sequence replication

On Wed, Feb 11, 2026 at 9:21 AM shveta malik <shveta.malik@gmail.com> wrote:

Additionally, the existing ALTER SUBSCRIPTION … REFRESH SEQUENCES
command should be enhanced so that it can be executed on disabled
subscriptions and perform sequence synchronization independently,
without relying on the sequence sync worker. Supporting execution on a
disabled subscription is necessary because, if REFRESH SEQUENCES is
run while the subscription is enabled, the apply worker may start
immediately, ingest new transactions, and advance the replication
slot's LSN beyond the point at which sequences were last synchronized
again.

Note: This approach may conservatively report that sequences need to
be synchronized even when no sequence values have actually changed.
This limitation is inherent to the design, as during an upgrade we
don't connect to the publisher and decode WAL between the two LSNs to
determine whether any sequence changes actually occurred.

I like the idea. In particular, I like the approach of providing a
REFRESH SEQUENCE command to the user. Even if we don’t implement the
check during the upgrade process, we can clearly document that users
should verify sequence drift before upgrading. If any sequence drift
is detected, they should run REFRESH-SEQ manually.

Yeah, implementing such checks are not required in upgrade but it is
better to document the use of this command in upgrade context.

--
With Regards,
Amit Kapila.

#19Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: shveta malik (#17)
Re: [PATCH] Support automatic sequence replication

Hi,

On Fri, Feb 13, 2026 at 4:56 PM shveta malik <shveta.malik@gmail.com> wrote:

On Fri, Feb 13, 2026 at 4:48 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Feb 13, 2026 at 11:39 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

Is this expected behavior?

1) Publisher:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create publication t1_pub for table t1;
create publication t1_seq_pub for all sequences;

2) Subscriber:

create sequence t1_seq;
create table t1 (id int default nextval('t1_seq') primary key, a int);

create subscription t1_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_pub with (create_slot = false, slot_name = 't1_sub');
create subscription t1_seq_sub connection 'host=127.0.0.1 port=37500 dbname=test user=$USER' publication t1_seq_pub with (create_slot = false, slot_name = 't1_seq_sub');

select * from pg_subscription_rel;
select * from pg_sequences;

3) Publisher:

insert into t1(a) values(10);
select * from pg_sequences;

4) Subscriber:

select * from pg_sequences; -- in sync with publisher.
insert into t1(a) values(20);
select * from pg_sequences; -- the sequence gets deviated from publisher.

After a few minutes, re-running the above shows that the sequence value is reset to match the publisher. However, any new insert on the subscriber fails:

insert into t1(a) values(30);
ERROR: 23505: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (id)=(2) already exists.
SCHEMA NAME: public
TABLE NAME: t1
CONSTRAINT NAME: t1_pkey

--

Automatic sequence replication resets the last_value on the subscriber to match the publisher, which leads to duplicate key conflicts and prevents further inserts on the subscriber.

This is possible even without automatic sequence replication, say when
the user uses REFRESH SEQUENCES command just before values(30). This
is because sequence replication is mainly provided for upgrade
purposes where sequences can be made up-to-date before upgrade. We
should update this information in docs, if not already present.

Yes. It is not present currently.

Having said that, we can possibly detect such synchronization as the
sequence_update type of conflict and don't allow it to update on
subscribers but that will be a separate patch.

I agree with this. It will not be in scope of the current patch.

Okay, agreed - it can be handled as a separate item.

--
With Regards,
Ashutosh Sharma.

#20Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Ajin Cherian (#12)
Re: [PATCH] Support automatic sequence replication

Hi,

On Thu, Feb 12, 2026 at 2:54 PM Ajin Cherian <itsajin@gmail.com> wrote:

On Fri, Feb 6, 2026 at 8:15 PM shveta malik <shveta.malik@gmail.com> wrote:

On Thu, Feb 5, 2026 at 10:33 AM Ajin Cherian <itsajin@gmail.com> wrote:

Thank You for the patch. I haven’t reviewed the details yet, but it
would be good to evaluate whether we really need to start the sequence
sync worker so deep inside the apply worker. Currently, the caller of
ProcessSequencesForSync(), namely ProcessSyncingRelations() is invoked
for every apply message to process possible state-changes of relation
and start worker (tablesync/sequence-sync etc). Since monitoring
state-change behavior is no longer required for sequences, should we
consider moving this logic to the main loop of the apply worker,
possibly within LogicalRepApplyLoop()? There, we could check whether
the table has any sequences and, if a worker is not already running,
start one. Thoughts?

I've moved this to LogicalRepApplyLoop()

On Mon, Feb 9, 2026 at 10:55 AM Peter Smith <smithpb2250@gmail.com> wrote:

Some review comments for v3-0001.

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

copy_sequences:

1.
- if (sync_status == COPYSEQ_SUCCESS)
+
+ /*
+ * For sequences in READY state, only sync if there's drift
+ */
+ if (relstate == SUBREL_STATE_READY && sync_status == COPYSEQ_SUCCESS)
+ {
+ should_sync = check_sequence_drift(seqinfo);
+ if (should_sync)
+ drift_detected = true;
+ }
+
+ if (sync_status == COPYSEQ_SUCCESS && should_sync)
sync_status = copy_sequence(seqinfo,
- sequence_rel->rd_rel->relowner);
+ sequence_rel->rd_rel->relowner,
+ relstate);
+ else if (sync_status == COPYSEQ_SUCCESS && !should_sync)
+ sync_status = COPYSEQ_NOWORK;

I'm struggling somewhat to follow this logic.

For example, every condition refers to COPYSEQ_SUCCESS -- is that
really necessary; can't this all be boiled down to something like
below?

SUGGESTION

/*
* For sequences in INIT state, always sync.
* Otherwise, for sequences in READY state, only sync if there's drift.
*/
if (sync_status == COPYSEQ_SUCCESS)
{
if ((relstate == SUBREL_STATE_INIT) || check_sequence_drift(seqinfo))
sync_status = copy_sequence(...);
else
sync_status = COPYSEQ_NOWORK;
}

Changed as suggested.

On Wed, Feb 11, 2026 at 2:30 PM shveta malik <shveta.malik@gmail.com> wrote:

On Fri, Feb 6, 2026 at 2:47 PM shveta malik <shveta.malik@gmail.com> wrote:

Few more comments:

1)
+ /* Run sync for sequences in READY state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_READY);
+
+ /* Call initial sync for sequences in INIT state */
+ sequence_copied |= LogicalRepSyncSequences(LogRepWorkerWalRcvConn,
SUBREL_STATE_INIT);

Above logic means we ping primary twice for one seq-sync cycle? Is it
possible to do it in below way:

Get all sequences from the publisher in one go.
If local-seq's state is INIT, copy those sequences, move the state to READY.
If local-seq's state is READY, check the drift and proceed accordingly.

i.e, there should be a single call to LogicalRepSyncSequences() and
relstate shouldn't even be an argument.

Changed as suggested.

2)
GetSequence:
+ /* Open and lock the sequence relation */
+ seqrel = table_open(relid, AccessShareLock);

GetSequence is called from check_sequence_drift and
check_sequence_drift() from copy_sequences(). In copy_sequences(), we
already open the seq's (localrelid) table in
get_and_validate_seq_info() and give it as output (see sequence_rel).
Same can be passed to this instead of trying opening the table again.

3)
+ /* Verify it's actually a sequence */
+ if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ RelationGetRelationName(seqrel))));

Changed these.

Other than these, I've changed seqinfos from being a global static
list to a list that gets passed around and destroyed in each
iteration.
I haven't addressed the upgrade issue raised by Vignesh and Shveta in
this patch. I will address that in the next patch.
Here's patch v4 addressing the above comments.

How about retaining ALTER SUBSCRIPTION ... REFRESH SEQUENCES command?

It can be useful in scenarios where automatic sequence replication
cannot be enabled, for example, due to the max_worker_processes limit
on the server preventing a new worker from being registered. Since
increasing max_worker_processes requires a server restart, which the
user may not want to perform immediately, this command would provide a
way to manually synchronize the sequences.

Thoughts?

--

One minor comment:

 * Sequence state transitions follow this pattern:
- *   INIT -> READY
+ *  INIT --> READY ->-+
+ *             ^      | (check/synchronzize)
+ *             |      |
+ *             +--<---+

"synchronzize" → "synchronize"

--
With Regards,
Ashutosh Sharma.

#21Amit Kapila
amit.kapila16@gmail.com
In reply to: Ashutosh Sharma (#20)
#22shveta malik
shveta.malik@gmail.com
In reply to: Amit Kapila (#21)
#23Amit Kapila
amit.kapila16@gmail.com
In reply to: shveta malik (#22)
#24shveta malik
shveta.malik@gmail.com
In reply to: shveta malik (#22)
#25shveta malik
shveta.malik@gmail.com
In reply to: Amit Kapila (#23)
#26Peter Smith
smithpb2250@gmail.com
In reply to: shveta malik (#24)
#27Amit Kapila
amit.kapila16@gmail.com
In reply to: shveta malik (#25)
#28Amit Kapila
amit.kapila16@gmail.com
In reply to: Peter Smith (#26)
#29shveta malik
shveta.malik@gmail.com
In reply to: Peter Smith (#26)
#30shveta malik
shveta.malik@gmail.com
In reply to: Amit Kapila (#27)
#31Dilip Kumar
dilipbalaut@gmail.com
In reply to: Ajin Cherian (#1)
#32Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#31)
#33Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#32)
#34Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#33)
#35Ajin Cherian
itsajin@gmail.com
In reply to: shveta malik (#24)
#36Hayato Kuroda (Fujitsu)
kuroda.hayato@fujitsu.com
In reply to: Ajin Cherian (#35)
#37Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#34)
#38Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#37)
#39Amit Kapila
amit.kapila16@gmail.com
In reply to: Hayato Kuroda (Fujitsu) (#36)
#40Hayato Kuroda (Fujitsu)
kuroda.hayato@fujitsu.com
In reply to: Dilip Kumar (#37)
#41Amit Kapila
amit.kapila16@gmail.com
In reply to: Hayato Kuroda (Fujitsu) (#36)
#42Amit Kapila
amit.kapila16@gmail.com
In reply to: Hayato Kuroda (Fujitsu) (#36)
#43Dilip Kumar
dilipbalaut@gmail.com
In reply to: Hayato Kuroda (Fujitsu) (#40)
#44Ajin Cherian
itsajin@gmail.com
In reply to: Amit Kapila (#42)
#45Amit Kapila
amit.kapila16@gmail.com
In reply to: Ajin Cherian (#44)
#46Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#43)
#47Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#45)
#48Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#47)
#49shveta malik
shveta.malik@gmail.com
In reply to: Amit Kapila (#48)
#50Ajin Cherian
itsajin@gmail.com
In reply to: Ajin Cherian (#44)
#51Nisha Moond
nisha.moond412@gmail.com
In reply to: Ajin Cherian (#50)
#52Amit Kapila
amit.kapila16@gmail.com
In reply to: Nisha Moond (#51)
#53Amit Kapila
amit.kapila16@gmail.com
In reply to: Ajin Cherian (#50)
#54Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: Amit Kapila (#53)
#55Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: Zhijie Hou (Fujitsu) (#54)
#56Hayato Kuroda (Fujitsu)
kuroda.hayato@fujitsu.com
In reply to: Zhijie Hou (Fujitsu) (#55)
#57shveta malik
shveta.malik@gmail.com
In reply to: Zhijie Hou (Fujitsu) (#55)
#58Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: Hayato Kuroda (Fujitsu) (#56)
#59Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: shveta malik (#57)
#60shveta malik
shveta.malik@gmail.com
In reply to: Zhijie Hou (Fujitsu) (#59)
#61shveta malik
shveta.malik@gmail.com
In reply to: shveta malik (#60)
#62Amit Kapila
amit.kapila16@gmail.com
In reply to: shveta malik (#60)
#63Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: Amit Kapila (#62)
#64Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: shveta malik (#61)
#65Hayato Kuroda (Fujitsu)
kuroda.hayato@fujitsu.com
In reply to: Hayato Kuroda (Fujitsu) (#56)
#66shveta malik
shveta.malik@gmail.com
In reply to: Zhijie Hou (Fujitsu) (#64)
#67Chao Li
li.evan.chao@gmail.com
In reply to: Zhijie Hou (Fujitsu) (#64)
#68Zhijie Hou (Fujitsu)
houzj.fnst@fujitsu.com
In reply to: Chao Li (#67)
#69shveta malik
shveta.malik@gmail.com
In reply to: shveta malik (#66)
#70Hayato Kuroda (Fujitsu)
kuroda.hayato@fujitsu.com
In reply to: Zhijie Hou (Fujitsu) (#63)
#71shveta malik
shveta.malik@gmail.com
In reply to: Hayato Kuroda (Fujitsu) (#70)
#72Chao Li
li.evan.chao@gmail.com
In reply to: Zhijie Hou (Fujitsu) (#63)