Thread
-
[PATCH v1 05/12] prune_xid-hist: core functions
Renan Alves Fonseca <renanfonseca@gmail.com> — 2025-05-27T14:39:57Z
--- src/backend/utils/activity/pgstat_relation.c | 251 +++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 28587e2916b..33d6a384929 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -20,6 +20,7 @@ #include "access/twophase_rmgr.h" #include "access/xact.h" #include "catalog/catalog.h" +#include "storage/procarray.h" #include "utils/memutils.h" #include "utils/pgstat_internal.h" #include "utils/rel.h" @@ -460,6 +461,256 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta) } } +// TODO: use TransactionIdPrecedes and TransactionIdFollows in all xact comparisons +static int +find_last_containing_bin(TransactionId *bounds, TransactionId xid) +{ + int bin; + + for (bin = PRUNE_XID_HIST_NBINS - 1; bin > 0; bin--) + if (bounds[bin - 1] == InvalidTransactionId || xid > bounds[bin - 1]) + break; + return bin; +} + +static int +find_first_containing_bin(TransactionId *bounds, TransactionId xid) +{ + int bin; + + for (bin = 0; bin < PRUNE_XID_HIST_NBINS - 1; bin++) + if (bounds[bin] != InvalidTransactionId && xid < bounds[bin]) + break; + return bin; +} + + +static void +hist_prune_xid_update(PruneXidHistogramTransient *hist, TransactionId xid, PgStat_Counter delta) +{ + int bin; + + if (xid == InvalidTransactionId) + return; + + bin = find_last_containing_bin(hist->bounds, xid); + + if (bin == PRUNE_XID_HIST_NBINS - 1) + { + /* handle open bin */ + if (delta > 0) + hist->freqs[bin] += delta; + else + hist->neg_freq += delta; + hist->bounds[bin] = Max(hist->bounds[bin], xid); + } + else + { + hist->freqs[bin] += delta; + } +} + + +/* + * At certain moments, we know that freqs cannot be negative. Then, we can + * rectify the histogram by moving the negative counts forward. Both + * histograms, before and after this operation, respect the lower bound + * guarantees. But the resultant is a tighter bound. + */ +static void +hist_prune_xid_rectify(PruneXidHistogram *hist) +{ + PgStat_Counter excess = 0; + int bin; + + for (bin = 0; bin < PRUNE_XID_HIST_NBINS - 1; bin++) + { + hist->freqs[bin] += excess; + excess = 0; + + if (hist->freqs[bin] < 0) + { + excess = hist->freqs[bin]; + hist->freqs[bin] = 0; + } + } + hist->freqs[bin] += excess; +} + +static void +hist_shift_left(PruneXidHistogram *hist, int from_bin) +{ + PgStat_Counter *freqs = hist->freqs; + TransactionId *bounds = hist->bounds; + int bin; + + Assert(from_bin >= 0); + + for (bin = from_bin; bin < PRUNE_XID_HIST_NBINS - 1; bin++) + { + bounds[bin] = bounds[bin + 1]; + freqs[bin] = freqs[bin + 1]; + } + bounds[bin] = 0; + freqs[bin] = 0; + return; +} + +/* + * Merge intermediate adjacent bins in order to make space for a new open bin. + */ +static void +hist_prune_xid_balance(PruneXidHistogram *hist) +{ + PgStat_Counter *freqs = hist->freqs; + TransactionId *bounds = hist->bounds; + int bin; + + /* do nothing if open bin is empty */ + if (freqs[PRUNE_XID_HIST_NBINS - 1] == 0) + return; + if (freqs[PRUNE_XID_HIST_NBINS - 1] == freqs[PRUNE_XID_HIST_NBINS - 2]) + return; + + /* if histogram is partially fulfilled, just shift bins */ + if (bounds[0] == 0) + { + hist_shift_left(hist, 0); + return; + } + + /* + * if second bin upper bound xid is smaller than oldest non removable xid, + * merge first two bins + */ + if (freqs[1] < GetOldestNonRemovableTransactionId(NULL)) + { + freqs[0] += freqs[1]; + bounds[0] = bounds[1]; + hist_shift_left(hist, 1); + return; + } + + /* + * if open bin is too "big", merge two "small" adjacent bins. + */ + { + PgStat_Counter open_bin_freq = freqs[PRUNE_XID_HIST_NBINS - 1]; + PgStat_Counter smallest_pair_freq = 0; + int smallest_bin = 0; + + for (bin = 0; bin < PRUNE_XID_HIST_NBINS - 1; bin++) + if (freqs[bin] + freqs[bin + 1] > smallest_pair_freq) + { + smallest_pair_freq = freqs[bin] + freqs[bin + 1]; + smallest_bin = bin; + } + if (open_bin_freq > smallest_pair_freq) + { + freqs[smallest_bin] += freqs[smallest_bin + 1]; + bounds[smallest_bin] = bounds[smallest_bin + 1]; + hist_shift_left(hist, smallest_bin + 1); + } + return; + } +} + +/* update transient histogram */ +void +pgstat_update_transient_prune_xid_histogram(PruneXidHistogramTransient *hist, TransactionId old_xid, TransactionId new_xid) +{ + if (old_xid == new_xid) + return; + hist_prune_xid_update(hist, old_xid, -1); + hist_prune_xid_update(hist, new_xid, 1); +} + +/* update per-backend prune_xid_histogram */ +void +pgstat_update_relation_prune_xid_histogram(Relation rel, TransactionId old_xid, TransactionId new_xid) +{ + if (pgstat_should_count_relation(rel)) + { + PgStat_TableStatus *pgstat_info = rel->pgstat_info; + PruneXidHistogramTransient *hist = &pgstat_info->counts.prune_xid_hist; + + Assert(old_xid != new_xid); + hist_prune_xid_update(hist, old_xid, -1); + hist_prune_xid_update(hist, new_xid, 1); + + } +} + + +/* + * This function updates prune_xid histogram of respective table in + * shared PgStat_StatTabEntry using information from auxiliary histogram. + */ +void +pgstat_update_shared_prune_xid_histogram(PgStat_StatTabEntry *tabentry, const PruneXidHistogramTransient *source) +{ + int bin_source, + bin_target; + PgStat_Counter delta; + TransactionId xid; + PruneXidHistogram *target = &tabentry->prune_xid_hist; + + /* process main, positive, freqs */ + for (bin_source = 0; bin_source < PRUNE_XID_HIST_NBINS; bin_source++) + { + delta = source->freqs[bin_source]; + xid = source->bounds[bin_source]; + + if (xid == InvalidTransactionId || delta == 0) + /* skip bins in partially filled histograms */ + continue; + + bin_target = find_last_containing_bin(target->bounds, xid); + target->freqs[bin_target] += delta; + + if (bin_target == PRUNE_XID_HIST_NBINS - 1) + target->bounds[bin_target] = Max(target->bounds[bin_target], xid); + } + + /* process negative freq */ + xid = source->bounds[PRUNE_XID_HIST_NBINS - 2] + 1; + bin_target = find_first_containing_bin(target->bounds, xid); + target->freqs[bin_target] += source->neg_freq; + + /* finally, balance histogram */ + hist_prune_xid_balance(target); + hist_prune_xid_rectify(target); +} + +static void +pgstat_init_local_prune_xid_histogram(PgStat_TableStatus *lstats, PgStat_EntryRef *entry_ref) +{ + int bin; + PgStatShared_Relation *shtabstats; + + shtabstats = (PgStatShared_Relation *) entry_ref->shared_stats; + + for (bin = 0; bin < PRUNE_XID_HIST_NBINS; bin++) + { + lstats->counts.prune_xid_hist.bounds[bin] = shtabstats->stats.prune_xid_hist.bounds[bin]; + lstats->counts.prune_xid_hist.freqs[bin] = 0; + } + lstats->counts.prune_xid_hist.neg_freq = 0; +} + +const PgStat_Counter * +pgstat_get_prune_xid_histogram_freqs(PgStat_StatTabEntry *tabentry) +{ + return tabentry->prune_xid_hist.freqs; +} + +const TransactionId * +pgstat_get_prune_xid_histogram_bounds(PgStat_StatTabEntry *tabentry) +{ + return tabentry->prune_xid_hist.bounds; +} + + /* * Support function for the SQL-callable pgstat* functions. Returns * the collected statistics for one table or NULL. NULL doesn't mean -- 2.47.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v1-0006-prune_xid-hist-initialize-shared-histogram-on-TRU.patch