From 364036ee5c0dfbb0f3a18c3b18c0a4b901c923ff Mon Sep 17 00:00:00 2001 From: Alena Rybakina Date: Tue, 11 Jun 2024 10:11:37 +0300 Subject: [PATCH 2/3] Machinery for grabbing an extended vacuum statistics on heap and index relations. Remember, statistic on heap and index relations a bit different (see ExtVacReport to find out more information). The concept of the ExtVacReport structure has been complicated to store statistic information for two kinds of relations: for heap and index relations. ExtVacReportType variable helps to determine what the kind is considering now. --- src/backend/access/heap/vacuumlazy.c | 98 ++++++++- src/backend/catalog/system_views.sql | 41 ++++ src/backend/utils/activity/pgstat.c | 45 +++- src/backend/utils/activity/pgstat_relation.c | 3 +- src/backend/utils/adt/pgstatfuncs.c | 108 +++++---- src/include/catalog/pg_proc.dat | 9 + src/include/pgstat.h | 53 ++++- .../vacuum-extending-in-repetable-read.out | 7 +- .../vacuum-extending-in-repetable-read.spec | 2 +- src/test/regress/expected/opr_sanity.out | 7 +- src/test/regress/expected/rules.out | 26 +++ .../expected/vacuum_index_statistics.out | 207 ++++++++++++++++++ .../expected/vacuum_tables_statistics.out | 3 +- src/test/regress/parallel_schedule | 1 + .../regress/sql/vacuum_index_statistics.sql | 158 +++++++++++++ 15 files changed, 684 insertions(+), 84 deletions(-) create mode 100644 src/test/regress/expected/vacuum_index_statistics.out create mode 100644 src/test/regress/sql/vacuum_index_statistics.sql diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 51459e2f1e..0da1df97ec 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -248,6 +248,13 @@ typedef struct LVExtStatCounters PgStat_Counter blocks_hit; } LVExtStatCounters; +typedef struct LVExtStatCountersIdx +{ + LVExtStatCounters common; + int64 pages_deleted; + int64 tuples_removed; +} LVExtStatCountersIdx; + /* non-export function prototypes */ static void lazy_scan_heap(LVRelState *vacrel); static bool heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno, @@ -410,6 +417,46 @@ extvac_stats_end(Relation rel, LVExtStatCounters *counters, rel->pgstat_info->counts.blocks_hit - counters->blocks_hit; } +static void +extvac_stats_start_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx *counters) +{ + 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; + } +} + +static void +extvac_stats_end_idx(Relation rel, IndexBulkDeleteResult *stats, + LVExtStatCountersIdx *counters, ExtVacReport *report) +{ + 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->index.tuples_deleted = + stats->tuples_removed - counters->tuples_removed; + report->index.pages_deleted = + stats->pages_deleted - counters->pages_deleted; + } +} + /* * heap_vacuum_rel() -- perform VACUUM for one heap relation * @@ -713,14 +760,15 @@ heap_vacuum_rel(Relation rel, VacuumParams *params, 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.pages_frozen = vacrel->set_frozen_pages; - extVacReport.pages_all_visible = vacrel->set_all_visible_pages; - extVacReport.tuples_deleted = vacrel->tuples_deleted; - extVacReport.tuples_frozen = vacrel->tuples_frozen; - extVacReport.dead_tuples = vacrel->recently_dead_tuples + vacrel->missed_dead_tuples; - extVacReport.index_vacuum_count = vacrel->num_index_scans; + extVacReport.type = PGSTAT_EXTVAC_HEAP; + extVacReport.heap.pages_scanned = vacrel->scanned_pages; + extVacReport.heap.pages_removed = vacrel->removed_pages; + extVacReport.heap.pages_frozen = vacrel->set_frozen_pages; + extVacReport.heap.pages_all_visible = vacrel->set_all_visible_pages; + extVacReport.heap.tuples_deleted = vacrel->tuples_deleted; + extVacReport.heap.tuples_frozen = vacrel->tuples_frozen; + extVacReport.heap.dead_tuples = vacrel->recently_dead_tuples + vacrel->missed_dead_tuples; + extVacReport.heap.index_vacuum_count = vacrel->num_index_scans; /* * Report results to the cumulative stats system, too. @@ -2570,6 +2618,10 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + ExtVacReport extVacReport; + + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; @@ -2588,6 +2640,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); @@ -2596,6 +2649,13 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, istat = vac_bulkdel_one_index(&ivinfo, istat, (void *) vacrel->dead_items, vacrel->dead_items_info); + /* Make extended vacuum stats report for index */ + extvac_stats_end_idx(indrel, istat, &extVacCounters, &extVacReport); + + pgstat_report_vacuum(RelationGetRelid(indrel), + indrel->rd_rel->relisshared, + 0, 0, &extVacReport); + /* Revert to the previous phase information for error traceback */ restore_vacuum_error_info(vacrel, &saved_err_info); pfree(vacrel->indname); @@ -2620,6 +2680,10 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + LVExtStatCountersIdx extVacCounters; + ExtVacReport extVacReport; + + extvac_stats_start_idx(indrel, istat, &extVacCounters); ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; @@ -2639,12 +2703,20 @@ 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); + + pgstat_report_vacuum(RelationGetRelid(indrel), + indrel->rd_rel->relisshared, + 0, 0, &extVacReport); + /* Revert to the previous phase information for error traceback */ restore_vacuum_error_info(vacrel, &saved_err_info); pfree(vacrel->indname); @@ -3255,7 +3327,7 @@ vacuum_error_callback(void *arg) { case VACUUM_ERRCB_PHASE_SCAN_HEAP: if(geterrelevel() >= ERROR) - pgstat_report_vacuum_error(errinfo->reloid); + pgstat_report_vacuum_error(errinfo->reloid, PGSTAT_EXTVAC_HEAP); if (BlockNumberIsValid(errinfo->blkno)) { if (OffsetNumberIsValid(errinfo->offnum)) @@ -3272,7 +3344,7 @@ vacuum_error_callback(void *arg) case VACUUM_ERRCB_PHASE_VACUUM_HEAP: if(geterrelevel() >= ERROR) - pgstat_report_vacuum_error(errinfo->reloid); + pgstat_report_vacuum_error(errinfo->reloid, PGSTAT_EXTVAC_HEAP); if (BlockNumberIsValid(errinfo->blkno)) { if (OffsetNumberIsValid(errinfo->offnum)) @@ -3288,16 +3360,22 @@ vacuum_error_callback(void *arg) break; case VACUUM_ERRCB_PHASE_VACUUM_INDEX: + if(geterrelevel() >= ERROR) + pgstat_report_vacuum_error(errinfo->indoid, PGSTAT_EXTVAC_INDEX); errcontext("while vacuuming index \"%s\" of relation \"%s.%s\"", errinfo->indname, errinfo->relnamespace, errinfo->relname); break; case VACUUM_ERRCB_PHASE_INDEX_CLEANUP: + if(geterrelevel() >= ERROR) + pgstat_report_vacuum_error(errinfo->indoid, PGSTAT_EXTVAC_INDEX); errcontext("while cleaning up index \"%s\" of relation \"%s.%s\"", errinfo->indname, errinfo->relnamespace, errinfo->relname); break; case VACUUM_ERRCB_PHASE_TRUNCATE: + if(geterrelevel() >= ERROR) + pgstat_report_vacuum_error(errinfo->reloid, PGSTAT_EXTVAC_HEAP); if (BlockNumberIsValid(errinfo->blkno)) errcontext("while truncating relation \"%s.%s\" to %u blocks", errinfo->relnamespace, errinfo->relname, errinfo->blkno); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 5deed61054..abe8896816 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1428,3 +1428,44 @@ WHERE db.datname = current_database() AND rel.oid = stats.relid AND ns.oid = rel.relnamespace; + +CREATE VIEW pg_stats_vacuum_indexes AS +SELECT + rel.oid as relid, + ns.nspname AS "schema", + rel.relname AS 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.system_time, + stats.user_time, + stats.total_time, + + stats.interrupts +FROM + pg_database db, + pg_class rel, + pg_namespace ns, + pg_stats_vacuum_indexes(db.oid, rel.oid) stats +WHERE + db.datname = current_database() AND + rel.oid = stats.relid AND + ns.oid = rel.relnamespace; + diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index 56f4daafbe..0efd7760bc 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -756,17 +756,33 @@ 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->pages_frozen += src->pages_frozen; - dst->pages_all_visible += src->pages_all_visible; - dst->tuples_deleted += src->tuples_deleted; - dst->tuples_frozen += src->tuples_frozen; - dst->dead_tuples += src->dead_tuples; - dst->index_vacuum_count += src->index_vacuum_count; + 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_HEAP) + { + dst->heap.pages_scanned += src->heap.pages_scanned; + dst->heap.pages_removed += src->heap.pages_removed; + dst->heap.pages_frozen += src->heap.pages_frozen; + dst->heap.pages_all_visible += src->heap.pages_all_visible; + dst->heap.tuples_deleted += src->heap.tuples_deleted; + dst->heap.tuples_frozen += src->heap.tuples_frozen; + dst->heap.dead_tuples += src->heap.dead_tuples; + dst->heap.index_vacuum_count += src->heap.index_vacuum_count; + } + else if (dst->type == PGSTAT_EXTVAC_INDEX) + { + dst->index.pages_deleted += src->index.pages_deleted; + dst->index.tuples_deleted += src->index.tuples_deleted; + } + } } /* ------------------------------------------------------------ @@ -989,7 +1005,8 @@ pgstat_update_snapshot(PgStat_Kind kind) PG_TRY(); { pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_SNAPSHOT; - pgstat_build_snapshot(PGSTAT_KIND_RELATION); + if (kind == PGSTAT_KIND_RELATION) + pgstat_build_snapshot(PGSTAT_KIND_RELATION); } PG_FINALLY(); { @@ -1044,6 +1061,10 @@ pgstat_build_snapshot(PgStat_Kind statKind) if (p->dropped) continue; + if (statKind != PGSTAT_KIND_INVALID && statKind != p->key.kind) + /* Load stat of specific type, if defined */ + continue; + Assert(pg_atomic_read_u32(&p->refcount) > 0); stats_data = dsa_get_address(pgStatLocal.dsa, p->body); diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index d40d43cdb4..5b06b04faa 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -211,7 +211,7 @@ pgstat_drop_relation(Relation rel) * --------- */ void -pgstat_report_vacuum_error(Oid tableoid) +pgstat_report_vacuum_error(Oid tableoid, ExtVacReportType m_type) { PgStat_EntryRef *entry_ref; PgStatShared_Relation *shtabentry; @@ -228,6 +228,7 @@ pgstat_report_vacuum_error(Oid tableoid) tabentry = &shtabentry->stats; tabentry->vacuum_ext.interrupts++; + tabentry->vacuum_ext.type = m_type; pgstat_unlock_entry(entry_ref); } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 3c9f0037d4..1e87e4f819 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -2035,6 +2035,8 @@ pg_stat_have_stats(PG_FUNCTION_ARGS) } #define EXTVACHEAPSTAT_COLUMNS 27 +#define EXTVACIDXSTAT_COLUMNS 19 +#define EXTVACSTAT_COLUMNS Max(EXTVACHEAPSTAT_COLUMNS, EXTVACIDXSTAT_COLUMNS) static Oid CurrentDatabaseId = InvalidOid; @@ -2078,12 +2080,12 @@ static void tuplestore_put_for_relation(Oid relid, Tuplestorestate *tupstore, TupleDesc tupdesc, PgStat_StatTabEntry *tabentry, int ncolumns) { - Datum values[EXTVACHEAPSTAT_COLUMNS]; - bool nulls[EXTVACHEAPSTAT_COLUMNS]; + Datum values[EXTVACSTAT_COLUMNS]; + bool nulls[EXTVACSTAT_COLUMNS]; char buf[256]; int i = 0; - memset(nulls, 0, EXTVACHEAPSTAT_COLUMNS * sizeof(bool)); + memset(nulls, 0, EXTVACSTAT_COLUMNS * sizeof(bool)); values[i++] = ObjectIdGetDatum(relid); @@ -2096,16 +2098,25 @@ tuplestore_put_for_relation(Oid relid, Tuplestorestate *tupstore, tabentry->vacuum_ext.blks_hit); values[i++] = Int64GetDatum(tabentry->vacuum_ext.blks_hit); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.pages_scanned); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.pages_removed); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.pages_frozen); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.pages_all_visible); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.tuples_deleted); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.tuples_frozen); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.dead_tuples); - values[i++] = Int64GetDatum(tabentry->vacuum_ext.index_vacuum_count); - values[i++] = Int64GetDatum(tabentry->rev_all_frozen_pages); - values[i++] = Int64GetDatum(tabentry->rev_all_visible_pages); + if (tabentry->vacuum_ext.type == PGSTAT_EXTVAC_HEAP) + { + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.pages_scanned); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.pages_removed); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.pages_frozen); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.pages_all_visible); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.tuples_deleted); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.tuples_frozen); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.dead_tuples); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.heap.index_vacuum_count); + values[i++] = Int64GetDatum(tabentry->rev_all_frozen_pages); + values[i++] = Int64GetDatum(tabentry->rev_all_visible_pages); + + } + else if (tabentry->vacuum_ext.type == PGSTAT_EXTVAC_INDEX) + { + values[i++] = Int64GetDatum(tabentry->vacuum_ext.index.pages_deleted); + values[i++] = Int64GetDatum(tabentry->vacuum_ext.index.tuples_deleted); + } values[i++] = Int64GetDatum(tabentry->vacuum_ext.wal_records); values[i++] = Int64GetDatum(tabentry->vacuum_ext.wal_fpi); @@ -2134,7 +2145,7 @@ tuplestore_put_for_relation(Oid relid, Tuplestorestate *tupstore, * Get the vacuum statistics for the heap tables or indexes. */ static Datum -pg_stats_vacuum(FunctionCallInfo fcinfo, int ncolumns) +pg_stats_vacuum(FunctionCallInfo fcinfo, ExtVacReportType type, int ncolumns) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; @@ -2142,7 +2153,6 @@ pg_stats_vacuum(FunctionCallInfo fcinfo, int ncolumns) Tuplestorestate *tupstore; TupleDesc tupdesc; Oid dbid = PG_GETARG_OID(0); - Oid relid = PG_GETARG_OID(1); PgStat_StatTabEntry *tabentry; InitMaterializedSRF(fcinfo, 0); @@ -2169,40 +2179,45 @@ pg_stats_vacuum(FunctionCallInfo fcinfo, int ncolumns) MemoryContextSwitchTo(oldcontext); - /* Load table statistics for specified database. */ - if (OidIsValid(relid)) + if (type == PGSTAT_EXTVAC_INDEX || type == PGSTAT_EXTVAC_HEAP) { - tabentry = fetch_dbstat_tabentry(dbid, relid); - if (tabentry == NULL) - /* Table don't exists or isn't an heap relation. */ - PG_RETURN_NULL(); + Oid relid = PG_GETARG_OID(1); - tuplestore_put_for_relation(relid, tupstore, tupdesc, tabentry, ncolumns); - } - else - { - SnapshotIterator hashiter; - PgStat_SnapshotEntry *entry; - Oid storedMyDatabaseId = MyDatabaseId; + /* Load table statistics for specified database. */ + if (OidIsValid(relid)) + { + tabentry = fetch_dbstat_tabentry(dbid, relid); + if (tabentry == NULL || tabentry->vacuum_ext.type != type) + /* Table don't exists or isn't an heap relation. */ + PG_RETURN_NULL(); - pgstat_update_snapshot(PGSTAT_KIND_RELATION); - MyDatabaseId = storedMyDatabaseId; + tuplestore_put_for_relation(relid, tupstore, tupdesc, tabentry, ncolumns); + } + else + { + SnapshotIterator hashiter; + PgStat_SnapshotEntry *entry; + Oid storedMyDatabaseId = MyDatabaseId; + pgstat_update_snapshot(PGSTAT_KIND_RELATION); + MyDatabaseId = storedMyDatabaseId; - /* Iterate the snapshot */ - InitSnapshotIterator(pgStatLocal.snapshot.stats, &hashiter); - while ((entry = ScanStatSnapshot(pgStatLocal.snapshot.stats, &hashiter)) != NULL) - { - Oid reloid; + /* Iterate the snapshot */ + InitSnapshotIterator(pgStatLocal.snapshot.stats, &hashiter); + + while ((entry = ScanStatSnapshot(pgStatLocal.snapshot.stats, &hashiter)) != NULL) + { + Oid reloid; - CHECK_FOR_INTERRUPTS(); + CHECK_FOR_INTERRUPTS(); - tabentry = (PgStat_StatTabEntry *) entry->data; - reloid = entry->key.objoid; + tabentry = (PgStat_StatTabEntry *) entry->data; + reloid = entry->key.objoid; - if (tabentry != NULL) - tuplestore_put_for_relation(reloid, tupstore, tupdesc, tabentry, ncolumns); + if (tabentry != NULL && tabentry->vacuum_ext.type == type) + tuplestore_put_for_relation(reloid, tupstore, tupdesc, tabentry, ncolumns); + } } } PG_RETURN_NULL(); @@ -2214,7 +2229,18 @@ pg_stats_vacuum(FunctionCallInfo fcinfo, int ncolumns) Datum pg_stats_vacuum_tables(PG_FUNCTION_ARGS) { - return pg_stats_vacuum(fcinfo, EXTVACHEAPSTAT_COLUMNS); + return pg_stats_vacuum(fcinfo, PGSTAT_EXTVAC_HEAP, EXTVACHEAPSTAT_COLUMNS); + + PG_RETURN_NULL(); +} + +/* + * Get the vacuum statistics for the indexes. + */ +Datum +pg_stats_vacuum_indexes(PG_FUNCTION_ARGS) +{ + return pg_stats_vacuum(fcinfo, PGSTAT_EXTVAC_INDEX, EXTVACIDXSTAT_COLUMNS); PG_RETURN_NULL(); } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6ccd350213..366175ab43 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12193,4 +12193,13 @@ proargmodes => '{i,i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', proargnames => '{dboid,reloid,relid,total_blks_read,total_blks_hit,total_blks_dirtied,total_blks_written,rel_blks_read,rel_blks_hit,pages_scanned,pages_removed,pages_frozen,pages_all_visible,tuples_deleted,tuples_frozen,dead_tuples,index_vacuum_count,rev_all_frozen_pages,rev_all_visible_pages,wal_records,wal_fpi,wal_bytes,blk_read_time,blk_write_time,delay_time,system_time,user_time,total_time,interrupts}', prosrc => 'pg_stats_vacuum_tables' }, +{ oid => '4702', + descr => 'pg_stats_vacuum_indexes return stats values', + proname => 'pg_stats_vacuum_indexes', provolatile => 's', prorettype => 'record',proisstrict => 'f', + proretset => 't', + proargtypes => 'oid oid', + proallargtypes => '{oid,oid,oid,int8,int8,int8,int8,int8,int8,int8,int8,int8,int8,numeric,float8,float8,float8,float8,float8,float8,int4}', + proargmodes => '{i,i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{dboid,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,system_time,user_time,total_time,interrupts}', + prosrc => 'pg_stats_vacuum_indexes' } ] diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 65b030b336..2e22d69fff 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -137,11 +137,20 @@ typedef struct PgStat_BackendSubEntry PgStat_Counter sync_error_count; } PgStat_BackendSubEntry; + +/* Type of ExtVacReport */ +typedef enum ExtVacReportType +{ + PGSTAT_EXTVAC_INVALID = 0, + PGSTAT_EXTVAC_HEAP = 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 @@ -173,14 +182,38 @@ typedef struct ExtVacReport /* Interruptions on any errors. */ int32 interrupts; - int64 pages_scanned; /* number of pages we examined */ - int64 pages_removed; /* number of pages removed by vacuum */ - int64 pages_frozen; /* number of pages marked in VM as frozen */ - int64 pages_all_visible; /* number of pages marked in VM as all-visible */ - int64 tuples_deleted; /* tuples deleted by vacuum */ - int64 tuples_frozen; /* tuples frozen up by vacuum */ - int64 dead_tuples; /* number of deleted tuples which vacuum cannot clean up by vacuum operation */ - int64 index_vacuum_count; /* number of index vacuumings */ + 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; /* number of pages we examined */ + int64 pages_removed; /* number of pages removed by vacuum */ + int64 pages_frozen; /* number of pages marked in VM as frozen */ + int64 pages_all_visible; /* number of pages marked in VM as all-visible */ + int64 tuples_deleted; /* tuples deleted by vacuum */ + int64 tuples_frozen; /* tuples frozen up by vacuum */ + int64 dead_tuples; /* number of deleted tuples which vacuum cannot clean up by vacuum operation */ + int64 index_vacuum_count; /* number of index vacuumings */ + } heap; + struct + { + int64 pages_deleted; /* number of pages deleted by vacuum */ + int64 tuples_deleted; /* tuples deleted by vacuum */ + } index; + } /* per_type_stats */; } ExtVacReport; /* ---------- @@ -659,7 +692,7 @@ extern void pgstat_report_vacuum(Oid tableoid, bool shared, extern void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter); -extern void pgstat_report_vacuum_error(Oid tableoid); +extern void pgstat_report_vacuum_error(Oid tableoid, ExtVacReportType m_type); /* * If stats are enabled, but pending data hasn't been prepared yet, call 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 eafe1a1461..b1a9cb90bc 100644 --- a/src/test/isolation/expected/vacuum-extending-in-repetable-read.out +++ b/src/test/isolation/expected/vacuum-extending-in-repetable-read.out @@ -9,10 +9,9 @@ step s2_print_vacuum_stats_table: FROM pg_stats_vacuum_tables vt, pg_class c WHERE vt.relname = 'test_vacuum_stat_isolation' AND vt.relid = c.oid; -relname |tuples_deleted|dead_tuples|tuples_frozen ---------------------------+--------------+-----------+------------- -test_vacuum_stat_isolation| 0| 0| 0 -(1 row) +relname|tuples_deleted|dead_tuples|tuples_frozen +-------+--------------+-----------+------------- +(0 rows) step s1_begin_repeatable_read: BEGIN transaction ISOLATION LEVEL REPEATABLE READ; diff --git a/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec b/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec index 655051b512..2fb6bf5b22 100644 --- a/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec +++ b/src/test/isolation/specs/vacuum-extending-in-repetable-read.spec @@ -48,4 +48,4 @@ permutation s1_commit s2_checkpoint s2_vacuum - s2_print_vacuum_stats_table + s2_print_vacuum_stats_table \ No newline at end of file diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 72f677ab40..95dd4c4fc1 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -32,10 +32,11 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR prokind NOT IN ('f', 'a', 'w', 'p') OR provolatile NOT IN ('i', 's', 'v') OR proparallel NOT IN ('s', 'r', 'u'); - oid | proname -------+------------------------ + oid | proname +------+------------------------- 4701 | pg_stats_vacuum_tables -(1 row) + 4702 | pg_stats_vacuum_indexes +(2 rows) -- prosrc should never be null; it can be empty only if prosqlbody isn't null SELECT p1.oid, p1.proname diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 201b2bc1b7..9c49355dcb 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2606,6 +2606,32 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname, JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr, unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL))) WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); +pg_stats_vacuum_indexes| SELECT rel.oid AS relid, + ns.nspname AS schema, + 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.system_time, + stats.user_time, + stats.total_time, + stats.interrupts + FROM pg_database db, + pg_class rel, + pg_namespace ns, + LATERAL pg_stats_vacuum_indexes(db.oid, 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, system_time, user_time, total_time, interrupts) + WHERE ((db.datname = current_database()) AND (rel.oid = stats.relid) AND (ns.oid = rel.relnamespace)); pg_stats_vacuum_tables| SELECT rel.oid AS relid, ns.nspname AS schema, rel.relname, diff --git a/src/test/regress/expected/vacuum_index_statistics.out b/src/test/regress/expected/vacuum_index_statistics.out new file mode 100644 index 0000000000..af7c194ae4 --- /dev/null +++ b/src/test/regress/expected/vacuum_index_statistics.out @@ -0,0 +1,207 @@ +-- +-- Test cumulative vacuum stats system +-- +-- Check the wall statistics collected during vacuum operation: +-- number of frozen and visible pages set by vacuum; +-- number of frozen and visible pages removed by backend. +-- Statistic wal_fpi is not displayed in this test because its behavior is unstable. +-- +-- conditio sine qua non +SHOW track_counts; -- must be on + track_counts +-------------- + on +(1 row) + +-- not enabled by default, but we want to test it... +SET track_functions TO 'all'; +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- + +(1 row) + +\set sample_size 10000 +SET vacuum_freeze_min_age = 0; +SET vacuum_freeze_table_age = 0; +--SET stats_fetch_consistency = snapshot; +CREATE TABLE vestat (x int primary key) WITH (autovacuum_enabled = off, fillfactor = 10); +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +ANALYZE vestat; +SELECT oid AS ioid from pg_class where relname = 'vestat_pkey' \gset +DELETE FROM vestat WHERE x % 2 = 0; +-- Before the first vacuum execution extended stats view is empty. +SELECT vt.relname,relpages,pages_deleted,tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + relname | relpages | pages_deleted | tuples_deleted +---------+----------+---------------+---------------- +(0 rows) + +SELECT relpages AS irp +FROM pg_class c +WHERE relname = 'vestat_pkey' \gset +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; +-- The table and index extended vacuum statistics should show us that +-- vacuum frozed pages and clean up pages, but pages_removed stayed the same +-- because of not full table have cleaned up +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted = 0 AS pages_deleted,tuples_deleted > 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + relname | relpages | pages_deleted | tuples_deleted +-------------+----------+---------------+---------------- + vestat_pkey | t | t | t +(1 row) + +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +-- Look into WAL records deltas. +SELECT wal_records > 0 AS diWR, wal_bytes > 0 AS diWB +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey'; + diwr | diwb +------+------ + t | t +(1 row) + +DELETE FROM vestat;; +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; +-- pages_removed must be increased +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted-:ipd > 0 AS pages_deleted,tuples_deleted-:itd > 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + relname | relpages | pages_deleted | tuples_deleted +-------------+----------+---------------+---------------- + vestat_pkey | t | t | t +(1 row) + +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset +-- Store WAL advances into variables +SELECT wal_records-:iwr AS diwr, wal_bytes-:iwb AS diwb, wal_fpi-:ifpi AS difpi +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +-- WAL advance should be detected. +SELECT :diwr > 0 AS diWR, :diwb > 0 AS diWB; + diwr | diwb +------+------ + t | t +(1 row) + +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +DELETE FROM vestat WHERE x % 2 = 0; +-- VACUUM FULL doesn't report to stat collector. So, no any advancements of statistics +-- are detected here. +VACUUM FULL vestat; +-- It is necessary to check the wal statistics +CHECKPOINT; +-- Store WAL advances into variables +SELECT wal_records-:iwr AS diwr2, wal_bytes-:iwb AS diwb2, wal_fpi-:ifpi AS difpi2 +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +-- WAL and other statistics advance should not be detected. +SELECT :diwr2=0 AS diWR, :difpi2=0 AS iFPI, :diwb2=0 AS diWB; + diwr | ifpi | diwb +------+------+------ + t | t | t +(1 row) + +SELECT vt.relname,relpages-:irp < 0 AS relpages,pages_deleted-:ipd = 0 AS pages_deleted,tuples_deleted-:itd = 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + relname | relpages | pages_deleted | tuples_deleted +-------------+----------+---------------+---------------- + vestat_pkey | t | t | t +(1 row) + +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +DELETE FROM vestat; +TRUNCATE vestat; +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; +-- Store WAL advances into variables after removing all tuples from the table +SELECT wal_records-:iwr AS diwr3, wal_bytes-:iwb AS diwb3, wal_fpi-:ifpi AS difpi3 +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset +--There are nothing changed +SELECT :diwr3=0 AS diWR, :difpi3=0 AS iFPI, :diwb3=0 AS diWB; + diwr | ifpi | diwb +------+------+------ + t | t | t +(1 row) + +-- +-- Now, the table and index is compressed into zero number of pages. Check it +-- in vacuum extended statistics. +-- The pages_frozen, pages_scanned values shouldn't be changed +-- +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted-:ipd = 0 AS pages_deleted,tuples_deleted-:itd = 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + relname | relpages | pages_deleted | tuples_deleted +-------------+----------+---------------+---------------- + vestat_pkey | f | t | t +(1 row) + +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + pg_stat_force_next_flush +-------------------------- + +(1 row) + +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +ANALYZE vestat; +-- must be empty +SELECT pages_frozen, pages_all_visible, rev_all_frozen_pages,rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + pages_frozen | pages_all_visible | rev_all_frozen_pages | rev_all_visible_pages +--------------+-------------------+----------------------+----------------------- + 910 | 910 | 455 | 455 +(1 row) + +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; +-- vacuum should freezed pages, but there is nothing to defreeze +SELECT pages_frozen > 0 AS pages_frozen,pages_all_visible > 0 AS pages_all_visible,rev_all_frozen_pages = 0 AS rev_all_frozen_pages,rev_all_visible_pages = 0 AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + pages_frozen | pages_all_visible | rev_all_frozen_pages | rev_all_visible_pages +--------------+-------------------+----------------------+----------------------- + t | t | f | f +(1 row) + +SELECT pages_frozen AS pf, pages_all_visible AS pv, rev_all_frozen_pages AS hafp,rev_all_visible_pages AS havp +FROM pg_stats_vacuum_tables WHERE relname = 'vestat' \gset +UPDATE vestat SET x = x+1001; +ERROR: duplicate key value violates unique constraint "vestat_pkey" +DETAIL: Key (x)=(1002) already exists. +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; +SELECT pages_frozen > :pf AS pages_frozen,pages_all_visible > :pv AS pages_all_visible,rev_all_frozen_pages > :hafp AS rev_all_frozen_pages,rev_all_visible_pages > :havp AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + pages_frozen | pages_all_visible | rev_all_frozen_pages | rev_all_visible_pages +--------------+-------------------+----------------------+----------------------- + f | f | f | f +(1 row) + +SELECT pages_frozen AS pf, pages_all_visible AS pv, rev_all_frozen_pages AS hafp,rev_all_visible_pages AS havp +FROM pg_stats_vacuum_tables WHERE relname = 'vestat' \gset +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; +SELECT pages_frozen = :pf AS pages_frozen,pages_all_visible = :pv AS pages_all_visible,rev_all_frozen_pages = :hafp AS rev_all_frozen_pages,rev_all_visible_pages = :havp AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + pages_frozen | pages_all_visible | rev_all_frozen_pages | rev_all_visible_pages +--------------+-------------------+----------------------+----------------------- + t | t | t | t +(1 row) + +DROP TABLE vestat; diff --git a/src/test/regress/expected/vacuum_tables_statistics.out b/src/test/regress/expected/vacuum_tables_statistics.out index 536441b36e..6f0d3b10d0 100644 --- a/src/test/regress/expected/vacuum_tables_statistics.out +++ b/src/test/regress/expected/vacuum_tables_statistics.out @@ -40,8 +40,7 @@ FROM pg_stats_vacuum_tables vt, pg_class c WHERE vt.relname = 'vestat' AND vt.relid = c.oid; relname | pages_frozen | tuples_deleted | relpages | pages_scanned | pages_removed ---------+--------------+----------------+----------+---------------+--------------- - vestat | 0 | 0 | 455 | 0 | 0 -(1 row) +(0 rows) SELECT relpages AS rp FROM pg_class c diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 45e20d30ab..66bf65228f 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -140,4 +140,5 @@ test: tablespace # ---------- # Check vacuum statistics # ---------- +test: vacuum_index_statistics test: vacuum_tables_statistics \ No newline at end of file diff --git a/src/test/regress/sql/vacuum_index_statistics.sql b/src/test/regress/sql/vacuum_index_statistics.sql new file mode 100644 index 0000000000..2ccbf53346 --- /dev/null +++ b/src/test/regress/sql/vacuum_index_statistics.sql @@ -0,0 +1,158 @@ +-- +-- Test cumulative vacuum stats system +-- +-- Check the wall statistics collected during vacuum operation: +-- number of frozen and visible pages set by vacuum; +-- number of frozen and visible pages removed by backend. +-- Statistic wal_fpi is not displayed in this test because its behavior is unstable. +-- +-- conditio sine qua non +SHOW track_counts; -- must be on +-- not enabled by default, but we want to test it... +SET track_functions TO 'all'; + + +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); + +\set sample_size 10000 +SET vacuum_freeze_min_age = 0; +SET vacuum_freeze_table_age = 0; +--SET stats_fetch_consistency = snapshot; +CREATE TABLE vestat (x int primary key) WITH (autovacuum_enabled = off, fillfactor = 10); +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +ANALYZE vestat; + +SELECT oid AS ioid from pg_class where relname = 'vestat_pkey' \gset + +DELETE FROM vestat WHERE x % 2 = 0; +-- Before the first vacuum execution extended stats view is empty. +SELECT vt.relname,relpages,pages_deleted,tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; +SELECT relpages AS irp +FROM pg_class c +WHERE relname = 'vestat_pkey' \gset + +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; + +-- The table and index extended vacuum statistics should show us that +-- vacuum frozed pages and clean up pages, but pages_removed stayed the same +-- because of not full table have cleaned up +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted = 0 AS pages_deleted,tuples_deleted > 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset + +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +-- Look into WAL records deltas. +SELECT wal_records > 0 AS diWR, wal_bytes > 0 AS diWB +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey'; + +DELETE FROM vestat;; +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; + +-- pages_removed must be increased +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted-:ipd > 0 AS pages_deleted,tuples_deleted-:itd > 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset + +-- Store WAL advances into variables +SELECT wal_records-:iwr AS diwr, wal_bytes-:iwb AS diwb, wal_fpi-:ifpi AS difpi +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +-- WAL advance should be detected. +SELECT :diwr > 0 AS diWR, :diwb > 0 AS diWB; + +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +DELETE FROM vestat WHERE x % 2 = 0; +-- VACUUM FULL doesn't report to stat collector. So, no any advancements of statistics +-- are detected here. +VACUUM FULL vestat; +-- It is necessary to check the wal statistics +CHECKPOINT; + +-- Store WAL advances into variables +SELECT wal_records-:iwr AS diwr2, wal_bytes-:iwb AS diwb2, wal_fpi-:ifpi AS difpi2 +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +-- WAL and other statistics advance should not be detected. +SELECT :diwr2=0 AS diWR, :difpi2=0 AS iFPI, :diwb2=0 AS diWB; + +SELECT vt.relname,relpages-:irp < 0 AS relpages,pages_deleted-:ipd = 0 AS pages_deleted,tuples_deleted-:itd = 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; +SELECT vt.relname,relpages AS irp,pages_deleted AS ipd,tuples_deleted AS itd +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid \gset + +-- Store WAL advances into variables +SELECT wal_records AS iwr,wal_bytes AS iwb,wal_fpi AS ifpi FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +DELETE FROM vestat; +TRUNCATE vestat; +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128, INDEX_CLEANUP ON) vestat; +-- it is necessary to check the wal statistics +CHECKPOINT; + +-- Store WAL advances into variables after removing all tuples from the table +SELECT wal_records-:iwr AS diwr3, wal_bytes-:iwb AS diwb3, wal_fpi-:ifpi AS difpi3 +FROM pg_stats_vacuum_indexes WHERE relname = 'vestat_pkey' \gset + +--There are nothing changed +SELECT :diwr3=0 AS diWR, :difpi3=0 AS iFPI, :diwb3=0 AS diWB; + +-- +-- Now, the table and index is compressed into zero number of pages. Check it +-- in vacuum extended statistics. +-- The pages_frozen, pages_scanned values shouldn't be changed +-- +SELECT vt.relname,relpages-:irp = 0 AS relpages,pages_deleted-:ipd = 0 AS pages_deleted,tuples_deleted-:itd = 0 AS tuples_deleted +FROM pg_stats_vacuum_indexes vt, pg_class c +WHERE vt.relname = 'vestat_pkey' AND vt.relid = c.oid; + +-- ensure pending stats are flushed +SELECT pg_stat_force_next_flush(); +INSERT INTO vestat SELECT x FROM generate_series(1,:sample_size) as x; +ANALYZE vestat; + +-- must be empty +SELECT pages_frozen, pages_all_visible, rev_all_frozen_pages,rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; + +-- vacuum should freezed pages, but there is nothing to defreeze +SELECT pages_frozen > 0 AS pages_frozen,pages_all_visible > 0 AS pages_all_visible,rev_all_frozen_pages = 0 AS rev_all_frozen_pages,rev_all_visible_pages = 0 AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; +SELECT pages_frozen AS pf, pages_all_visible AS pv, rev_all_frozen_pages AS hafp,rev_all_visible_pages AS havp +FROM pg_stats_vacuum_tables WHERE relname = 'vestat' \gset + +UPDATE vestat SET x = x+1001; +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; + +SELECT pages_frozen > :pf AS pages_frozen,pages_all_visible > :pv AS pages_all_visible,rev_all_frozen_pages > :hafp AS rev_all_frozen_pages,rev_all_visible_pages > :havp AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; +SELECT pages_frozen AS pf, pages_all_visible AS pv, rev_all_frozen_pages AS hafp,rev_all_visible_pages AS havp +FROM pg_stats_vacuum_tables WHERE relname = 'vestat' \gset + +VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) vestat; + +SELECT pages_frozen = :pf AS pages_frozen,pages_all_visible = :pv AS pages_all_visible,rev_all_frozen_pages = :hafp AS rev_all_frozen_pages,rev_all_visible_pages = :havp AS rev_all_visible_pages +FROM pg_stats_vacuum_tables WHERE relname = 'vestat'; + +DROP TABLE vestat; \ No newline at end of file -- 2.34.1