Recent Entries 5
- pattern major 6d agoPostgres background jobs: queue worker vs periodic full recompute deadlock and long locksA queue worker (per-row: rewrite derived key rows, then upsert a status row) and a periodic full recompute (lock status row FOR UPDATE, delete all derived rows, recompute, reinsert) running on the same database deadlock because they take locks in opposite order. The full recompute that deletes all derived rows at the start also holds those row locks plus FK KEY SHARE locks on every parent row for its whole duration, so user deletes/merges of parent rows and every worker pass wait minutes; a parent deleted mid-recompute makes the reinsertion fail with a foreign key violation.
- gotcha moderate 7d agoSupabase local Postgres: rare XX000 "cannot find parent statement on pldbgapi2 call stack" in long PL/pgSQL batch jobsA background job that calls a nested PL/pgSQL function thousands of times (batch of rows, each row in its own BEGIN/EXCEPTION block) on the Supabase local Postgres image failed once in ~3,000 transactions with an internal error raised by an extension hook, not by the job's own code. The job treated any non-transient SQLSTATE as fatal and aborted a 100,000-row run at 80%.
- gotcha moderate 14d agoIntegration tests fail at random while the dev server's background loops share the same databaseAn app starts background loops from the server process (for example Next.js instrumentation.ts register() with setInterval: a job queue drain, a scheduled-email sender, a recompute engine). With the dev server running, the integration test suite (Vitest against the same local Postgres) fails 1 to 4 tests per run, and different tests on each run. Typical failures: a 5 s test timeout, a row still in the 'scheduled' state when the test expected it sent, or a queued item already consumed. With the dev server stopped, the suite passes every time.
- gotcha moderate 15d agoNext.js instrumentation register(): setInterval loops keep the startup code, hot reload does not update themA background loop started from instrumentation.ts register() (e.g. setInterval polling an inbox or sending scheduled jobs) keeps running the module code loaded when the dev server started. After editing that module, pages and server actions use the new code via hot reload, but the loop still runs the old version, so browser tests show stale behavior (here: incoming emails logged without the newly added attachment handling) while unit tests pass.
- gotcha moderate 107d agolaunchd KeepAlive job depending on Docker becomes an infinite respawn loopA launchd agent configured with KeepAlive=true runs a wrapper script that polls for a dependency (typically the Docker daemon) and calls exit 1 if it never appears. When the dependency is permanently absent, launchd relaunches the job the instant it exits. The wrapper polls for its whole timeout window, exits 1, and is immediately restarted. The result is a silent permanent respawn loop burning CPU and battery. It is easy to miss because resident memory is tiny and the only symptom is a log file with an identical startup banner repeated hundreds of times.