/* ------------------------------------------------------------------------- * * pgstat_wal.c * Implementation of WAL statistics. * * This file contains the implementation of WAL statistics. It is kept * separate from pgstat.c to enforce the line between the statistics access / * storage implementation and the details about individual types of * statistics. * * Copyright (c) 2001-2023, PostgreSQL Global Development Group * * IDENTIFICATION * src/backend/utils/activity/pgstat_wal.c * ------------------------------------------------------------------------- */ #include "postgres.h" #include "utils/pgstat_internal.h" #include "executor/instrument.h" PgStat_PendingWalStats PendingWalStats = {0}; /* * WAL usage counters saved from pgWalUsage at the previous call to * pgstat_report_wal(). This is used to calculate how much WAL usage * happens between pgstat_report_wal() calls, by subtracting * the previous counters from the current ones. */ static WalUsage prevWalUsage; /* * Calculate how much WAL usage counters have increased and update * shared WAL and IO statistics. * * Must be called by processes that generate WAL, that do not call * pgstat_report_stat(), like walwriter. */ void pgstat_report_wal(bool force) { pgstat_flush_wal(force); pgstat_flush_io(force); } /* * Support function for the SQL-callable pgstat* functions. Returns * a pointer to the WAL statistics struct. */ PgStat_WalStats * pgstat_fetch_stat_wal(void) { pgstat_snapshot_fixed(PGSTAT_KIND_WAL); return &pgStatLocal.snapshot.wal; } /* * Calculate how much WAL usage counters have increased by subtracting the * previous counters from the current ones. * * If nowait is true, this function returns true if the lock could not be * acquired. Otherwise return false. */ bool pgstat_flush_wal(bool nowait) { PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal; WalUsage wal_usage_diff = {0}; Assert(IsUnderPostmaster || !IsPostmasterEnvironment); Assert(pgStatLocal.shmem != NULL && !pgStatLocal.shmem->is_shutdown); /* * This function can be called even if nothing at all has happened. Avoid * taking lock for nothing in that case. */ if (!pgstat_have_pending_wal()) return false; /* * We don't update the WAL usage portion of the local WalStats elsewhere. * Calculate how much WAL usage counters were increased by subtracting the * previous counters from the current ones. */ WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevWalUsage); if (!nowait) LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); else if (!LWLockConditionalAcquire(&stats_shmem->lock, LW_EXCLUSIVE)) return true; #define WALSTAT_ACC(fld, var_to_add) \ (stats_shmem->stats.fld += var_to_add.fld) #define WALSTAT_ACC_INSTR_TIME(fld) \ (stats_shmem->stats.fld += INSTR_TIME_GET_MICROSEC(PendingWalStats.fld)) WALSTAT_ACC(wal_records, wal_usage_diff); WALSTAT_ACC(wal_fpi, wal_usage_diff); WALSTAT_ACC(wal_bytes, wal_usage_diff); WALSTAT_ACC(wal_buffers_full, PendingWalStats); WALSTAT_ACC(wal_write, PendingWalStats); WALSTAT_ACC(wal_sync, PendingWalStats); WALSTAT_ACC_INSTR_TIME(wal_write_time); WALSTAT_ACC_INSTR_TIME(wal_sync_time); #undef WALSTAT_ACC_INSTR_TIME #undef WALSTAT_ACC LWLockRelease(&stats_shmem->lock); /* * Save the current counters for the subsequent calculation of WAL usage. */ prevWalUsage = pgWalUsage; /* * Clear out the statistics buffer, so it can be re-used. */ MemSet(&PendingWalStats, 0, sizeof(PendingWalStats)); return false; } void pgstat_init_wal(void) { /* * Initialize prevWalUsage with pgWalUsage so that pgstat_flush_wal() can * calculate how much pgWalUsage counters are increased by subtracting * prevWalUsage from pgWalUsage. */ prevWalUsage = pgWalUsage; } /* * To determine whether any WAL activity has occurred since last time, not * only the number of generated WAL records but also the numbers of WAL * writes and syncs need to be checked. Because even transaction that * generates no WAL records can write or sync WAL data when flushing the * data pages. */ bool pgstat_have_pending_wal(void) { return pgWalUsage.wal_records != prevWalUsage.wal_records || PendingWalStats.wal_write != 0 || PendingWalStats.wal_sync != 0; } void pgstat_wal_reset_all_cb(TimestampTz ts) { PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal; LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); memset(&stats_shmem->stats, 0, sizeof(stats_shmem->stats)); stats_shmem->stats.stat_reset_timestamp = ts; LWLockRelease(&stats_shmem->lock); } void pgstat_wal_snapshot_cb(void) { PgStatShared_Wal *stats_shmem = &pgStatLocal.shmem->wal; LWLockAcquire(&stats_shmem->lock, LW_SHARED); memcpy(&pgStatLocal.snapshot.wal, &stats_shmem->stats, sizeof(pgStatLocal.snapshot.wal)); LWLockRelease(&stats_shmem->lock); }