From c2255595be2c9e75a1c3b6c2a4b7a2f16b82e85e Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Mon, 21 Dec 2020 20:44:11 -0800
Subject: [PATCH] Instrument heap_compute_xid_horizon_for_tuples().

---
 src/backend/access/heap/heapam.c | 38 ++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index a9583f3103..bb2488feb0 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -6998,6 +6998,7 @@ heap_compute_xid_horizon_for_tuples(Relation rel,
 									int nitems)
 {
 	TransactionId latestRemovedXid = InvalidTransactionId;
+	TransactionId latestRemovedXidWithHotChain = InvalidTransactionId;
 	BlockNumber hblkno;
 	Buffer		buf = InvalidBuffer;
 	Page		hpage;
@@ -7092,6 +7093,36 @@ heap_compute_xid_horizon_for_tuples(Relation rel,
 			hitemid = PageGetItemId(hpage, hoffnum);
 		}
 
+		/*
+		 * Instrumentation: follow HOT chain without changing function's
+		 * behavior.
+		 *
+		 * Maybe latestRemovedXidWithHotChain instrumentation variable ends up
+		 * ahead of authoritative latestRemovedXid value (which should not
+		 * happen), and maybe the discrepancy makes the crucial difference for
+		 * the operation as a whole (which could cause problems for hot
+		 * standby during REDO of caller's record).
+		 */
+		if (ItemIdHasStorage(hitemid))
+		{
+			ItemId			hotitemid = hitemid;
+			HeapTupleHeader hothtuphdr;
+
+			hothtuphdr = (HeapTupleHeader) PageGetItem(hpage, hotitemid);
+			while (HeapTupleHeaderIsHotUpdated(hothtuphdr))
+			{
+				OffsetNumber hotoff = ItemPointerGetOffsetNumber(&hothtuphdr->t_ctid);
+
+				hotitemid = PageGetItemId(hpage, hotoff);
+				if (!ItemIdHasStorage(hotitemid))
+					break;
+
+				hothtuphdr = (HeapTupleHeader) PageGetItem(hpage, hotitemid);
+				HeapTupleHeaderAdvanceLatestRemovedXid(hothtuphdr,
+													   &latestRemovedXidWithHotChain);
+			}
+		}
+
 		/*
 		 * If the heap item has storage, then read the header and use that to
 		 * set latestRemovedXid.
@@ -7134,6 +7165,13 @@ heap_compute_xid_horizon_for_tuples(Relation rel,
 	 * LP_DEAD, hence must already have generated a conflict.
 	 */
 
+	if (TransactionIdPrecedes(latestRemovedXid, latestRemovedXidWithHotChain))
+		elog(WARNING, "hot chain bug, latestRemovedXid: %u, latestRemovedXidWithHotChain: %u",
+			 latestRemovedXid, latestRemovedXidWithHotChain);
+	else
+		elog(WARNING, "works okay this time, latestRemovedXid: %u, latestRemovedXidWithHotChain: %u",
+			 latestRemovedXid, latestRemovedXidWithHotChain);
+
 	return latestRemovedXid;
 }
 
-- 
2.27.0

