From ce9170d84d96eba77110947808f4e1815232d468 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Fri, 24 Jul 2026 07:41:48 +0530
Subject: [PATCH] Fix issues reported by claude

1) Stop a running sequence synchronization worker when
ALTER SUBSCRIPTION ... DISABLE is executed.
2) Restore the invariant that publisher-side synchronization
slots are dropped last by moving the sequence-removal loop
before the slot-drop loop.
3) Restore psql tab completion for
ALTER SUBSCRIPTION ... REFRESH PUBLICATION WITH (.
4) Make pg_stat_subscription report NULL for fields that are
not applicable to sequence synchronization workers, and update
the corresponding documentation.
5) Update the pg_subscription_rel.srsublsn catalog documentation
to describe its semantics for sequence rows.
---
 doc/src/sgml/catalogs.sgml                 |  6 ++-
 doc/src/sgml/monitoring.sgml               | 19 ++++----
 src/backend/commands/subscriptioncmds.c    | 56 +++++++++++-----------
 src/backend/replication/logical/launcher.c |  2 +-
 src/backend/replication/logical/worker.c   | 14 +++++-
 src/bin/psql/tab-complete.in.c             |  3 ++
 6 files changed, 60 insertions(+), 40 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 4b474c13917..6066c4784f4 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -8893,7 +8893,11 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <para>
        Remote LSN of the state change used for synchronization coordination
        when in <literal>s</literal> or <literal>r</literal> states,
-       otherwise null
+       otherwise null. For sequences, this instead holds the publisher
+       sequence's page LSN as of the last synchronization, which does not
+       track replication progress the way it does for tables; see
+       <xref linkend="sequences-out-of-sync"/> for how it is used to detect
+       out-of-sync sequences.
       </para></entry>
      </row>
     </tbody>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b087d499041..070141bad1f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2468,8 +2468,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Process ID of the leader apply worker if this process is a parallel
-       apply worker; NULL if this process is a leader apply worker or a table
-       synchronization worker
+       apply worker; NULL if this process is a leader apply worker, a table
+       synchronization worker or a sequence synchronization worker
       </para></entry>
      </row>
 
@@ -2479,7 +2479,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        OID of the relation that the worker is synchronizing; NULL for the
-       leader apply worker and parallel apply workers
+       leader apply worker, parallel apply workers and the sequence
+       synchronization worker
       </para></entry>
      </row>
 
@@ -2489,7 +2490,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Last write-ahead log location received, the initial value of
-       this field being 0; NULL for parallel apply workers
+       this field being 0; NULL for parallel apply workers and the sequence
+       synchronization worker
       </para></entry>
      </row>
 
@@ -2499,7 +2501,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Send time of last message received from origin WAL sender; NULL for
-       parallel apply workers
+       parallel apply workers and the sequence synchronization worker
       </para></entry>
      </row>
 
@@ -2509,7 +2511,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Receipt time of last message received from origin WAL sender; NULL for
-       parallel apply workers
+       parallel apply workers and the sequence synchronization worker
       </para></entry>
      </row>
 
@@ -2519,7 +2521,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Last write-ahead log location reported to origin WAL sender; NULL for
-       parallel apply workers
+       parallel apply workers and the sequence synchronization worker
       </para></entry>
      </row>
 
@@ -2529,7 +2531,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Time of last write-ahead log location reported to origin WAL
-       sender; NULL for parallel apply workers
+       sender; NULL for parallel apply workers and the sequence synchronization
+       worker
       </para></entry>
      </row>
     </tbody>
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 7f946c5b454..482db0a8942 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -1288,34 +1288,6 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data,
 			}
 		}
 
