CONCEPT Cited by 1 source
MVCC horizon¶
Definition¶
The MVCC horizon (also "xmin horizon" or "vacuum horizon") is the
transaction-id cutoff below which Postgres knows no active transaction
can still see old row versions — and therefore the point below which
VACUUM can reclaim dead tuples. The
horizon is set by the xmin of the oldest currently-active
transaction anywhere on the cluster (plus any hot-standby feedback
constraints). Every dead tuple newer than the horizon is retained,
because it might still be visible to someone's snapshot.
Why it matters¶
Postgres implements MVCC by writing a new row version for every update and marking the old version dead. A healthy database reclaims those dead tuples as soon as nobody can see them anymore. But "nobody can see them" is a global property: any single long-running transaction, anywhere, holds the horizon fixed and blocks reclamation of every dead tuple created after that transaction started.
This is the failure mode that defeats Postgres-backed job queues in mixed-workload clusters:
- A 2-minute analytics query pins the horizon for 2 minutes.
- Three 40-second analytics queries staggered 20 seconds apart keep
the horizon pinned continuously — no individual query hits
statement_timeoutbut the effect on vacuum is identical to one query that never ends (Source: sources/2026-04-11-planetscale-keeping-a-postgres-queue-healthy).
Why timeouts alone don't help¶
Postgres ships several timeouts that can kill long-running work:
| Timeout | Postgres version | What it kills |
|---|---|---|
statement_timeout |
7.3+ | Individual SQL statement exceeding duration |
idle_in_transaction_session_timeout |
9.6+ | Session idle inside an open transaction |
transaction_timeout |
17.0+ | Any transaction exceeding duration |
All three target duration of a single unit of work. None of them limits concurrency of a workload class. Continuously-overlapping short-ish queries can keep the horizon pinned indefinitely without any individual query tripping a timeout — which is the exact failure mode modern Postgres queue writers trip over (Source: sources/2026-04-11-planetscale-keeping-a-postgres-queue-healthy).
The production-grade fix is a workload-class resource budget that caps the number of concurrently running queries tagged as low-priority, so vacuum windows actually open up between them.
Relation to other concepts¶
- concepts/postgres-mvcc-hot-updates — HOT updates minimize the rate at which dead tuples accumulate per update. The MVCC horizon controls whether they can be reclaimed. Both halves of MVCC matter independently; a HOT-optimized workload can still die from a pinned horizon if it shares a cluster with slow readers.
- concepts/dead-tuples-vacuum — the entities that accumulate when the horizon is pinned.
- concepts/postgres-autovacuum — the process that reclaims dead tuples, but only those older than the horizon.
- concepts/postgres-queue-table — the workload shape most sensitive to horizon pinning (high write churn, index-ordered scans, dead-tuple accumulation visible on the hot path).
Seen in¶
- sources/2026-04-11-planetscale-keeping-a-postgres-queue-healthy
— Simeon Griggs (PlanetScale) frames the horizon as the reason
modern Postgres queues still degrade in mixed-workload clusters
despite B-tree and
SKIP LOCKEDimprovements. Worked example: three 40-sec analytics queries staggered 20 sec apart pin the horizon continuously, and no Postgres-upstream timeout can stop them. Fix: a workload-class resource budget (PlanetScale's Traffic Control) caps concurrentaction=analyticsworkers to 1, letting vacuum run in the gaps between queries.