From a2f606d5dba1d7902f925a1c9af95cd27ade7488 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Mon, 29 Jul 2024 18:03:44 +0200
Subject: [PATCH v20240730 2/6] atomic LSN counter

---
 src/backend/access/gist/gist.c       | 24 ++++++++++++------------
 src/backend/access/gist/gistbuild.c  | 19 +++++++++++++++----
 src/backend/access/gist/gistutil.c   | 10 +++-------
 src/backend/access/gist/gistvacuum.c |  6 +++---
 src/include/access/gist_private.h    |  8 ++++----
 5 files changed, 37 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index f5f56fb2503..63d5580120d 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -182,7 +182,7 @@ gistinsert(Relation r, Datum *values, bool *isnull,
 						 values, isnull, true /* size is currently bogus */ );
 	itup->t_tid = *ht_ctid;
 
-	gistdoinsert(r, itup, 0, giststate, heapRel, false, false);
+	gistdoinsert(r, itup, 0, giststate, heapRel, false, NULL);
 
 	/* cleanup */
 	MemoryContextSwitchTo(oldCxt);
@@ -231,7 +231,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 				bool markfollowright,
 				Relation heapRel,
 				bool is_build,
-				bool is_parallel)
+				pg_atomic_uint64 *fakelsn)
 {
 	BlockNumber blkno = BufferGetBlockNumber(buffer);
 	Page		page = BufferGetPage(buffer);
@@ -508,8 +508,8 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 		 */
 		if (is_build)
 		{
-			if (is_parallel)
-				recptr = gistGetFakeLSN(rel, is_parallel);
+			if (fakelsn)
+				recptr = pg_atomic_fetch_add_u64(fakelsn, 1);
 			else
 				recptr = GistBuildLSN;
 		}
@@ -520,7 +520,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 									   dist, oldrlink, oldnsn, leftchildbuf,
 									   markfollowright);
 			else
-				recptr = gistGetFakeLSN(rel, false);
+				recptr = gistGetFakeLSN(rel);
 		}
 
 		for (ptr = dist; ptr; ptr = ptr->next)
@@ -580,8 +580,8 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 
 		if (is_build)
 		{
-			if (is_parallel)
-				recptr = gistGetFakeLSN(rel, is_parallel);
+			if (fakelsn)
+				recptr = pg_atomic_fetch_add_u64(fakelsn, 1);
 			else
 				recptr = GistBuildLSN;
 		}
@@ -603,7 +603,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 										leftchildbuf);
 			}
 			else
-				recptr = gistGetFakeLSN(rel, false);
+				recptr = gistGetFakeLSN(rel);
 		}
 		PageSetLSN(page, recptr);
 
@@ -647,7 +647,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 void
 gistdoinsert(Relation r, IndexTuple itup, Size freespace,
 			 GISTSTATE *giststate, Relation heapRel, bool is_build,
-			 bool is_parallel)
+			 pg_atomic_uint64 *fakelsn)
 {
 	ItemId		iid;
 	IndexTuple	idxtuple;
@@ -661,7 +661,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace,
 	state.r = r;
 	state.heapRel = heapRel;
 	state.is_build = is_build;
-	state.is_parallel = is_parallel;
+	state.fakelsn = fakelsn;
 
 	/* Start from the root */
 	firststack.blkno = GIST_ROOT_BLKNO;
@@ -1320,7 +1320,7 @@ gistinserttuples(GISTInsertState *state, GISTInsertStack *stack,
 							   true,
 							   state->heapRel,
 							   state->is_build,
-							   state->is_parallel);
+							   state->fakelsn);
 
 	/*
 	 * Before recursing up in case the page was split, release locks on the
@@ -1739,7 +1739,7 @@ gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
 			PageSetLSN(page, recptr);
 		}
 		else
-			PageSetLSN(page, gistGetFakeLSN(rel, false));
+			PageSetLSN(page, gistGetFakeLSN(rel));
 
 		END_CRIT_SECTION();
 	}
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index c8fa67beebb..c7ac2487cda 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -140,6 +140,9 @@ typedef struct GISTShared
 	double		reltuples;
 	double		indtuples;
 
+	/* Used to generate LSNs during parallel build. */
+	pg_atomic_uint64	parallelLSN;
+
 	/*
 	 * ParallelTableScanDescData data follows. Can't directly embed here, as
 	 * implementations of the parallel table scan desc interface might need
@@ -217,6 +220,7 @@ typedef struct
 	 */
 	bool		is_parallel;
 	GISTLeader *gist_leader;
+	pg_atomic_uint64 *fakelsn;
 
 	/*
 	 * Extra data structures used during a sorting build.
@@ -1043,7 +1047,7 @@ gistBuildCallback(Relation index,
 		 * locked, we call gistdoinsert directly.
 		 */
 		gistdoinsert(index, itup, buildstate->freespace,
-					 buildstate->giststate, buildstate->heaprel, true, false);
+					 buildstate->giststate, buildstate->heaprel, true, NULL);
 	}
 
 	MemoryContextSwitchTo(oldCtx);
