v3-0001-Fix-DROP-SUBSCRIPTION-deadlock-with-new-database-V15.patch

application/octet-stream

Filename: v3-0001-Fix-DROP-SUBSCRIPTION-deadlock-with-new-database-V15.patch
Type: application/octet-stream
Part: 0
Message: Re: BUG #18988: DROP SUBSCRIPTION locks not-yet-accessed database

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v3-0001
Subject: Fix DROP SUBSCRIPTION deadlock with new database creation
File+
src/backend/commands/subscriptioncmds.c 1 5
src/backend/replication/logical/worker.c 15 0
From 404441fac6f62141a5a65ee98bd90924d52558dd Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumarb@google.com>
Date: Wed, 13 Aug 2025 07:51:55 +0000
Subject: [PATCH v3] Fix DROP SUBSCRIPTION deadlock with new database creation

DROP SUBSCRIPTION previously acquired an AccessExclusiveLock on the
pg_subscription catalog to prevent the replication launcher from starting
a new worker. However, this caused a deadlock. New database creation also
need to acquire an AccessShareLock on this same catalog during their
initialization phase. This created a lock conflict where DROP SUBSCRIPTION
would block this, while simultaneously waiting for a connection to
complete for dropping the replication slot.

This commit resolves the deadlock by having DROP SUBSCRIPTION acquire a less
restrictive AccessShareLock on the catalog instead.

To address the original concern of orphaned workers, a new check is
implemented.  The replication worker now takes a shared object lock on the
subscription itself.

If a worker starts for a subscription that no longer exists, it immediately detects
this condition and exits. This ensures that no orphan workers are created without
the need for an overly broad AccessExclusiveLock on the system catalog.
---
 src/backend/commands/subscriptioncmds.c  |  6 +-----
 src/backend/replication/logical/worker.c | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 334717c0e96..1f0b6926c53 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -1340,11 +1340,7 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
 	Form_pg_subscription form;
 	List	   *rstates;
 
-	/*
-	 * Lock pg_subscription with AccessExclusiveLock to ensure that the
-	 * launcher doesn't restart new worker during dropping the subscription
-	 */
-	rel = table_open(SubscriptionRelationId, AccessExclusiveLock);
+	rel = table_open(SubscriptionRelationId, AccessShareLock);
 
 	tup = SearchSysCache2(SUBSCRIPTIONNAME, MyDatabaseId,
 						  CStringGetDatum(stmt->subname));
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 155bf7c44ee..0c2c6209242 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -3638,6 +3638,12 @@ ApplyWorkerMain(Datum main_arg)
 	StartTransactionCommand();
 	oldctx = MemoryContextSwitchTo(ApplyContext);
 
+	/*
+	 * Lock the subscription to prevent it from being concurrently dropped,
+	 * then re-verify its existence.
+	 */
+	LockSharedObject(SubscriptionRelationId, MyLogicalRepWorker->subid, 0,
+					 AccessShareLock);
 	MySubscription = GetSubscription(MyLogicalRepWorker->subid, true);
 	if (!MySubscription)
 	{
@@ -3645,9 +3651,18 @@ ApplyWorkerMain(Datum main_arg)
 				(errmsg("logical replication apply worker for subscription %u will not "
 						"start because the subscription was removed during startup",
 						MyLogicalRepWorker->subid)));
+
+		/*
+		 * The shared object lock on subid is automatically released by
+		 * proc_exit(), so no explicit unlock is necessary here.
+		 */
 		proc_exit(0);
 	}
 
+	/*
+	 * The shared object lock on subid will be released at transaction end.
+	 */
+
 	MySubscriptionValid = true;
 	MemoryContextSwitchTo(oldctx);
 
-- 
2.51.0.rc0.205.g4a044479a3-goog