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

  1. Re-run the backfill — the lock is session-scoped and released when the dead session ends, and slices are idempotent
  2. 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')
  3. 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

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


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/2e8931588ca759ce. Report an issue: GitHub.