On Fri, Sep 1, 2023 at 8:38 PM Peter Geoghegan <p...@bowt.ie> wrote:
>
> On Fri, Sep 1, 2023 at 12:34 PM Melanie Plageman
> <melanieplage...@gmail.com> wrote:
> > I don't see why the visibility map shouldn't be updated so that all of
> > the pages show all visible and all frozen for this relation after the
> > vacuum full.
>
> There was a similar issue with COPY FREEZE. It was fixed relatively
> recently -- see commit 7db0cd21.

Thanks for digging that up for me!

My first thought after looking a bit at the vacuum full/cluster code
is that we could add an all_visible flag to the RewriteState and set
it to false in heapam_relation_copy_for_cluster() in roughly the same
cases as heap_page_is_all_visible(), then, if rwstate->all_visible is
true in raw_heap_insert(), when we need to advance to the next block,
we set the page all visible and update the VM. Either way, we reset
all_visible to true since we are advancing to the next block.

I wrote a rough outline of that idea in the attached patches. It
doesn't emit WAL for the VM update or handle toast tables or anything
(it is just a rough sketch), but I just wondered if this was in the
right direction.

- Melanie
From eda40534e169e0bac0d11d89947130b44653b925 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplage...@gmail.com>
Date: Sun, 3 Sep 2023 16:17:28 -0400
Subject: [PATCH v0 3/3] set all_visible in VM vac full

---
 src/backend/access/heap/heapam_handler.c | 15 ++++++++++++++-
 src/backend/access/heap/rewriteheap.c    | 16 ++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 5a17112c91..a37be8cfef 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -791,6 +791,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
 		HeapTuple	tuple;
 		Buffer		buf;
 		bool		isdead;
+		TransactionId xmin;
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -853,10 +854,18 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
 				break;
 			case HEAPTUPLE_RECENTLY_DEAD:
 				*tups_recently_dead += 1;
-				/* fall through */
+				isdead = false;
+				rwstate->all_visible = false;
+				break;
 			case HEAPTUPLE_LIVE:
 				/* Live or recently dead, must copy it */
+				xmin = HeapTupleHeaderGetXmin(tuple->t_data);
 				isdead = false;
+				if (!HeapTupleHeaderXminCommitted(tuple->t_data))
+					rwstate->all_visible = false;
+
+				else if (!TransactionIdPrecedes(xmin, OldestXmin))
+					rwstate->all_visible = false;
 				break;
 			case HEAPTUPLE_INSERT_IN_PROGRESS:
 
@@ -873,6 +882,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
 					elog(WARNING, "concurrent insert in progress within table \"%s\"",
 						 RelationGetRelationName(OldHeap));
 				/* treat as live */
+				rwstate->all_visible = false;
 				isdead = false;
 				break;
 			case HEAPTUPLE_DELETE_IN_PROGRESS:
@@ -886,6 +896,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
 						 RelationGetRelationName(OldHeap));
 				/* treat as recently dead */
 				*tups_recently_dead += 1;
+				rwstate->all_visible = false;
 				isdead = false;
 				break;
 			default:
@@ -906,6 +917,8 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
 				*tups_vacuumed += 1;
 				*tups_recently_dead -= 1;
 			}
+			else
+				rwstate->all_visible = false;
 			continue;
 		}
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 93e75dc7c2..b3d63200d4 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
 #include "access/transam.h"
 #include "access/xact.h"
 #include "access/xloginsert.h"
+#include "access/visibilitymap.h"
 #include "catalog/catalog.h"
 #include "common/file_utils.h"
 #include "lib/ilist.h"
@@ -234,6 +235,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 	state->rs_freeze_xid = freeze_xid;
 	state->rs_cutoff_multi = cutoff_multi;
 	state->rs_cxt = rw_cxt;
+	state->all_visible = true;
 
 	/* Initialize hash tables used to track update chains */
 	hash_ctl.keysize = sizeof(TidHashKey);
@@ -647,6 +649,8 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 			 * enforce saveFreeSpace unconditionally.
 			 */
 
