diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index ccc030f..beedf90 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -631,6 +631,18 @@ CREATE VIEW pg_stat_activity AS WHERE S.datid = D.oid AND S.usesysid = U.oid; +CREATE VIEW pg_stat_vacuum_progress AS + SELECT + S.pid, + S.total_pages, + S.scanned_pages, + S.total_heap_pages, + S.scanned_heap_pages, + S.total_index_pages, + S.scanned_index_pages, + S.percent_complete + FROM pg_stat_get_vacuum_progress(NULL) AS S; + CREATE VIEW pg_stat_replication AS SELECT S.pid, diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index a01cfb4..24832eb 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -439,7 +439,13 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, Relation *Irel, int nindexes, bool scan_all) { BlockNumber nblocks, - blkno; + blkno, + total_pages, + scanned_total_pages = 0, + total_heap_pages, + rel_index_pages = 0, + total_index_pages = 0, + scanned_index_pages = 0; HeapTupleData tuple; char *relname; BlockNumber empty_pages, @@ -471,7 +477,14 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, indstats = (IndexBulkDeleteResult **) palloc0(nindexes * sizeof(IndexBulkDeleteResult *)); - nblocks = RelationGetNumberOfBlocks(onerel); + total_heap_pages = nblocks = RelationGetNumberOfBlocks(onerel); + + for (i = 0; i < nindexes; i++) + rel_index_pages += RelationGetNumberOfBlocks(Irel[i]); + + total_index_pages = rel_index_pages; + total_pages = total_heap_pages + rel_index_pages; + vacrelstats->rel_pages = nblocks; vacrelstats->scanned_pages = 0; vacrelstats->nonempty_pages = 0; @@ -520,7 +533,14 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, vacuum_delay_point(); } if (next_not_all_visible_block >= SKIP_PAGES_THRESHOLD) + { skipping_all_visible_blocks = true; + if(!scan_all) + { + total_heap_pages = total_heap_pages - next_not_all_visible_block; + total_pages = total_pages - next_not_all_visible_block; + } + } else skipping_all_visible_blocks = false; @@ -559,7 +579,14 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, * following blocks. */ if (next_not_all_visible_block - blkno > SKIP_PAGES_THRESHOLD) + { skipping_all_visible_blocks = true; + if(!scan_all) + { + total_heap_pages = total_heap_pages - (next_not_all_visible_block - blkno); + total_pages = total_pages - (next_not_all_visible_block - blkno); + } + } else skipping_all_visible_blocks = false; all_visible_according_to_vm = false; @@ -596,11 +623,30 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, /* Log cleanup info before we touch indexes */ vacuum_log_cleanup_info(onerel, vacrelstats); + /* + * If passes through indexes exceed 1 add + * pages equal to rel_index_pages to the count of + * total pages to be scanned. + */ + if (vacrelstats->num_index_scans >= 1) + { + total_index_pages = total_index_pages + rel_index_pages; + total_pages = total_heap_pages + total_index_pages; + } + /* Remove index entries */ for (i = 0; i < nindexes; i++) + { lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); + scanned_index_pages += RelationGetNumberOfBlocks(Irel[i]); + scanned_total_pages = scanned_total_pages + RelationGetNumberOfBlocks(Irel[i]); + /* Report progress to the statistics collector */ + pgstat_report_progress(total_pages, scanned_total_pages, total_heap_pages, + vacrelstats->scanned_pages, total_index_pages, + scanned_index_pages); + } /* Remove tuples from heap */ lazy_vacuum_heap(onerel, vacrelstats); @@ -658,6 +704,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, UnlockReleaseBuffer(buf); vacrelstats->scanned_pages++; vacrelstats->pinskipped_pages++; + scanned_total_pages++; continue; } LockBuffer(buf, BUFFER_LOCK_UNLOCK); @@ -666,6 +713,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, } vacrelstats->scanned_pages++; + scanned_total_pages++; page = BufferGetPage(buf); @@ -1062,6 +1110,17 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, */ if (vacrelstats->num_dead_tuples == prev_dead_count) RecordPageWithFreeSpace(onerel, blkno, freespace); + + /* + * Reporting vacuum progress to statistics collector + */ + if (blkno == nblocks - 1 && vacrelstats->num_dead_tuples == 0 && nindexes != 0 + && vacrelstats->num_index_scans == 0) + total_pages = total_pages - total_index_pages; + + pgstat_report_progress(total_pages, scanned_total_pages, total_heap_pages, + vacrelstats->scanned_pages, total_index_pages, + scanned_index_pages); } pfree(frozen); @@ -1093,11 +1152,30 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, /* Log cleanup info before we touch indexes */ vacuum_log_cleanup_info(onerel, vacrelstats); + /* + * If passes through indexes exceed 1 add + * pages equal to rel_index_pages to the count of + * total pages to be scanned. + */ + if (vacrelstats->num_index_scans >= 1) + { + total_index_pages = total_index_pages + rel_index_pages; + total_pages = total_heap_pages + total_index_pages; + } + /* Remove index entries */ for (i = 0; i < nindexes; i++) + { lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); + scanned_index_pages += RelationGetNumberOfBlocks(Irel[i]); + scanned_total_pages = scanned_total_pages + RelationGetNumberOfBlocks(Irel[i]); + /* Report progress to the statistics collector */ + pgstat_report_progress(total_pages, scanned_total_pages, total_heap_pages, + vacrelstats->scanned_pages, total_index_pages, + scanned_index_pages); + } /* Remove tuples from heap */ lazy_vacuum_heap(onerel, vacrelstats); vacrelstats->num_index_scans++; diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index ab018c4..a531fb8 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -2731,7 +2731,6 @@ pgstat_bestart(void) beentry->st_clienthostname[NAMEDATALEN - 1] = '\0'; beentry->st_appname[NAMEDATALEN - 1] = '\0'; beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0'; - pgstat_increment_changecount_after(beentry); /* Update app name to current GUC setting */ @@ -2851,6 +2850,36 @@ pgstat_report_activity(BackendState state, const char *cmd_str) pgstat_increment_changecount_after(beentry); } +/* --------------------------------------------- + * Called from VACUUM after every heap page scan or index scan + * to report progress + * --------------------------------------------- + */ + +void +pgstat_report_progress(BlockNumber total_pages, BlockNumber scanned_pages, + BlockNumber heap_total_pages, BlockNumber heap_scanned_pages, + BlockNumber index_total_pages, BlockNumber index_scanned_pages) +{ + volatile PgBackendStatus *beentry = MyBEEntry; + + if (!beentry) + return; + + if (!pgstat_track_activities) + return; + + pgstat_increment_changecount_before(beentry); + beentry->progress_param_float[0] = scanned_pages * 100 / total_pages; + beentry->progress_param[0] = total_pages; + beentry->progress_param[1] = scanned_pages; + beentry->progress_param[2] = heap_total_pages; + beentry->progress_param[3] = heap_scanned_pages; + beentry->progress_param[4] = index_total_pages; + beentry->progress_param[5] = index_scanned_pages; + pgstat_increment_changecount_after(beentry); +} + /* ---------- * pgstat_report_appname() - * diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index f7c9bf6..197aa52 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -53,6 +53,7 @@ extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS); extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_vacuum_progress(PG_FUNCTION_ARGS); extern Datum pg_backend_pid(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS); extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS); @@ -523,7 +524,110 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } } +/* + * Returns VACUUM progress values stored by each backend + * executing VACUUM. + */ +Datum +pg_stat_get_vacuum_progress(PG_FUNCTION_ARGS) +{ +#define PG_STAT_GET_PROGRESS_COLS 30 + int num_backends = pgstat_fetch_stat_numbackends(); + int curr_backend; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + MemoryContextSwitchTo(oldcontext); + + for (curr_backend = 1; curr_backend <= num_backends; curr_backend++) + { + Datum values[PG_STAT_GET_PROGRESS_COLS]; + bool nulls[PG_STAT_GET_PROGRESS_COLS]; + LocalPgBackendStatus *local_beentry; + PgBackendStatus *beentry; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + local_beentry = pgstat_fetch_stat_local_beentry(curr_backend); + if (!local_beentry) + continue; + + beentry = &local_beentry->backendStatus; + /* Report values for only those backends which are running VACUUM */ + if(!beentry || (strncmp(beentry->st_activity,"VACUUM",6) + && strncmp(beentry->st_activity,"vacuum",6))) + continue; + + values[0] = Int32GetDatum(beentry->st_procpid); + if (beentry->progress_param[0] != 0) + values[1] = UInt32GetDatum(beentry->progress_param[0]); + else + nulls[1] = true; + + if (beentry->progress_param[1] != 0) + values[2] = UInt32GetDatum(beentry->progress_param[1]); + else + nulls[2] = true; + if (beentry->progress_param[2] != 0) + values[3] = UInt32GetDatum(beentry->progress_param[2]); + else + nulls[3] = true; + + if (beentry->progress_param[3] != 0) + values[4] = UInt32GetDatum(beentry->progress_param[3]); + else + nulls[4] = true; + + if (beentry->progress_param[4] != 0) + values[5] = UInt32GetDatum(beentry->progress_param[4]); + else + nulls[5] = true; + + if (beentry->progress_param[5] != 0) + values[6] = UInt32GetDatum(beentry->progress_param[5]); + else + nulls[6] = true; + + if (beentry->progress_param_float[0] != 0) + values[7] = Float8GetDatum(beentry->progress_param_float[0]); + else + nulls[7] = true; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} /* * Returns activity of PG backends. */ @@ -584,7 +688,6 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) if (!be || be->st_procpid != pid) continue; - } /* Get the next one in the list */ @@ -790,7 +893,6 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) return (Datum) 0; } - Datum pg_backend_pid(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index fcba3c5..f6d5897 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2299,7 +2299,6 @@ static struct config_int ConfigureNamesInt[] = -1, -1, INT_MAX, NULL, NULL, NULL }, - { {"log_autovacuum_min_duration", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Sets the minimum execution time above which " diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index eb55b3a..e5bac75 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2781,6 +2781,7 @@ DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 0 f DESCR("statistics: currently active backend IDs"); DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ )); DESCR("statistics: information about currently active backends"); +DATA(insert OID = 3308 ( pg_stat_get_vacuum_progress PGNSP PGUID 12 1 1 0 0 f f f f f t s r 1 0 2249 "23" "{23,23,23,23,23,23,23,23,701}" "{i,o,o,o,o,o,o,o,o}" "{pid,pid,total_pages,scanned_pages,total_heap_pages,scanned_heap_pages,total_index_pages,scanned_index_pages,percent_complete}" _null_ _null_ pg_stat_get_vacuum_progress _null_ _null_ _null_ )); DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s r 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ )); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index e3a31af..e5f1ac9 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -155,7 +155,6 @@ extern int vacuum_freeze_table_age; extern int vacuum_multixact_freeze_min_age; extern int vacuum_multixact_freeze_table_age; - /* in commands/vacuum.c */ extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel); extern void vacuum(int options, RangeVar *relation, Oid relid, diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 9ecc163..c4a665a 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -20,6 +20,7 @@ #include "utils/hsearch.h" #include "utils/relcache.h" +#include "storage/block.h" /* ---------- * Paths for the statistics files (relative to installation's $PGDATA). @@ -205,6 +206,8 @@ typedef struct PgStat_MsgHdr #define PGSTAT_MAX_MSG_SIZE 1000 #define PGSTAT_MSG_PAYLOAD (PGSTAT_MAX_MSG_SIZE - sizeof(PgStat_MsgHdr)) +#define N_PROGRESS_PARAM 10 +#define PROGRESS_MESSAGE_LENGTH 20 /* ---------- * PgStat_MsgDummy A dummy message, ignored by the collector @@ -776,6 +779,11 @@ typedef struct PgBackendStatus /* current command string; MUST be null-terminated */ char *st_activity; + + uint32 progress_param[N_PROGRESS_PARAM]; + double progress_param_float[N_PROGRESS_PARAM]; + char progress_message[PROGRESS_MESSAGE_LENGTH][N_PROGRESS_PARAM]; + } PgBackendStatus; /* @@ -928,6 +936,9 @@ extern void pgstat_initialize(void); extern void pgstat_bestart(void); extern void pgstat_report_activity(BackendState state, const char *cmd_str); +extern void pgstat_report_progress(BlockNumber total_pages, BlockNumber scanned_pages, + BlockNumber total_heap_pages, BlockNumber scanned_heap_pages, + BlockNumber total_index_pages, BlockNumber scanned_index_pages); extern void pgstat_report_tempfile(size_t filesize); extern void pgstat_report_appname(const char *appname); extern void pgstat_report_xact_timestamp(TimestampTz tstamp);