Skip to main content

PG.L3.MISSING_STATEMENT_TIMEOUT — Bounding how long a step may hold what it took

  • Category: safety
  • Level: 3
  • Confidence: deterministic
  • Downtime class: blocking
  • Stability: stable
  • Suites: lint
  • Applies to: PostgreSQL 18

lock_timeout protects you from a lock you cannot get. statement_timeout protects you from one you did.

Once a strong-lock operation has acquired its ACCESS EXCLUSIVE lock, it holds it for as long as the work takes — and everything else waits, for exactly as long. A table rewrite that was estimated at thirty seconds and turns out to take twenty minutes is not a slow deploy; it is twenty minutes of outage, and nothing stops it on its own.

statement_timeout is what caps the damage a single deploy step can do.

Why an estimate is not a bound

The two rules exist separately because the failures are separate. Estimates come from the row count you had when you wrote the migration, from a staging database an order of magnitude smaller, from a bigint change you thought was a metadata change. Every one of those is a reasonable belief that can be wrong by a factor of a hundred.

A timeout does not need the estimate to be right. It needs the ceiling to be tolerable.

Flagged

DB::statement('ALTER TABLE orders ADD COLUMN notes text');

Preferred

DB::statement("SET lock_timeout = '3s'");
DB::statement("SET statement_timeout = '30s'");
DB::statement('ALTER TABLE orders ADD COLUMN notes text');

Set it high enough to be honest

A statement_timeout shorter than the work genuinely needs turns a long migration into a migration that never completes, retried forever. If a step really does need ten minutes under a lock, the answer is not a ten-minute timeout — it is a different migration strategy (add-backfill-swap), and the timeout stays as the ceiling for when that goes wrong too.

SQLens sets its own, and never yours

Worth saying plainly on this page: the audit and catalog readers bound their own sessions with these settings before touching anything. What SQLens will not do is change your application's connection — its reader runs on a separate named connection built from a copy of your config, so the session it seals is its own.

Sources

The fix material this rule carries

A finding from this rule carries machine-readable fix material, using this sequence:

  • timeout_preamble — Bound how long the statement may wait and how long it may run before it is given up on.

The payload is material for you or an agent to apply. SQLens writes no migration and runs no DDL. See the remediation payload for every field, the placeholder semantics, and the version rules a consumer has to follow.