From d09c2f688fc8776b239a39f0cd9cda5488dba812 Mon Sep 17 00:00:00 2001 From: Alena Rybakina Date: Tue, 9 Dec 2025 10:56:54 +0300 Subject: [PATCH 2/5] Machinery for grabbing an extended vacuum statistics on index relations. They are gathered separatelly from table statistics. As for tables, we gather vacuum shared buffers statistics for index relations like value of total_blks_hit, total_blks_read, total_blks_dirtied, wal statistics, io time during flushing buffer pages to disk, delay and total time. Due to the fact that such statistics are common as for tables, as for indexes we set them in the union ExtVacReport structure. We only added some determination 'type' field to highlight what kind belong to these statistics: PGSTAT_EXTVAC_TABLE or PGSTAT_EXTVAC_INDEX. Generally, PGSTAT_EXTVAC_INVALID type leads to wrong code process. Some statistics belong only one type of both tables or indexes. So, we added substructures sych table and index inside ExtVacReport structure. Therefore, we gather only for tables such statistics like number of scanned, removed pages, their charecteristics according VM (all-visible and frozen). In addition, for tables we gather number frozen, deleted and recently dead tuples and how many times vacuum processed indexes for tables. Controversally for indexes we gather number of deleted pages and deleted tuples only. As for tables, deleted pages and deleted tuples reflect the overall performance of the vacuum for the index relationship. Since the vacuum cleans up references to tuple indexes before cleaning up table tuples, which adds some complexity to the vacuum process, namely the vacuum switches from cleaning up a table to its indexes and back during its operation, we need to save the vacuum statistics collected for the heap before it starts cleaning up the indexes. That's why it's necessary to track the vacuum statistics for the heap several times during the vacuum procedure. To avoid sending the statistics to the Cumulative Statistics System several times, we save these statistics in the LVRelState structure and only after vacuum finishes cleaning up the heap, it sends them to the Cumulative Statistics System. 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 | 232 +++++++++++++---- src/backend/catalog/system_views.sql | 32 +++ src/backend/commands/vacuumparallel.c | 10 + src/backend/utils/activity/pgstat_relation.c | 45 ++-- src/backend/utils/adt/pgstatfuncs.c | 92 ++++++- src/include/catalog/pg_proc.dat | 9 + src/include/commands/vacuum.h | 25 ++ src/include/pgstat.h | 77 ++++-- .../vacuum-extending-in-repetable-read.out | 4 +- .../t/050_vacuum_extending_basic_test.pl | 237 +++++++++++++++++- src/test/regress/expected/rules.out | 22 ++ 11 files changed, 681 insertions(+), 104 deletions(-) diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 66e09d0a0cf..719ce90d96d 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -290,6 +290,7 @@ typedef struct LVRelState char *dbname; char *relnamespace; Oid reloid; + Oid indoid; char *relname; char *indname; /* Current index name */ BlockNumber blkno; /* used only for heap operations */ @@ -412,6 +413,7 @@ typedef struct LVRelState int32 wraparound_failsafe_count; /* number of emergency vacuums to * prevent anti-wraparound * shutdown */ + ExtVacReport extVacReportIdx; } LVRelState; @@ -423,19 +425,6 @@ typedef struct LVSavedErrInfo VacErrPhase phase; } LVSavedErrInfo; -/* - * Counters and usage data for extended stats tracking. - */ -typedef struct LVExtStatCounters -{ - TimestampTz starttime; - WalUsage walusage; - BufferUsage bufusage; - double VacuumDelayTime; - PgStat_Counter blocks_fetched; - PgStat_Counter blocks_hit; -} LVExtStatCounters; - /* non-export function prototypes */ static void lazy_scan_heap(LVRelState *vacrel); static void heap_vacuum_eager_scan_setup(LVRelState *vacrel, @@ -565,27 +554,25 @@ extvac_stats_end(Relation rel, LVExtStatCounters * counters, endtime = GetCurrentTimestamp(); TimestampDifference(counters->starttime, endtime, &secs, &usecs); - memset(report, 0, sizeof(ExtVacReport)); - /* * Fill additional statistics on a vacuum processing operation. */ - 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->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->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); + report->blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage.local_blk_read_time); report->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); - report->blk_write_time = INSTR_TIME_GET_MILLISEC(bufusage.shared_blk_write_time); - report->delay_time = VacuumDelayTime - counters->VacuumDelayTime; + report->blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage.local_blk_write_time); + report->blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage.shared_blk_write_time); + report->delay_time += VacuumDelayTime - counters->VacuumDelayTime; - report->total_time = secs * 1000. + usecs / 1000.; + report->total_time += secs * 1000. + usecs / 1000.; if (!rel->pgstat_info || !pgstat_track_counts) @@ -595,12 +582,122 @@ extvac_stats_end(Relation rel, LVExtStatCounters * counters, */ return; - report->blks_fetched = + report->blks_fetched += rel->pgstat_info->counts.blocks_fetched - counters->blocks_fetched; - report->blks_hit = + report->blks_hit += rel->pgstat_info->counts.blocks_hit - counters->blocks_hit; } +void +extvac_stats_start_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters) +{ + /* Set initial values for common heap and index statistics */ + extvac_stats_start(rel, &counters->common); + counters->pages_deleted = counters->tuples_removed = 0; + + if (stats != NULL) + { + /* + * XXX: Why do we need this code here? If it is needed, I feel lack of + * comments, describing the reason. + */ + counters->tuples_removed = stats->tuples_removed; + counters->pages_deleted = stats->pages_deleted; + } +} + +void +extvac_stats_end_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters, ExtVacReport * report) +{ + memset(report, 0, sizeof(ExtVacReport)); + + extvac_stats_end(rel, &counters->common, report); + report->type = PGSTAT_EXTVAC_INDEX; + + if (stats != NULL) + { + /* + * if something goes wrong or an user doesn't want to track a database + * activity - just suppress it. + */ + + /* Fill index-specific extended stats fields */ + report->tuples_deleted = + stats->tuples_removed - counters->tuples_removed; + report->index.pages_deleted = + stats->pages_deleted - counters->pages_deleted; + } +} + +/* Accumulate vacuum statistics for heap. + * + * Because of complexity of vacuum processing: it switch procesing between + * the heap relation to index relations and visa versa, we need to store + * gathered statistics information for heap relations several times before + * the vacuum starts processing the indexes again. + * + * It is necessary to gather correct statistics information for heap and indexes + * otherwice the index statistics information would be added to his parent heap + * statistics information and it would be difficult to analyze it later. + * + * We can't subtract union vacuum statistics information for index from the heap relations + * because of total and delay time time statistics collecting during parallel vacuum + * procudure. +*/ +static void +accumulate_heap_vacuum_statistics(LVRelState *vacrel, ExtVacReport * extVacStats) +{ + /* Fill heap-specific extended stats fields */ + 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->vm_new_frozen_pages; + extVacStats->table.vm_new_visible_pages = vacrel->vm_new_visible_pages; + extVacStats->table.vm_new_visible_frozen_pages = vacrel->vm_new_visible_frozen_pages; + extVacStats->tuples_deleted = vacrel->tuples_deleted; + extVacStats->table.tuples_frozen = vacrel->tuples_frozen; + extVacStats->table.recently_dead_tuples = vacrel->recently_dead_tuples; + 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->table.wraparound_failsafe_count = vacrel->wraparound_failsafe_count; + + extVacStats->blk_read_time -= vacrel->extVacReportIdx.blk_read_time; + extVacStats->blk_write_time -= vacrel->extVacReportIdx.blk_write_time; + extVacStats->total_blks_dirtied -= vacrel->extVacReportIdx.total_blks_dirtied; + extVacStats->total_blks_hit -= vacrel->extVacReportIdx.total_blks_hit; + extVacStats->total_blks_read -= vacrel->extVacReportIdx.total_blks_read; + extVacStats->total_blks_written -= vacrel->extVacReportIdx.total_blks_written; + extVacStats->wal_bytes -= vacrel->extVacReportIdx.wal_bytes; + extVacStats->wal_fpi -= vacrel->extVacReportIdx.wal_fpi; + extVacStats->wal_records -= vacrel->extVacReportIdx.wal_records; + + extVacStats->total_time -= vacrel->extVacReportIdx.total_time; + extVacStats->delay_time -= vacrel->extVacReportIdx.delay_time; + +} + +static void +accumulate_idxs_vacuum_statistics(LVRelState *vacrel, ExtVacReport * extVacIdxStats) +{ + /* Fill heap-specific extended stats fields */ + vacrel->extVacReportIdx.blk_read_time += extVacIdxStats->blk_read_time; + vacrel->extVacReportIdx.blk_write_time += extVacIdxStats->blk_write_time; + vacrel->extVacReportIdx.total_blks_dirtied += extVacIdxStats->total_blks_dirtied; + vacrel->extVacReportIdx.total_blks_hit += extVacIdxStats->total_blks_hit; + vacrel->extVacReportIdx.total_blks_read += extVacIdxStats->total_blks_read; + vacrel->extVacReportIdx.total_blks_written += extVacIdxStats->total_blks_written; + vacrel->extVacReportIdx.wal_bytes += extVacIdxStats->wal_bytes; + vacrel->extVacReportIdx.wal_fpi += extVacIdxStats->wal_fpi; + vacrel->extVacReportIdx.wal_records += extVacIdxStats->wal_records; + vacrel->extVacReportIdx.delay_time += extVacIdxStats->delay_time; + + vacrel->extVacReportIdx.total_time += extVacIdxStats->total_time; +} + /* * Helper to set up the eager scanning state for vacuuming a single relation. @@ -760,11 +857,9 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, char **indnames = NULL; LVExtStatCounters extVacCounters; ExtVacReport extVacReport; - ExtVacReport allzero; /* Initialize vacuum statistics */ - memset(&allzero, 0, sizeof(ExtVacReport)); - extVacReport = allzero; + memset(&extVacReport, 0, sizeof(ExtVacReport)); verbose = (params.options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -820,6 +915,8 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, errcallback.previous = error_context_stack; error_context_stack = &errcallback; + memset(&vacrel->extVacReportIdx, 0, sizeof(ExtVacReport)); + /* Set up high level stuff about rel and its indexes */ vacrel->rel = rel; vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes, @@ -1078,20 +1175,6 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, /* Make generic extended vacuum stats report */ extvac_stats_end(rel, &extVacCounters, &extVacReport); - /* Fill heap-specific extended stats fields */ - extVacReport.pages_scanned = vacrel->scanned_pages; - extVacReport.pages_removed = vacrel->removed_pages; - extVacReport.vm_new_frozen_pages = vacrel->vm_new_frozen_pages; - extVacReport.vm_new_visible_pages = vacrel->vm_new_visible_pages; - extVacReport.vm_new_visible_frozen_pages = vacrel->vm_new_visible_frozen_pages; - extVacReport.tuples_deleted = vacrel->tuples_deleted; - extVacReport.tuples_frozen = vacrel->tuples_frozen; - extVacReport.recently_dead_tuples = vacrel->recently_dead_tuples; - extVacReport.missed_dead_tuples = vacrel->missed_dead_tuples; - extVacReport.missed_dead_pages = vacrel->missed_dead_pages; - extVacReport.index_vacuum_count = vacrel->num_index_scans; - extVacReport.wraparound_failsafe_count = vacrel->wraparound_failsafe_count; - /* * Report results to the cumulative stats system, too. * @@ -1102,6 +1185,13 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, * soon in cases where the failsafe prevented significant amounts of heap * vacuuming. */ + + /* + * Make generic extended vacuum stats report and fill heap-specific + * extended stats fields. + */ + extvac_stats_end(vacrel->rel, &extVacCounters, &extVacReport); + accumulate_heap_vacuum_statistics(vacrel, &extVacReport); pgstat_report_vacuum(rel, Max(vacrel->new_live_tuples, 0), vacrel->recently_dead_tuples + @@ -2811,10 +2901,20 @@ lazy_vacuum_all_indexes(LVRelState *vacrel) } else { + LVExtStatCounters counters; + ExtVacReport extVacReport; + + memset(&extVacReport, 0, sizeof(ExtVacReport)); + + extvac_stats_start(vacrel->rel, &counters); + /* Outsource everything to parallel variant */ parallel_vacuum_bulkdel_all_indexes(vacrel->pvs, old_live_tuples, vacrel->num_index_scans); + extvac_stats_end(vacrel->rel, &counters, &extVacReport); + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); + /* * Do a postcheck to consider applying wraparound failsafe now. Note * that parallel VACUUM only gets the precheck and this postcheck. @@ -3244,10 +3344,20 @@ lazy_cleanup_all_indexes(LVRelState *vacrel) } else { + LVExtStatCounters counters; + ExtVacReport extVacReport; + + memset(&extVacReport, 0, sizeof(ExtVacReport)); + + extvac_stats_start(vacrel->rel, &counters); + /* Outsource everything to parallel variant */ parallel_vacuum_cleanup_all_indexes(vacrel->pvs, reltuples, vacrel->num_index_scans, estimated_count); + + extvac_stats_end(vacrel->rel, &counters, &extVacReport); + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); } /* Reset the progress counters */ @@ -3273,6 +3383,11 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + ExtVacReport extVacReport; + + /* Set initial statistics values to gather vacuum statistics for the index */ + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; @@ -3291,6 +3406,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); @@ -3299,6 +3415,15 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, istat = vac_bulkdel_one_index(&ivinfo, istat, vacrel->dead_items, vacrel->dead_items_info); + /* Make extended vacuum stats report for index */ + extvac_stats_end_idx(indrel, istat, &extVacCounters, &extVacReport); + + if (!ParallelVacuumIsActive(vacrel)) + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); + + pgstat_report_vacuum(indrel, + 0, 0, 0, &extVacReport); + /* Revert to the previous phase information for error traceback */ restore_vacuum_error_info(vacrel, &saved_err_info); pfree(vacrel->indname); @@ -3323,6 +3448,11 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + ExtVacReport extVacReport; + + /* Set initial statistics values to gather vacuum statistics for the index */ + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; @@ -3342,12 +3472,22 @@ 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); + /* Make extended vacuum stats report for index */ + extvac_stats_end_idx(indrel, istat, &extVacCounters, &extVacReport); + + if (!ParallelVacuumIsActive(vacrel)) + accumulate_idxs_vacuum_statistics(vacrel, &extVacReport); + + pgstat_report_vacuum(indrel, + 0, 0, 0, &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/catalog/system_views.sql b/src/backend/catalog/system_views.sql index ffb407d414f..47b6a00d297 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1502,3 +1502,35 @@ FROM pg_class rel JOIN pg_namespace ns ON ns.oid = rel.relnamespace, LATERAL pg_stat_get_vacuum_tables(rel.oid) stats WHERE rel.relkind = 'r'; + +CREATE VIEW pg_stat_vacuum_indexes AS +SELECT + rel.oid as relid, + ns.nspname AS schemaname, + rel.relname AS relname, + + total_blks_read AS total_blks_read, + total_blks_hit AS total_blks_hit, + total_blks_dirtied AS total_blks_dirtied, + total_blks_written AS total_blks_written, + + rel_blks_read AS rel_blks_read, + rel_blks_hit AS rel_blks_hit, + + pages_deleted AS pages_deleted, + tuples_deleted AS tuples_deleted, + + wal_records AS wal_records, + wal_fpi AS wal_fpi, + wal_bytes AS wal_bytes, + + blk_read_time AS blk_read_time, + blk_write_time AS blk_write_time, + + delay_time AS delay_time, + total_time AS total_time +FROM + pg_class rel + JOIN pg_namespace ns ON ns.oid = rel.relnamespace, + LATERAL pg_stat_get_vacuum_indexes(rel.oid) stats +WHERE rel.relkind = 'i'; \ No newline at end of file diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 114cd7c31d3..43450685b09 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -868,6 +868,8 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, IndexBulkDeleteResult *istat = NULL; IndexBulkDeleteResult *istat_res; IndexVacuumInfo ivinfo; + LVExtStatCountersIdx extVacCounters; + ExtVacReport extVacReport; /* * Update the pointer to the corresponding bulk-deletion result if someone @@ -876,6 +878,9 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, if (indstats->istat_updated) istat = &(indstats->istat); + /* Set initial statistics values to gather vacuum statistics for the index */ + extvac_stats_start_idx(indrel, &(indstats->istat), &extVacCounters); + ivinfo.index = indrel; ivinfo.heaprel = pvs->heaprel; ivinfo.analyze_only = false; @@ -904,6 +909,11 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, RelationGetRelationName(indrel)); } + /* Make extended vacuum stats report for index */ + extvac_stats_end_idx(indrel, istat_res, &extVacCounters, &extVacReport); + pgstat_report_vacuum(indrel, + 0, 0, 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 diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 361713479e8..4bd6afc3794 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -1036,20 +1036,35 @@ pgstat_accumulate_extvac_stats(ExtVacReport * dst, ExtVacReport * src, if (!accumulate_reltype_specific_info) return; - dst->blks_fetched += src->blks_fetched; - dst->blks_hit += src->blks_hit; - - dst->pages_scanned += src->pages_scanned; - dst->pages_removed += src->pages_removed; - dst->vm_new_frozen_pages += src->vm_new_frozen_pages; - dst->vm_new_visible_pages += src->vm_new_visible_pages; - dst->vm_new_visible_frozen_pages += src->vm_new_visible_frozen_pages; - dst->tuples_deleted += src->tuples_deleted; - dst->tuples_frozen += src->tuples_frozen; - dst->recently_dead_tuples += src->recently_dead_tuples; - dst->index_vacuum_count += src->index_vacuum_count; - dst->wraparound_failsafe_count += src->wraparound_failsafe_count; - dst->missed_dead_pages += src->missed_dead_pages; - dst->missed_dead_tuples += src->missed_dead_tuples; + if (dst->type == PGSTAT_EXTVAC_INVALID) + dst->type = src->type; + Assert(src->type == PGSTAT_EXTVAC_INVALID || src->type == dst->type); + + if (dst->type == src->type) + { + dst->blks_fetched += src->blks_fetched; + dst->blks_hit += src->blks_hit; + + if (dst->type == PGSTAT_EXTVAC_TABLE) + { + dst->table.pages_scanned += src->table.pages_scanned; + dst->table.pages_removed += src->table.pages_removed; + dst->table.vm_new_frozen_pages += src->table.vm_new_frozen_pages; + dst->table.vm_new_visible_pages += src->table.vm_new_visible_pages; + dst->table.vm_new_visible_frozen_pages += src->table.vm_new_visible_frozen_pages; + dst->tuples_deleted += src->tuples_deleted; + dst->table.tuples_frozen += src->table.tuples_frozen; + dst->table.recently_dead_tuples += src->table.recently_dead_tuples; + dst->table.index_vacuum_count += src->table.index_vacuum_count; + dst->table.missed_dead_pages += src->table.missed_dead_pages; + dst->table.missed_dead_tuples += src->table.missed_dead_tuples; + dst->table.wraparound_failsafe_count += src->table.wraparound_failsafe_count; + } + else if (dst->type == PGSTAT_EXTVAC_INDEX) + { + dst->index.pages_deleted += src->index.pages_deleted; + dst->tuples_deleted += src->tuples_deleted; + } + } } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index d7dfda0c1a7..755751c3b46 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -2360,18 +2360,19 @@ pg_stat_get_vacuum_tables(PG_FUNCTION_ARGS) extvacuum->blks_hit); values[i++] = Int64GetDatum(extvacuum->blks_hit); - values[i++] = Int64GetDatum(extvacuum->pages_scanned); - values[i++] = Int64GetDatum(extvacuum->pages_removed); - values[i++] = Int64GetDatum(extvacuum->vm_new_frozen_pages); - values[i++] = Int64GetDatum(extvacuum->vm_new_visible_pages); - values[i++] = Int64GetDatum(extvacuum->vm_new_visible_frozen_pages); - values[i++] = Int64GetDatum(extvacuum->missed_dead_pages); + values[i++] = Int64GetDatum(extvacuum->table.pages_scanned); + values[i++] = Int64GetDatum(extvacuum->table.pages_removed); + values[i++] = Int64GetDatum(extvacuum->table.vm_new_frozen_pages); + values[i++] = Int64GetDatum(extvacuum->table.vm_new_visible_pages); + values[i++] = Int64GetDatum(extvacuum->table.vm_new_visible_frozen_pages); + values[i++] = Int64GetDatum(extvacuum->table.missed_dead_pages); values[i++] = Int64GetDatum(extvacuum->tuples_deleted); - values[i++] = Int64GetDatum(extvacuum->tuples_frozen); - values[i++] = Int64GetDatum(extvacuum->recently_dead_tuples); - values[i++] = Int64GetDatum(extvacuum->missed_dead_tuples); - values[i++] = Int32GetDatum(extvacuum->wraparound_failsafe_count); - values[i++] = Int64GetDatum(extvacuum->index_vacuum_count); + values[i++] = Int64GetDatum(extvacuum->table.tuples_frozen); + values[i++] = Int64GetDatum(extvacuum->table.recently_dead_tuples); + values[i++] = Int64GetDatum(extvacuum->table.missed_dead_tuples); + + values[i++] = Int32GetDatum(extvacuum->table.wraparound_failsafe_count); + values[i++] = Int64GetDatum(extvacuum->table.index_vacuum_count); values[i++] = Int64GetDatum(extvacuum->wal_records); values[i++] = Int64GetDatum(extvacuum->wal_fpi); @@ -2393,3 +2394,72 @@ pg_stat_get_vacuum_tables(PG_FUNCTION_ARGS) /* Returns the record as Datum */ PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); } + +/* + * Get the vacuum statistics for the heap tables. + */ +Datum +pg_stat_get_vacuum_indexes(PG_FUNCTION_ARGS) +{ +#define PG_STAT_GET_VACUUM_INDEX_STATS_COLS 16 + + Oid relid = PG_GETARG_OID(0); + PgStat_StatTabEntry *tabentry; + ExtVacReport *extvacuum; + TupleDesc tupdesc; + Datum values[PG_STAT_GET_VACUUM_INDEX_STATS_COLS] = {0}; + bool nulls[PG_STAT_GET_VACUUM_INDEX_STATS_COLS] = {0}; + char buf[256]; + int i = 0; + + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + tabentry = pgstat_fetch_stat_tabentry(relid); + + if (tabentry == NULL) + { + InitMaterializedSRF(fcinfo, 0); + PG_RETURN_VOID(); + } + else + { + extvacuum = &(tabentry->vacuum_ext); + } + + i = 0; + + values[i++] = ObjectIdGetDatum(relid); + + values[i++] = Int64GetDatum(extvacuum->total_blks_read); + values[i++] = Int64GetDatum(extvacuum->total_blks_hit); + values[i++] = Int64GetDatum(extvacuum->total_blks_dirtied); + values[i++] = Int64GetDatum(extvacuum->total_blks_written); + + values[i++] = Int64GetDatum(extvacuum->blks_fetched - + extvacuum->blks_hit); + values[i++] = Int64GetDatum(extvacuum->blks_hit); + + values[i++] = Int64GetDatum(extvacuum->index.pages_deleted); + values[i++] = Int64GetDatum(extvacuum->tuples_deleted); + + values[i++] = Int64GetDatum(extvacuum->wal_records); + values[i++] = Int64GetDatum(extvacuum->wal_fpi); + + /* Convert to numeric, like pg_stat_statements */ + snprintf(buf, sizeof buf, UINT64_FORMAT, extvacuum->wal_bytes); + values[i++] = DirectFunctionCall3(numeric_in, + CStringGetDatum(buf), + ObjectIdGetDatum(0), + Int32GetDatum(-1)); + + values[i++] = Float8GetDatum(extvacuum->blk_read_time); + values[i++] = Float8GetDatum(extvacuum->blk_write_time); + values[i++] = Float8GetDatum(extvacuum->delay_time); + values[i++] = Float8GetDatum(extvacuum->total_time); + + Assert(i == PG_STAT_GET_VACUUM_INDEX_STATS_COLS); + + /* Returns the record as Datum */ + PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 915a5a7822f..e957781b623 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12630,4 +12630,13 @@ proname => 'pg_stat_get_rev_all_frozen_pages', provolatile => 's', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_rev_all_frozen_pages' }, +{ oid => '8004', + descr => 'pg_stat_get_vacuum_indexes return stats values', + proname => 'pg_stat_get_vacuum_indexes', prorows => 1000, provolatile => 's', prorettype => 'record',proisstrict => 'f', + proretset => 't', + proargtypes => 'oid', + proallargtypes => '{oid,oid,int8,int8,int8,int8,int8,int8,int8,int8,int8,int8,numeric,float8,float8,float8,float8}', + proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{reloid,relid,total_blks_read,total_blks_hit,total_blks_dirtied,total_blks_written,rel_blks_read,rel_blks_hit,pages_deleted,tuples_deleted,wal_records,wal_fpi,wal_bytes,blk_read_time,blk_write_time,delay_time,total_time}', + prosrc => 'pg_stat_get_vacuum_indexes' } ] diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 6b997bc7fb1..b48ace6084b 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -25,6 +25,7 @@ #include "storage/buf.h" #include "storage/lock.h" #include "utils/relcache.h" +#include "pgstat.h" /* * Flags for amparallelvacuumoptions to control the participation of bulkdelete @@ -300,6 +301,26 @@ typedef struct VacDeadItemsInfo int64 num_items; /* current # of entries */ } VacDeadItemsInfo; +/* + * Counters and usage data for extended stats tracking. + */ +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; + /* GUC parameters */ extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for PostGIS */ extern PGDLLIMPORT int vacuum_freeze_min_age; @@ -413,4 +434,8 @@ extern double anl_random_fract(void); extern double anl_init_selection_state(int n); extern double anl_get_next_S(double t, int n, double *stateptr); +extern void extvac_stats_start_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters); +extern void extvac_stats_end_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx * counters, ExtVacReport * report); #endif /* VACUUM_H */ diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 46d12fa3bd0..f2881dbb6f9 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -114,11 +114,19 @@ typedef struct PgStat_BackendSubEntry PgStat_Counter conflict_count[CONFLICT_NUM_TYPES]; } PgStat_BackendSubEntry; +/* Type of ExtVacReport */ +typedef enum ExtVacReportType +{ + PGSTAT_EXTVAC_INVALID = 0, + PGSTAT_EXTVAC_TABLE = 1, + PGSTAT_EXTVAC_INDEX = 2 +} ExtVacReportType; + /* ---------- * * ExtVacReport * - * Additional statistics of vacuum processing over a heap relation. + * Additional statistics of vacuum processing over a relation. * pages_removed is the amount by which the physically shrank, * if any (ie the change in its total size on disk) * pages_deleted refer to free space within the index file @@ -155,23 +163,58 @@ typedef struct ExtVacReport * point, in msec */ double total_time; /* total time of a vacuum operation, in msec */ - int64 pages_scanned; /* heap pages examined (not skipped by VM) */ - int64 pages_removed; /* heap pages removed by vacuum "truncation" */ - int64 vm_new_frozen_pages; /* pages marked in VM as frozen */ - int64 vm_new_visible_pages; /* pages marked in VM as all-visible */ - int64 vm_new_visible_frozen_pages; /* pages marked in VM as - * all-visible and frozen */ - int64 missed_dead_tuples; /* tuples not pruned by vacuum due to - * failure to get a cleanup lock */ - int64 missed_dead_pages; /* pages with missed dead tuples */ int64 tuples_deleted; /* tuples deleted by vacuum */ - int64 tuples_frozen; /* tuples frozen up by vacuum */ - int64 recently_dead_tuples; /* deleted tuples that are still - * visible to some transaction */ - int64 index_vacuum_count; /* the number of index vacuumings */ - int32 wraparound_failsafe_count; /* number of emergency vacuums to - * prevent anti-wraparound - * shutdown */ + + ExtVacReportType type; /* heap, index, etc. */ + + /* ---------- + * + * There are separate metrics of statistic for tables and indexes, + * which collect during vacuum. + * The union operator allows to combine these statistics + * so that each metric is assigned to a specific class of collected statistics. + * Such a combined structure was called per_type_stats. + * The name of the structure itself is not used anywhere, + * it exists only for understanding the code. + * ---------- + */ + union + { + struct + { + int64 pages_scanned; /* heap pages examined (not skipped by + * VM) */ + int64 pages_removed; /* heap pages removed by vacuum + * "truncation" */ + int64 pages_frozen; /* pages marked in VM as frozen */ + int64 pages_all_visible; /* pages marked in VM as + * all-visible */ + int64 tuples_frozen; /* tuples frozen up by vacuum */ + int64 recently_dead_tuples; /* deleted tuples that are + * still visible to some + * transaction */ + int64 vm_new_frozen_pages; /* pages marked in VM as + * frozen */ + int64 vm_new_visible_pages; /* pages marked in VM as + * all-visible */ + int64 vm_new_visible_frozen_pages; /* pages marked in VM as + * all-visible and + * frozen */ + int64 missed_dead_tuples; /* tuples not pruned by vacuum due + * to failure to get a cleanup + * lock */ + int64 missed_dead_pages; /* pages with missed dead tuples */ + int64 index_vacuum_count; /* number of index vacuumings */ + int32 wraparound_failsafe_count; /* number of emergency + * vacuums to prevent + * anti-wraparound + * shutdown */ + } table; + struct + { + int64 pages_deleted; /* number of pages deleted by vacuum */ + } index; + } /* per_type_stats */ ; } ExtVacReport; /* ---------- diff --git a/src/test/isolation/expected/vacuum-extending-in-repetable-read.out b/src/test/isolation/expected/vacuum-extending-in-repetable-read.out index 87f7e40b4a6..6d960423912 100644 --- a/src/test/isolation/expected/vacuum-extending-in-repetable-read.out +++ b/src/test/isolation/expected/vacuum-extending-in-repetable-read.out @@ -34,7 +34,7 @@ step s2_print_vacuum_stats_table: relname |tuples_deleted|recently_dead_tuples|missed_dead_tuples|missed_dead_pages|tuples_frozen --------------------------+--------------+--------------------+------------------+-----------------+------------- -test_vacuum_stat_isolation| 0| 100| 0| 0| 0 +test_vacuum_stat_isolation| 0| 600| 0| 0| 0 (1 row) step s1_commit: COMMIT; @@ -48,6 +48,6 @@ step s2_print_vacuum_stats_table: relname |tuples_deleted|recently_dead_tuples|missed_dead_tuples|missed_dead_pages|tuples_frozen --------------------------+--------------+--------------------+------------------+-----------------+------------- -test_vacuum_stat_isolation| 100| 100| 0| 0| 101 +test_vacuum_stat_isolation| 300| 600| 0| 0| 303 (1 row) diff --git a/src/test/recovery/t/050_vacuum_extending_basic_test.pl b/src/test/recovery/t/050_vacuum_extending_basic_test.pl index 7e25a3fe63f..8f7b1e2909b 100644 --- a/src/test/recovery/t/050_vacuum_extending_basic_test.pl +++ b/src/test/recovery/t/050_vacuum_extending_basic_test.pl @@ -2,9 +2,10 @@ # Test cumulative vacuum stats system using TAP # # This test validates the accuracy and behavior of cumulative vacuum statistics -# across tables using: +# across heap tables, indexes using: # # • pg_stat_vacuum_tables +# • pg_stat_vacuum_indexes # # A polling helper function repeatedly checks the stats views until expected # deltas appear or a configurable timeout expires. This guarantees that @@ -62,7 +63,7 @@ $node->safe_psql($dbname, q{ $node->safe_psql( $dbname, - "CREATE TABLE vestat (x int) + "CREATE TABLE vestat (x int PRIMARY KEY) WITH (autovacuum_enabled = off, fillfactor = 10); INSERT INTO vestat SELECT x FROM generate_series(1, $size_tab) AS g(x); ANALYZE vestat;" @@ -80,12 +81,15 @@ my $updated = 0; #------------------------------------------------------------------------------ # wait_for_vacuum_stats # -# Polls pg_stat_vacuum_tables until the table-level counters exceed -# the provided baselines, or until the configured timeout elapses. +# Polls pg_stat_vacuum_tables and pg_stat_vacuum_indexes until both the +# table-level and index-level counters exceed the provided baselines, or until +# the configured timeout elapses. # # Expected named args (baseline values): # tab_tuples_deleted # tab_wal_records +# idx_tuples_deleted +# idx_wal_records # # Returns: 1 if the condition is met before timeout, 0 otherwise. #------------------------------------------------------------------------------ @@ -94,6 +98,8 @@ sub wait_for_vacuum_stats { my (%args) = @_; my $tab_tuples_deleted = $args{tab_tuples_deleted} or 0; my $tab_wal_records = $args{tab_wal_records} or 0; + my $idx_tuples_deleted = $args{idx_tuples_deleted} or 0; + my $idx_wal_records = $args{idx_wal_records} or 0; my $start = time(); while ((time() - $start) < $timeout) { @@ -101,9 +107,14 @@ sub wait_for_vacuum_stats { my $result_query = $node->safe_psql( $dbname, "VACUUM vestat; - SELECT tuples_deleted > $tab_tuples_deleted AND wal_records > $tab_wal_records + SELECT + (SELECT (tuples_deleted > $tab_tuples_deleted AND wal_records > $tab_wal_records) FROM pg_stat_vacuum_tables - WHERE relname = 'vestat';" + WHERE relname = 'vestat') + AND + (SELECT (tuples_deleted > $idx_tuples_deleted AND wal_records > $idx_wal_records) + FROM pg_stat_vacuum_indexes + WHERE relname = 'vestat_pkey');" ); return 1 if ($result_query eq 't'); @@ -126,6 +137,12 @@ my $wal_records = 0; my $wal_bytes = 0; my $wal_fpi = 0; +my $index_tuples_deleted = 0; +my $index_pages_deleted = 0; +my $index_wal_records = 0; +my $index_wal_bytes = 0; +my $index_wal_fpi = 0; + my $pages_frozen_prev = 0; my $tuples_deleted_prev = 0; my $pages_scanned_prev = 0; @@ -134,11 +151,17 @@ my $wal_records_prev = 0; my $wal_bytes_prev = 0; my $wal_fpi_prev = 0; +my $index_tuples_deleted_prev = 0; +my $index_pages_deleted_prev = 0; +my $index_wal_records_prev = 0; +my $index_wal_bytes_prev = 0; +my $index_wal_fpi_prev = 0; + #------------------------------------------------------------------------------ # fetch_vacuum_stats # -# Reads current values of relevant vacuum counters for the test table, -# storing them in package variables for subsequent comparisons. +# Reads current values of relevant vacuum counters for the test table and its +# primary index, storing them in package variables for subsequent comparisons. #------------------------------------------------------------------------------ sub fetch_vacuum_stats { @@ -153,6 +176,18 @@ sub fetch_vacuum_stats { $base_statistics =~ s/\s*\|\s*/ /g; # transform " | " into space ($pages_frozen, $tuples_deleted, $pages_scanned, $pages_removed, $wal_records, $wal_bytes, $wal_fpi) = split /\s+/, $base_statistics; + + # --- index stats --- + my $index_base_statistics = $node->safe_psql( + $dbname, + "SELECT tuples_deleted, pages_deleted, wal_records, wal_bytes, wal_fpi + FROM pg_stat_vacuum_indexes + WHERE relname = 'vestat_pkey';" + ); + + $index_base_statistics =~ s/\s*\|\s*/ /g; # transform " | " into space + ($index_tuples_deleted, $index_pages_deleted, $index_wal_records, $index_wal_bytes, $index_wal_fpi) + = split /\s+/, $index_base_statistics; } #------------------------------------------------------------------------------ @@ -169,6 +204,12 @@ sub save_vacuum_stats { $wal_records_prev = $wal_records; $wal_bytes_prev = $wal_bytes; $wal_fpi_prev = $wal_fpi; + + $index_tuples_deleted_prev = $index_tuples_deleted; + $index_pages_deleted_prev = $index_pages_deleted; + $index_wal_records_prev = $index_wal_records; + $index_wal_bytes_prev = $index_wal_bytes; + $index_wal_fpi_prev = $index_wal_fpi; } #------------------------------------------------------------------------------ @@ -195,7 +236,20 @@ sub print_vacuum_stats_on_error { " pages_removed = $pages_removed\n" . " wal_records = $wal_records\n" . " wal_bytes = $wal_bytes\n" . - " wal_fpi = $wal_fpi\n" + " wal_fpi = $wal_fpi\n" . + "Index statistics:\n" . + " Before test:\n" . + " tuples_deleted = $index_tuples_deleted_prev\n" . + " pages_removed = $index_pages_deleted_prev\n" . + " wal_records = $index_wal_records_prev\n" . + " wal_bytes = $index_wal_bytes_prev\n" . + " wal_fpi = $index_wal_fpi_prev\n" . + " After test:\n" . + " tuples_deleted = $index_tuples_deleted\n" . + " pages_removed = $index_pages_deleted\n" . + " wal_records = $index_wal_records\n" . + " wal_bytes = $index_wal_bytes\n" . + " wal_fpi = $index_wal_fpi\n" ); }; @@ -203,7 +257,8 @@ sub print_vacuum_stats_on_error { # fetch_vacuum_stats during mismatch # # Print current values and old values of relevant vacuum counters for the test -# table, storing them in package variables for subsequent comparisons. +# table and its primary index, storing them in package variables for subsequent +# comparisons. #------------------------------------------------------------------------------ sub fetch_error_base_tab_vacuum_statistics { @@ -258,6 +313,54 @@ sub fetch_error_wal_tab_vacuum_statistics { ); } +sub fetch_error_base_idx_vacuum_statistics { + + # fetch actual base vacuum statistics + my $base_statistics = $node->safe_psql( + $dbname, + "SELECT tuples_deleted, pages_deleted + FROM pg_stat_vacuum_indexes + WHERE relname = 'vestat_pkey';" + ); + $base_statistics =~ s/\s*\|\s*/ /g; # transform " | " in space + my ($cur_tuples_deleted, $cur_pages_deleted) = split /\s+/, $base_statistics; + + diag( + "BASE STATS MISMATCH FOR INDEX:\n" . + " Baseline:\n" . + " tuples_deleted = $index_tuples_deleted\n" . + " pages_removed = $index_pages_deleted\n" . + " Current:\n" . + " tuples_deleted = $cur_tuples_deleted\n" . + " pages_deleted = $cur_pages_deleted\n" + ); +} + +sub fetch_error_wal_idx_vacuum_statistics { + + my $wal_raw = $node->safe_psql( + $dbname, + "SELECT wal_records, wal_bytes, wal_fpi + FROM pg_stat_vacuum_indexes + WHERE relname = 'vestat_pkey';" + ); + + $wal_raw =~ s/\s*\|\s*/ /g; # transform " | " in space + my ($cur_wal_rec, $cur_wal_bytes, $cur_wal_fpi) = split /\s+/, $wal_raw; + + diag( + "WAL STATS MISMATCH FOR INDEX:\n" . + " Baseline:\n" . + " wal_records = $index_wal_records\n" . + " wal_bytes = $index_wal_bytes\n" . + " wal_fpi = $index_wal_fpi\n" . + " Current:\n" . + " wal_records = $cur_wal_rec\n" . + " wal_bytes = $cur_wal_bytes\n" . + " wal_fpi = $cur_wal_fpi\n" + ); +} + #------------------------------------------------------------------------------ # Test 1: Delete half the rows, run VACUUM, and wait for stats to advance #------------------------------------------------------------------------------ @@ -270,7 +373,9 @@ $node->safe_psql($dbname, "VACUUM vestat;"); # Poll the stats view until expected deltas appear or timeout $updated = wait_for_vacuum_stats( tab_tuples_deleted => 0, - tab_wal_records => 0 + tab_wal_records => 0, + idx_tuples_deleted => 0, + idx_wal_records => 0, ); ok($updated, 'vacuum stats updated after vacuuming half-deleted table (tuples_deleted and wal_fpi advanced)') or diag "Timeout waiting for pg_stats_vacuum_* update after $timeout seconds after vacuuming half-deleted table"; @@ -290,6 +395,12 @@ ok($wal_records > $wal_records_prev, 'table wal_records has increased'); ok($wal_bytes > $wal_bytes_prev, 'table wal_bytes has increased'); ok($wal_fpi > $wal_fpi_prev, 'table wal_fpi has increased'); +ok($index_pages_deleted == $index_pages_deleted_prev, 'index pages_deleted stay the same'); +ok($index_tuples_deleted > $index_tuples_deleted_prev, 'index tuples_deleted has increased'); +ok($index_wal_records > $index_wal_records_prev, 'index wal_records has increased'); +ok($index_wal_bytes > $index_wal_bytes_prev, 'index wal_bytes has increased'); +ok($index_wal_fpi == $index_wal_fpi_prev, 'index wal_fpi stay the same'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -307,6 +418,8 @@ $node->safe_psql($dbname, "VACUUM vestat;"); $updated = wait_for_vacuum_stats( tab_tuples_deleted => $tuples_deleted_prev, tab_wal_records => $wal_records_prev, + idx_tuples_deleted => $index_tuples_deleted_prev, + idx_wal_records => $index_wal_records_prev, ); ok($updated, 'vacuum stats updated after vacuuming all-deleted table (tuples_deleted and wal_records advanced)') @@ -327,6 +440,12 @@ ok($wal_records > $wal_records_prev, 'table wal_records has increased'); ok($wal_bytes > $wal_bytes_prev, 'table wal_bytes has increased'); ok($wal_fpi == $wal_fpi_prev, 'table wal_fpi stay the same'); +ok($index_pages_deleted > $index_pages_deleted_prev, 'index pages_deleted has increased'); +ok($index_tuples_deleted > $index_tuples_deleted_prev, 'index tuples_deleted has increased'); +ok($index_wal_records > $index_wal_records_prev, 'index wal_records has increased'); +ok($index_wal_bytes > $index_wal_bytes_prev, 'index wal_bytes has increased'); +ok($index_wal_fpi == $index_wal_fpi_prev, 'index wal_fpi stay the same'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -357,6 +476,12 @@ ok($wal_records == $wal_records_prev, 'table wal_records stay the same'); ok($wal_bytes == $wal_bytes_prev, 'table wal_bytes stay the same'); ok($wal_fpi == $wal_fpi_prev, 'table wal_fpi stay the same'); +ok($index_pages_deleted == $index_pages_deleted_prev, 'index pages_deleted stay the same'); +ok($index_tuples_deleted == $index_tuples_deleted_prev, 'index tuples_deleted stay the same'); +ok($index_wal_records == $index_wal_records_prev, 'index wal_records stay the same'); +ok($index_wal_bytes == $index_wal_bytes_prev, 'index wal_bytes stay the same'); +ok($index_wal_fpi == $index_wal_fpi_prev, 'index wal_fpi stay the same'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -379,6 +504,8 @@ $node->safe_psql( $updated = wait_for_vacuum_stats( tab_tuples_deleted => $tuples_deleted, tab_wal_records => $wal_records, + idx_tuples_deleted => $index_tuples_deleted, + idx_wal_records => $index_wal_records, ); ok($updated, 'vacuum stats updated after updating tuples in the table (tuples_deleted and wal_records advanced)') @@ -399,6 +526,12 @@ ok($wal_records > $wal_records_prev, 'table wal_records has increased'); ok($wal_bytes > $wal_bytes_prev, 'table wal_bytes has increased'); ok($wal_fpi > $wal_fpi_prev, 'table wal_fpi has increased'); +ok($index_pages_deleted > $index_pages_deleted_prev, 'index pages_deleted has increased'); +ok($index_tuples_deleted > $index_tuples_deleted_prev, 'index tuples_deleted has increased'); +ok($index_wal_records > $index_wal_records_prev, 'index wal_records has increased'); +ok($index_wal_bytes > $index_wal_bytes_prev, 'index wal_bytes has increased'); +ok($index_wal_fpi > $index_wal_fpi_prev, 'index wal_fpi has increased'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -421,7 +554,9 @@ $node->safe_psql($dbname, "VACUUM vestat;"); $updated = wait_for_vacuum_stats( tab_tuples_deleted => 0, - tab_wal_records => $wal_records_prev + tab_wal_records => $wal_records_prev, + idx_tuples_deleted => 0, + idx_wal_records => 0, ); ok($updated, 'vacuum stats updated after updating tuples and trancation in the table (tuples_deleted and wal_records advanced)') @@ -442,6 +577,12 @@ ok($wal_records > $wal_records_prev, 'table wal_records has increased'); ok($wal_bytes > $wal_bytes_prev, 'table wal_bytes has increased'); ok($wal_fpi == $wal_fpi_prev, 'table wal_fpi stay the same'); +ok($index_pages_deleted == $index_pages_deleted_prev, 'index pages_deleted stay the same'); +ok($index_tuples_deleted == $index_tuples_deleted_prev, 'index tuples_deleted stay the same'); +ok($index_wal_records == $index_wal_records_prev, 'index wal_records stay the same'); +ok($index_wal_bytes == $index_wal_bytes_prev, 'index wal_bytes stay the same'); +ok($index_wal_fpi == $index_wal_fpi_prev, 'index wal_fpi stay the same'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -464,7 +605,9 @@ $node->safe_psql( $updated = wait_for_vacuum_stats( tab_tuples_deleted => 0, - tab_wal_records => $wal_records + tab_wal_records => $wal_records, + idx_tuples_deleted => 0, + idx_wal_records => 0, ); ok($updated, 'vacuum stats updated after deleting all tuples and trancation in the table (tuples_deleted and wal_records advanced)') @@ -485,6 +628,12 @@ ok($wal_records > $wal_records_prev, 'table wal_records has increased'); ok($wal_bytes > $wal_bytes_prev, 'table wal_bytes has increased'); ok($wal_fpi == $wal_fpi_prev, 'table wal_fpi stay the same'); +ok($index_pages_deleted == $index_pages_deleted_prev, 'index pages_deleted stay the same'); +ok($index_tuples_deleted == $index_tuples_deleted_prev, 'index tuples_deleted stay the same'); +ok($index_wal_records == $index_wal_records_prev, 'index wal_records stay the same'); +ok($index_wal_bytes == $index_wal_bytes_prev, 'index wal_bytes stay the same'); +ok($index_wal_fpi == $index_wal_fpi_prev, 'index wal_fpi stay the same'); + } or print_vacuum_stats_on_error(); # End of subtest # Save statistics for the next test @@ -513,6 +662,34 @@ $base_stats = $node->safe_psql( ); ok($base_stats eq 't', 'heap vacuum stats return from the current relation and database as expected'); +$reloid = $node->safe_psql( + $dbname, + q{ + SELECT oid FROM pg_class WHERE relname = 'vestat_pkey'; + } +); + +# Check if we can get vacuum statistics of particular index relation in the current database +$base_stats = $node->safe_psql( + $dbname, + "SELECT count(*) = 1 FROM pg_stat_vacuum_indexes($dboid, $reloid);" +); +ok($base_stats eq 't', 'index vacuum stats return from the current relation and database as expected'); + +# Check if we return empty results if vacuum statistics with particular oid doesn't exist +$base_stats = $node->safe_psql( + $dbname, + "SELECT count(*) = 0 FROM pg_stats_vacuum_tables($dboid, 1);" +); +ok($base_stats eq 't', 'table vacuum stats return no rows, as expected'); + +$base_stats = $node->safe_psql( + $dbname, + "SELECT count(*) = 0 FROM pg_stat_vacuum_indexes($dboid, 1);" +); +ok($base_stats eq 't', 'index vacuum stats return no rows, as expected'); + + #------------------------------------------------------------------------------ # Test 9: Check relation-level vacuum statistics from another database #------------------------------------------------------------------------------ @@ -523,6 +700,14 @@ $base_stats = $node->safe_psql( FROM pg_stat_vacuum_tables WHERE relname = 'vestat';" ); +ok($base_stats eq 't', 'check the printing table vacuum extended statistics from another database are not available'); + +$base_stats = $node->safe_psql( + 'postgres', + "SELECT count(*) = 0 + FROM pg_stat_vacuum_indexes + WHERE relname = 'vestat_pkey';" +); ok($base_stats eq 't', 'check the printing heap vacuum extended statistics from another database are not available'); $reloid = $node->safe_psql( @@ -543,6 +728,23 @@ $base_stats = $node->safe_psql( is($base_stats, 't', 'vacuum stats for common heap objects available'); +my $indoid = $node->safe_psql( + $dbname, + q{ + SELECT oid FROM pg_class WHERE relname = 'pg_shdepend_reference_index'; + } +); + +$base_stats = $node->safe_psql( + $dbname, + qq{ + SELECT count(*) = 1 + FROM pg_stat_vacuum_indexes(0, $indoid); + } +); + +is($base_stats, 't', 'vacuum stats for common index objects available'); + #------------------------------------------------------------------------------ # Test 11: Cleanup checks: ensure functions return empty sets for OID = 0 #------------------------------------------------------------------------------ @@ -562,6 +764,15 @@ $base_stats = $node->safe_psql( ); ok($base_stats eq 't', 'pg_stat_vacuum_tables correctly returns no rows for OID = 0'); +$base_stats = $node->safe_psql( + $dbname, + q{ + SELECT COUNT(*) = 0 + FROM pg_stat_vacuum_indexes WHERE relid = 0; + } +); +ok($base_stats eq 't', 'pg_stat_vacuum_indexes correctly returns no rows for OID = 0'); + $node->safe_psql('postgres', "DROP DATABASE $dbname;" ); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index e4a77878beb..7e6029394cb 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2330,6 +2330,28 @@ pg_stat_user_tables| SELECT relid, rev_all_visible_pages FROM pg_stat_all_tables WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_vacuum_indexes| SELECT rel.oid AS relid, + ns.nspname AS schemaname, + rel.relname, + stats.total_blks_read, + stats.total_blks_hit, + stats.total_blks_dirtied, + stats.total_blks_written, + stats.rel_blks_read, + stats.rel_blks_hit, + stats.pages_deleted, + stats.tuples_deleted, + stats.wal_records, + stats.wal_fpi, + stats.wal_bytes, + stats.blk_read_time, + stats.blk_write_time, + stats.delay_time, + stats.total_time + FROM (pg_class rel + JOIN pg_namespace ns ON ((ns.oid = rel.relnamespace))), + LATERAL pg_stat_get_vacuum_indexes(rel.oid) stats(relid, total_blks_read, total_blks_hit, total_blks_dirtied, total_blks_written, rel_blks_read, rel_blks_hit, pages_deleted, tuples_deleted, wal_records, wal_fpi, wal_bytes, blk_read_time, blk_write_time, delay_time, total_time) + WHERE (rel.relkind = 'i'::"char"); pg_stat_vacuum_tables| SELECT ns.nspname AS schemaname, rel.relname, stats.relid, -- 2.39.5 (Apple Git-154)