wraparoundStatus

Return transaction id and multixact wraparound headroom: age(datfrozenxid) and age(datminmxid) for every database, the oldest tables by age(relfrozenxid) including TOAST tables (often the relation actually holding the horizon back), each xid and multixact age as a percentage of…

read-onlyany connectionsweeps instancesweeps group

Synopsis

wraparoundStatus([limit], [schema])

Description

Return transaction id and multixact wraparound headroom: age(datfrozenxid) and age(datminmxid) for every database, the oldest tables by age(relfrozenxid) including TOAST tables (often the relation actually holding the horizon back), each xid and multixact age as a percentage of the effective freeze_max_age and of the limit at which the cluster stops accepting write transactions, plus the per-table freeze storage parameters and last vacuum times.

That limit is 2144483647, not 2^31: PostgreSQL refuses new transaction ids three million short of wraparound, and warns in the log forty million short of it -- wraparound_warn_limit and xids_until_warn_limit carry the earlier one, which is the threshold an operator has usually already seen fire. Two alarms live in these numbers and only one is an emergency: past autovacuum_freeze_max_age PostgreSQL forces an anti-wraparound vacuum, which is loud maintenance working as designed, while approaching the wraparound limit ends in the server refusing writes -- xid_percent_of_freeze_max_age against xid_percent_of_wraparound_limit tells them apart and xids_until_wraparound_limit is the budget.

Recovery does NOT need single-user mode: the documentation says plainly that stopping the postmaster is neither necessary nor desirable, and the fix is a plain database-wide VACUUM in normal multi-user mode. Not VACUUM FULL, which needs an xid of its own and will fail; not VACUUM FREEZE, which does more than the minimum needed to restore service. Past vacuum_failsafe_age VACUUM takes extraordinary measures -- and it is any VACUUM, not only autovacuum: the cost-based delay stops being applied, non-essential work such as index vacuuming is bypassed, AND the Buffer Access Strategy is disabled so the vacuum is free to use all of shared_buffers.

That third effect is why the cache looks wrecked during one, which bufferCacheSummary will show and nothing else explains. Default 1.6 billion, silently raised to no less than 105% of autovacuum_freeze_max_age. Rank tables by relfrozenxid age rather than size, since the oldest object sets the horizon however small it is, and read toast_for -- a TOAST table is frequently the offender and carries nobody's name. If the age will not fall, vacuum is not the problem: nothing can be frozen past the oldest transaction still visible to something, so more workers and a manual VACUUM FREEZE achieve nothing while the horizon is held.

Four things hold it -- a replication slot (replicationSlots), a long-running transaction (currentActivity.backend_xmin), a standby with hot_standby_feedback, and a prepared transaction, which is invisible in pg_stat_activity and not read by this server: query pg_prepared_xacts directly when nothing else explains it.

Parameters

limit optionalinteger
how many tables to return, oldest first. Defaults to 20
schema optionalstring
restrict the table list to one schema; omit to cover the whole database, which is what wraparound risk is actually measured over

Also accepts connection, instance, group, described once under arguments every tool takes.

Output

Transaction id and multixact headroom. An absent extension or a missing grant is reported as {error, hint} instead.

FieldType
databasesobject | null
limitsobject | null
tablesarray | null

Scope

A physical replica is byte-identical here, so asking each member of a replication group adds nothing. Dead tuples and the last vacuum and analyze times describe work that only happens on a primary; on a replica they are noise, not a second opinion.

Example mocked data

Request

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "wraparoundStatus",
    "arguments": {
      "limit": 1
    }
  }
}

Result

{
  "databases": {
    "shop": {
      "xid_age": 182004412,
      "xid_percent_of_freeze_max_age": 91.0,
      "xid_percent_of_wraparound_limit": 8.5,
      "mxid_age": 1204,
      "mxid_percent_of_freeze_max_age": 0.0
    }
  },
  "limits": {
    "autovacuum_freeze_max_age": 200000000,
    "wraparound_limit": 2144483647,
    "wraparound_warn_limit": 2104483647
  },
  "tables": [
    {
      "schema": "shop",
      "name": "events_2025_09",
      "kind": "table",
      "toast_for": null,
      "xid_age": 182004410,
      "freeze_max_age": 200000000,
      "size": 1182990336,
      "last_autovacuum": "2026-06-01T04:12:00Z"
    }
  ]
}

Invented values on a fictional shop database, shaped by and checked against this tool's output schema. Real output is returned as structuredContent to clients that negotiate MCP 2025-06-18 or later.

See also

diskUsage, databaseSize, serverSettings, currentActivity, currentLocks, databaseStats, statementStats, progressStats, ioStats, checkpointStats, tableIOStats, hostCapacity, bufferCacheSummary, bufferCacheContents