Andrey Antukh 8a192dc68f ♻️ Lock each consumer's pendings instead of the audit-log rows
The drain locked the audit_log rows it reads, but the rows it writes
are the pendings: each consumer owns its queue and deletes its own
pendings. Locking the shared table made the consumers wait for each
other, and the events consumer had to skip the locked rows with SKIP
LOCKED to avoid that wait.

The lock moves to audit_log_pending, so every consumer locks its own
queue and no consumer waits for another one. The rows of audit_log are
only read now, and every delete that takes them away (the drained
chunk and the orphan sweep) skips the ones another transaction holds
instead of waiting for them.

The SKIP LOCKED variant of the chunk query is gone: the events have to
be executed in order, like an event log, so a second run (another
backend, or a retry overlapping the previous one) waits for the
pendings of the run in flight and takes the next events. Skipping the
locked ones would hand the newer events to the second run while the
older ones were still in flight.

The lock is FOR NO KEY UPDATE, not FOR UPDATE: deleting an audit_log
row makes the foreign key check take FOR KEY SHARE on the pending rows
that reference it, and those belong to the other consumers. FOR KEY
SHARE does not conflict with FOR NO KEY UPDATE, so the check never
waits for a drain in flight and the two consumers cannot deadlock. It
still conflicts with itself, so two runs of the same consumer take
turns.

AI-assisted-by: minimax-m3.1-flash
2026-10-23 17:34:54 +00:00
..
2026-10-07 10:41:29 +02:00
2024-05-29 19:05:04 +02:00