PostgreSQL 19 monitoring - new views, wait events and log controls
19’s monitoring changes are not one feature; they are a dozen small answers to questions that used to end in “you cannot see that from inside the database”. This page is organised as a runbook: the question you are trying to answer, and what 19 gives you to answer it.
“Which lock type is serialising my writers?” - pg_stat_lock
pg_locks tells you who holds what right now. New in 19, pg_stat_lock accumulates contention
per lock type, cluster-wide, since the last reset:
SELECT locktype, waits, wait_time, fastpath_exceeded
FROM pg_stat_lock ORDER BY wait_time DESC;
| Column | Meaning |
|---|---|
locktype |
Same values as pg_locks.locktype |
waits |
Acquisitions that waited longer than deadlock_timeout before succeeding |
wait_time |
Total milliseconds waited, same condition |
fastpath_exceeded |
Times the fast-path slot limit was hit - raising max_locks_per_transaction reduces this |
stats_reset |
When the counters were last reset |
The subtlety: waits only counts acquisitions that waited longer than deadlock_timeout - a
noisy-but-fast queue never registers. It measures locks that hurt, not locks that exist.
pg_stat_get_lock() is the function form.
“Is the standby promoted, paused or still replaying?” - pg_stat_recovery
At most one row, and empty unless the server is in recovery and you hold pg_read_all_stats:
SELECT promote_triggered, pause_state,
last_replayed_end_lsn, replay_end_lsn,
recovery_last_xact_time, current_chunk_start_time
FROM pg_stat_recovery;
pause_state reports not paused / pause requested / paused;
recovery_last_xact_time is the commit time on the primary of the last replayed transaction;
current_chunk_start_time marks when replay caught up with the newest WAL chunk received. Before
19 this state was spread across pg_is_in_recovery(), pg_last_wal_replay_lsn() and log parsing.
“Is the apply side of replication healthy?” - subscription and slot counters
pg_stat_replication_slotsgainsmem_exceeded_count(times logical decoding exceededlogical_decoding_work_mem) and the slot-sync trioslotsync_skip_count,slotsync_last_skip,slotsync_skip_reason- the last explains why a standby skipped synchronising a failover slot, which previously failed silently.pg_stat_subscription_statsrenamessync_error_counttosync_table_error_countand addssync_seq_error_countfor sequence sync errors, plusupdate_deletedcounting updates ignored because the row was concurrently deleted (needsretain_dead_tuples- see replication).pg_stat_wal_receiver.statusgains aconnectingvalue.
“What is the session waiting on?” - new wait events
| Wait event | Waiting on |
|---|---|
CopyFromRead, CopyToWrite |
I/O on a pipe, file or program during COPY |
CommitDelay |
commit_delay sleep before WAL flush |
| WAL write/flush LSN events | Waiting for specific WAL write and flush LSNs |
WaitLSN |
Shared wait-for-LSN state (the WAIT command) |
BUFFER |
Renamed from BUFFERPIN - dashboards matching the old name must be updated |
“How much WAL is full-page images?” - wal_fpi_bytes
pg_stat_wal gains wal_fpi_bytes (and pg_stat_get_backend_wal() the per-backend equivalent),
and VACUUM/ANALYZE logging now reports full-page-write bytes too. Checkpoints after bulk writes
are where this bites: wal_fpi_bytes / wal_bytes tells you how much of your WAL volume is page
images rather than records.
SELECT wal_bytes, wal_fpi_bytes,
round(100.0 * wal_fpi_bytes / wal_bytes, 1) AS fpi_pct
FROM pg_stat_wal;
“Who vacuumed this, and how hard?” - progress view columns
pg_stat_progress_vacuum gains started_by (including autovacuum_wraparound) and mode;
pg_stat_progress_analyze gains started_by. pg_stat_autovacuum_scores shows why autovacuum
picked the table it did - both are covered on the
autovacuum page.
“When were these counters reset?” - stats_reset everywhere
stats_reset columns are added to pg_stat_all_tables, pg_stat_all_indexes,
pg_statio_all_sequences (and their sys/user variants), pg_stat_user_functions and
pg_stat_database_conflicts - so “is this counter meaningful since last night” is answerable
without tracking resets yourself. pg_stat_progress_basebackup gains backup_type
(full/incremental); pg_stats/pg_stats_ext gain OID columns; and
pg_dsm_registry_allocations reports dynamic shared memory usage.
“Can I turn the noise down on just the noisy process?” - logging changes
log_min_messagesaccepts a process-type:level syntax, so a chatty component (say, the checkpointer or a walsender) can be quieter than the rest. A value without a colon keeps the old all-types behaviour.log_lock_waitsnow defaults on - sessions waiting pastdeadlock_timeoutlog the lock automatically. That is a real behaviour change for quiet systems: more log volume by default, more signal for contention.log_autovacuum_min_durationnow covers vacuum only;log_autoanalyze_min_durationis the separate control for autovacuum-triggered analyses.debug_print_raw_parselogs raw parse trees (also on at debug level 3+); remote-server messages (replication, postgres_fdw, dblink) now appear in local log format;pg_get_multixact_stats()reports multixact activity.
Wraparound warning: 100 million, not 40
The warning that a transaction-ID or multixact wraparound is approaching now fires below 100 million remaining IDs instead of 40 million - to clients and the server log. If your alerting keys on the old threshold, expect warnings to appear sooner on the same workload; if it keys on the message, nothing changes.
What changed, and in which release
| Question | PG 18 and earlier | PG 19 |
|---|---|---|
| Lock-type contention over time | Not measurable | pg_stat_lock |
| Recovery state on a standby | Scattered functions | pg_stat_recovery |
| Decoding memory pressure | Invisible | mem_exceeded_count |
| Why a slot did not sync | Silent | slotsync_skip_* columns |
| FPI share of WAL | wal_fpi count only |
wal_fpi_bytes bytes |
| Lock waits in the log | log_lock_waits off by default |
On by default |
| Log level per process type | One global log_min_messages |
type:level syntax |
| Wraparound warning threshold | 40 million | 100 million |
BUFFERPIN wait events |
BUFFERPIN |
Renamed BUFFER |
| Autovacuum-initiated analyse logging | Shared log_autovacuum_min_duration |
log_autoanalyze_min_duration |
Where it breaks
- Thresholds silently different on upgrade -
log_lock_waitson by default means more log volume; the 100-million wraparound warning fires earlier. Both are deliberate, both surprise. pg_stat_locklooking empty on a busy system - sub-deadlock_timeoutwaits are not counted; absence of rows means no painful waits, not no contention.- Renames breaking dashboards -
sync_error_countandBUFFERPINare gone under those names; alert on the new ones. pg_stat_recoveryreturning nothing - it returns a row only in recovery, and only forpg_read_all_statsholders; an empty result on a standby with the right privileges is itself a signal.- Beta caveat - one column was already renamed mid-cycle; others could follow before GA.
What this is not for
- Real-time per-session state - cumulative views lag by process flush (up to about a second);
pg_stat_activityremains the live view. - Replacing external metrics - these views feed an exporter; they do not retain history across resets or restarts (stats reset on crash recovery).
PostgreSQL release support
5 supported| Release | Latest | Released | End of life | Status |
|---|---|---|---|---|
18 | 18.6 | Sept 2025 | Nov 2030 | Supported |
17 | 17.11 | Sept 2024 | Nov 2029 | Supported |
16 | 16.15 | Sept 2023 | Nov 2028 | Supported |
15 | 15.19 | Oct 2022 | Nov 2027 | Supported |
14 | 14.24 | Sept 2021 | Nov 2026 | Supported |
13 | 13.23 | Sept 2020 | Nov 2025 | End of life |
12 | 12.22 | Oct 2019 | Nov 2024 | End of life |
11 | 11.22 | Oct 2018 | Nov 2023 | End of life |
Data as of 2026-10-01 from endoflife.date. Refresh with node scripts/fetch-versions.mjs.
What to watch
Specific things that would change what this page concludes.
- Whether any of the new view columns are renamed between Beta 4 and GA - one was already renamed in the cycle (sync_error_count became sync_table_error_count)
Where PostgreSQL sits
How this component relates to the rest of the stack. Items without a link are not covered here yet.