diff --git a/contrib/Makefile b/contrib/Makefile index bd251f6..f3d3fe6 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -47,7 +47,8 @@ SUBDIRS = \ tsm_system_time \ tsearch2 \ unaccent \ - vacuumlo + vacuumlo \ + vacuum_progress ifeq ($(with_openssl),yes) SUBDIRS += sslinfo diff --git a/contrib/vacuum_progress/Makefile b/contrib/vacuum_progress/Makefile new file mode 100644 index 0000000..5fa5fc4 --- /dev/null +++ b/contrib/vacuum_progress/Makefile @@ -0,0 +1,15 @@ +# contrib/passwordcheck/Makefile + +MODULE_big = vacuum_progress +OBJS = vacuum_progress.o $(WIN32RES) + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/vacuum_progress +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/vacuum_progress/vacuum_progress.c b/contrib/vacuum_progress/vacuum_progress.c new file mode 100644 index 0000000..0c862cb --- /dev/null +++ b/contrib/vacuum_progress/vacuum_progress.c @@ -0,0 +1,126 @@ +/*------------------------------------------------------------------------- + * + * vacuum_progress.c + * + * IDENTIFICATION + * contrib/vacuum_progress/vacuum_progress.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/vacuum.h" +#include "portability/instr_time.h" +#include "fmgr.h" +#include "funcapi.h" +#include "storage/block.h" +#include "access/htup_details.h" +#include "pgstat.h" + +PG_MODULE_MAGIC; + +extern void _PG_init(void); +#define SKIP_PAGES_THRESHOLD ((BlockNumber) 32) + +typedef struct PgStat_Vacuum +{ + instr_time start_time; + double elapsed_time; + double remaining_time; + uint32 total_pages; + uint32 scanned_pages; + uint32 vacuumed_pages; +} PgStat_Vacuum; + +PgStat_Vacuum backend_stat; + +/* + * Calculating progress at the end of every page scanned by lazy_scan_heap. + * This can be improved to calculate progress at specified intervals only. + * + * Vacuum Progress is measured in terms of pages scanned + * versus total pages to be scanned. + */ + +static void +calculate_progress(BlockNumber vacuumed_pages, BlockNumber pages_to_be_skipped, + BlockNumber scanned_pages) +{ + uint32 percent_complete; + instr_time elapsed_time; + INSTR_TIME_SET_CURRENT(elapsed_time); + INSTR_TIME_SUBTRACT(elapsed_time,backend_stat.start_time); + backend_stat.elapsed_time = INSTR_TIME_GET_DOUBLE(elapsed_time); + + backend_stat.scanned_pages = scanned_pages; + backend_stat.vacuumed_pages = vacuumed_pages; + + if(pages_to_be_skipped > SKIP_PAGES_THRESHOLD) + { + backend_stat.total_pages = backend_stat.total_pages - pages_to_be_skipped; + } + + percent_complete = scanned_pages * 100 / backend_stat.total_pages; + + backend_stat.remaining_time = backend_stat.elapsed_time * + (backend_stat.total_pages - scanned_pages) / scanned_pages; + elog(LOG,"%d pages vaccumed %d scanned_pages %d total pages\n", + vacuumed_pages, scanned_pages, backend_stat.total_pages); + elog(LOG,"%f s elapsed time %f s remaining time", backend_stat.elapsed_time, + backend_stat.remaining_time); +} + +/* + * Storing VACUUM start time and total pages to be scanned + * in a global struct shared across backends. + */ +static void +initial_vacuum_stats(BlockNumber total_pages) +{ + backend_stat.total_pages= total_pages; + INSTR_TIME_SET_CURRENT(backend_stat.start_time); +} + +/* + * SQL callable function for creating a view displaying VACUUM progress + */ +PG_FUNCTION_INFO_V1(pg_get_vacuum_progress); +Datum +pg_get_vacuum_progress(PG_FUNCTION_ARGS) +{ + TupleDesc tupdesc; + Datum value[5]; + bool null[5]; + + tupdesc = CreateTemplateTupleDesc(5, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "VacuumedPages", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ScannedPages", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "TotalPages", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "ElapsedTime", + FLOAT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "RemainingTime", + FLOAT8OID, -1, 0); + + BlessTupleDesc(tupdesc); + + value[0] = UInt32GetDatum(backend_stat.vacuumed_pages); + value[1] = UInt32GetDatum(backend_stat.scanned_pages); + value[2] = UInt32GetDatum(backend_stat.total_pages); + value[3] = Float8GetDatum(backend_stat.elapsed_time); + value[4] = Float8GetDatum(backend_stat.remaining_time); + PG_RETURN_DATUM(HeapTupleGetDatum( + heap_form_tuple(tupdesc, value, null))); +} + +/* + * Module initialization function + */ +void +_PG_init(void) +{ + vacuum_stats = calculate_progress; + vacuum_stat_initial = initial_vacuum_stats; +} diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index a01cfb4..6e6bab0 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -157,7 +157,8 @@ 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); - +vacuum_stats_type vacuum_stats = NULL; +vacuum_stats_initial_type vacuum_stat_initial = NULL; /* * lazy_vacuum_rel() -- perform LAZY VACUUM for one heap relation @@ -470,8 +471,13 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, indstats = (IndexBulkDeleteResult **) palloc0(nindexes * sizeof(IndexBulkDeleteResult *)); - nblocks = RelationGetNumberOfBlocks(onerel); + + if(vacuum_stat_initial) + { + (*vacuum_stat_initial)(nblocks); + } + vacrelstats->rel_pages = nblocks; vacrelstats->scanned_pages = 0; vacrelstats->nonempty_pages = 0; @@ -520,7 +526,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, vacuum_delay_point(); } if (next_not_all_visible_block >= SKIP_PAGES_THRESHOLD) + { skipping_all_visible_blocks = true; + } else skipping_all_visible_blocks = false; @@ -559,7 +567,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, * following blocks. */ if (next_not_all_visible_block - blkno > SKIP_PAGES_THRESHOLD) + { skipping_all_visible_blocks = true; + } else skipping_all_visible_blocks = false; all_visible_according_to_vm = false; @@ -1062,6 +1072,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, */ if (vacrelstats->num_dead_tuples == prev_dead_count) RecordPageWithFreeSpace(onerel, blkno, freespace); + if(vacuum_stats) + { + (*vacuum_stats)(vacuumed_pages, (next_not_all_visible_block - blkno), vacrelstats->scanned_pages); + } } pfree(frozen); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index e3a31af..a700b94 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -192,6 +192,15 @@ extern void vacuum_delay_point(void); extern void lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, BufferAccessStrategy bstrategy); +typedef void (*vacuum_stats_type) (BlockNumber vacuumed_pages, BlockNumber pages_to_be_skipped, + BlockNumber scanned_pages); + +extern PGDLLIMPORT vacuum_stats_type vacuum_stats; + +typedef void (*vacuum_stats_initial_type) (BlockNumber nblocks); + +extern PGDLLIMPORT vacuum_stats_initial_type vacuum_stat_initial; + /* in commands/analyze.c */ extern void analyze_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params, List *va_cols, bool in_outer_xact,