Instrumented pages/tuples frozen in autovacuum's server log out (and VACUUM VERBOSE)
Attached is another autovacuum (and VACUUM VERBOSE) instrumentation
patch. This one adds instrumentation about freezing to the report
autovacuum makes to the server log. Specifically, it makes the output
look like this:
regression=# vacuum (freeze,verbose) foo;
INFO: aggressively vacuuming "regression.public.foo"
INFO: finished vacuuming "regression.public.foo": index scans: 0
pages: 0 removed, 45 remain, 45 scanned (100.00% of total)
tuples: 0 removed, 10000 remain, 0 are dead but not yet removable
removable cutoff: 751, which was 0 XIDs old when operation ended
new relfrozenxid: 751, which is 2 XIDs ahead of previous value
XIDs processed: 45 pages from table (100.00% of total) had 10000 tuples frozen
index scan not needed: 0 pages from table (0.00% of total) had 0 dead
item identifiers removed
I/O timings: read: 0.023 ms, write: 0.000 ms
avg read rate: 2.829 MB/s, avg write rate: 5.658 MB/s
buffer usage: 95 hits, 2 misses, 4 dirtied
WAL usage: 91 records, 1 full page images, 133380 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
VACUUM
Notice the new line about freezing, which we always output -- it's the
line that begins with "XIDs processed", that appears about half way
down. The new line is deliberately placed after the existing "new
relfrozenxid" line and before the existing line about dead item
identifiers. This placement of the new instrumentation seems logical
to me; freezing is related to relfrozenxid (obviously), but doesn't
need to be shoehorned into the prominent early line that reports on
tuples removed/remain[ing].
Like its neighboring "dead item identifier" line, this new line shows
the number of items/tuples affected, and the number of heap pages
affected -- with heap pages shown both as an absolute number and as a
percentage of rel_pages (in parentheses). The main cost associated
with freezing is the WAL overhead, so emphasizing pages here seems
like the way to go -- pages are more interesting than tuples. This
format also makes it relatively easy to get a sense of the *relative*
costs of the overhead of each distinct class/category of maintenance
performed.
--
Peter Geoghegan
Attachments:
v1-0001-Add-freezing-instrumentation-to-VACUUM-VERBOSE.patchapplication/octet-stream; name=v1-0001-Add-freezing-instrumentation-to-VACUUM-VERBOSE.patchDownload
From 17df21554292428a539dade5e198eedbb7d753e9 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Sat, 20 Aug 2022 14:59:18 -0700
Subject: [PATCH v1] Add freezing instrumentation to VACUUM VERBOSE.
---
src/backend/access/heap/vacuumlazy.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index b802ed247..c213bf4c7 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -198,6 +198,7 @@ typedef struct LVRelState
BlockNumber rel_pages; /* total number of pages */
BlockNumber scanned_pages; /* # pages examined (not skipped via VM) */
BlockNumber removed_pages; /* # pages removed by relation truncation */
+ BlockNumber frozen_pages; /* # pages with newly frozen tuples */
BlockNumber lpdead_item_pages; /* # pages with LP_DEAD items */
BlockNumber missed_dead_pages; /* # pages with missed dead tuples */
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
@@ -212,6 +213,7 @@ typedef struct LVRelState
int num_index_scans;
/* Counters that follow are only for scanned_pages */
int64 tuples_deleted; /* # deleted from table */
+ int64 tuples_frozen; /* # newly frozen */
int64 lpdead_items; /* # deleted from indexes */
int64 live_tuples; /* # live tuples remaining */
int64 recently_dead_tuples; /* # dead, but not yet removable */
@@ -470,6 +472,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/* Initialize page counters explicitly (be tidy) */
vacrel->scanned_pages = 0;
vacrel->removed_pages = 0;
+ vacrel->frozen_pages = 0;
vacrel->lpdead_item_pages = 0;
vacrel->missed_dead_pages = 0;
vacrel->nonempty_pages = 0;
@@ -484,6 +487,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/* Initialize remaining counters (be tidy) */
vacrel->num_index_scans = 0;
vacrel->tuples_deleted = 0;
+ vacrel->tuples_frozen = 0;
vacrel->lpdead_items = 0;
vacrel->live_tuples = 0;
vacrel->recently_dead_tuples = 0;
@@ -721,6 +725,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
_("new relminmxid: %u, which is %d MXIDs ahead of previous value\n"),
vacrel->NewRelminMxid, diff);
}
+ appendStringInfo(&buf, _("XIDs processed: %u pages from table (%.2f%% of total) had %lld tuples frozen\n"),
+ vacrel->frozen_pages,
+ orig_rel_pages == 0 ? 100.0 :
+ 100.0 * vacrel->frozen_pages / orig_rel_pages,
+ (long long) vacrel->tuples_frozen);
if (vacrel->do_index_vacuuming)
{
if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
@@ -1549,11 +1558,11 @@ lazy_scan_prune(LVRelState *vacrel,
HeapTupleData tuple;
HTSV_Result res;
int tuples_deleted,
+ tuples_frozen,
lpdead_items,
live_tuples,
recently_dead_tuples;
int nnewlpdead;
- int nfrozen;
TransactionId NewRelfrozenXid;
MultiXactId NewRelminMxid;
OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
@@ -1574,6 +1583,7 @@ retry:
NewRelfrozenXid = vacrel->NewRelfrozenXid;
NewRelminMxid = vacrel->NewRelminMxid;
tuples_deleted = 0;
+ tuples_frozen = 0;
lpdead_items = 0;
live_tuples = 0;
recently_dead_tuples = 0;
@@ -1600,7 +1610,6 @@ retry:
prunestate->all_visible = true;
prunestate->all_frozen = true;
prunestate->visibility_cutoff_xid = InvalidTransactionId;
- nfrozen = 0;
for (offnum = FirstOffsetNumber;
offnum <= maxoff;
@@ -1779,11 +1788,11 @@ retry:
vacrel->relminmxid,
vacrel->FreezeLimit,
vacrel->MultiXactCutoff,
- &frozen[nfrozen], &tuple_totally_frozen,
+ &frozen[tuples_frozen], &tuple_totally_frozen,
&NewRelfrozenXid, &NewRelminMxid))
{
/* Will execute freeze below */
- frozen[nfrozen++].offset = offnum;
+ frozen[tuples_frozen++].offset = offnum;
}
/*
@@ -1809,10 +1818,12 @@ retry:
* Consider the need to freeze any items with tuple storage from the page
* first (arbitrary)
*/
- if (nfrozen > 0)
+ if (tuples_frozen > 0)
{
Assert(prunestate->hastup);
+ vacrel->frozen_pages++;
+
/*
* At least one tuple with storage needs to be frozen -- execute that
* now.
@@ -1826,7 +1837,7 @@ retry:
MarkBufferDirty(buf);
/* execute collected freezes */
- for (int i = 0; i < nfrozen; i++)
+ for (int i = 0; i < tuples_frozen; i++)
{
HeapTupleHeader htup;
@@ -1842,7 +1853,7 @@ retry:
XLogRecPtr recptr;
recptr = log_heap_freeze(vacrel->rel, buf, vacrel->FreezeLimit,
- frozen, nfrozen);
+ frozen, tuples_frozen);
PageSetLSN(page, recptr);
}
@@ -1914,6 +1925,7 @@ retry:
/* Finally, add page-local counts to whole-VACUUM counts */
vacrel->tuples_deleted += tuples_deleted;
+ vacrel->tuples_frozen += tuples_frozen;
vacrel->lpdead_items += lpdead_items;
vacrel->live_tuples += live_tuples;
vacrel->recently_dead_tuples += recently_dead_tuples;
--
2.34.1
On Sat, Aug 20, 2022 at 7:29 PM Peter Geoghegan <pg@bowt.ie> wrote:
XIDs processed: 45 pages from table (100.00% of total) had 10000 tuples
frozen
I like this addition, but I would also like to see how many pages got newly
set to all frozen by the vacuum. Would that be a convenient thing to also
report here?
Also, isn't all of vacuuming about XID processing? I think "frozen:" would
be a more suitable line prefix.
Cheers,
Jeff
On Wed, Aug 31, 2022 at 7:49 PM Jeff Janes <jeff.janes@gmail.com> wrote:
I like this addition, but I would also like to see how many pages got newly set to all frozen by the vacuum.
I'd say that that's independent work. Though I'm happy to discuss it now.
It would be fairly straightforward to show something about the VM
itself, but it's not entirely clear what aspects of the VM should be
emphasized. Are we reporting on the state of the table, or work
performed by VACUUM? You said you were interested in the latter
already, but why prefer that over a summary of the contents of the VM
at the end of the VACUUM? Are you concerned about the cost of setting
pages all-visible? Do you have an interest in how VACUUM manages to
set VM pages over time? Something else?
We already call visibilitymap_count() at the end of every VACUUM,
which scans the authoritative VM to produce a more-or-less consistent
summary of the VM at that point in time. This information is then used
to update pg_class.relallvisible (we don't do anything with the
all-frozen number at all). Why not show that information in
VERBOSE/autovacuum's log message? Does it really matter *when* a page
became all-visible/all-frozen/unset?
Also, isn't all of vacuuming about XID processing? I think "frozen:" would be a more suitable line prefix.
That also works for me. I have no strong feelings here.
--
Peter Geoghegan
On Wed, Aug 31, 2022 at 7:49 PM Jeff Janes <jeff.janes@gmail.com> wrote:
I think "frozen:" would be a more suitable line prefix.
Attached revision does it that way.
Barring any objections I will commit this patch within the next few days.
Thanks
--
Peter Geoghegan
Attachments:
v2-0001-Instrument-freezing-in-autovacuum-log-reports.patchapplication/octet-stream; name=v2-0001-Instrument-freezing-in-autovacuum-log-reports.patchDownload
From 18ef5627b975271dada524e1273f9a7e4e1566e5 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Sat, 20 Aug 2022 14:59:18 -0700
Subject: [PATCH v2] Instrument freezing in autovacuum log reports.
Add a new line to the the log reports produced by autovacuum (and VACUUM
VERBOSE) that describes how many pages had one or more tuples frozen,
and the number of tuples frozen in total.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Jeff Janes <jeff.janes@gmail.com>
Discussion: https://postgr.es/m/CAH2-WznTY6D0zyE8VLrC6Gd4kh_HGAXxnTPtcOQOOsxzLx9zog@mail.gmail.com
---
src/backend/access/heap/vacuumlazy.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 21eaf1d8c..dfbe37472 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -198,6 +198,7 @@ typedef struct LVRelState
BlockNumber rel_pages; /* total number of pages */
BlockNumber scanned_pages; /* # pages examined (not skipped via VM) */
BlockNumber removed_pages; /* # pages removed by relation truncation */
+ BlockNumber frozen_pages; /* # pages with newly frozen tuples */
BlockNumber lpdead_item_pages; /* # pages with LP_DEAD items */
BlockNumber missed_dead_pages; /* # pages with missed dead tuples */
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
@@ -212,6 +213,7 @@ typedef struct LVRelState
int num_index_scans;
/* Counters that follow are only for scanned_pages */
int64 tuples_deleted; /* # deleted from table */
+ int64 tuples_frozen; /* # newly frozen */
int64 lpdead_items; /* # deleted from indexes */
int64 live_tuples; /* # live tuples remaining */
int64 recently_dead_tuples; /* # dead, but not yet removable */
@@ -470,6 +472,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/* Initialize page counters explicitly (be tidy) */
vacrel->scanned_pages = 0;
vacrel->removed_pages = 0;
+ vacrel->frozen_pages = 0;
vacrel->lpdead_item_pages = 0;
vacrel->missed_dead_pages = 0;
vacrel->nonempty_pages = 0;
@@ -484,6 +487,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/* Initialize remaining counters (be tidy) */
vacrel->num_index_scans = 0;
vacrel->tuples_deleted = 0;
+ vacrel->tuples_frozen = 0;
vacrel->lpdead_items = 0;
vacrel->live_tuples = 0;
vacrel->recently_dead_tuples = 0;
@@ -721,6 +725,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
_("new relminmxid: %u, which is %d MXIDs ahead of previous value\n"),
vacrel->NewRelminMxid, diff);
}
+ appendStringInfo(&buf, _("frozen: %u pages from table (%.2f%% of total) had %lld tuples frozen\n"),
+ vacrel->frozen_pages,
+ orig_rel_pages == 0 ? 100.0 :
+ 100.0 * vacrel->frozen_pages / orig_rel_pages,
+ (long long) vacrel->tuples_frozen);
if (vacrel->do_index_vacuuming)
{
if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
@@ -1549,11 +1558,11 @@ lazy_scan_prune(LVRelState *vacrel,
HeapTupleData tuple;
HTSV_Result res;
int tuples_deleted,
+ tuples_frozen,
lpdead_items,
live_tuples,
recently_dead_tuples;
int nnewlpdead;
- int nfrozen;
TransactionId NewRelfrozenXid;
MultiXactId NewRelminMxid;
OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
@@ -1574,6 +1583,7 @@ retry:
NewRelfrozenXid = vacrel->NewRelfrozenXid;
NewRelminMxid = vacrel->NewRelminMxid;
tuples_deleted = 0;
+ tuples_frozen = 0;
lpdead_items = 0;
live_tuples = 0;
recently_dead_tuples = 0;
@@ -1600,7 +1610,6 @@ retry:
prunestate->all_visible = true;
prunestate->all_frozen = true;
prunestate->visibility_cutoff_xid = InvalidTransactionId;
- nfrozen = 0;
for (offnum = FirstOffsetNumber;
offnum <= maxoff;
@@ -1779,11 +1788,12 @@ retry:
vacrel->relminmxid,
vacrel->FreezeLimit,
vacrel->MultiXactCutoff,
- &frozen[nfrozen], &tuple_totally_frozen,
+ &frozen[tuples_frozen],
+ &tuple_totally_frozen,
&NewRelfrozenXid, &NewRelminMxid))
{
/* Will execute freeze below */
- frozen[nfrozen++].offset = offnum;
+ frozen[tuples_frozen++].offset = offnum;
}
/*
@@ -1809,10 +1819,12 @@ retry:
* Consider the need to freeze any items with tuple storage from the page
* first (arbitrary)
*/
- if (nfrozen > 0)
+ if (tuples_frozen > 0)
{
Assert(prunestate->hastup);
+ vacrel->frozen_pages++;
+
/*
* At least one tuple with storage needs to be frozen -- execute that
* now.
@@ -1826,7 +1838,7 @@ retry:
MarkBufferDirty(buf);
/* execute collected freezes */
- for (int i = 0; i < nfrozen; i++)
+ for (int i = 0; i < tuples_frozen; i++)
{
HeapTupleHeader htup;
@@ -1842,7 +1854,7 @@ retry:
XLogRecPtr recptr;
recptr = log_heap_freeze(vacrel->rel, buf, vacrel->FreezeLimit,
- frozen, nfrozen);
+ frozen, tuples_frozen);
PageSetLSN(page, recptr);
}
@@ -1914,6 +1926,7 @@ retry:
/* Finally, add page-local counts to whole-VACUUM counts */
vacrel->tuples_deleted += tuples_deleted;
+ vacrel->tuples_frozen += tuples_frozen;
vacrel->lpdead_items += lpdead_items;
vacrel->live_tuples += live_tuples;
vacrel->recently_dead_tuples += recently_dead_tuples;
--
2.34.1