+			if (state->all_visible)
+				PageSetAllVisible(page);
 			/* XLOG stuff */
 			if (RelationNeedsWAL(state->rs_new_rel))
 				log_newpage(&state->rs_new_rel->rd_locator,
@@ -655,6 +659,18 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 							page,
 							true);
 
+			if (state->all_visible)
+			{
+				Buffer vmbuffer = InvalidBuffer;
+				visibilitymap_pin(state->rs_new_rel, state->rs_blockno, &vmbuffer);
+				visibilitymap_set_unbuffered(state->rs_new_rel, state->rs_blockno, vmbuffer,
+									VISIBILITYMAP_ALL_VISIBLE | VISIBILITYMAP_ALL_FROZEN);
+				if (BufferIsValid(vmbuffer))
+					ReleaseBuffer(vmbuffer);
+			}
+
+			state->all_visible = true;
+
 			/*
 			 * Now write the page. We say skipFsync = true because there's no
 			 * need for smgr to schedule an fsync for this write; we'll do it
-- 
2.37.2

From 9d5f8b8f9ccf759df1bb078b106047c0e15e6f01 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplage...@gmail.com>
Date: Sun, 3 Sep 2023 16:14:21 -0400
Subject: [PATCH v0 1/3] Extern RewriteStateData

---
 src/backend/access/heap/rewriteheap.c | 29 -----------------------
 src/include/access/rewriteheap.h      | 33 +++++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 424958912c..93e75dc7c2 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -125,35 +125,6 @@
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
-/*
- * State associated with a rewrite operation. This is opaque to the user
- * of the rewrite facility.
- */
-typedef struct RewriteStateData
-{
-	Relation	rs_old_rel;		/* source heap */
-	Relation	rs_new_rel;		/* destination heap */
-	Page		rs_buffer;		/* page currently being built */
-	BlockNumber rs_blockno;		/* block where page will go */
-	bool		rs_buffer_valid;	/* T if any tuples in buffer */
-	bool		rs_logical_rewrite; /* do we need to do logical rewriting */
-	TransactionId rs_oldest_xmin;	/* oldest xmin used by caller to determine
-									 * tuple visibility */
-	TransactionId rs_freeze_xid;	/* Xid that will be used as freeze cutoff
-									 * point */
-	TransactionId rs_logical_xmin;	/* Xid that will be used as cutoff point
-									 * for logical rewrites */
-	MultiXactId rs_cutoff_multi;	/* MultiXactId that will be used as cutoff
-									 * point for multixacts */
-	MemoryContext rs_cxt;		/* for hash tables and entries and tuples in
-								 * them */
-	XLogRecPtr	rs_begin_lsn;	/* XLogInsertLsn when starting the rewrite */
-	HTAB	   *rs_unresolved_tups; /* unmatched A tuples */
-	HTAB	   *rs_old_new_tid_map; /* unmatched B tuples */
-	HTAB	   *rs_logical_mappings;	/* logical remapping files */
-	uint32		rs_num_rewrite_mappings;	/* # in memory mappings */
-}			RewriteStateData;
-
 /*
  * The lookup keys for the hash tables are tuple TID and xmin (we must check
  * both to avoid false matches from dead tuples).  Beware that there is
diff --git a/src/include/access/rewriteheap.h b/src/include/access/rewriteheap.h
index 1125457053..d5108ba2d8 100644
--- a/src/include/access/rewriteheap.h
+++ b/src/include/access/rewriteheap.h
@@ -14,13 +14,46 @@
 #define REWRITE_HEAP_H
 
 #include "access/htup.h"
+#include "access/xlogdefs.h"
 #include "storage/itemptr.h"
 #include "storage/relfilelocator.h"
+#include "storage/bufpage.h"
 #include "utils/relcache.h"
+#include "utils/hsearch.h"
 
 /* struct definition is private to rewriteheap.c */
 typedef struct RewriteStateData *RewriteState;
 
