On Thu, 24 Sept 2026 at 08:57, Antonin Houska <[email protected]> wrote: > > Thom Brown <[email protected]> wrote: > > > On Wed, 23 Sept 2026 at 17:22, Antonin Houska <[email protected]> wrote: > > > > > > shihao zhong <[email protected]> wrote: > > > > > > > > or whether the relfilenode should be re-checked after the snapshot is > > > > > built > > > > > > > > Holding the toast lock from the start deadlocks. A session that asks for > > > > AccessExclusiveLock gets an XID before it waits, and the decoding worker > > > > waits for all XIDs while it sets up. > > > > > > The same (supposedly low) deadlock risk already exists for the main > > > table, see > > > this comment in rebuild_relation(): > > > > > > /* > > > * Start the worker that decodes data changes applied while we're > > > * copying the table contents. > > > * > > > * Note that the worker has to wait for all transactions with XID > > > * already assigned to finish. If some of those transactions is > > > * waiting for a lock conflicting with ShareUpdateExclusiveLock on our > > > * table (e.g. it runs CREATE INDEX), we can end up in a deadlock. > > > * Not sure this risk is worth unlocking/locking the table (and its > > > * clustering index) and checking again if it's still eligible for > > > * REPACK CONCURRENTLY. > > > */ > > > start_repack_decoding_worker(tableOid); > > > > > > I'm not sure if locking the TOAST relation earlier would make the > > > situation > > > worse. > > > > > > The reason TOAST relation is not locked until copy_table_data() does so is > > > that CLUSTER / VACUUM FULL in v18 did it this way (not sure what the > > > reason > > > for such design was). I haven't changed that for REPACK exactly because I > > > failed to envision this stale relfilenode issue. > > > > I gave that a try, and it does. It just swaps the lost update for a > > deadlock. > > > > If you lock the toast up front and something rewrites it at the same > > time (which is the thing that triggers this in the first place, e.g. a > > REPACK of the toast table), REPACK falls over: > > > > Session 1: > > BEGIN; > > INSERT INTO test VALUES (999999, 'x'); > > > > Session 2: > > REPACK (CONCURRENTLY) test; > > > > Session 1: > > CREATE INDEX ON test (big); > > > > ERROR: deadlock detected > > DETAIL: Process 214534 waits for ShareLock on transaction 1774005; > > blocked by process 214579. > > Process 214579 waits for AccessExclusiveLock on relation 3672470 of > > database 5; blocked by process 214534. > > CONTEXT: REPACK decoding worker > > > > The rewrite already has an XID by the time it waits, and the worker > > waits for that XID whilst it sets up, so the two just sit on each > > other. It doesn't matter which lock we take either because anything > > that would stop the rewrite conflicts with it. > > IMO this example does not exactly demonstrate the problem described in the > comment above: if REPACK (CONCURRENTLY) waits for AccessExclusiveLock, it's > going to perform the relation swap, so the worker should already be gone. > > On the other hand, the message > > "Process ... waits for ShareLock on transaction ..." > > is what the deadlock detector would report for the decoding worker. However, > where would the request for AccessExclusiveLock come from in that case? CREATE > INDEX only uses it to lock the new index relation, however that cannot be > locked by other backends until the transaction has committed (because it's not > visible before commit). > > What exactly have you changed in the code?
Apologies, I seem to have incorrectly paired tests with different results during my copy and pasting. I'll see if I can untangle it later today. Thom
