0001-Update-FSM-when-updating-VM-even-if-freespace-is-zer.patch

text/x-patch

Filename: 0001-Update-FSM-when-updating-VM-even-if-freespace-is-zer.patch
Type: text/x-patch
Part: 0
Message: Re: eliminate xl_heap_visible to reduce WAL (and eventually set VM on-access)

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: format-patch
Series: patch 0001
Subject: Update FSM when updating VM even if freespace is zero
File+
src/backend/access/heap/heapam_xlog.c 5 1
From eb9403676d61c32f45139ef6559366182afd608f Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Mon, 13 Apr 2026 11:50:32 -0400
Subject: [PATCH] Update FSM when updating VM even if freespace is zero

add323da40a started updating the visibility map in the same WAL record
as pruning and freezing. This included updating the freespace map during
replay, which we've done since ab7dbd681.

add323da40a, however, conditioned doing so on there being > 0 freespace,
which differed from the previous state. The FSM is not WAL-logged and is
instead updated heuristically on standbys. In rare cases, if the FSM is
not updated while replaying inserts, there is 0 freespace, and vacuum
replay doesn't update the FSM when setting the page
all-visible/all-frozen, after the standby is promoted and runs vacuum,
it may skip those pages and then propagate overly optimistic numbers up
the FSM, causing slowness when searching for freespace for new tuples.

Fix it by always updating the FSM when replaying setting the VM.

Author: Melanie Plageman <melanieplageman@gmail.com>
Reported-by: Alexey Makhmutov <a.makhmutov@postgrespro.ru>
Discussion: https://postgr.es/m/ead2f110-c736-48f5-99e1-023dc9acbf0b%40postgrespro.ru
---
 src/backend/access/heap/heapam_xlog.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c
index f3f419d3dc1..9ed7024e814 100644
--- a/src/backend/access/heap/heapam_xlog.c
+++ b/src/backend/access/heap/heapam_xlog.c
@@ -38,6 +38,7 @@ heap_xlog_prune_freeze(XLogReaderState *record)
 	Buffer		vmbuffer = InvalidBuffer;
 	uint8		vmflags = 0;
 	Size		freespace = 0;
+	bool		do_update_fsm = false;
 
 	XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
 	memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
@@ -211,7 +212,10 @@ heap_xlog_prune_freeze(XLogReaderState *record)
 							XLHP_HAS_DEAD_ITEMS |
 							XLHP_HAS_NOW_UNUSED_ITEMS)) ||
 			(vmflags & VISIBILITYMAP_VALID_BITS))
+		{
 			freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
+			do_update_fsm = true;
+		}
 
 		/*
 		 * We want to avoid holding an exclusive lock on the heap buffer while
@@ -248,7 +252,7 @@ heap_xlog_prune_freeze(XLogReaderState *record)
 	if (BufferIsValid(vmbuffer))
 		UnlockReleaseBuffer(vmbuffer);
 
-	if (freespace > 0)
+	if (do_update_fsm)
 		XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
 }
 
-- 
2.43.0