multica-ai/multica · error
acquire advisory lock 4246: %w
Error message
acquire advisory lock 4246: %w
What it means
Executing SELECT pg_advisory_lock(4246) on the dedicated session connection failed. Note this is the blocking variant: a second backfill normally WAITS here, not errors. An actual error means the session broke (network drop, server restart, admin terminating the backend), the context was cancelled while queued, or a statement timeout fired while waiting for the lock.
Source
Thrown at server/cmd/backfill_task_usage_hourly/main.go:108
defer pool.Close()
if err := pool.Ping(ctx); err != nil {
return fmt.Errorf("ping database: %w", err)
}
// Serialise against the cron rollup and any other backfill run via
// advisory lock 4246 — the same id the cron entry checks with
// pg_try_advisory_lock. While this backfill holds it, the cron tick
// no-ops instead of racing on task_usage_hourly row locks; a second
// concurrent backfill blocks here until this one finishes. The lock
// is held on a dedicated session connection for the whole run.
lockConn, err := pool.Acquire(ctx)
if err != nil {
return fmt.Errorf("acquire advisory-lock connection: %w", err)
}
defer lockConn.Release()
if _, err := lockConn.Exec(ctx, `SELECT pg_advisory_lock(4246)`); err != nil {
return fmt.Errorf("acquire advisory lock 4246: %w", err)
}
defer func() {
// Unlock on a fresh context so a cancelled ctx (SIGINT) does not
// skip the release. Releasing the connection afterwards would end
// the session anyway, but an explicit unlock frees it immediately.
_, _ = lockConn.Exec(context.Background(), `SELECT pg_advisory_unlock(4246)`)
}()
var minTS, maxTS pgtype.Timestamptz
if err := pool.QueryRow(ctx, `SELECT MIN(created_at), MAX(created_at) FROM task_usage`).Scan(&minTS, &maxTS); err != nil {
return fmt.Errorf("scan task_usage time range: %w", err)
}
if !minTS.Valid {
slog.Info("task_usage is empty; nothing to backfill")
if *dryRun {
return nil
}
return stampWatermark(ctx, pool)View on GitHub (pinned to 2c0912b6ec)
Solutions
- Re-run the backfill — the lock is session-scoped and released when the dead session ends, and slices are idempotent
- If it recurs, query pg_locks/pg_stat_activity to see who holds advisory lock 4246 (SELECT pid, grantee... FROM pg_locks WHERE locktype='advisory')
- Disable/raise statement_timeout for this session if a DB-level timeout is aborting the wait
Defensive patterns
Strategy: retry
Try / catch
if _, err := lockConn.Exec(ctx, `SELECT pg_advisory_lock(4246)`); err != nil {
return fmt.Errorf("acquire advisory lock 4246: %w", err)
} Prevention
- Expect this to BLOCK, not fail, when contended — a failure means the session died
- Disable statement_timeout for the locking session
- Re-run after server restarts; slices are idempotent
When it happens
Trigger: SIGINT cancels ctx while this backfill is queued behind the cron rollup holding 4246; Postgres restarts mid-wait; pg_cancel_backend/pg_terminate_backend from an operator; statement_timeout set in the session.
Common situations: Two backfills or a backfill plus cron tick contending on lock 4246 with the second one interrupted while blocked; server failover during the wait.
Related errors
- acquire migration advisory lock: %w
- acquire advisory lock %d: %w
- ErrRepoBusy
- connect to database: %w
- ping database: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/2e8931588ca759ce.
Report an issue: GitHub.