-		/*
-		 * Drop the tablesync slots associated with removed tables. This has
-		 * to be at the end because otherwise if there is an error while doing
-		 * the database operations we won't be able to rollback dropped slots.
-		 */
-		foreach_ptr(SubRemoveRels, sub_remove_rel, sub_remove_rels)
-		{
-			if (sub_remove_rel->state != SUBREL_STATE_READY &&
-				sub_remove_rel->state != SUBREL_STATE_SYNCDONE)
-			{
-				char		syncslotname[NAMEDATALEN] = {0};
-
-				/*
-				 * For READY/SYNCDONE states we know the tablesync slot has
-				 * already been dropped by the tablesync worker.
-				 *
-				 * For other states, there is no certainty, maybe the slot
-				 * does not exist yet. Also, if we fail after removing some of
-				 * the slots, next time, it will again try to drop already
-				 * dropped slots and fail. For these reasons, we allow
-				 * missing_ok = true for the drop.
-				 */
-				ReplicationSlotNameForTablesync(sub->oid, sub_remove_rel->relid,
-												syncslotname, sizeof(syncslotname));
-				ReplicationSlotDropAtPubNode(wrconn, syncslotname, true);
-			}
-		}
-
 		/*
 		 * Next remove state for sequences we should not care about anymore
 		 * using the data we collected above
@@ -1343,6 +1315,34 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data,
 										sub->name));
 			}
 		}
+
+		/*
+		 * Drop the tablesync slots associated with removed tables. This has
+		 * to be at the end because otherwise if there is an error while doing
+		 * the database operations we won't be able to rollback dropped slots.
+		 */
+		foreach_ptr(SubRemoveRels, sub_remove_rel, sub_remove_rels)
+		{
+			if (sub_remove_rel->state != SUBREL_STATE_READY &&
+				sub_remove_rel->state != SUBREL_STATE_SYNCDONE)
+			{
+				char		syncslotname[NAMEDATALEN] = {0};
+
+				/*
+				 * For READY/SYNCDONE states we know the tablesync slot has
+				 * already been dropped by the tablesync worker.
+				 *
+				 * For other states, there is no certainty, maybe the slot
+				 * does not exist yet. Also, if we fail after removing some of
+				 * the slots, next time, it will again try to drop already
+				 * dropped slots and fail. For these reasons, we allow
+				 * missing_ok = true for the drop.
+				 */
+				ReplicationSlotNameForTablesync(sub->oid, sub_remove_rel->relid,
+												syncslotname, sizeof(syncslotname));
+				ReplicationSlotDropAtPubNode(wrconn, syncslotname, true);
+			}
+		}
 	}
 	PG_FINALLY();
 	{
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 313e31ff2e3..67ca8ec9b12 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -824,7 +824,7 @@ logicalrep_worker_detach(void)
 		{
 			LogicalRepWorker *w = (LogicalRepWorker *) lfirst(lc);
 
-			if (isParallelApplyWorker(w))
+			if (isParallelApplyWorker(w) || isSequenceSyncWorker(w))
 				logicalrep_worker_stop_internal(w, SIGTERM);
 		}
 
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 0ff5cef63cd..0bd19074010 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -5985,8 +5985,18 @@ SetupApplyOrSyncWorker(int worker_slot)
 	 */
 
 	/* Initialise stats to a sanish value */
-	MyLogicalRepWorker->last_send_time = MyLogicalRepWorker->last_recv_time =
-		MyLogicalRepWorker->reply_time = GetCurrentTimestamp();
+	if (am_sequencesync_worker())
+	{
+		MyLogicalRepWorker->last_send_time =
+			MyLogicalRepWorker->last_recv_time =
+			MyLogicalRepWorker->reply_time = 0;
+	}
+	else
+	{
+		MyLogicalRepWorker->last_send_time =
+			MyLogicalRepWorker->last_recv_time =
+			MyLogicalRepWorker->reply_time = GetCurrentTimestamp();
+	}
 
 	/* Load the libpq-specific functions */
 	load_file("libpqwalreceiver", false);
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 1cacc8c3ea2..17dcabe755f 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2354,6 +2354,9 @@ match_previous_words(int pattern_id,
 	/* ALTER SUBSCRIPTION <name> REFRESH */
 	else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH"))
 		COMPLETE_WITH("PUBLICATION", "SEQUENCES");
+	/* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
+	else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"))
+		COMPLETE_WITH("WITH (");
 	/* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
 	else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("))
 		COMPLETE_WITH("copy_data");
-- 
2.50.1 (Apple Git-155)

