* process this tuple as part of freezing its page, and return true. Return
* false if nothing can be changed about the tuple right now.
*
+ * FreezePageConflictXid is advanced only for xmin/xvac freezing, not for xmax
+ * changes. We only remove xmax state here when it is lock-only, or when the
+ * updater XID (including an updater member of a MultiXact) must be aborted;
+ * otherwise, the tuple would already be removable. Neither case affects
+ * visibility on a standby.
+ *
* Also sets *totally_frozen to true if the tuple will be totally frozen once
* caller executes returned freeze plan (or if the tuple was already totally
* frozen by an earlier VACUUM). This indicates that there are no remaining
/* Verify that xmin committed if and when freeze plan is executed */
if (freeze_xmin)
+ {
frz->checkflags |= HEAP_FREEZE_CHECK_XMIN_COMMITTED;
+ if (TransactionIdFollows(xid, pagefrz->FreezePageConflictXid))
+ pagefrz->FreezePageConflictXid = xid;
+ }
}
/*
*/
replace_xvac = pagefrz->freeze_required = true;
+ if (TransactionIdFollows(xid, pagefrz->FreezePageConflictXid))
+ pagefrz->FreezePageConflictXid = xid;
+
/* Will set replace_xvac flags in freeze plan below */
}
pagefrz.freeze_required = true;
pagefrz.FreezePageRelfrozenXid = FreezeLimit;
pagefrz.FreezePageRelminMxid = MultiXactCutoff;
+ pagefrz.FreezePageConflictXid = InvalidTransactionId;
pagefrz.NoFreezePageRelfrozenXid = FreezeLimit;
pagefrz.NoFreezePageRelminMxid = MultiXactCutoff;
/* initialize page freezing working state */
prstate->pagefrz.freeze_required = false;
+ prstate->pagefrz.FreezePageConflictXid = InvalidTransactionId;
if (prstate->attempt_freeze)
{
Assert(new_relfrozen_xid && new_relmin_mxid);
* PruneState.
*/
prstate->deadoffsets = presult->deadoffsets;
- prstate->frz_conflict_horizon = InvalidTransactionId;
/*
* Vacuum may update the VM after we're done. We can keep track of
* critical section.
*/
heap_pre_freeze_checks(prstate->buffer, prstate->frozen, prstate->nfrozen);
-
- /*
- * Calculate what the snapshot conflict horizon should be for a record
- * freezing tuples. We can use the visibility_cutoff_xid as our cutoff
- * for conflicts when the whole page is eligible to become all-frozen
- * in the VM once we're done with it. Otherwise, we generate a
- * conservative cutoff by stepping back from OldestXmin.
- */
- if (prstate->set_all_frozen)
- prstate->frz_conflict_horizon = prstate->visibility_cutoff_xid;
- else
- {
- /* Avoids false conflicts when hot_standby_feedback in use */
- prstate->frz_conflict_horizon = prstate->cutoffs->OldestXmin;
- TransactionIdRetreat(prstate->frz_conflict_horizon);
- }
+ Assert(TransactionIdPrecedes(prstate->pagefrz.FreezePageConflictXid,
+ prstate->cutoffs->OldestXmin));
}
else if (prstate->nfrozen > 0)
{
/*
* The snapshotConflictHorizon for the whole record should be the
* most conservative of all the horizons calculated for any of the
- * possible modifications. If this record will prune tuples, any
- * transactions on the standby older than the youngest xmax of the
- * most recently removed tuple this record will prune will
- * conflict. If this record will freeze tuples, any transactions
- * on the standby with xids older than the youngest tuple this
- * record will freeze will conflict.
+ * possible modifications. If this record will prune tuples, any
+ * queries on the standby older than the newest xid of the most
+ * recently removed tuple this record will prune will conflict. If
+ * this record will freeze tuples, any queries on the standby with
+ * xids older than the newest tuple this record will freeze will
+ * conflict.
*/
TransactionId conflict_xid;
- if (TransactionIdFollows(prstate.frz_conflict_horizon,
+ if (TransactionIdFollows(prstate.pagefrz.FreezePageConflictXid,
prstate.latest_xid_removed))
- conflict_xid = prstate.frz_conflict_horizon;
+ conflict_xid = prstate.pagefrz.FreezePageConflictXid;
else
conflict_xid = prstate.latest_xid_removed;
TransactionId FreezePageRelfrozenXid;
MultiXactId FreezePageRelminMxid;
+ /*
+ * Newest XID that this page's freeze actions will remove from tuple
+ * visibility metadata (currently xmin and/or xvac). It is used to derive
+ * the snapshot conflict horizon for a WAL record that freezes tuples. On
+ * a standby, we must not replay that change while any snapshot could
+ * still treat that XID as running.
+ *
+ * It's only used if we execute freeze plans for this page, so there is no
+ * corresponding "no freeze" tracker.
+ */
+ TransactionId FreezePageConflictXid;
+
/*
* "No freeze" NewRelfrozenXid/NewRelminMxid trackers.
*