From ad7371272d9e12f5bfeb6c89474916772480f04e Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Sat, 18 Jul 2026 18:28:02 +0530 Subject: [PATCH] Test: add test for REFRESH SEQUENCES stopping a running worker ALTER SUBSCRIPTION ... REFRESH SEQUENCES stops any running sequence synchronization worker before resetting sequences to INIT, rather than failing or letting the worker apply a stale value it had already fetched. Add a test to 036_sequences.pl that uses the injection point at sequencesync-before-copy-sequence to block a worker after it has fetched a sequence's publisher value but before applying it, issue a second REFRESH SEQUENCES while the worker is blocked, and confirm it succeeds, resets the sequence to INIT, and that a freshly launched worker converges on the publisher's latest value rather than the stale one. --- .../replication/logical/sequencesync.c | 4 + src/test/subscription/t/036_sequences.pl | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c index 63ad46d7fd7..5d7d8505d9d 100644 --- a/src/backend/replication/logical/sequencesync.c +++ b/src/backend/replication/logical/sequencesync.c @@ -65,6 +65,7 @@ #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" +#include "utils/injection_point.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -561,8 +562,11 @@ copy_sequences(WalReceiverConn *conn) sync_status = get_and_validate_seq_info(slot, &sequence_rel, &seqinfo, &seqidx); if (sync_status == COPYSEQ_SUCCESS) + { + INJECTION_POINT("sequencesync-before-copy-sequence", NULL); sync_status = copy_sequence(seqinfo, sequence_rel->rd_rel->relowner); + } switch (sync_status) { diff --git a/src/test/subscription/t/036_sequences.pl b/src/test/subscription/t/036_sequences.pl index 77ac9386cd8..1328cf47805 100644 --- a/src/test/subscription/t/036_sequences.pl +++ b/src/test/subscription/t/036_sequences.pl @@ -242,6 +242,90 @@ $node_publisher->safe_psql( $node_subscriber->poll_query_until('postgres', $synced_query) or die "Timed out while waiting for subscriber to synchronize data"; +########## +# ALTER SUBSCRIPTION ... REFRESH SEQUENCES must not fail merely because a +# sequencesync worker is already running for this subscription. Instead, it +# must stop the running worker and reset every sequence to INIT again, so a +# freshly launched worker discards any stale value the stopped worker had +# already fetched but not yet applied, and resynchronizes with the +# publisher's latest values. +########## + +my $injection_points_supported = + $node_subscriber->check_extension('injection_points'); + +if ($injection_points_supported != 0) +{ + $node_subscriber->append_conf('postgresql.conf', + "shared_preload_libraries = 'injection_points'"); + $node_subscriber->restart; + + $node_subscriber->safe_psql('postgres', + "CREATE EXTENSION injection_points;"); + + # Block the sequencesync worker just before it applies a sequence's + # fetched values. + $node_subscriber->safe_psql('postgres', + "SELECT injection_points_attach('sequencesync-before-copy-sequence', 'wait');" + ); + + $node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES"); + + $node_subscriber->wait_for_event('logical replication sequencesync worker', + 'sequencesync-before-copy-sequence'); + + $result = $node_subscriber->safe_psql('postgres', + "SELECT srsubstate FROM pg_subscription_rel WHERE srrelid = 'regress_s3'::regclass" + ); + is($result, 'i', 'sequence is still in INIT state while the worker is blocked'); + + # Advance the sequence on the publisher, so the value already fetched by + # the blocked worker becomes stale. + $node_publisher->safe_psql( + 'postgres', qq( + INSERT INTO regress_seq_test SELECT nextval('regress_s3') FROM generate_series(1,200); + )); + + # A second REFRESH SEQUENCES while the worker is still blocked must + # succeed: it stops the blocked worker (discarding its stale fetched + # value) and resets the sequence to INIT again, rather than failing + # because a sequence synchronization worker is running. + $node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES"); + + $result = $node_subscriber->safe_psql('postgres', + "SELECT srsubstate FROM pg_subscription_rel WHERE srrelid = 'regress_s3'::regclass" + ); + is($result, 'i', + 'sequence remains in INIT state after the second REFRESH SEQUENCES stops the worker' + ); + + # Detach the injection point so the freshly launched worker is not + # blocked by it too. + $node_subscriber->safe_psql('postgres', + "SELECT injection_points_wakeup('sequencesync-before-copy-sequence'); + SELECT injection_points_detach('sequencesync-before-copy-sequence');" + ); + + $node_subscriber->poll_query_until('postgres', $synced_query) + or die "Timed out while waiting for subscriber to synchronize data"; + + # The freshly launched worker must reflect the publisher's latest value + # (200), not the stale value (1) already fetched by the stopped worker. + $result = $node_subscriber->safe_psql( + 'postgres', qq( + SELECT last_value, is_called FROM regress_s3; + )); + is($result, '200|t', + 'REFRESH SEQUENCES resynchronizes with the latest publisher value after stopping a running worker' + ); +} +else +{ + note 'Injection points not supported by this build'; +} + ########## # ALTER SUBSCRIPTION ... REFRESH PUBLICATION should report an error when: # a) sequence definitions differ between the publisher and subscriber, or -- 2.50.1 (Apple Git-155)