You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On Supabase Micro with the pooler (transaction mode, port 5432), an autopilot cycle's UPDATE minion_jobs SET lock_until = now() + ($1::double precision * interval '1 millisecond'), updated_at = now() occasionally leaves a backend session stuck in state='active', wait_event='ClientRead', holding a RowExclusiveLock on minion_jobs indefinitely. Seen twice on my instance: first at 22h age, then again at 23h51m age, blocking any subsequent ALTER TABLE minion_jobs (e.g. migration 15).
Held RowExclusiveLock on minion_jobs. Not naturally released; had to pg_terminate_backend() manually.
Suspected pattern
Autopilot cycle (or a worker) issues a lock-refresh UPDATE on minion_jobs.
The postgres.js client dies (launchctl reload, SIGKILL, crash) before COMMIT/ROLLBACK.
Pgbouncer keeps the upstream Postgres backend alive but the client side is gone, so the backend sits in ClientRead waiting for the next command that never arrives.
On Supabase Micro there's no default idle_in_transaction_session_timeout for pooler-held sessions, so these never time out on their own.
Autovacuum can't clean them up because the session holds active locks, not idle.
Impact
Blocks schema migrations on the same table (reproduced: migration 15's ALTER hung until I terminated the backend).
Burns a connection slot on Micro (default 10 total from the pooler, smaller budget than it looks).
Dead weight that accumulates over time if autopilot restarts enough.
Set session-level timeouts in postgres.js on connect.idle_in_transaction_session_timeout = '2min' + statement_timeout = '5min' as the first statements after establishing a connection. Postgres.js exposes this via the connection.application_name / connection hook. This ensures an abandoned in-flight transaction gets killed on the server side even when the client vanishes.
Wrap autopilot's lock-refresh UPDATE in sql.begin(async tx => ...). Not sure if it's currently a single-statement autocommit; if yes, this wouldn't change the situation, but if the statement is part of a longer implicit transaction, wrapping ensures ROLLBACK on client failure.
Document for Supabase users: set ALTER DATABASE postgres SET idle_in_transaction_session_timeout = '5min'; at the DB level (requires direct connection, not pooler). Belt-and-suspenders even if feat: GBrain v0.1.0 — Postgres-native personal knowledge brain #1 is implemented.
Environment
gbrain 0.16.4 (autopilot via launchctl, ~150s cycle)
Supabase Micro (no PITR, no paid-tier restore-to-project) in eu-west-1
postgres.js 3.x via the session pooler (port 5432)
15,580 pages, ~130k chunks in the brain
Happy to test a candidate fix on my instance if that helps.
Summary
On Supabase Micro with the pooler (transaction mode, port 5432), an autopilot cycle's
UPDATE minion_jobs SET lock_until = now() + ($1::double precision * interval '1 millisecond'), updated_at = now()occasionally leaves a backend session stuck instate='active',wait_event='ClientRead', holding aRowExclusiveLockonminion_jobsindefinitely. Seen twice on my instance: first at 22h age, then again at 23h51m age, blocking any subsequentALTER TABLE minion_jobs(e.g. migration 15).Observed state (from
pg_stat_activity)Held
RowExclusiveLockonminion_jobs. Not naturally released; had topg_terminate_backend()manually.Suspected pattern
minion_jobs.idle_in_transaction_session_timeoutfor pooler-held sessions, so these never time out on their own.Impact
Suggested mitigations (any one helps; ideally #1)
idle_in_transaction_session_timeout = '2min'+statement_timeout = '5min'as the first statements after establishing a connection. Postgres.js exposes this via theconnection.application_name/connectionhook. This ensures an abandoned in-flight transaction gets killed on the server side even when the client vanishes.sql.begin(async tx => ...). Not sure if it's currently a single-statement autocommit; if yes, this wouldn't change the situation, but if the statement is part of a longer implicit transaction, wrapping ensures ROLLBACK on client failure.ALTER DATABASE postgres SET idle_in_transaction_session_timeout = '5min';at the DB level (requires direct connection, not pooler). Belt-and-suspenders even if feat: GBrain v0.1.0 — Postgres-native personal knowledge brain #1 is implemented.Environment
Happy to test a candidate fix on my instance if that helps.