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.