From 176e8fbfcb460941e74e0aed3bfe06bea831c336 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Thu, 15 Oct 2026 00:11:16 -0400 Subject: [PATCH v1] Lock pg_database before WrapLimitsVacuumLock in vac_truncate_clog vac_truncate_clog() locked pg_database while holding WrapLimitsVacuumLock. If that lock had to wait, the backend could not be canceled and could not absorb a ProcSignalBarrier. Together with DROP DATABASE and VACUUM FULL of pg_database this formed a cycle the deadlock detector cannot see, and new connections hung. Lock pg_database first. The scan stays under the LWLock. Reported-by: Ann Calla Discussion: https://postgr.es/m/19755-6ffb2ecb4e3fd782@postgresql.org Backpatch-through: 14 --- src/backend/commands/vacuum.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a257dd8d21e..ce7c5f70288 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -1866,6 +1866,12 @@ vac_truncate_clog(TransactionId frozenXID, bool bogus = false; bool frozenAlreadyWrapped = false; + /* + * Lock pg_database before the LWLock. A lock wait while holding an + * LWLock could neither be canceled nor absorb a ProcSignalBarrier. + */ + relation = table_open(DatabaseRelationId, AccessShareLock); + /* Restrict task to one backend per cluster; see SimpleLruTruncate(). */ LWLockAcquire(WrapLimitsVacuumLock, LW_EXCLUSIVE); @@ -1891,8 +1897,6 @@ vac_truncate_clog(TransactionId frozenXID, * worst possible outcome is that pg_xact is not truncated as aggressively * as it could be. */ - relation = table_open(DatabaseRelationId, AccessShareLock); - scan = table_beginscan_catalog(relation, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) -- 2.37.1 (Apple Git-137.1)