From 423c1372e9cec6716d8037d2a9c7f39e5bc3e95a Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Mon, 13 Jul 2026 18:09:25 +0530 Subject: [PATCH v2] Fix sequence synchronization for concurrently dropped sequences A sequence can be dropped concurrently after the sequence synchronization worker has collected its metadata but before it reads the sequence data. In this case, 'has_sequence_privilege()' returns NULL rather than false. 'get_and_validate_seq_info()' assumed the privilege value was never NULL, causing an assertion failure in assert-enabled builds. Treat a NULL privilege value as indicating that the sequence no longer exists and report it using the existing "missing sequence on publisher" path. --- .../replication/logical/sequencesync.c | 15 ++++-- src/test/subscription/t/036_sequences.pl | 50 ++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c index 770fa5de10b..da15b9ca290 100644 --- a/src/backend/replication/logical/sequencesync.c +++ b/src/backend/replication/logical/sequencesync.c @@ -289,14 +289,23 @@ get_and_validate_seq_info(TupleTableSlot *slot, Relation *sequence_rel, *seqinfo = seqinfo_local = (LogicalRepSequenceInfo *) list_nth(seqinfos, *seqidx); + /* + * has_sequence_privilege() itself returns NULL, rather than false, when + * the sequence has been dropped concurrently after it was identified in + * the catalog snapshot (see has_sequence_privilege_id()). Treat that as + * a missing sequence on the publisher. + */ + datum = slot_getattr(slot, ++col, &isnull); + if (isnull) + return COPYSEQ_SKIPPED; + + remote_has_select_priv = DatumGetBool(datum); + /* * The remote sequence state can be NULL if the publisher lacks the * required privileges or if the sequence was dropped concurrently after * it was identified in the catalog snapshot (see pg_get_sequence_data()). */ - remote_has_select_priv = DatumGetBool(slot_getattr(slot, ++col, &isnull)); - Assert(!isnull); - datum = slot_getattr(slot, ++col, &isnull); if (isnull) return remote_has_select_priv ? COPYSEQ_SKIPPED : diff --git a/src/test/subscription/t/036_sequences.pl b/src/test/subscription/t/036_sequences.pl index 8b02b24a7e9..a649bcd765a 100644 --- a/src/test/subscription/t/036_sequences.pl +++ b/src/test/subscription/t/036_sequences.pl @@ -188,6 +188,54 @@ is($result, '1|f', 'REFRESH PUBLICATION will not sync newly published sequence with copy_data as false' ); +########## +# A sequence dropped concurrently on the publisher, while the sequencesync +# worker's batch query is executing, must be treated the same as any other +# concurrently-dropped sequence (reported as "missing sequence on publisher"). +########## + +my $log_offset = -s $node_subscriber->logfile; + +# Block the sequencesync worker's batch query on the publisher: an +# uncommitted DROP SEQUENCE holds AccessExclusiveLock, on which the +# pg_get_sequence_data() call in the batch query will wait. +my $pub_session = $node_publisher->background_psql('postgres'); +$pub_session->query_safe( + qq( + BEGIN; + DROP SEQUENCE regress_s3; +)); + +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES"); + +# Wait until the worker's batch query is blocked on the still uncommitted +# DROP. +$node_publisher->poll_query_until( + 'postgres', qq( + SELECT EXISTS ( + SELECT 1 FROM pg_locks + WHERE relation = 'regress_s3'::regclass AND NOT granted); +)) or die "timed out waiting for sequencesync worker to block on publisher"; + +# Commit the DROP while the batch query is blocked inside it, so the query +# resumes against a sequence that no longer exists. +$pub_session->query_safe("COMMIT"); +$pub_session->quit; + +$node_subscriber->wait_for_log( + qr/WARNING: ( [A-Z0-9]+:)? missing sequence on publisher \("public.regress_s3"\)/, + $log_offset); + +$node_publisher->safe_psql( + 'postgres', qq( + CREATE SEQUENCE regress_s3; +)); + +# Wait for the missing sequence added to be synced +$node_subscriber->poll_query_until('postgres', $synced_query) + or die "Timed out while waiting for subscriber to synchronize data"; + ########## # ALTER SUBSCRIPTION ... REFRESH PUBLICATION should report an error when: # a) sequence definitions differ between the publisher and subscriber, or @@ -206,7 +254,7 @@ $node_subscriber->safe_psql( CREATE SEQUENCE regress_s4 START 10 INCREMENT 2; )); -my $log_offset = -s $node_subscriber->logfile; +$log_offset = -s $node_subscriber->logfile; # Do ALTER SUBSCRIPTION ... REFRESH PUBLICATION $node_subscriber->safe_psql('postgres', -- 2.50.1 (Apple Git-155)