+/*
+ * State associated with a rewrite operation.
+ */
+typedef struct RewriteStateData
+{
+	Relation	rs_old_rel;		/* source heap */
+	Relation	rs_new_rel;		/* destination heap */
+	Page		rs_buffer;		/* page currently being built */
+	BlockNumber rs_blockno;		/* block where page will go */
+	bool all_visible;
+	bool		rs_buffer_valid;	/* T if any tuples in buffer */
+	bool		rs_logical_rewrite; /* do we need to do logical rewriting */
+	TransactionId rs_oldest_xmin;	/* oldest xmin used by caller to determine
+									 * tuple visibility */
+	TransactionId rs_freeze_xid;	/* Xid that will be used as freeze cutoff
+									 * point */
+	TransactionId rs_logical_xmin;	/* Xid that will be used as cutoff point
+									 * for logical rewrites */
+	MultiXactId rs_cutoff_multi;	/* MultiXactId that will be used as cutoff
+									 * point for multixacts */
+	MemoryContext rs_cxt;		/* for hash tables and entries and tuples in
+								 * them */
+	XLogRecPtr	rs_begin_lsn;	/* XLogInsertLsn when starting the rewrite */
+	HTAB	   *rs_unresolved_tups; /* unmatched A tuples */
+	HTAB	   *rs_old_new_tid_map; /* unmatched B tuples */
+	HTAB	   *rs_logical_mappings;	/* logical remapping files */
+	uint32		rs_num_rewrite_mappings;	/* # in memory mappings */
+}			RewriteStateData;
+
+
 extern RewriteState begin_heap_rewrite(Relation old_heap, Relation new_heap,
 									   TransactionId oldest_xmin, TransactionId freeze_xid,
 									   MultiXactId cutoff_multi);
-- 
2.37.2

From a80f1b3dbd9cc0bf060673fd860e0d59d2ca2159 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplage...@gmail.com>
Date: Sun, 3 Sep 2023 16:14:56 -0400
Subject: [PATCH v0 2/3] VM update for a heap block not in SB

WIP: needs to emit WAL, do error checking, and handle more conditions
than it currently does
---
 src/backend/access/heap/visibilitymap.c | 29 +++++++++++++++++++++++++
 src/include/access/visibilitymap.h      |  3 +++
 2 files changed, 32 insertions(+)

diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 2e18cd88bc..adbc62dd7a 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -315,6 +315,35 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 	LockBuffer(vmBuf, BUFFER_LOCK_UNLOCK);
 }
 
+/*
+ * TODO: emit WAL
+ */
+void
+visibilitymap_set_unbuffered(Relation rel, BlockNumber heap_block,
+		Buffer vm_buffer, uint8 flags)
+{
+	uint32		mapByte = HEAPBLK_TO_MAPBYTE(heap_block);
+	uint8		mapOffset = HEAPBLK_TO_OFFSET(heap_block);
+	Page		page;
+	uint8	   *map;
+
+	page = BufferGetPage(vm_buffer);
+	map = (uint8 *) PageGetContents(page);
+	LockBuffer(vm_buffer, BUFFER_LOCK_EXCLUSIVE);
+
+	if (flags != (map[mapByte] >> mapOffset & VISIBILITYMAP_VALID_BITS))
+	{
+		START_CRIT_SECTION();
+
+		map[mapByte] |= (flags << mapOffset);
+		MarkBufferDirty(vm_buffer);
+
+		END_CRIT_SECTION();
+	}
+
+	LockBuffer(vm_buffer, BUFFER_LOCK_UNLOCK);
+}
+
 /*
  *	visibilitymap_get_status - get status of bits
  *
diff --git a/src/include/access/visibilitymap.h b/src/include/access/visibilitymap.h
index daaa01a257..83ca9b3b73 100644
--- a/src/include/access/visibilitymap.h
+++ b/src/include/access/visibilitymap.h
@@ -34,6 +34,9 @@ extern bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf);
 extern void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 							  XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid,
 							  uint8 flags);
+
+extern void
+visibilitymap_set_unbuffered(Relation rel, BlockNumber heap_block, Buffer vm_buffer, uint8 flags);
 extern uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf);
 extern void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen);
 extern BlockNumber visibilitymap_prepare_truncate(Relation rel,
-- 
2.37.2

Reply via email to