v1-0001-Fix-drop-subcription-deadlock-with-create-databas.patch

application/octet-stream

Filename: v1-0001-Fix-drop-subcription-deadlock-with-create-databas.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 v1-0001
Subject: Fix drop subcription deadlock with create database
File+
src/backend/commands/subscriptioncmds.c 1 5
src/backend/replication/logical/launcher.c 27 5
From ca245e5934f2225fa3e71b40792bab2a2d41620b Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumarb@google.com>
Date: Tue, 5 Aug 2025 05:40:12 +0000
Subject: [PATCH v1] Fix drop subcription deadlock with create database

The DROP SUBSCRIPTION command previously acquired an AccessExclusiveLock
on the pg_subscription catalog to prevent the launcher from starting new
workers. This created a deadlock because new database connections require
an AccessShareLock on the same catalog to build their cache.

This commit resolves the deadlock by downgrading the lock on the pg_subscription
catalog to AccessShareLock during drop subscriber. To prevent the launcher from
starting a new worker for the subscription being dropped, we acquired a shared
object lock on the subscription, which was already held by DROP SUBSCRIPTION in
exclusive mode.
---
 src/backend/commands/subscriptioncmds.c    |  6 +---
 src/backend/replication/logical/launcher.c | 32 ++++++++++++++++++----
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 9467f58a23d..05478e53530 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -1571,11 +1571,7 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
 	List	   *rstates;
 	bool		must_use_password;
 
-	/*
-	 * 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/launcher.c b/src/backend/replication/logical/launcher.c
index 294d0d74d8c..f3c48e9b4f0 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -35,6 +35,7 @@
 #include "replication/walreceiver.h"
 #include "replication/worker_internal.h"
 #include "storage/ipc.h"
+#include "storage/lmgr.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "tcop/tcopprot.h"
@@ -42,6 +43,7 @@
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 
 /* max sleep time between cycles (3min) */
 #define DEFAULT_NAPTIME_PER_CYCLE 180000L
@@ -1147,11 +1149,9 @@ ApplyLauncherMain(Datum main_arg)
 	pqsignal(SIGTERM, die);
 	BackgroundWorkerUnblockSignals();
 
-	/*
-	 * Establish connection to nailed catalogs (we only ever access
-	 * pg_subscription).
-	 */
-	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/* Connect to postgres, as we need to look up the syscache. */
+	BackgroundWorkerInitializeConnection("postgres", NULL, 0);
 
 	/* Enter main loop */
 	for (;;)
@@ -1209,6 +1209,25 @@ ApplyLauncherMain(Datum main_arg)
 			if (last_start == 0 ||
 				(elapsed = TimestampDifferenceMilliseconds(last_start, now)) >= wal_retrieve_retry_interval)
 			{
+				HeapTuple	tup;
+
+				/*
+				 * Lock the subscription to prevent it from being concurrently dropped,
+				 * then re-verify its existence.
+				 */
+				StartTransactionCommand();
+				LockSharedObject(SubscriptionRelationId, sub->oid, 0,
+								 AccessShareLock);
+				tup = SearchSysCache1(SUBSCRIPTIONOID,
+									  ObjectIdGetDatum(sub->oid));
+				if (!HeapTupleIsValid(tup))
+				{
+					UnlockSharedObject(SubscriptionRelationId, sub->oid, 0,
+									   AccessShareLock);
+					CommitTransactionCommand();
+					continue;
+				}
+
 				ApplyLauncherSetWorkerStartTime(sub->oid, now);
 				if (!logicalrep_worker_launch(WORKERTYPE_APPLY,
 											  sub->dbid, sub->oid, sub->name,
@@ -1225,6 +1244,9 @@ ApplyLauncherMain(Datum main_arg)
 					wait_time = Min(wait_time,
 									wal_retrieve_retry_interval);
 				}
+				UnlockSharedObject(SubscriptionRelationId, sub->oid, 0,
+								   AccessShareLock);
+				CommitTransactionCommand();
 			}
 			else
 			{
-- 
2.50.1.565.gc32cd1483b-goog