vacuum_skip_busy_pages.v2.patch
application/octet-stream
Filename: vacuum_skip_busy_pages.v2.patch
Type: application/octet-stream
Part: 0
Message:
Re: heap vacuum & cleanup locks
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| src/backend/access/heap/heapam.c | 38 | 0 |
| src/backend/commands/vacuum.c | 1 | 1 |
| src/backend/commands/vacuumlazy.c | 88 | 8 |
| src/include/access/heapam.h | 2 | 0 |
| src/include/commands/vacuum.h | 1 | 1 |
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b2d1901..81422af 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3841,6 +3841,44 @@ recheck_xvac:
return changed;
}
+/*
+ * heap_tuple_needs_freeze
+ *
+ * Check to see whether any of the XID fields of a tuple (xmin, xmax, xvac)
+ * are older than the specified cutoff XID. If so, return TRUE.
+ *
+ * It doesn't matter whether the tuple is alive or dead, we are checking
+ * to see if a tuple needs to be removed or frozen to avoid wraparound.
+ */
+bool
+heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ Buffer buf)
+{
+ TransactionId xid;
+
+ xid = HeapTupleHeaderGetXmin(tuple);
+ if (TransactionIdIsNormal(xid) &&
+ TransactionIdPrecedes(xid, cutoff_xid))
+ return true;
+
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ {
+ xid = HeapTupleHeaderGetXmax(tuple);
+ if (TransactionIdIsNormal(xid) &&
+ TransactionIdPrecedes(xid, cutoff_xid))
+ return true;
+ }
+
+ if (tuple->t_infomask & HEAP_MOVED)
+ {
+ xid = HeapTupleHeaderGetXvac(tuple);
+ if (TransactionIdIsNormal(xid) &&
+ TransactionIdPrecedes(xid, cutoff_xid))
+ return true;
+ }
+
+ return false;
+}
/* ----------------
* heap_markpos - mark scan position
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f42504c..d08ac65 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1054,7 +1054,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
vacstmt->freeze_min_age, vacstmt->freeze_table_age);
}
else
- lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
+ lazy_vacuum_rel(onerel, vacstmt, vac_strategy, for_wraparound);
/* Roll back any GUC changes executed by index functions */
AtEOXact_GUC(false, save_nestlevel);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index b197b45..8a09a01 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -115,8 +115,10 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
- Relation *Irel, int nindexes, bool scan_all);
+ Relation *Irel, int nindexes, bool scan_all,
+ bool for_wraparound);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
+static bool lazy_scan_page_for_wraparound(Buffer buf);
static void lazy_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats,
LVRelStats *vacrelstats);
@@ -146,7 +148,7 @@ static int vac_cmp_itemptr(const void *left, const void *right);
*/
void
lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
- BufferAccessStrategy bstrategy)
+ BufferAccessStrategy bstrategy, bool for_wraparound)
{
LVRelStats *vacrelstats;
Relation *Irel;
@@ -193,7 +195,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
vacrelstats->hasindex = (nindexes > 0);
/* Do the vacuuming */
- lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, scan_all);
+ lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, scan_all,
+ for_wraparound);
/* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock);
@@ -327,7 +330,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
*/
static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
- Relation *Irel, int nindexes, bool scan_all)
+ Relation *Irel, int nindexes, bool scan_all, bool for_wraparound)
{
BlockNumber nblocks,
blkno;
@@ -453,8 +456,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
vacuum_delay_point();
- vacrelstats->scanned_pages++;
-
/*
* If we are close to overrunning the available space for dead-tuple
* TIDs, pause and do a cycle of vacuuming before we tackle this page.
@@ -486,7 +487,41 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
RBM_NORMAL, vac_strategy);
/* We need buffer cleanup lock so that we can prune HOT chains. */
- LockBufferForCleanup(buf);
+ if (!ConditionalLockBufferForCleanup(buf))
+ {
+ /*
+ * It's OK to skip vacuuming a page, as long as its not got data
+ * that needs to be cleaned for wraparound avoidance.
+ */
+ if (!for_wraparound)
+ {
+ ReleaseBuffer(buf);
+ continue;
+ }
+
+ /*
+ * If this is a wraparound checking vacuum, then we read the page
+ * with share lock to see if any xids need to be frozen. If the
+ * page doesn't need attention we just skip and continue. If it
+ * does, we wait for cleanup lock.
+ *
+ * We could defer the lock request further by remembering the page
+ * and coming back to it later, of we could even register
+ * ourselves for multiple buffers and then service whichever one
+ * is received first. For now, this seems good enough.
+ */
+ LockBuffer(buf, BUFFER_LOCK_SHARE);
+ if (!lazy_scan_page_for_wraparound(buf))
+ {
+ UnlockReleaseBuffer(buf);
+ continue;
+ }
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ LockBufferForCleanup(buf);
+ /* drop through to normal processing */
+ }
+
+ vacrelstats->scanned_pages++;
page = BufferGetPage(buf);
@@ -932,7 +967,8 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]);
buf = ReadBufferExtended(onerel, MAIN_FORKNUM, tblk, RBM_NORMAL,
vac_strategy);
- LockBufferForCleanup(buf);
+ if (!ConditionalLockBufferForCleanup(buf))
+ continue;
tupindex = lazy_vacuum_page(onerel, tblk, buf, tupindex, vacrelstats);
/* Now that we've compacted the page, record its available space */
@@ -1010,6 +1046,50 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
}
/*
+ * lazy_scan_page_for_wraparound() -- scan page to see if any tuples
+ * need to be cleaned to avoid wraparound
+ *
+ * Returns true if the page needs to be vacuumed using cleanup lock.
+ */
+static bool
+lazy_scan_page_for_wraparound(Buffer buf)
+{
+ Page page;
+ OffsetNumber offnum,
+ maxoff;
+ HeapTupleHeader tupleheader;
+
+ page = BufferGetPage(buf);
+
+ if (PageIsNew(page) || PageIsEmpty(page))
+ {
+ /* PageIsNew probably shouldn't happen... */
+ return false;
+ }
+
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum))
+ {
+ ItemId itemid;
+
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsNormal(itemid))
+ continue;
+
+ tupleheader = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ if (heap_tuple_needs_freeze(tupleheader, FreezeLimit, buf))
+ return true;
+ } /* scan along page */
+
+ return false;
+}
+
+
+/*
* lazy_vacuum_index() -- vacuum one index relation.
*
* Delete all the index entries pointing to tuples listed in
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 776ea5c..85cbeb3 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -111,6 +111,8 @@ extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
Buffer buf);
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ Buffer buf);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
extern void simple_heap_delete(Relation relation, ItemPointer tid);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index d8fd0ca..60f21f5 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -162,7 +162,7 @@ extern void vacuum_delay_point(void);
/* in commands/vacuumlazy.c */
extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
- BufferAccessStrategy bstrategy);
+ BufferAccessStrategy bstrategy, bool for_wraparound);
/* in commands/analyze.c */
extern void analyze_rel(Oid relid, VacuumStmt *vacstmt,