From a1c3d9a2706c03790debbf7acd1ba70dcaa254ae Mon Sep 17 00:00:00 2001
From: Ajin Cherian <itsajin@gmail.com>
Date: Tue, 8 Jul 2025 06:18:23 -0400
Subject: [PATCH] Fix a deadlock during ALTER SUBSCRIPTION ... DROP PUBLICATION

When user drops a publication from a subscription, this will result in a
publication refresh on the subscriber which will try and drop any pending origins.
Meanwhile the apply worker could also be trying to cleanup origins. There could be
a deadlock if the order of locking of SubscriptionRelRelationId and
ReplicationOriginRelationId are inverted between the
process_syncing_tables_for_apply() and AlterSubscription_refresh().

The fix is to get an AccessExclusiveLock on SubscriptionRelRelationId in
process_syncing_tables_for_apply() in advance.
---
 src/backend/replication/logical/tablesync.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index e6159ac..b2f9bad 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -379,6 +379,7 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
 	static HTAB *last_start_times = NULL;
 	ListCell   *lc;
 	bool		started_tx = false;
+	Relation	rel = NULL;
 
 	Assert(!IsTransactionState());
 
@@ -470,7 +471,14 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
 				 * refresh for the subscription where we remove the table
 				 * state and its origin and by this time the origin might be
 				 * already removed. So passing missing_ok = true.
+				 *
+				 * Also Lock pg_subscription_rel with AccessExclusiveLock to
+				 * prevent any deadlocks with user concurrently performing
+				 * refresh on the subscription.
 				 */
+
+				rel = table_open(SubscriptionRelRelationId, AccessExclusiveLock);
+
 				ReplicationOriginNameForTablesync(MyLogicalRepWorker->subid,
 												  rstate->relid,
 												  originname,
@@ -483,6 +491,9 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
 				UpdateSubscriptionRelState(MyLogicalRepWorker->subid,
 										   rstate->relid, rstate->state,
 										   rstate->lsn);
+
+				/* close and unlock table */
+				table_close(rel, AccessExclusiveLock);
 			}
 		}
 		else
-- 
1.8.3.1

