I have an AFTER INSERT
trigger for table A that updates multiple rows in table B (incrementing a column in table B). Normally, the web server would insert multiple rows into table A using a single insert statement with the READ COMMITTED
isolation level, which would cause the trigger to fire for each row inserted. As there are multiple web servers at once, there will always be multiple concurrent insert statements/transactions.
Initially, this led to Postgres reporting deadlocks when the trigger was updating the rows in table B. To address this, I changed the trigger function to update the rows in table B in the order of their PK:
UPDATE table_b
SET counter = counter + 1
WHERE ctid = ANY(ARRAY(
SELECT ctid FROM table_b
WHERE name = 'some name' AND store = 'store id'
ORDER BY id
FOR UPDATE
))
The deadlocks were still happening after this change. I thought the above statement didn't work, so I changed it to use a PL/pgSQL FOR
loop:
FOR row_b IN (
SELECT id
FROM table_b
WHERE name = 'some name' AND store = 'store id'
ORDER BY id
)
LOOP
UPDATE table_b
SET counter = counter + 1
WHERE id = row_b.id;
END LOOP;
But this also didn't work and deadlocks were still happening. The source of the deadlock was reported to be at the update statement of table B (when incrementing the counter
of the row). This was the exact error reported by Postgres:
Process 316 waits for ShareLock on transaction 850467907; blocked by process 426.
Process 426 waits for ShareLock on transaction 850467903; blocked by process 316.
I'm confused about how a deadlock could still happen in this situation.