| /* ------------------------------------------------------------------------- |
| * |
| * 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. |
| * |
| * "force" set to true ensures that the statistics are flushed; note that |
| * this needs to acquire the pgstat shmem LWLock, waiting on it. When |
| * set to false, the statistics may not be flushed if the lock could not |
| * be acquired. |
| */ |
| void |
| pgstat_report_wal(bool force) |
| { |
| bool nowait; |
| |
| /* like in pgstat.c, don't wait for lock acquisition when !force */ |
| nowait = !force; |
| |
| /* flush wal stats */ |
| pgstat_flush_wal(nowait); |
| |
| /* flush IO stats */ |
| pgstat_flush_io(nowait); |
| } |
| |
| /* |
| * 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); |
| } |