Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Use TransactionXmin instead of RecentGlobalXmin in heap_abort_speculative().
authorAndres Freund <andres@anarazel.de>
Mon, 6 Apr 2020 00:47:30 +0000 (17:47 -0700)
committerAndres Freund <andres@anarazel.de>
Mon, 6 Apr 2020 00:48:43 +0000 (17:48 -0700)
There's a very low risk that RecentGlobalXmin could be far enough in
the past to be older than relfrozenxid, or even wrapped
around. Luckily the consequences of that having happened wouldn't be
too bad - the page wouldn't be pruned for a while.

Avoid that risk by using TransactionXmin instead. As that's announced
via MyPgXact->xmin, it is protected against wrapping around (see code
comments for details around relfrozenxid).

Author: Andres Freund
Discussion: https://postgr.es/m/20200328213023.s4eyijhdosuc4vcj@alap3.anarazel.de
Backpatch: 9.5-

src/backend/access/heap/heapam.c

index 9554704456c92170acf282e523888c4851dc1dab..5dc7e30168a56b7a74f50b14eaee552c62fa979e 100644 (file)
@@ -5782,6 +5782,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
    Page        page;
    BlockNumber block;
    Buffer      buffer;
+   TransactionId prune_xid;
 
    Assert(ItemPointerIsValid(tid));
 
@@ -5824,13 +5825,21 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
    START_CRIT_SECTION();
 
    /*
-    * The tuple will become DEAD immediately.  Flag that this page
-    * immediately is a candidate for pruning by setting xmin to
-    * RecentGlobalXmin.  That's not pretty, but it doesn't seem worth
-    * inventing a nicer API for this.
+    * The tuple will become DEAD immediately.  Flag that this page is a
+    * candidate for pruning by setting xmin to TransactionXmin. While not
+    * immediately prunable, it is the oldest xid we can cheaply determine
+    * that's safe against wraparound / being older than the table's
+    * relfrozenxid.  To defend against the unlikely case of a new relation
+    * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
+    * if so (vacuum can't subsequently move relfrozenxid to beyond
+    * TransactionXmin, so there's no race here).
     */
-   Assert(TransactionIdIsValid(RecentGlobalXmin));
-   PageSetPrunable(page, RecentGlobalXmin);
+   Assert(TransactionIdIsValid(TransactionXmin));
+   if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
+       prune_xid = relation->rd_rel->relfrozenxid;
+   else
+       prune_xid = TransactionXmin;
+   PageSetPrunable(page, prune_xid);
 
    /* store transaction information of xact deleting the tuple */
    tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);