From 8af355a19f68a04d24c616734c7f0286705b8030 Mon Sep 17 00:00:00 2001 From: amit Date: Fri, 11 Mar 2016 18:19:52 +0900 Subject: [PATCH] WIP: Implement progress reporting for VACUUM command. This basically utilizes the pgstat_progress* API to report a handful of paramters to indicate its progress. lazy_vacuum_rel() and lazy_scan_heap() have been altered to report command start/end, command target table, and the following parameters: processing phase and number of heap blocks at the start, current heap block number in the main scan loop (whenever it changes) and the number of index vacuum passes (every time a index vacuum round is finished). Following processing phases are identified and reported whenever one changes into another: 'scanning heap', 'vacuuming indexes', 'vacuuming heap', and finally 'cleanup'. To indicate the progress of a given index vacuuming round (the 'vacuuming indexes' phase), index_bulk_delete() is passed one more callback function called lazy_vacuum_index_progress() of type IndexBulkDeleteProgressCallback. This means the signature of ambulkdelete now has 2 more arguments named progress_callback and progress_callback_state. An AM should call it with the state parameter whenever a new block is read as part of a vacuum scan. lazy_vacuum_index_progress() simply reports the updated counter which is maintained in LVRelStats.index_blocks_vacuumed for a given vacuuming round using the pgstat_progress* API. A view named pg_stat_progress_vacuum has been added that shows these values. --- doc/src/sgml/monitoring.sgml | 111 ++++++++++++++++++++++++++++++++++ src/backend/access/brin/brin.c | 4 +- src/backend/access/gin/ginvacuum.c | 18 +++++- src/backend/access/gist/gistvacuum.c | 6 +- src/backend/access/hash/hash.c | 6 +- src/backend/access/index/indexam.c | 7 ++- src/backend/access/nbtree/nbtree.c | 20 +++++- src/backend/access/spgist/spgvacuum.c | 12 +++- src/backend/catalog/index.c | 3 +- src/backend/catalog/system_views.sql | 25 ++++++++ src/backend/commands/vacuumlazy.c | 67 +++++++++++++++++++- src/include/access/amapi.h | 4 +- src/include/access/brin_internal.h | 4 +- src/include/access/genam.h | 7 ++- src/include/access/gin_private.h | 4 +- src/include/access/gist_private.h | 4 +- src/include/access/hash.h | 4 +- src/include/access/nbtree.h | 4 +- src/include/access/spgist.h | 4 +- src/test/regress/expected/rules.out | 23 +++++++ 20 files changed, 317 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index ec5328e..bff5e43 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -507,6 +507,12 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser yet included in pg_stat_user_functions). + + pg_stat_progress_vacuumpg_stat_progress_vacuum + One row for each backend (including autovacuum worker processes) running + VACUUM, showing current progress in terms of heap pages it + has finished processing. + @@ -2204,6 +2210,111 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i controls exactly which functions are tracked. + + <structname>pg_stat_progress_vacuum</structname> View + + + + Column + Type + Description + + + + + + pid + integer + Process ID of backend + + + datid + oid + OID of the database this backend is connected to + + + datname + name + Name of the database this backend is connected to + + + relid + oid + OID of the table being vacuumed + + + processing_phase + text + Current processing phase of vacuum. + Possible values are: + + + + scanning heap + + + + + vacuuming indexes + + + + + vacuuming heap + + + + + cleanup + + + + + + + total_heap_blocks + bigint + Total number of heap blocks in the table + + + current_heap_block + bigint + Current heap block being processed + + + total_index_blocks + bigint + Total number of index blocks to be processed + + + index_blocks_vacuumed + bigint + Number of index blocks processed in current vacuum round + + + index_vacuum_count + bigint + Number of times index vacuum round has been performed so far + + + percent_done + numeric + + Amount of work finished in percent in terms of table blocks processed + + + + +
+ + + The pg_stat_progress_vacuum view will contain + one row for each backend (including autovacuum worker processes), showing + parameters that can help determine the progress of VACUUM + command running in it. Note that the backends running + VACUUM FULL are not shown. + + diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index c740952..ed25d79 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -706,7 +706,9 @@ brinbuildempty(Relation index) */ IndexBulkDeleteResult * brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { /* allocate stats if first time through, else re-use existing struct */ if (stats == NULL) diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index 6a4b98a..e1dd5bd 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -29,6 +29,8 @@ struct GinVacuumState IndexBulkDeleteResult *result; IndexBulkDeleteCallback callback; void *callback_state; + IndexBulkDeleteProgressCallback progress_callback; + void *progress_callback_state; GinState ginstate; BufferAccessStrategy strategy; MemoryContext tmpCxt; @@ -116,6 +118,8 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, bool hasVoidPage = FALSE; MemoryContext oldCxt; + if (gvs->progress_callback) + gvs->progress_callback(gvs->progress_callback_state); buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno, RBM_NORMAL, gvs->strategy); page = BufferGetPage(buffer); @@ -322,6 +326,8 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, me = parent->child; } + if (gvs->progress_callback) + gvs->progress_callback(gvs->progress_callback_state); buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno, RBM_NORMAL, gvs->strategy); page = BufferGetPage(buffer); @@ -515,7 +521,9 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3 IndexBulkDeleteResult * ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { Relation index = info->index; BlockNumber blkno = GIN_ROOT_BLKNO; @@ -532,6 +540,8 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, gvs.index = index; gvs.callback = callback; gvs.callback_state = callback_state; + gvs.progress_callback = progress_callback; + gvs.progress_callback_state = progress_callback_state; gvs.strategy = info->strategy; initGinState(&gvs.ginstate, index); @@ -548,6 +558,8 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, stats->num_index_tuples = 0; gvs.result = stats; + if (progress_callback) + progress_callback(progress_callback_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); @@ -581,6 +593,8 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, Assert(blkno != InvalidBlockNumber); UnlockReleaseBuffer(buffer); + if (progress_callback) + progress_callback(progress_callback_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); } @@ -624,6 +638,8 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, if (blkno == InvalidBlockNumber) /* rightmost page */ break; + if (progress_callback) + progress_callback(progress_callback_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); LockBuffer(buffer, GIN_EXCLUSIVE); diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index 7947ff9..e692f30 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -137,7 +137,9 @@ pushStackIfSplited(Page page, GistBDItem *stack) */ IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { Relation rel = info->index; GistBDItem *stack, @@ -162,6 +164,8 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexTuple idxtuple; ItemId iid; + if (progress_callback) + progress_callback(progress_callback_state); buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno, RBM_NORMAL, info->strategy); LockBuffer(buffer, GIST_SHARE); diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 3d48c4f..02d5ca5 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -490,7 +490,9 @@ hashendscan(IndexScanDesc scan) */ IndexBulkDeleteResult * hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { Relation rel = info->index; double tuples_removed; @@ -556,6 +558,8 @@ loop_top: vacuum_delay_point(); + if (progress_callback) + progress_callback(progress_callback_state); buf = _hash_getbuf_with_strategy(rel, blkno, HASH_WRITE, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE, info->strategy); diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 54b71cb..899f67f 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -617,7 +617,9 @@ IndexBulkDeleteResult * index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state) + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { Relation indexRelation = info->index; @@ -625,7 +627,8 @@ index_bulk_delete(IndexVacuumInfo *info, CHECK_REL_PROCEDURE(ambulkdelete); return indexRelation->rd_amroutine->ambulkdelete(info, stats, - callback, callback_state); + callback, callback_state, + progress_callback, progress_callback_state); } /* ---------------- diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index f2905cb..7d279fc 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -55,6 +55,8 @@ typedef struct IndexBulkDeleteResult *stats; IndexBulkDeleteCallback callback; void *callback_state; + IndexBulkDeleteProgressCallback progress_callback; + void *progress_callback_state; BTCycleId cycleid; BlockNumber lastBlockVacuumed; /* highest blkno actually vacuumed */ BlockNumber lastBlockLocked; /* highest blkno we've cleanup-locked */ @@ -71,6 +73,8 @@ static void btbuildCallback(Relation index, void *state); static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state, BTCycleId cycleid); static void btvacuumpage(BTVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno); @@ -669,7 +673,9 @@ btrestrpos(IndexScanDesc scan) */ IndexBulkDeleteResult * btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { Relation rel = info->index; BTCycleId cycleid; @@ -684,7 +690,9 @@ btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, { cycleid = _bt_start_vacuum(rel); - btvacuumscan(info, stats, callback, callback_state, cycleid); + btvacuumscan(info, stats, callback, callback_state, + progress_callback, progress_callback_state, + cycleid); } PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel)); _bt_end_vacuum(rel); @@ -716,7 +724,7 @@ btvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) if (stats == NULL) { stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); - btvacuumscan(info, stats, NULL, NULL, 0); + btvacuumscan(info, stats, NULL, NULL, NULL, NULL, 0); } /* Finally, vacuum the FSM */ @@ -752,6 +760,8 @@ btvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state, BTCycleId cycleid) { Relation rel = info->index; @@ -773,6 +783,8 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, vstate.stats = stats; vstate.callback = callback; vstate.callback_state = callback_state; + vstate.progress_callback = progress_callback; + vstate.progress_callback_state = progress_callback_state; vstate.cycleid = cycleid; vstate.lastBlockVacuumed = BTREE_METAPAGE; /* Initialise at first block */ vstate.lastBlockLocked = BTREE_METAPAGE; @@ -910,6 +922,8 @@ restart: * recycle all-zero pages, not fail. Also, we want to use a nondefault * buffer access strategy. */ + if (vstate->progress_callback) + vstate->progress_callback(vstate->progress_callback_state); buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, info->strategy); LockBuffer(buf, BT_READ); diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 15b867f..6e2303e 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -44,6 +44,8 @@ typedef struct spgBulkDeleteState IndexBulkDeleteResult *stats; IndexBulkDeleteCallback callback; void *callback_state; + IndexBulkDeleteProgressCallback progress_callback; + void *progress_callback_state; /* Additional working state */ SpGistState spgstate; /* for SPGiST operations that need one */ @@ -612,6 +614,8 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno) /* call vacuum_delay_point while not holding any buffer lock */ vacuum_delay_point(); + if (bds->progress_callback) + bds->progress_callback(bds->progress_callback_state); buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, bds->info->strategy); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); @@ -883,7 +887,9 @@ spgvacuumscan(spgBulkDeleteState *bds) */ IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, - IndexBulkDeleteCallback callback, void *callback_state) + IndexBulkDeleteCallback callback, void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state) { spgBulkDeleteState bds; @@ -894,6 +900,8 @@ spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, bds.stats = stats; bds.callback = callback; bds.callback_state = callback_state; + bds.progress_callback = progress_callback; + bds.progress_callback_state = progress_callback_state; spgvacuumscan(&bds); @@ -935,6 +943,8 @@ spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) bds.stats = stats; bds.callback = dummy_callback; bds.callback_state = NULL; + bds.progress_callback = NULL; + bds.progress_callback_state = NULL; spgvacuumscan(&bds); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 31a1438..7486c38 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2837,7 +2837,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) state.htups = state.itups = state.tups_inserted = 0; (void) index_bulk_delete(&ivinfo, NULL, - validate_index_callback, (void *) &state); + validate_index_callback, (void *) &state, + NULL, NULL); /* Execute the sort */ tuplesort_performsort(state.tuplesort); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 84aa061..1d745c9 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -972,3 +972,28 @@ RETURNS jsonb LANGUAGE INTERNAL STRICT IMMUTABLE AS 'jsonb_set'; + +CREATE VIEW pg_stat_progress_vacuum AS + SELECT + S.pid AS pid, + S.datid AS datid, + D.datname AS datname, + S.relid AS relid, + CASE S.param1 + WHEN 1 THEN 'scanning heap' + WHEN 2 THEN 'vacuuming indexes' + WHEN 3 THEN 'vacuuming heap' + WHEN 4 THEN 'cleanup' + ELSE 'unknown phase' + END AS processing_phase, + S.param2 AS total_heap_blocks, + S.param3 AS current_heap_block, + pg_indexes_size(S.relid) / 8192 AS total_index_blocks, + S.param4 AS index_blocks_vacuumed, + S.param5 AS index_vacuum_count, + CASE S.param2 + WHEN 0 THEN round(100.0, 2) + ELSE round((S.param3 + 1) * 100.0 / S.param2, 2) + END AS percent_done + FROM pg_database D, pg_stat_get_progress_info('VACUUM') AS S + WHERE S.datid = D.oid; diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index fe87243..468dc68 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -97,6 +97,28 @@ */ #define SKIP_PAGES_THRESHOLD ((BlockNumber) 32) +/* + * Progress parameters of (lazy) vacuum reported to pgstat progress tracking + * facility + */ +#define PROG_PARAM_VAC_PHASE 0 +#define PROG_PARAM_VAC_HEAP_BLKS 1 +#define PROG_PARAM_VAC_CURR_HEAP_BLK 2 +#define PROG_PARAM_VAC_IDX_BLKS_VACUUMED 3 +#define PROG_PARAM_VAC_IDX_VACUUM_COUNT 4 + +/* + * Following distinct phases of lazy vacuum are identified. #1, #2 and #3 + * run in a cyclical manner due to possibly limited memory to work with, + * whereby #1 is periodically interrupted to run #2, followed by #3, and + * back to #1. Cycle repeats until all blocks of the relation have been + * covered by #1. + */ +#define LV_PHASE_SCAN_HEAP 1 +#define LV_PHASE_VACUUM_INDEX 2 +#define LV_PHASE_VACUUM_HEAP 3 +#define LV_PHASE_CLEANUP 4 + typedef struct LVRelStats { /* hasindex = true means two-pass strategy; false means one-pass */ @@ -122,6 +144,7 @@ typedef struct LVRelStats int num_index_scans; TransactionId latestRemovedXid; bool lock_waiter_detected; + double index_blocks_vacuumed; } LVRelStats; @@ -159,6 +182,7 @@ static bool lazy_tid_reaped(ItemPointer itemptr, void *state); static int vac_cmp_itemptr(const void *left, const void *right); static bool heap_page_is_all_visible(Relation rel, Buffer buf, TransactionId *visibility_cutoff_xid, bool *all_frozen); +static void lazy_vacuum_index_progress(void *state); /* @@ -481,6 +505,12 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, lazy_space_alloc(vacrelstats, nblocks); frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage); + /* We're about to begin the main heap scan.*/ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_SCAN_HEAP); + + /* Report total_heap_blocks that will be processed. */ + pgstat_progress_update_param(PROG_PARAM_VAC_HEAP_BLKS, nblocks); + /* * Except when aggressive is set, we want to skip pages that are * all-visible according to the visibility map, but only when we can skip @@ -668,11 +698,19 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, vacuum_log_cleanup_info(onerel, vacrelstats); /* Remove index entries */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_VACUUM_INDEX); + vacrelstats->index_blocks_vacuumed = 0; for (i = 0; i < nindexes; i++) lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); + + /* Report incremented count of number of index vacuums so far */ + pgstat_progress_update_param(PROG_PARAM_VAC_IDX_VACUUM_COUNT, + vacrelstats->num_index_scans + 1); + /* Remove tuples from heap */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_VACUUM_HEAP); lazy_vacuum_heap(onerel, vacrelstats); /* @@ -682,6 +720,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, */ vacrelstats->num_dead_tuples = 0; vacrelstats->num_index_scans++; + + /* Report going back to scanning heap */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_SCAN_HEAP); } /* @@ -695,6 +736,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, */ visibilitymap_pin(onerel, blkno, &vmbuffer); + /* Report current_heap_block that will be worked on. */ + pgstat_progress_update_param(PROG_PARAM_VAC_CURR_HEAP_BLK, blkno); + buf = ReadBufferExtended(onerel, MAIN_FORKNUM, blkno, RBM_NORMAL, vac_strategy); @@ -1212,16 +1256,25 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, vacuum_log_cleanup_info(onerel, vacrelstats); /* Remove index entries */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_VACUUM_INDEX); + vacrelstats->index_blocks_vacuumed = 0; for (i = 0; i < nindexes; i++) lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); + + /* Report incremented count of number of index vacuums so far */ + pgstat_progress_update_param(PROG_PARAM_VAC_IDX_VACUUM_COUNT, + vacrelstats->num_index_scans + 1); + /* Remove tuples from heap */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_VACUUM_HEAP); lazy_vacuum_heap(onerel, vacrelstats); vacrelstats->num_index_scans++; } /* Do post-vacuum cleanup and statistics update for each index */ + pgstat_progress_update_param(PROG_PARAM_VAC_PHASE, LV_PHASE_CLEANUP); for (i = 0; i < nindexes; i++) lazy_cleanup_index(Irel[i], indstats[i], vacrelstats); @@ -1507,7 +1560,9 @@ lazy_vacuum_index(Relation indrel, /* Do bulk deletion */ *stats = index_bulk_delete(&ivinfo, *stats, - lazy_tid_reaped, (void *) vacrelstats); + lazy_tid_reaped, (void *) vacrelstats, + lazy_vacuum_index_progress, + (void *) vacrelstats); ereport(elevel, (errmsg("scanned index \"%s\" to remove %d row versions", @@ -1912,6 +1967,16 @@ lazy_tid_reaped(ItemPointer itemptr, void *state) return (res != NULL); } +static void +lazy_vacuum_index_progress(void *state) +{ + LVRelStats *vacrelstats = (LVRelStats *) state; + + vacrelstats->index_blocks_vacuumed++; + pgstat_progress_update_param(PROG_PARAM_VAC_IDX_BLKS_VACUUMED, + vacrelstats->index_blocks_vacuumed); +} + /* * Comparator routines for use with qsort() and bsearch(). */ diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 35f1061..6c5825c 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -50,7 +50,9 @@ typedef bool (*aminsert_function) (Relation indexRelation, typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); /* post-VACUUM cleanup */ typedef IndexBulkDeleteResult *(*amvacuumcleanup_function) (IndexVacuumInfo *info, diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 47317af..ac81207 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -98,7 +98,9 @@ extern void brinendscan(IndexScanDesc scan); extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern bytea *brinoptions(Datum reloptions, bool validate); diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 81907d5..cb049b5 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -79,6 +79,9 @@ typedef struct IndexBulkDeleteResult /* Typedef for callback function to determine if a tuple is bulk-deletable */ typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state); +/* Typedef for callback function to be called for every index page read */ +typedef void (*IndexBulkDeleteProgressCallback) (void *state); + /* struct definitions appear in relscan.h */ typedef struct IndexScanDescData *IndexScanDesc; typedef struct SysScanDescData *SysScanDesc; @@ -153,7 +156,9 @@ extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap); extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern bool index_can_return(Relation indexRelation, int attno); diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index d2ea588..07a5c78 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -891,7 +891,9 @@ extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key); extern IndexBulkDeleteResult *ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs, diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f9732ba..6633293 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -544,7 +544,9 @@ extern XLogRecPtr gistGetFakeLSN(Relation rel); extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 3a68390..37edfdf 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -259,7 +259,9 @@ extern void hashendscan(IndexScanDesc scan); extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern bytea *hashoptions(Datum reloptions, bool validate); diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 9046b16..3e990ed 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -671,7 +671,9 @@ extern void btrestrpos(IndexScanDesc scan); extern IndexBulkDeleteResult *btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *btvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); extern bool btcanreturn(Relation index, int attno); diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h index 1994f71..e40236c 100644 --- a/src/include/access/spgist.h +++ b/src/include/access/spgist.h @@ -199,7 +199,9 @@ extern bool spgcanreturn(Relation index, int attno); extern IndexBulkDeleteResult *spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, - void *callback_state); + void *callback_state, + IndexBulkDeleteProgressCallback progress_callback, + void *progress_callback_state); extern IndexBulkDeleteResult *spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 22ea06c..fca60a4 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1747,6 +1747,29 @@ pg_stat_database_conflicts| SELECT d.oid AS datid, pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock FROM pg_database d; +pg_stat_progress_vacuum| SELECT s.pid, + s.datid, + d.datname, + s.relid, + CASE s.param1 + WHEN 1 THEN 'scanning heap'::text + WHEN 2 THEN 'vacuuming indexes'::text + WHEN 3 THEN 'vacuuming heap'::text + WHEN 4 THEN 'cleanup'::text + ELSE 'unknown phase'::text + END AS processing_phase, + s.param2 AS total_heap_blocks, + s.param3 AS current_heap_block, + (pg_indexes_size((s.relid)::regclass) / 8192) AS total_index_blocks, + s.param4 AS index_blocks_vacuumed, + s.param5 AS index_vacuum_count, + CASE s.param2 + WHEN 0 THEN round(100.0, 2) + ELSE round(((((s.param3 + 1))::numeric * 100.0) / (s.param2)::numeric), 2) + END AS percent_done + FROM pg_database d, + pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) + WHERE (s.datid = d.oid); pg_stat_replication| SELECT s.pid, s.usesysid, u.rolname AS usename, -- 2.3.2 (Apple Git-55)