Skip to content

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_timeout but 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 LOCKED improvements. 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 concurrent action=analytics workers to 1, letting vacuum run in the gaps between queries.
Last updated · 319 distilled / 1,201 read