@@ -1098,6 +1102,7 @@ gistBuildParallelCallback(Relation index,
 						  void *state)
 {
 	GISTBuildState *buildstate = (GISTBuildState *) state;
+
 	IndexTuple	itup;
 	MemoryContext oldCtx;
 
@@ -1118,7 +1123,8 @@ gistBuildParallelCallback(Relation index,
 	 * locked, we call gistdoinsert directly.
 	 */
 	gistdoinsert(index, itup, buildstate->freespace,
-				 buildstate->giststate, buildstate->heaprel, true, true);
+				 buildstate->giststate, buildstate->heaprel, true,
+				 buildstate->fakelsn);
 
 	MemoryContextSwitchTo(oldCtx);
 	MemoryContextReset(buildstate->giststate->tempCxt);
@@ -1292,8 +1298,7 @@ gistbufferinginserttuples(GISTBuildState *buildstate, Buffer buffer, int level,
 							   InvalidBuffer,
 							   &splitinfo,
 							   false,
-							   buildstate->heaprel, true,
-							   buildstate->is_parallel);
+							   buildstate->heaprel, true, NULL);
 
 	/*
 	 * If this is a root split, update the root path item kept in memory. This
@@ -1925,6 +1930,9 @@ _gist_begin_parallel(GISTBuildState *buildstate, Relation heap, Relation index,
 	gistshared->reltuples = 0.0;
 	gistshared->indtuples = 0.0;
 
+	/* initialize the counter used to generate fake LSNs */
+	pg_atomic_init_u64(&gistshared->parallelLSN, 1);
+
 	table_parallelscan_initialize(heap,
 								  ParallelTableScanFromGistShared(gistshared),
 								  snapshot);
@@ -1974,6 +1982,7 @@ _gist_begin_parallel(GISTBuildState *buildstate, Relation heap, Relation index,
 	/* Save leader state now that it's clear build will be parallel */
 	buildstate->is_parallel = true;
 	buildstate->gist_leader = gistleader;
+	buildstate->fakelsn = &gistshared->parallelLSN;
 
 	/* Join heap scan ourselves */
 	if (leaderparticipates)
@@ -2202,6 +2211,8 @@ _gist_parallel_build_main(dsm_segment *seg, shm_toc *toc)
 	buildstate.is_parallel = true;
 	buildstate.gist_leader = NULL;
 
+	buildstate.fakelsn = &gistshared->parallelLSN;
+
 	/*
 	 * Create a temporary memory context that is reset once for each tuple
 	 * processed.  (Note: we don't bother to make this a child of the
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 733d5849317..78e98d68b15 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1012,7 +1012,7 @@ gistproperty(Oid index_oid, int attno,
  * purpose.
  */
 XLogRecPtr
-gistGetFakeLSN(Relation rel, bool is_parallel)
+gistGetFakeLSN(Relation rel)
 {
 	if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
 	{
@@ -1035,12 +1035,8 @@ gistGetFakeLSN(Relation rel, bool is_parallel)
 		static XLogRecPtr lastlsn = InvalidXLogRecPtr;
 		XLogRecPtr	currlsn = GetXLogInsertRecPtr();
 
-		/*
-		 * Shouldn't be called for WAL-logging relations, but parallell
-		 * builds are an exception - we need the fake LSN to detect
-		 * concurrent changes.
-		 */
-		Assert(is_parallel || !RelationNeedsWAL(rel));
+		/* Shouldn't be called for WAL-logging relations */
+		Assert(!RelationNeedsWAL(rel));
 
 		/* No need for an actual record if we already have a distinct LSN */
 		if (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 082804e9c7d..24fb94f473e 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -181,7 +181,7 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	if (RelationNeedsWAL(rel))
 		vstate.startNSN = GetInsertRecPtr();
 	else
-		vstate.startNSN = gistGetFakeLSN(rel, false);
+		vstate.startNSN = gistGetFakeLSN(rel);
 
 	/*
 	 * The outer loop iterates over all index pages, in physical order (we
@@ -376,7 +376,7 @@ restart:
 				PageSetLSN(page, recptr);
 			}
 			else
-				PageSetLSN(page, gistGetFakeLSN(rel, false));
+				PageSetLSN(page, gistGetFakeLSN(rel));
 
 			END_CRIT_SECTION();
 
@@ -664,7 +664,7 @@ gistdeletepage(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	if (RelationNeedsWAL(info->index))
 		recptr = gistXLogPageDelete(leafBuffer, txid, parentBuffer, downlink);
 	else
-		recptr = gistGetFakeLSN(info->index, false);
+		recptr = gistGetFakeLSN(info->index);
 	PageSetLSN(parentPage, recptr);
 	PageSetLSN(leafPage, recptr);
 
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index d5b22bc1018..09f535ff56e 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -255,7 +255,7 @@ typedef struct
 	Relation	heapRel;
 	Size		freespace;		/* free space to be left */
 	bool		is_build;
-	bool		is_parallel;
+	pg_atomic_uint64 *fakelsn;
 
 	GISTInsertStack *stack;
 } GISTInsertState;
@@ -416,7 +416,7 @@ extern void gistdoinsert(Relation r,
 						 GISTSTATE *giststate,
 						 Relation heapRel,
 						 bool is_build,
-						 bool is_parallel);
+						 pg_atomic_uint64 *fakelsn);
 
 /* A List of these is returned from gistplacetopage() in *splitinfo */
 typedef struct
@@ -434,7 +434,7 @@ extern bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 							bool markfollowright,
 							Relation heapRel,
 							bool is_build,
-							bool is_parallel);
+							pg_atomic_uint64 *fakelsn);
 
 extern SplitPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup,
 								  int len, GISTSTATE *giststate);
@@ -535,7 +535,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
 							 GISTENTRY *entry2, bool isnull2,
 							 Datum *dst, bool *dstisnull);
 
-extern XLogRecPtr gistGetFakeLSN(Relation rel, bool is_parallel);
+extern XLogRecPtr gistGetFakeLSN(Relation rel);
 
 /* gistvacuum.c */
 extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
-- 
2.45.2

