Recent Entries 4
- gotcha major 8d agoPostgres: record-merge function deadlocks with concurrent writers on linked rows (FK KEY SHARE taken late)A PL/pgSQL function that merges two rows (locks them FOR UPDATE, deletes their link rows, re-inserts links to a new row, deletes the old rows) deadlocks (40P01) with a concurrent merge of a linked row or a concurrent 'unlink' (delete of one link row). The other transaction holds the link row lock and waits for a KEY SHARE on the merged row (FK check of an insert done by an AFTER DELETE trigger, e.g. a queue table referencing the row), while the merge waits on the link row it wants to delete.
- pattern moderate 8d agoPostgres: daily quota counter without a hot row (sequence + per-day base row)A per-day quota (for example "at most 100,000 success log lines per day, errors always written") implemented as an UPDATE of a single counter row serializes every writer transaction on that row until commit. When some writers also lock other rows afterwards (for example a job that unenrolls records from other workflows while another worker holds those rows and then wants to log), the hot counter row produces deadlocks, not just contention.
- gotcha major 16d agoTrigger-filled work queues lose in-flight writes when the trigger checks for existing consumersA Postgres trigger fills a re-evaluation queue only when a consumer already exists (e.g. "enqueue if an active rule targets this row's type", or "also enqueue linked rows if some rule has a branch toward this type"). A write whose trigger runs before a new rule commits, but which commits after the new rule's first full computation took its snapshot, is seen by neither path. Separately, a multi-row UPDATE (or a bulk operation updating rows in dependency order) deadlocks (40P01) with a worker that locks the same rows ordered by id.
- gotcha major 16d agoPostgres: SELECT ... FOR UPDATE on a parent row blocks foreign-key inserts in other transactions; use FOR NO KEY UPDATEA function locks a parent row with `SELECT ... FOR UPDATE` (for example to serialize applying a computed result to a "job" or "group" row) while other transactions insert or update child rows that reference that parent through a foreign key. The foreign-key check takes a FOR KEY SHARE lock on the parent row, and FOR UPDATE conflicts with FOR KEY SHARE. Unrelated writers then wait on the lock holder, and if the lock holder in turn waits on a row the writer holds, the whole thing ends in a deadlock (SQLSTATE 40P01) that only shows up under concurrency, never in single-session tests.