When an agent encounters this scenario, Schema provides these diagnostic steps automatically.
At 1.8 billion transaction IDs, you're in a critical window—PostgreSQL starts warning at 40 million from wraparound and goes read-only at 3 million remaining. Start by confirming the exact age and urgency via datfrozenxid metrics, then immediately hunt for blockers preventing vacuum from advancing the frozen XID horizon: long-running transactions, prepared transactions, and replication slots. Finally, assess whether autovacuum is running and if manual VACUUM FREEZE is needed on the oldest tables.
1Confirm current XID age and calculate time to emergency
Check `database_wraparound_age_datfrozenxid` to get the exact transaction age—if it's above 1.5 billion (75% of the 2 billion limit), you're in the danger zone. Cross-reference with `before_xid_wraparound` to see how many transactions remain before forced shutdown. At 1.8 billion, you likely have 200 million XIDs left, which sounds like a lot but can burn through quickly on high-transaction workloads. The `transaction-id-wraparound-risk` insight flags tables above 150 million as requiring immediate action, and `transaction-id-wraparound-prevention` notes that PostgreSQL refuses new transactions at 3 million remaining.
2Hunt for long-running and prepared transactions blocking vacuum
Check `backend_xid` and `backend_xmin` across all sessions in pg_stat_activity—any transaction with age(backend_xmin) in the hundreds of millions is preventing vacuum from advancing the freeze horizon. Also query pg_prepared_xacts for orphaned prepared transactions, which are a common silent killer. The `transaction-id-wraparound-prevention` insight specifically calls out that old prepared transactions and long-running snapshots are the primary blockers. Terminate or resolve these immediately—even a single ancient transaction can prevent the entire database from advancing its frozen XID.
3Verify autovacuum is running and not being suppressed
Check `postgresql-vacuum-count` and `postgresql-autovacuumed` to confirm vacuum activity is happening, and use `postgresql-last-vacuum-age` to identify tables that haven't been vacuumed in weeks or months. If last vacuum age is extremely high on large tables, autovacuum may be disabled, too conservative (high vacuum_scale_factor), or getting canceled by statement_timeout. The `transaction-id-wraparound-emergency-autovacuum` insight warns that emergency autovacuum kicks in at autovacuum_freeze_max_age (default 200M), but if you're at 1.8B, that emergency vacuum has either failed or is being blocked.
4Identify the specific tables closest to wraparound
Query pg_class for age(relfrozenxid) on all tables and sort descending—tables with age above 1.5 billion are your immediate fire drill. The `postgresql-relation-xmin` metric can help track transaction visibility, but you specifically need relfrozenxid age here. Focus manual VACUUM FREEZE efforts on the oldest tables first, as the `transaction-id-wraparound-risk` and `transaction-id-wraparound-write-outage` insights explain that individual table ages determine when the database goes read-only (at autovacuum_freeze_max_age, default 200M, but you're way past that).
5Check for replication slots holding back the XID horizon
Query pg_replication_slots for any slots with non-NULL xmin or catalog_xmin values—these hold back vacuum globally even if no other transactions are running. The `transaction-id-wraparound-prevention` insight specifically mentions dropping old replication slots as a required action. Unused or abandoned logical replication slots are a classic cause of sudden wraparound emergencies. If you find old slots, drop them immediately unless they're actively needed for replication.
6Assess multixact ID wraparound risk separately
Check `database_wraparound_age_datminmxid` to see if you also have a multixact ID problem—multixacts have a separate 2 billion limit and can cause wraparound independently. The `multixact-id-wraparound-emergency-autovacuum` insight notes that multixact wraparound triggers emergency vacuum at autovacuum_multixact_freeze_max_age (default 400M). If datminmxid age is also high (above 1 billion), you have two separate emergencies and need to run VACUUM to advance both datfrozenxid and datminmxid.