From 706bb5399a4d656d8dfb0868f1ae8b0497660ab3 Mon Sep 17 00:00:00 2001 From: Andrey Borodin Date: Sun, 25 Oct 2026 05:28:11 +0500 Subject: [PATCH v1] Avoid counting deleted B-tree pages as free twice. VACUUM can delete pages ahead of its physical scan position. If such a page becomes recyclable before the scan reaches it, btvacuumpage() counts it as free, and _bt_pendingfsm_finalize() can count it again. This can make pages_free exceed pages_deleted, failing an assertion or underflowing the count of deleted pages not yet available for reuse. Only save pending FSM entries for pages at or behind the scan position, matching the existing accounting for pages_deleted. Leave pages ahead of the scan to btvacuumpage(). Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/19794-b44efb1b558039cd@postgresql.org Backpatch-through: 14 --- src/backend/access/nbtree/nbtpage.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 410514d8e10..f56f9e0d905 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -2779,15 +2779,17 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno, */ stats->pages_newly_deleted++; if (target <= scanblkno) + { stats->pages_deleted++; - /* - * Remember information about the target page (now a newly deleted page) - * in dedicated vstate space for later. The page will be considered as a - * candidate to place in the FSM at the end of the current btvacuumscan() - * call. - */ - _bt_pendingfsm_add(vstate, target, safexid); + /* + * Remember the page for possible recycling at the end of the scan. + * Pages ahead of scanblkno will be visited by btvacuumpage(), which + * might count them as free. Don't add those pages here, or + * _bt_pendingfsm_finalize() could count them as free a second time. + */ + _bt_pendingfsm_add(vstate, target, safexid); + } /* Success - hold on to lock on leafbuf (might also have been target) */ return true; -- That's all, folks. May the source be with you.