btree-page-del-v1.patch
text/plain
Filename: btree-page-del-v1.patch
Type: text/plain
Part: 0
Message:
B-tree page deletion boundary cases
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 v1
| File | + | − |
|---|---|---|
| src/backend/access/nbtree/README | 0 | 0 |
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 561ffbb..6932c2b 100644
*** a/src/backend/access/nbtree/README
--- b/src/backend/access/nbtree/README
***************
*** 261,272 **** we need to be sure we don't miss or re-scan any items.
A deleted page can only be reclaimed once there is no scan or search that
has a reference to it; until then, it must stay in place with its
! right-link undisturbed. We implement this by waiting until all
! transactions that were running at the time of deletion are dead; which is
overly strong, but is simple to implement within Postgres. When marked
dead, a deleted page is labeled with the next-transaction counter value.
VACUUM can reclaim the page for re-use when this transaction number is
! older than the oldest open transaction.
Reclaiming a page doesn't actually change its state on disk --- we simply
record it in the shared-memory free space map, from which it will be
--- 261,274 ----
A deleted page can only be reclaimed once there is no scan or search that
has a reference to it; until then, it must stay in place with its
! right-link undisturbed. We implement this by waiting until all active
! snapshots and registered snapshots as of the deletion are gone; which is
overly strong, but is simple to implement within Postgres. When marked
dead, a deleted page is labeled with the next-transaction counter value.
VACUUM can reclaim the page for re-use when this transaction number is
! older than RecentGlobalXmin. As collateral damage, this implementation
! also waits for running XIDs with no snapshots and for snapshots taken
! until the next transaction to allocate an XID commits.
Reclaiming a page doesn't actually change its state on disk --- we simply
record it in the shared-memory free space map, from which it will be
diff --git a/src/backend/access/nbtree/index c5e147f..e6dec61 100644
*** a/src/backend/access/nbtree/nbtpage.c
--- b/src/backend/access/nbtree/nbtpage.c
***************
*** 558,576 **** _bt_getbuf(Relation rel, BlockNumber blkno, int access)
*/
if (XLogStandbyInfoActive())
{
- TransactionId latestRemovedXid;
-
BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
! /*
! * opaque->btpo.xact is the threshold value not the
! * value to measure conflicts against. We must retreat
! * by one from it to get the correct conflict xid.
! */
! latestRemovedXid = opaque->btpo.xact;
! TransactionIdRetreat(latestRemovedXid);
!
! _bt_log_reuse_page(rel, blkno, latestRemovedXid);
}
/* Okay to use page. Re-initialize and return it */
--- 558,566 ----
*/
if (XLogStandbyInfoActive())
{
BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
! _bt_log_reuse_page(rel, blkno, opaque->btpo.xact);
}
/* Okay to use page. Re-initialize and return it */
***************
*** 685,691 **** bool
_bt_page_recyclable(Page page)
{
BTPageOpaque opaque;
- TransactionId cutoff;
/*
* It's possible to find an all-zeroes page in an index --- for example, a
--- 675,680 ----
***************
*** 698,715 **** _bt_page_recyclable(Page page)
/*
* Otherwise, recycle if deleted and too old to have any processes
! * interested in it. If we are generating records for Hot Standby
! * defer page recycling until RecentGlobalXmin to respect user
! * controls specified by vacuum_defer_cleanup_age or hot_standby_feedback.
*/
- if (XLogStandbyInfoActive())
- cutoff = RecentGlobalXmin;
- else
- cutoff = RecentXmin;
-
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (P_ISDELETED(opaque) &&
! TransactionIdPrecedesOrEquals(opaque->btpo.xact, cutoff))
return true;
return false;
}
--- 687,697 ----
/*
* Otherwise, recycle if deleted and too old to have any processes
! * interested in it.
*/
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (P_ISDELETED(opaque) &&
! TransactionIdPrecedes(opaque->btpo.xact, RecentGlobalXmin))
return true;
return false;
}
***************
*** 1376,1382 **** _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
/*
* Mark the page itself deleted. It can be recycled when all current
! * transactions are gone.
*/
page = BufferGetPage(buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
--- 1358,1370 ----
/*
* Mark the page itself deleted. It can be recycled when all current
! * transactions are gone. Storing GetTopTransactionId() would work, but
! * we're in VACUUM and would not otherwise have an XID. Having already
! * updated links to the target, ReadNewTransactionId() suffices as an
! * upper bound. Any scan having retained a now-stale link is advertising
! * in its PGXACT an xmin less than or equal to the value we read here. It
! * will continue to do so, holding back RecentGlobalXmin, for the duration
! * of that scan.
*/
page = BufferGetPage(buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
diff --git a/src/backend/access/nbtree/nbtindex 3b351a8..deca38c 100644
*** a/src/backend/access/nbtree/nbtxlog.c
--- b/src/backend/access/nbtree/nbtxlog.c
***************
*** 968,974 **** btree_redo(XLogRecPtr lsn, XLogRecord *record)
/*
* Btree reuse page records exist to provide a conflict point
* when we reuse pages in the index via the FSM. That's all it
! * does though.
*/
{
xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) XLogRecGetData(record);
--- 968,978 ----
/*
* Btree reuse page records exist to provide a conflict point
* when we reuse pages in the index via the FSM. That's all it
! * does though. latestRemovedXid was the page's btpo.xact. The
! * btpo.xact < RecentGlobalXmin test in _bt_page_recyclable()
! * conceptually mirrors the pgxact->xmin > limitXmin test in
! * GetConflictingVirtualXIDs(). Consequently, one XID value
! * achieves the same exclusion effect on master and standby.
*/
{
xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) XLogRecGetData(record);