From 3a5e0bd82578d1fea63d6bda229dc4d0b224684e Mon Sep 17 00:00:00 2001 From: Alena Rybakina Date: Mon, 2 Mar 2026 23:09:32 +0300 Subject: [PATCH 2/3] Machinery for grabbing extended vacuum statistics. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add infrastructure inside lazy vacuum to gather extended per-vacuum metrics and expose them to extensions via a new hook. Core itself does not persist these metrics — that is the job of an extension (see ext_vacuum_statistics). Statistics are gathered separately for tables and indexes according to vacuum phases. The ExtVacReport union and type field distinguish PGSTAT_EXTVAC_TABLE vs PGSTAT_EXTVAC_INDEX. Heap vacuum stats are sent to the cumulative statistics system after vacuum has processed the indexes. Database vacuum statistics aggregate per-table and per-index statistics within the database. Common for tables, indexes, and database: total_blks_hit, total_blks_read and total_blks_dirtied are the number of hit, miss and dirtied pages in shared buffers during a vacuum operation. total_blks_dirtied counts only pages dirtied by this vacuum. blk_read_time and blk_write_time track access and flush time for buffer pages; blk_write_time can stay zero if no flushes occurred. total_time is wall-clock time from start to finish, including idle time (I/O and lock waits). delay_time is total vacuum sleep time in vacuum delay points. Both table and index report tuples_deleted (tuples removed by the vacuum), pages_removed (pages by which relation storage was reduced) and pages_deleted (freed pages; file size may remain unchanged). These are independent of WAL and buffer stats and are not summed at the database level. Table only: pages_frozen (pages marked all-frozen in the visibility map), pages_all_visible (pages marked all-visible in the visibility map), wraparound_failsafe_count (number of urgent anti-wraparound vacuums). Table and database share wraparound_failsafe (count of urgent anti-wraparound cleanups). Database only: errors (number of error-level errors caught during vacuum). set_report_vacuum_hook (set_report_vacuum_hook_type) -- called once per vacuumed relation/index with a PgStat_VacuumRelationCounts payload tagged by ExtVacReportType (PGSTAT_EXTVAC_TABLE / _INDEX / _DB / _INVALID). Authors: Alena Rybakina , Andrei Lepikhov , Andrei Zubkov Reviewed-by: Dilip Kumar , Masahiko Sawada , Ilia Evdokimov , jian he , Kirill Reshke , Alexander Korotkov , Jim Nasby , Sami Imseih , Karina Litskevich --- src/backend/access/heap/vacuumlazy.c | 234 ++++++++++++++++++- src/backend/commands/vacuum.c | 4 + src/backend/commands/vacuumparallel.c | 12 + src/backend/utils/activity/pgstat_relation.c | 24 ++ src/include/commands/vacuum.h | 29 +++ src/include/pgstat.h | 69 ++++++ 6 files changed, 367 insertions(+), 5 deletions(-) diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 39395aed0d5..e4d4c93d641 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -283,6 +283,8 @@ typedef struct LVRelState /* Error reporting state */ char *dbname; char *relnamespace; + Oid reloid; + Oid indoid; char *relname; char *indname; /* Current index name */ BlockNumber blkno; /* used only for heap operations */ @@ -410,6 +412,15 @@ typedef struct LVRelState * been permanently disabled. */ BlockNumber eager_scan_remaining_fails; + + int32 wraparound_failsafe_count; /* # of emergency vacuums for + * anti-wraparound */ + + /* + * We need to accumulate index statistics for later subtraction from heap + * stats. + */ + PgStat_VacuumRelationCounts extVacReportIdx; } LVRelState; @@ -485,6 +496,166 @@ static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel); +/* Extended vacuum statistics functions */ + +/* + * extvac_stats_start - Save cut-off values before start of relation processing. + */ +static void +extvac_stats_start(Relation rel, LVExtStatCounters * counters) +{ + memset(counters, 0, sizeof(LVExtStatCounters)); + counters->starttime = GetCurrentTimestamp(); + counters->walusage = pgWalUsage; + counters->bufusage = pgBufferUsage; + counters->VacuumDelayTime = VacuumDelayTime; + counters->blocks_fetched = 0; + counters->blocks_hit = 0; + + if (rel->pgstat_info && pgstat_track_counts) + { + counters->blocks_fetched = rel->pgstat_info->counts.blocks_fetched; + counters->blocks_hit = rel->pgstat_info->counts.blocks_hit; + } +} + +/* + * extvac_stats_end - Finish extended vacuum statistic gathering and form report. + */ +static void +extvac_stats_end(Relation rel, LVExtStatCounters * counters, + PgStat_CommonCounts * report) +{ + WalUsage walusage; + BufferUsage bufusage; + TimestampTz endtime; + long secs; + int usecs; + + memset(report, 0, sizeof(PgStat_CommonCounts)); + memset(&walusage, 0, sizeof(WalUsage)); + WalUsageAccumDiff(&walusage, &pgWalUsage, &counters->walusage); + memset(&bufusage, 0, sizeof(BufferUsage)); + BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &counters->bufusage); + endtime = GetCurrentTimestamp(); + TimestampDifference(counters->starttime, endtime, &secs, &usecs); + + report->total_blks_read = bufusage.local_blks_read + bufusage.shared_blks_read; + report->total_blks_hit = bufusage.local_blks_hit + bufusage.shared_blks_hit; + report->total_blks_dirtied = bufusage.local_blks_dirtied + bufusage.shared_blks_dirtied; + report->total_blks_written = bufusage.shared_blks_written; + report->wal_records = walusage.wal_records; + report->wal_fpi = walusage.wal_fpi; + report->wal_bytes = walusage.wal_bytes; + report->blk_read_time = INSTR_TIME_GET_MILLISEC(bufusage.local_blk_read_time) + + INSTR_TIME_GET_MILLISEC(bufusage.shared_blk_read_time); + report->blk_write_time = INSTR_TIME_GET_MILLISEC(bufusage.local_blk_write_time) + + INSTR_TIME_GET_MILLISEC(bufusage.shared_blk_write_time); + report->delay_time = VacuumDelayTime - counters->VacuumDelayTime; + report->total_time = secs * 1000.0 + usecs / 1000.0; + + if (rel->pgstat_info && pgstat_track_counts) + { + report->blks_fetched = rel->pgstat_info->counts.blocks_fetched - counters->blocks_fetched; + report->blks_hit = rel->pgstat_info->counts.blocks_hit - counters->blocks_hit; + } +} + +/* + * extvac_stats_start_idx - Start extended vacuum statistic gathering for index. + */ +void +extvac_stats_start_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters) +{ + extvac_stats_start(rel, &counters->common); + counters->pages_deleted = 0; + counters->tuples_removed = 0; + + if (stats != NULL) + { + counters->tuples_removed = stats->tuples_removed; + counters->pages_deleted = stats->pages_deleted; + } +} + + +/* + * extvac_stats_end_idx - Finish extended vacuum statistic gathering for index. + */ +void +extvac_stats_end_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters, PgStat_VacuumRelationCounts * report) +{ + memset(report, 0, sizeof(PgStat_VacuumRelationCounts)); + extvac_stats_end(rel, &counters->common, &report->common); + report->type = PGSTAT_EXTVAC_INDEX; + + if (stats != NULL) + { + report->common.tuples_deleted = stats->tuples_removed - counters->tuples_removed; + report->index.pages_deleted = stats->pages_deleted - counters->pages_deleted; + } +} + +/* + * Accumulate index stats into vacrel for later subtraction from heap stats. + * It needs to prevent double-counting of stats for heaps that + * include indexes because indexes are vacuumed before the heap. + * We need to be careful with buffer usage and wal usage during parallel vacuum + * because they are accumulated summarly for all indexes at once by leader after + * all workers have finished. + */ +static void +accumulate_idxs_vacuum_statistics(LVRelState *vacrel, + PgStat_VacuumRelationCounts * extVacIdxStats) +{ + vacrel->extVacReportIdx.common.blk_read_time += extVacIdxStats->common.blk_read_time; + vacrel->extVacReportIdx.common.blk_write_time += extVacIdxStats->common.blk_write_time; + vacrel->extVacReportIdx.common.total_blks_dirtied += extVacIdxStats->common.total_blks_dirtied; + vacrel->extVacReportIdx.common.total_blks_hit += extVacIdxStats->common.total_blks_hit; + vacrel->extVacReportIdx.common.total_blks_read += extVacIdxStats->common.total_blks_read; + vacrel->extVacReportIdx.common.total_blks_written += extVacIdxStats->common.total_blks_written; + vacrel->extVacReportIdx.common.wal_bytes += extVacIdxStats->common.wal_bytes; + vacrel->extVacReportIdx.common.wal_fpi += extVacIdxStats->common.wal_fpi; + vacrel->extVacReportIdx.common.wal_records += extVacIdxStats->common.wal_records; + vacrel->extVacReportIdx.common.delay_time += extVacIdxStats->common.delay_time; + vacrel->extVacReportIdx.common.total_time += extVacIdxStats->common.total_time; +} + +/* Build heap-specific extended stats */ +static void +accumulate_heap_vacuum_statistics(LVRelState *vacrel, PgStat_VacuumRelationCounts * extVacStats) +{ + extVacStats->type = PGSTAT_EXTVAC_TABLE; + extVacStats->table.pages_scanned = vacrel->scanned_pages; + extVacStats->table.pages_removed = vacrel->removed_pages; + extVacStats->table.vm_new_frozen_pages = vacrel->new_all_frozen_pages; + extVacStats->table.vm_new_visible_pages = vacrel->new_all_visible_pages; + extVacStats->table.vm_new_visible_frozen_pages = vacrel->new_all_visible_all_frozen_pages; + extVacStats->common.tuples_deleted = vacrel->tuples_deleted; + extVacStats->table.tuples_frozen = vacrel->tuples_frozen; + extVacStats->table.recently_dead_tuples = vacrel->recently_dead_tuples; + extVacStats->table.missed_dead_tuples = vacrel->missed_dead_tuples; + extVacStats->table.missed_dead_pages = vacrel->missed_dead_pages; + extVacStats->table.index_vacuum_count = vacrel->num_index_scans; + extVacStats->common.wraparound_failsafe_count = vacrel->wraparound_failsafe_count; + + /* Hook is invoked from pgstat_report_vacuum() when extstats is passed */ + + /* Subtract index stats from heap to avoid double-counting */ + extVacStats->common.blk_read_time -= vacrel->extVacReportIdx.common.blk_read_time; + extVacStats->common.blk_write_time -= vacrel->extVacReportIdx.common.blk_write_time; + extVacStats->common.total_blks_dirtied -= vacrel->extVacReportIdx.common.total_blks_dirtied; + extVacStats->common.total_blks_hit -= vacrel->extVacReportIdx.common.total_blks_hit; + extVacStats->common.total_blks_read -= vacrel->extVacReportIdx.common.total_blks_read; + extVacStats->common.total_blks_written -= vacrel->extVacReportIdx.common.total_blks_written; + extVacStats->common.wal_bytes -= vacrel->extVacReportIdx.common.wal_bytes; + extVacStats->common.wal_fpi -= vacrel->extVacReportIdx.common.wal_fpi; + extVacStats->common.wal_records -= vacrel->extVacReportIdx.common.wal_records; + extVacStats->common.total_time -= vacrel->extVacReportIdx.common.total_time; + extVacStats->common.delay_time -= vacrel->extVacReportIdx.common.delay_time; +} /* * Helper to set up the eager scanning state for vacuuming a single relation. @@ -643,7 +814,10 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params, ErrorContextCallback errcallback; char **indnames = NULL; Size dead_items_max_bytes = 0; + LVExtStatCounters extVacCounters; + PgStat_VacuumRelationCounts extVacReport; + memset(&extVacReport, 0, sizeof(extVacReport)); verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && params->log_vacuum_min_duration >= 0)); @@ -660,6 +834,9 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params, /* Used for instrumentation and stats report */ starttime = GetCurrentTimestamp(); + if (set_report_vacuum_hook) + extvac_stats_start(rel, &extVacCounters); + pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, RelationGetRelid(rel)); if (AmAutoVacuumWorkerProcess()) @@ -687,7 +864,9 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params, vacrel->dbname = get_database_name(MyDatabaseId); vacrel->relnamespace = get_namespace_name(RelationGetNamespace(rel)); vacrel->relname = pstrdup(RelationGetRelationName(rel)); + vacrel->reloid = RelationGetRelid(rel); vacrel->indname = NULL; + memset(&vacrel->extVacReportIdx, 0, sizeof(vacrel->extVacReportIdx)); vacrel->phase = VACUUM_ERRCB_PHASE_UNKNOWN; vacrel->verbose = verbose; errcallback.callback = vacuum_error_callback; @@ -803,6 +982,9 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params, vacrel->rel_pages = orig_rel_pages = RelationGetNumberOfBlocks(rel); vacrel->vistest = GlobalVisTestFor(rel); + /* Initialize wraparound failsafe count for extended vacuum stats */ + vacrel->wraparound_failsafe_count = 0; + /* Initialize state used to track oldest extant XID/MXID */ vacrel->NewRelfrozenXid = vacrel->cutoffs.OldestXmin; vacrel->NewRelminMxid = vacrel->cutoffs.OldestMxact; @@ -985,11 +1167,26 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params, * soon in cases where the failsafe prevented significant amounts of heap * vacuuming. */ - pgstat_report_vacuum(rel, - Max(vacrel->new_live_tuples, 0), - vacrel->recently_dead_tuples + - vacrel->missed_dead_tuples, - starttime); + if (set_report_vacuum_hook) + { + extvac_stats_end(rel, &extVacCounters, &extVacReport.common); + accumulate_heap_vacuum_statistics(vacrel, &extVacReport); + + pgstat_report_vacuum_ext(rel, + Max(vacrel->new_live_tuples, 0), + vacrel->recently_dead_tuples + + vacrel->missed_dead_tuples, + starttime, + &extVacReport); + } + else + pgstat_report_vacuum_ext(rel, + Max(vacrel->new_live_tuples, 0), + vacrel->recently_dead_tuples + + vacrel->missed_dead_tuples, + starttime, + NULL); + pgstat_progress_end_command(); if (instrument) @@ -2903,6 +3100,7 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel) int64 progress_val[3] = {0, 0, PROGRESS_VACUUM_MODE_FAILSAFE}; VacuumFailsafeActive = true; + vacrel->wraparound_failsafe_count++; /* * Abandon use of a buffer access strategy to allow use of all of @@ -3015,7 +3213,11 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + PgStat_VacuumRelationCounts extVacReport; + if (set_report_vacuum_hook) + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; ivinfo.analyze_only = false; @@ -3033,6 +3235,7 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, */ Assert(vacrel->indname == NULL); vacrel->indname = pstrdup(RelationGetRelationName(indrel)); + vacrel->indoid = RelationGetRelid(indrel); update_vacuum_error_info(vacrel, &saved_err_info, VACUUM_ERRCB_PHASE_VACUUM_INDEX, InvalidBlockNumber, InvalidOffsetNumber); @@ -3041,6 +3244,14 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, istat = vac_bulkdel_one_index(&ivinfo, istat, vacrel->dead_items, vacrel->dead_items_info); + if (set_report_vacuum_hook) + { + memset(&extVacReport, 0, sizeof(extVacReport)); + extvac_stats_end_idx(indrel, istat, &extVacCounters, &extVacReport); + pgstat_report_vacuum_ext(indrel, -1, -1, 0, &extVacReport); + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); + } + /* Revert to the previous phase information for error traceback */ restore_vacuum_error_info(vacrel, &saved_err_info); pfree(vacrel->indname); @@ -3065,7 +3276,11 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + PgStat_VacuumRelationCounts extVacReport; + if (set_report_vacuum_hook) + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; ivinfo.analyze_only = false; @@ -3084,12 +3299,21 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, */ Assert(vacrel->indname == NULL); vacrel->indname = pstrdup(RelationGetRelationName(indrel)); + vacrel->indoid = RelationGetRelid(indrel); update_vacuum_error_info(vacrel, &saved_err_info, VACUUM_ERRCB_PHASE_INDEX_CLEANUP, InvalidBlockNumber, InvalidOffsetNumber); istat = vac_cleanup_one_index(&ivinfo, istat); + if (set_report_vacuum_hook) + { + memset(&extVacReport, 0, sizeof(extVacReport)); + extvac_stats_end_idx(indrel, istat, &extVacCounters, &extVacReport); + pgstat_report_vacuum_ext(indrel, -1, -1, 0, &extVacReport); + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); + } + /* Revert to the previous phase information for error traceback */ restore_vacuum_error_info(vacrel, &saved_err_info); pfree(vacrel->indname); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 99d0db82ed7..a7fb73173f5 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -118,6 +118,9 @@ pg_atomic_uint32 *VacuumSharedCostBalance = NULL; pg_atomic_uint32 *VacuumActiveNWorkers = NULL; int VacuumCostBalanceLocal = 0; +/* Cumulative storage to report total vacuum delay time (msec). */ +double VacuumDelayTime = 0; + /* non-export function prototypes */ static List *expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, int options); @@ -2561,6 +2564,7 @@ vacuum_delay_point(bool is_analyze) exit(1); VacuumCostBalance = 0; + VacuumDelayTime += msec; /* * Balance and update limit values for autovacuum workers. We must do diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 41cefcfde54..200f12a2d1b 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -1076,6 +1076,8 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, IndexBulkDeleteResult *istat = NULL; IndexBulkDeleteResult *istat_res; IndexVacuumInfo ivinfo; + LVExtStatCountersIdx extVacCounters; + PgStat_VacuumRelationCounts extVacReport; /* * Update the pointer to the corresponding bulk-deletion result if someone @@ -1084,6 +1086,8 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, if (indstats->istat_updated) istat = &(indstats->istat); + if (set_report_vacuum_hook) + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = pvs->heaprel; ivinfo.analyze_only = false; @@ -1112,6 +1116,13 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, RelationGetRelationName(indrel)); } + if (set_report_vacuum_hook) + { + memset(&extVacReport, 0, sizeof(extVacReport)); + extvac_stats_end_idx(indrel, istat_res, &extVacCounters, &extVacReport); + pgstat_report_vacuum_ext(indrel, -1, -1, 0, &extVacReport); + } + /* * Copy the index bulk-deletion result returned from ambulkdelete and * amvacuumcleanup to the DSM segment if it's the first cycle because they @@ -1276,6 +1287,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) VacuumUpdateCosts(); VacuumCostBalance = 0; + VacuumDelayTime = 0; VacuumCostBalanceLocal = 0; VacuumSharedCostBalance = &(shared->cost_balance); VacuumActiveNWorkers = &(shared->active_nworkers); diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 92e1f60a080..226d7aa06d5 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -272,6 +272,30 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples, (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO); } +/* + * Hook for extensions to receive extended vacuum statistics. + * NULL when no extension has registered. + */ +set_report_vacuum_hook_type set_report_vacuum_hook = NULL; + +/* + * Report extended vacuum statistics to extensions via set_report_vacuum_hook. + * When livetuples/deadtuples/starttime are provided (heap case), also calls + * pgstat_report_vacuum. For indexes, pass -1, -1, 0 to skip pgstat_report_vacuum. + */ +void +pgstat_report_vacuum_ext(Relation rel, PgStat_Counter livetuples, + PgStat_Counter deadtuples, TimestampTz starttime, + PgStat_VacuumRelationCounts * extstats) +{ + pgstat_report_vacuum(rel, livetuples, deadtuples, starttime); + + if (extstats != NULL && set_report_vacuum_hook) + (*set_report_vacuum_hook) (RelationGetRelid(rel), + rel->rd_rel->relisshared, + extstats); +} + /* * Report that the table was just analyzed and flush IO statistics. * diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 956d9cea36d..a925f7da992 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -21,9 +21,11 @@ #include "catalog/pg_class.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" +#include "executor/instrument.h" #include "parser/parse_node.h" #include "storage/buf.h" #include "utils/relcache.h" +#include "pgstat.h" /* * Flags for amparallelvacuumoptions to control the participation of bulkdelete @@ -354,6 +356,33 @@ extern PGDLLIMPORT pg_atomic_uint32 *VacuumSharedCostBalance; extern PGDLLIMPORT pg_atomic_uint32 *VacuumActiveNWorkers; extern PGDLLIMPORT int VacuumCostBalanceLocal; +/* Cumulative storage to report total vacuum delay time (msec). */ +extern PGDLLIMPORT double VacuumDelayTime; + +/* Counters for extended vacuum statistics gathering */ +typedef struct LVExtStatCounters +{ + TimestampTz starttime; + WalUsage walusage; + BufferUsage bufusage; + double VacuumDelayTime; + PgStat_Counter blocks_fetched; + PgStat_Counter blocks_hit; +} LVExtStatCounters; + +typedef struct LVExtStatCountersIdx +{ + LVExtStatCounters common; + int64 pages_deleted; + int64 tuples_removed; +} LVExtStatCountersIdx; + +extern void extvac_stats_start_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx *counters); +extern void extvac_stats_end_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx *counters, + PgStat_VacuumRelationCounts *report); + extern PGDLLIMPORT bool VacuumFailsafeActive; extern PGDLLIMPORT double vacuum_cost_delay; extern PGDLLIMPORT int vacuum_cost_limit; diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 7db36cf8add..8d934973dc1 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -93,6 +93,64 @@ typedef struct PgStat_FunctionCounts /* * Working state needed to accumulate per-function-call timing statistics. */ +/* + * Extended vacuum statistics - passed to extensions via set_report_vacuum_hook. + * Type of entry: table (heap), index, or database aggregate. + */ +typedef enum ExtVacReportType +{ + PGSTAT_EXTVAC_INVALID = 0, + PGSTAT_EXTVAC_TABLE = 1, + PGSTAT_EXTVAC_INDEX = 2, + PGSTAT_EXTVAC_DB = 3, +} ExtVacReportType; + +typedef struct PgStat_CommonCounts +{ + int64 total_blks_read; + int64 total_blks_hit; + int64 total_blks_dirtied; + int64 total_blks_written; + int64 blks_fetched; + int64 blks_hit; + int64 wal_records; + int64 wal_fpi; + uint64 wal_bytes; + double blk_read_time; + double blk_write_time; + double delay_time; + double total_time; + int32 wraparound_failsafe_count; + int32 interrupts_count; + int64 tuples_deleted; +} PgStat_CommonCounts; + +typedef struct PgStat_VacuumRelationCounts +{ + PgStat_CommonCounts common; + ExtVacReportType type; + union + { + struct + { + int64 tuples_frozen; + int64 recently_dead_tuples; + int64 missed_dead_tuples; + int64 pages_scanned; + int64 pages_removed; + int64 vm_new_frozen_pages; + int64 vm_new_visible_pages; + int64 vm_new_visible_frozen_pages; + int64 missed_dead_pages; + int64 index_vacuum_count; + } table; + struct + { + int64 pages_deleted; + } index; + }; +} PgStat_VacuumRelationCounts; + typedef struct PgStat_FunctionCallUsage { /* Link to function's hashtable entry (must still be there at exit!) */ @@ -703,6 +761,17 @@ extern void pgstat_unlink_relation(Relation rel); extern void pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, TimestampTz starttime); + +extern void pgstat_report_vacuum_ext(Relation rel, + PgStat_Counter livetuples, + PgStat_Counter deadtuples, + TimestampTz starttime, + PgStat_VacuumRelationCounts * extstats); + +/* Hook for extensions to receive extended vacuum statistics */ +typedef void (*set_report_vacuum_hook_type) (Oid tableoid, bool shared, + PgStat_VacuumRelationCounts * params); +extern PGDLLIMPORT set_report_vacuum_hook_type set_report_vacuum_hook; extern void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter, TimestampTz starttime); -- 2.39.5 (Apple Git-154)