| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | anncalla(at)163(dot)com |
| Subject: | BUG #19755: Server-wide hang: DROP DATABASE waits forever for ProcSignalBarrier while VACUUM waits on pg_databas |
| Date: | 2026-10-12 12:25:57 |
| Message-ID: | 19755-6ffb2ecb4e3fd782@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19755
Logged by: Ann Calla
Email address: anncalla(at)163(dot)com
PostgreSQL version: 18.6
Operating system: Ubuntu 24.04 x86_64
Description:
Description
-----------
With several sessions concurrently running DROP DATABASE / CREATE DATABASE
and database-wide VACUUM FULL in other databases, the server can enter a
state that it never leaves:
* DROP DATABASE logs, every 5 seconds and indefinitely,
LOG: still waiting for backend with PID 1770 to accept
ProcSignalBarrier
STATEMENT: DROP DATABASE IF EXISTS sN
* the backend it names is a VACUUM that is waiting for a heavyweight lock
("VACUUM waiting" in the process title);
* every other VACUUM / ANALYZE / CREATE DATABASE, the autovacuum launcher
and all new connections ("startup waiting") queue up behind it, so the
server accepts no new sessions;
* no "deadlock detected" error is raised;
* the named VACUUM backend does not react to SIGTERM
(pg_terminate_backend equivalent) or to a manual SIGUSR1.
Only a server restart recovers. We first hit this with an automated test
workload (8 client sessions, one database per session); the server stayed
in this state for more than 24 hours until it was restarted.
How to reproduce
----------------
On a freshly initialised cluster, run the attached repro.sh inside the
server container (it only needs psql and a trusted local superuser):
docker run -d --name pg -e POSTGRES_HOST_AUTH_METHOD=trust
postgres:latest
docker cp repro.sh pg:/repro.sh
docker exec -u postgres pg bash /repro.sh 300
docker logs pg 2>&1 | grep ProcSignalBarrier
The script starts 12 sessions. Each one loops over:
DROP DATABASE IF EXISTS sN; CREATE DATABASE sN; -- connected to
"test"
-- then, connected to sN: a few CREATE TABLE / INSERT statements,
CREATE TEMP VIEW ...; ANALYZE; TRUNCATE t3; DISCARD TEMP;
VACUUM FULL;
VACUUM;
On a fresh cluster the hang appeared within two minutes in 2 of 2 runs.
It is timing dependent: on a cluster that had already been through a
restart, two runs of 4 and 9 minutes did not hang. The same workload with
"VACUUM FULL t0; VACUUM FULL t1; ..." (table-level) instead of the
database-wide "VACUUM FULL" ran for 6 minutes without hanging.
Stack traces
------------
Taken with gdb from the hung server (no debug symbols; full output of all
backends is attached as stacks.txt).
The DROP DATABASE backend:
epoll_wait <- WaitEventSetWait <- WaitLatch <-
ConditionVariableTimedSleep
<- WaitForProcSignalBarrier <- dropdb <- standard_ProcessUtility
The backend it is waiting for (PID 1770), and five other VACUUM backends:
epoll_wait <- WaitEventSetWait <- WaitLatch <- ProcSleep
<- LockAcquireExtended <- LockRelationOid <- relation_open <- table_open
<- vac_update_datfrozenxid <- vacuum <- ExecVacuum
Four further VACUUM backends:
... ProcSleep <- LockAcquireExtended <- LockRelationOid
<- try_relation_open <- vacuum_open_relation <- vacuum <- ExecVacuum
CREATE DATABASE:
... ProcSleep <- LockAcquireExtended <- LockRelationOid <- relation_open
<- table_open <- createdb
Analysis (from reading the source; not confirmed with a debug build)
---------------------------------------------------------------------
vac_update_datfrozenxid() calls vac_truncate_clog(), which is static and
appears to be inlined in this build. vac_truncate_clog() does
LWLockAcquire(WrapLimitsVacuumLock, LW_EXCLUSIVE);
...
relation = table_open(DatabaseRelationId, AccessShareLock);
so the heavyweight lock on pg_database is requested while an LWLock is
held, i.e. with interrupts held off. If that request has to wait, the
backend can neither absorb a ProcSignalBarrier nor be cancelled or
terminated. That would give this cycle, which the deadlock detector cannot
see because one edge is not a heavyweight lock:
A: DROP DATABASE holds RowExclusiveLock on pg_database,
waits in WaitForProcSignalBarrier() for B
B: VACUUM holds WrapLimitsVacuumLock (interrupts held off),
waits for AccessShareLock on pg_database behind C
C: VACUUM FULL (database-wide, now processing pg_database)
waits for AccessExclusiveLock on pg_database,
blocked by A
This matches the observations that the named backend ignores SIGTERM and
that the hang needs a database-wide VACUUM FULL, the only statement in the
workload that requests AccessExclusiveLock on pg_database.
Expected behaviour
------------------
DROP DATABASE should complete, or one of the participants should fail with
an error. A lock wait on pg_database should not be uninterruptible.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Andrey Rachitskiy | 2026-10-12 15:18:52 | Re: BUG #19732: first_value/last_value/nth_value return NULL with EXCLUDE TIES when the current row is outside its f |
| Previous Message | Matthias van de Meent | 2026-10-12 10:57:37 | Re: BUG #19519: REPACK can fail due to missing chunk for toast value |