aboutsummaryrefslogtreecommitdiff
path: root/src/include/access/heapam.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/access/heapam.h')
-rw-r--r--src/include/access/heapam.h91
1 files changed, 83 insertions, 8 deletions
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 53eb011766b..09a1993f4d7 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -113,6 +113,82 @@ typedef struct HeapTupleFreeze
OffsetNumber offset;
} HeapTupleFreeze;
+/*
+ * State used by VACUUM to track the details of freezing all eligible tuples
+ * on a given heap page.
+ *
+ * VACUUM prepares freeze plans for each page via heap_prepare_freeze_tuple
+ * calls (every tuple with storage gets its own call). This page-level freeze
+ * state is updated across each call, which ultimately determines whether or
+ * not freezing the page is required.
+ *
+ * Aside from the basic question of whether or not freezing will go ahead, the
+ * state also tracks the oldest extant XID/MXID in the table as a whole, for
+ * the purposes of advancing relfrozenxid/relminmxid values in pg_class later
+ * on. Each heap_prepare_freeze_tuple call pushes NewRelfrozenXid and/or
+ * NewRelminMxid back as required to avoid unsafe final pg_class values. Any
+ * and all unfrozen XIDs or MXIDs that remain after VACUUM finishes _must_
+ * have values >= the final relfrozenxid/relminmxid values in pg_class. This
+ * includes XIDs that remain as MultiXact members from any tuple's xmax.
+ *
+ * When 'freeze_required' flag isn't set after all tuples are examined, the
+ * final choice on freezing is made by vacuumlazy.c. It can decide to trigger
+ * freezing based on whatever criteria it deems appropriate. However, it is
+ * recommended that vacuumlazy.c avoid early freezing when freezing does not
+ * enable setting the target page all-frozen in the visibility map afterwards.
+ */
+typedef struct HeapPageFreeze
+{
+ /* Is heap_prepare_freeze_tuple caller required to freeze page? */
+ bool freeze_required;
+
+ /*
+ * "Freeze" NewRelfrozenXid/NewRelminMxid trackers.
+ *
+ * Trackers used when heap_freeze_execute_prepared freezes the page, and
+ * when page is "nominally frozen", which happens with pages where every
+ * call to heap_prepare_freeze_tuple produced no usable freeze plan.
+ *
+ * "Nominal freezing" enables vacuumlazy.c's approach of setting a page
+ * all-frozen in the visibility map when every tuple's 'totally_frozen'
+ * result is true. That always works in the same way, independent of the
+ * need to freeze tuples, and without complicating the general rule around
+ * 'totally_frozen' results (which is that 'totally_frozen' results are
+ * only to be trusted with a page that goes on to be frozen by caller).
+ *
+ * When we freeze a page, we generally freeze all XIDs < OldestXmin, only
+ * leaving behind XIDs that are ineligible for freezing, if any. And so
+ * you might wonder why these trackers are necessary at all; why should
+ * _any_ page that VACUUM freezes _ever_ be left with XIDs/MXIDs that
+ * ratchet back the top-level NewRelfrozenXid/NewRelminMxid trackers?
+ *
+ * It is useful to use a definition of "freeze the page" that does not
+ * overspecify how MultiXacts are affected. heap_prepare_freeze_tuple
+ * generally prefers to remove Multis eagerly, but lazy processing is used
+ * in cases where laziness allows VACUUM to avoid allocating a new Multi.
+ * The "freeze the page" trackers enable this flexibility.
+ */
+ TransactionId FreezePageRelfrozenXid;
+ MultiXactId FreezePageRelminMxid;
+
+ /*
+ * "No freeze" NewRelfrozenXid/NewRelminMxid trackers.
+ *
+ * These trackers are maintained in the same way as the trackers used when
+ * VACUUM scans a page that isn't cleanup locked. Both code paths are
+ * based on the same general idea (do less work for this page during the
+ * ongoing VACUUM, at the cost of having to accept older final values).
+ *
+ * When vacuumlazy.c caller decides to do "no freeze" processing, it must
+ * not go on to set the page all-frozen (setting the page all-visible
+ * could still be okay). heap_prepare_freeze_tuple's 'totally_frozen'
+ * results can only be used on a page that also gets frozen as instructed.
+ */
+ TransactionId NoFreezePageRelfrozenXid;
+ MultiXactId NoFreezePageRelminMxid;
+
+} HeapPageFreeze;
+
/* ----------------
* function prototypes for heap access method
*
@@ -180,19 +256,18 @@ extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
const struct VacuumCutoffs *cutoffs,
- HeapTupleFreeze *frz, bool *totally_frozen,
- TransactionId *relfrozenxid_out,
- MultiXactId *relminmxid_out);
+ HeapPageFreeze *pagefrz,
+ HeapTupleFreeze *frz, bool *totally_frozen);
extern void heap_freeze_execute_prepared(Relation rel, Buffer buffer,
- TransactionId FreezeLimit,
+ TransactionId snapshotConflictHorizon,
HeapTupleFreeze *tuples, int ntuples);
extern bool heap_freeze_tuple(HeapTupleHeader tuple,
TransactionId relfrozenxid, TransactionId relminmxid,
TransactionId FreezeLimit, TransactionId MultiXactCutoff);
-extern bool heap_tuple_would_freeze(HeapTupleHeader tuple,
- const struct VacuumCutoffs *cutoffs,
- TransactionId *relfrozenxid_out,
- MultiXactId *relminmxid_out);
+extern bool heap_tuple_should_freeze(HeapTupleHeader tuple,
+ const struct VacuumCutoffs *cutoffs,
+ TransactionId *NoFreezePageRelfrozenXid,
+ MultiXactId *NoFreezePageRelminMxid);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
extern void simple_heap_insert(Relation relation, HeapTuple tup);