Concurrent DROP TABLESPACE can miss a shared dependency

From: Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
To: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Concurrent DROP TABLESPACE can miss a shared dependency
Date: 2026-08-30 20:05:39
Message-ID: CAJTYsWXjAQFnGKzXsht3XK8KHhyhontwFJw4JAHsUSfs_ptR4g@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

I found a race between DROP TABLESPACE and a concurrent command that adds a
shared dependency on the tablespace. Both commands can succeed, leaving an
object whose pg_class.reltablespace and pg_shdepend entries refer to a
tablespace that no longer exists.

One way to reproduce this is:

1. One session updates the pg_tablespace row and keeps the transaction open.
2. A second session runs DROP TABLESPACE. It finds no dependencies, then
waits while deleting the pg_tablespace tuple.
3. A third session creates a partitioned table in the tablespace and commits.
4. The first session aborts, allowing DROP TABLESPACE to finish using the
result of its earlier dependency check.

ISTM the race is possible because shdepAddDependency() takes an
AccessShareLock on the referenced shared object and rechecks that it still
exists, but DropTableSpace() calls checkSharedDependencies() without first
taking the corresponding conflicting lock. DropRole() appears to follow
that protocol already.

For a fix, my first thought was to have DROP TABLESPACE take an
AccessExclusiveLock before checking its shared dependencies. However,
doing only that seems to introduce a lock-order problem with commands that
update pg_tablespace without first locking the tablespace. Such a command
can hold the catalog tuple while DROP TABLESPACE holds the object lock, and
then try to acquire the object lock itself when the transaction records a
tablespace dependency.

I went through the paths that update pg_tablespace, and I think ALTER
TABLESPACE RENAME/SET, DROP OWNED, and REASSIGN OWNED need to acquire an
AccessShareLock before updating the catalog tuple. Direct GRANT, REVOKE,
and ALTER OWNER appear to acquire an object lock already. The attached
0002 contains those changes, separately from the DROP-side change in 0001.
(They likely need to be squashed once the patch looks fine).

Does this AccessExclusiveLock/AccessShareLock protocol seem like the right
way to close the race? Also, have I missed another pg_tablespace update
path that should participate in the same protocol?

Regards,
Ayush

Attachment Content-Type Size
v1-0001-Prevent-orphaned-tablespace-dependencies.patch application/octet-stream 6.3 KB
v1-0002-Avoid-deadlocks-with-DROP-TABLESPACE.patch application/octet-stream 10.7 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Daniel Gustafsson 2026-08-30 20:14:17 Re: Online enable/disable data checksums functions return success even when the launcher fails to start
Previous Message Rithvika Devisetti 2026-08-30 19:12:49 Re: WAIT FOR command should do some query jumbling