From 42919ec064ec82e7afd86e64c3e1c1b75623778c Mon Sep 17 00:00:00 2001 From: "Imseih (AWS)" Date: Tue, 3 Jan 2023 10:16:57 -0600 Subject: [PATCH 1/1] Report index vacuum progress. Add 2 new columns to pg_stat_progress_vacuum. The columns are ndexes_total as the total indexes to be vacuumed or cleaned and indexes_processed as the number of indexes vacuumed or cleaned up so far. Author: Sami Imseih, based on suggestions by Nathan Bossart, Peter Geoghegan and Masahiko Sawada Reviewed by: Nathan Bossart, Masahiko Sawada Discussion: https://www.postgresql.org/message-id/flat/5478DFCD-2333-401A-B2F0-0D186AB09228@amazon.com --- contrib/bloom/blvacuum.c | 4 ++ doc/src/sgml/monitoring.sgml | 26 +++++++++ src/backend/access/gin/ginvacuum.c | 6 ++ src/backend/access/gist/gistvacuum.c | 4 ++ src/backend/access/hash/hash.c | 6 ++ src/backend/access/heap/vacuumlazy.c | 34 +++++++++++- src/backend/access/nbtree/nbtree.c | 2 + src/backend/access/spgist/spgvacuum.c | 3 + src/backend/catalog/index.c | 2 + src/backend/catalog/system_views.sql | 3 +- src/backend/commands/analyze.c | 1 + src/backend/commands/vacuumparallel.c | 74 ++++++++++++++++++++++++- src/backend/utils/activity/wait_event.c | 3 + src/include/access/genam.h | 5 +- src/include/commands/progress.h | 2 + src/include/commands/vacuum.h | 7 +++ src/include/utils/wait_event.h | 1 + src/test/regress/expected/rules.out | 4 +- 18 files changed, 180 insertions(+), 7 deletions(-) diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c index 2340d49e00..731c16782d 100644 --- a/contrib/bloom/blvacuum.c +++ b/contrib/bloom/blvacuum.c @@ -62,6 +62,8 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, *itupEnd; vacuum_delay_point(); + if (info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); @@ -192,6 +194,8 @@ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) Page page; vacuum_delay_point(); + if (info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 5bcba0fdec..76c9ed9691 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1767,6 +1767,10 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser ParallelFinish Waiting for parallel workers to finish computing. + + ParallelVacuumFinish + Waiting for parallel vacuum workers to finish index vacuum. + ProcArrayGroupUpdate Waiting for the group leader to clear the transaction ID at @@ -6842,6 +6846,28 @@ FROM pg_stat_get_backend_idset() AS backendid; Number of dead tuples collected since the last index vacuum cycle. + + + + indexes_total bigint + + + Number of indexes that wil be vacuumed. This value will be + 0 if there are no indexes to vacuum, INDEX_CLEANUP + is set to OFF, or vacuum failsafe is triggered. + See + for more on vacuum failsafe. + + + + + + indexes_completed bigint + + + Number of indexes vacuumed in the current vacuum cycle. + + diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index e5d310d836..195065290a 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -665,6 +665,9 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, vacuum_delay_point(); + if (info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); + for (i = 0; i < nRoot; i++) { ginVacuumPostingTree(&gvs, rootOfPostingTree[i]); @@ -751,6 +754,9 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) vacuum_delay_point(); + if (info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); + buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); LockBuffer(buffer, GIN_SHARE); diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index 3f60d3274d..8783c766ce 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -223,7 +223,11 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, break; /* Iterate over pages, then loop back to recheck length */ for (; blkno < num_pages; blkno++) + { gistvacuumpage(&vstate, blkno, blkno); + if (info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); + } } /* diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index eb258337d6..2426dcd968 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -505,6 +505,12 @@ loop_top: blkno = bucket_blkno; + /* + * For hash indexes, we report parallel vacuum progress + * for every bucket. + */ + if (info->report_parallel_progress) + parallel_vacuum_update_progress(info->parallel_vacuum_state); /* * We need to acquire a cleanup lock on the primary bucket page to out * wait concurrent scans before deleting the dead tuples. diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 06fd15405f..9d56d2be4e 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -411,6 +411,10 @@ heap_vacuum_rel(Relation rel, VacuumParams *params, Assert(params->index_cleanup == VACOPTVALUE_AUTO); } + /* report number of indexes to vacuum, if we are told to cleanup indexes */ + if (vacrel->do_index_cleanup) + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_TOTAL, vacrel->nindexes); + /* Initialize page counters explicitly (be tidy) */ vacrel->scanned_pages = 0; vacrel->removed_pages = 0; @@ -2325,6 +2329,12 @@ lazy_vacuum_all_indexes(LVRelState *vacrel) old_live_tuples, vacrel); + /* + * Done vacuuming an index. Increment the indexes completed + */ + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_COMPLETED, + idx + 1); + if (lazy_check_wraparound_failsafe(vacrel)) { /* Wraparound emergency -- end current index scan */ @@ -2359,11 +2369,14 @@ lazy_vacuum_all_indexes(LVRelState *vacrel) Assert(allindexes || vacrel->failsafe_active); /* - * Increase and report the number of index scans. + * Reset and report the number of indexes scanned. + * Also, increase and report the number of index + * scans. * * We deliberately include the case where we started a round of bulk * deletes that we weren't able to finish due to the failsafe triggering. */ + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_COMPLETED, 0); vacrel->num_index_scans++; pgstat_progress_update_param(PROGRESS_VACUUM_NUM_INDEX_VACUUMS, vacrel->num_index_scans); @@ -2613,10 +2626,17 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel) { vacrel->failsafe_active = true; - /* Disable index vacuuming, index cleanup, and heap rel truncation */ + /* + * Disable index vacuuming, index cleanup, and heap rel truncation + * + * Also, report to progress.h that we are no longer tracking + * index vacuum/cleanup. + */ vacrel->do_index_vacuuming = false; vacrel->do_index_cleanup = false; vacrel->do_rel_truncate = false; + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_TOTAL, 0); + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_COMPLETED, 0); ereport(WARNING, (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans", @@ -2664,6 +2684,12 @@ lazy_cleanup_all_indexes(LVRelState *vacrel) vacrel->indstats[idx] = lazy_cleanup_one_index(indrel, istat, reltuples, estimated_count, vacrel); + + /* + * Done cleaning an index. Increment the indexes completed + */ + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_COMPLETED, + idx + 1); } } else @@ -2698,10 +2724,12 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, ivinfo.index = indrel; ivinfo.analyze_only = false; ivinfo.report_progress = false; + ivinfo.report_parallel_progress = false; ivinfo.estimated_count = true; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = reltuples; ivinfo.strategy = vacrel->bstrategy; + ivinfo.parallel_vacuum_state = NULL; /* * Update error traceback information. @@ -2746,11 +2774,13 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, ivinfo.index = indrel; ivinfo.analyze_only = false; ivinfo.report_progress = false; + ivinfo.report_parallel_progress = false; ivinfo.estimated_count = estimated_count; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = reltuples; ivinfo.strategy = vacrel->bstrategy; + ivinfo.parallel_vacuum_state = NULL; /* * Update error traceback information. diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 1cc88da032..eb69409c6a 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -998,6 +998,8 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, if (info->report_progress) pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, scanblkno); + if (info->report_parallel_progress && (scanblkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(info->parallel_vacuum_state); } } diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 3adb18f2d8..b28036a830 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -843,6 +843,9 @@ spgvacuumscan(spgBulkDeleteState *bds) /* empty the pending-list after each page */ if (bds->pendingList != NULL) spgprocesspending(bds); + /* report parallel vacuum progress */ + if (bds->info->report_parallel_progress && (blkno % REPORT_PARALLEL_VACUUM_EVERY_PAGES) == 0) + parallel_vacuum_update_progress(bds->info->parallel_vacuum_state); } } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index e6579f2979..c734b2d4ce 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -3348,10 +3348,12 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) ivinfo.index = indexRelation; ivinfo.analyze_only = false; ivinfo.report_progress = true; + ivinfo.report_parallel_progress = false; ivinfo.estimated_count = true; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples; ivinfo.strategy = NULL; + ivinfo.parallel_vacuum_state = NULL; /* * Encode TIDs as int8 values for the sort, rather than directly sorting diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 447c9b970f..0ab864185f 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1165,7 +1165,8 @@ CREATE VIEW pg_stat_progress_vacuum AS END AS phase, S.param2 AS heap_blks_total, S.param3 AS heap_blks_scanned, S.param4 AS heap_blks_vacuumed, S.param5 AS index_vacuum_count, - S.param6 AS max_dead_tuples, S.param7 AS num_dead_tuples + S.param6 AS max_dead_tuples, S.param7 AS num_dead_tuples, + S.param8 AS indexes_total, S.param9 AS indexes_completed FROM pg_stat_get_progress_info('VACUUM') AS S LEFT JOIN pg_database D ON S.datid = D.oid; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c86e690980..84c1d2f3c7 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -712,6 +712,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params, ivinfo.message_level = elevel; ivinfo.num_heap_tuples = onerel->rd_rel->reltuples; ivinfo.strategy = vac_strategy; + ivinfo.parallel_vacuum_state = NULL; stats = index_vacuum_cleanup(&ivinfo, NULL); diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index bcd40c80a1..3cf429481a 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -30,6 +30,7 @@ #include "access/table.h" #include "access/xact.h" #include "catalog/index.h" +#include "commands/progress.h" #include "commands/vacuum.h" #include "optimizer/paths.h" #include "pgstat.h" @@ -103,6 +104,17 @@ typedef struct PVShared /* Counter for vacuuming and cleanup */ pg_atomic_uint32 idx; + + /* + * Counter for vacuuming and cleanup progress reporting. + * This value is used to report index vacuum/cleanup progress + * in parallel_vacuum_progress_report. We keep this + * counter to avoid having to loop through + * ParallelVacuumState->indstats to determine the number + * of indexes completed. + */ + pg_atomic_uint32 nindexes_completed; + } PVShared; /* Status used during parallel index vacuum or cleanup */ @@ -213,6 +225,7 @@ static void parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation static bool parallel_vacuum_index_is_parallel_safe(Relation indrel, int num_index_scans, bool vacuum); static void parallel_vacuum_error_callback(void *arg); +static void parallel_vacuum_wait_to_finish(ParallelVacuumState *pvs); /* * Try to enter parallel mode and create a parallel context. Then initialize @@ -364,6 +377,7 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes, pg_atomic_init_u32(&(shared->cost_balance), 0); pg_atomic_init_u32(&(shared->active_nworkers), 0); pg_atomic_init_u32(&(shared->idx), 0); + pg_atomic_init_u32(&(shared->nindexes_completed), 0); shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_SHARED, shared); pvs->shared = shared; @@ -618,8 +632,9 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan vacuum)); } - /* Reset the parallel index processing counter */ + /* Reset the parallel index processing counter ( index progress counter also ) */ pg_atomic_write_u32(&(pvs->shared->idx), 0); + pg_atomic_write_u32(&(pvs->shared->nindexes_completed), 0); /* Setup the shared cost-based vacuum delay and launch workers */ if (nworkers > 0) @@ -688,7 +703,13 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan */ if (nworkers > 0) { - /* Wait for all vacuum workers to finish */ + /* + * Wait for all indexes to be vacuumed while + * updating the parallel vacuum index progress, + * and then wait for all workers to finish. + */ + parallel_vacuum_wait_to_finish(pvs); + WaitForParallelWorkersToFinish(pvs->pcxt); for (int i = 0; i < pvs->pcxt->nworkers_launched; i++) @@ -839,9 +860,13 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, ivinfo.num_heap_tuples = pvs->shared->reltuples; ivinfo.strategy = pvs->bstrategy; + /* Only the leader should report parallel vacuum progress */ + ivinfo.report_parallel_progress = !IsParallelWorker(); /* Update error traceback information */ pvs->indname = pstrdup(RelationGetRelationName(indrel)); pvs->status = indstats->status; + /* Pass ParallelVacuumState to IndexVacuumInfo for progress reporting */ + ivinfo.parallel_vacuum_state = pvs; switch (indstats->status) { @@ -888,6 +913,12 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, pvs->status = PARALLEL_INDVAC_STATUS_COMPLETED; pfree(pvs->indname); pvs->indname = NULL; + + /* Update the number of indexes completed. */ + pg_atomic_add_fetch_u32(&(pvs->shared->nindexes_completed), 1); + + if (ivinfo.report_parallel_progress) + parallel_vacuum_update_progress(pvs); } /* @@ -1072,3 +1103,42 @@ parallel_vacuum_error_callback(void *arg) return; } } + +/* + * Read pvs->shared->nindexes_completed and update progress.h + * with indexes vacuumed so far. This function is called periodically + * by index AMs as well as parallel_vacuum_process_one_index. + * + * Note: This function should be used by the leader process only, + * and it's up to the caller to ensure this. + */ +void +parallel_vacuum_update_progress(ParallelVacuumState *pvs) +{ + Assert(!IsParallelWorker()); + + if (pvs) + pgstat_progress_update_param(PROGRESS_VACUUM_INDEX_COMPLETED, + pg_atomic_read_u32(&pvs->shared->nindexes_completed)); +} + +/* + * Waiting for all indexes to be vacuumed while updating the + * parallel index vacuum progress. + */ +void +parallel_vacuum_wait_to_finish(ParallelVacuumState *pvs) +{ + Assert(!IsParallelWorker()); + + while (pg_atomic_read_u32(&pvs->shared->nindexes_completed) < pvs->nindexes) + { + CHECK_FOR_INTERRUPTS(); + + parallel_vacuum_update_progress(pvs); + + (void) WaitLatch(MyLatch, WL_TIMEOUT | WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 1000L, + WAIT_EVENT_PARALLEL_VACUUM_FINISH); + ResetLatch(MyLatch); + } +} diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index f9574e800f..a7d4b8534a 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -460,6 +460,9 @@ pgstat_get_wait_ipc(WaitEventIPC w) case WAIT_EVENT_XACT_GROUP_UPDATE: event_name = "XactGroupUpdate"; break; + case WAIT_EVENT_PARALLEL_VACUUM_FINISH: + event_name = "ParallelVacuumFinish"; + break; /* no default case, so that compiler will warn */ } diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 83dbee0fe6..6e0f12cef0 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -21,8 +21,9 @@ #include "utils/relcache.h" #include "utils/snapshot.h" -/* We don't want this file to depend on execnodes.h. */ +/* We don't want this file to depend on execnodes.h or vacuum.h. */ struct IndexInfo; +struct ParallelVacuumState; /* * Struct for statistics returned by ambuild @@ -46,10 +47,12 @@ typedef struct IndexVacuumInfo Relation index; /* the index being vacuumed */ bool analyze_only; /* ANALYZE (without any actual vacuum) */ bool report_progress; /* emit progress.h status reports */ + bool report_parallel_progress; /* emit progress.h status reports for parallel vacuum */ bool estimated_count; /* num_heap_tuples is an estimate */ int message_level; /* ereport level for progress messages */ double num_heap_tuples; /* tuples remaining in heap */ BufferAccessStrategy strategy; /* access strategy for reads */ + struct ParallelVacuumState *parallel_vacuum_state; /* access parallel vacuum state for progress reporting */ } IndexVacuumInfo; /* diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index e5add41352..6b8b609a4f 100644 --- a/src/include/commands/progress.h +++ b/src/include/commands/progress.h @@ -25,6 +25,8 @@ #define PROGRESS_VACUUM_NUM_INDEX_VACUUMS 4 #define PROGRESS_VACUUM_MAX_DEAD_TUPLES 5 #define PROGRESS_VACUUM_NUM_DEAD_TUPLES 6 +#define PROGRESS_VACUUM_INDEX_TOTAL 7 +#define PROGRESS_VACUUM_INDEX_COMPLETED 8 /* Phases of vacuum (as advertised via PROGRESS_VACUUM_PHASE) */ #define PROGRESS_VACUUM_PHASE_SCAN_HEAP 1 diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 5efb942368..84a2216119 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -64,6 +64,12 @@ /* value for checking vacuum flags */ #define VACUUM_OPTION_MAX_VALID_VALUE ((1 << 3) - 1) +/* + * Parallel Index vacuum progress is reported every 1GB of blocks + * scanned. + */ +#define REPORT_PARALLEL_VACUUM_EVERY_PAGES ((BlockNumber) (((uint64) 1024 * 1024 * 1024) / BLCKSZ)) + /* Abstract type for parallel vacuum state */ typedef struct ParallelVacuumState ParallelVacuumState; @@ -368,5 +374,6 @@ extern bool std_typanalyze(VacAttrStats *stats); 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 parallel_vacuum_update_progress(ParallelVacuumState *pvs); #endif /* VACUUM_H */ diff --git a/src/include/utils/wait_event.h b/src/include/utils/wait_event.h index f53254ad1f..46930fe7f5 100644 --- a/src/include/utils/wait_event.h +++ b/src/include/utils/wait_event.h @@ -114,6 +114,7 @@ typedef enum WAIT_EVENT_PARALLEL_BITMAP_SCAN, WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN, WAIT_EVENT_PARALLEL_FINISH, + WAIT_EVENT_PARALLEL_VACUUM_FINISH, WAIT_EVENT_PROCARRAY_GROUP_UPDATE, WAIT_EVENT_PROC_SIGNAL_BARRIER, WAIT_EVENT_PROMOTE, diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index fb9f936d43..df6f230715 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2021,7 +2021,9 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param4 AS heap_blks_vacuumed, s.param5 AS index_vacuum_count, s.param6 AS max_dead_tuples, - s.param7 AS num_dead_tuples + s.param7 AS num_dead_tuples, + s.param8 AS indexes_total, + s.param9 AS indexes_completed FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))); pg_stat_recovery_prefetch| SELECT s.stats_reset, -- 2.32.1 (Apple Git-133)