From c405c4ba033b93149b727ea6b7d7a6e3e717a8b9 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Sun, 5 Apr 2026 16:10:06 -0400
Subject: [PATCH v8-alt 2/7] separate shared instrumentation initialization

---
 src/backend/access/index/indexam.c         | 51 ++---------
 src/backend/executor/execParallel.c        | 40 ++++++---
 src/backend/executor/nodeBitmapIndexscan.c | 10 ++-
 src/backend/executor/nodeIndexonlyscan.c   | 99 ++++++++++++----------
 src/backend/executor/nodeIndexscan.c       | 99 ++++++++++++----------
 src/include/access/genam.h                 |  7 +-
 src/include/access/relscan.h               |  1 -
 src/include/executor/execParallel.h        |  8 ++
 src/include/executor/nodeIndexonlyscan.h   |  6 ++
 src/include/executor/nodeIndexscan.h       |  6 ++
 10 files changed, 174 insertions(+), 153 deletions(-)

diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 23288a4f994..42e74b2442d 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -465,41 +465,26 @@ index_restrpos(IndexScanDesc scan)
 /*
  * index_parallelscan_estimate - estimate shared memory for parallel scan
  *
- * When instrument=true, estimate includes SharedIndexScanInstrumentation
- * space.  When parallel_aware=true, estimate includes whatever space the
- * index AM's amestimateparallelscan routine requested when called.
+ * Estimates the space needed for the ParallelIndexScanDesc, including the
+ * snapshot and any AM-specific parallel scan state.
  */
 Size
 index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
-							Snapshot snapshot, bool instrument,
-							bool parallel_aware, int nworkers)
+							Snapshot snapshot)
 {
 	Size		nbytes;
 
-	Assert(instrument || parallel_aware);
-
 	RELATION_CHECKS;
 
 	nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data);
 	nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot));
 	nbytes = MAXALIGN(nbytes);
 
-	if (instrument)
-	{
-		Size		sharedinfosz;
-
-		sharedinfosz = offsetof(SharedIndexScanInstrumentation, winstrument) +
-			nworkers * sizeof(IndexScanInstrumentation);
-		nbytes = add_size(nbytes, sharedinfosz);
-		nbytes = MAXALIGN(nbytes);
-	}
-
 	/*
 	 * If parallel scan index AM interface can't be used (or index AM provides
 	 * no such interface), assume there is no AM-specific data needed
 	 */
-	if (parallel_aware &&
-		indexRelation->rd_indam->amestimateparallelscan != NULL)
+	if (indexRelation->rd_indam->amestimateparallelscan != NULL)
 		nbytes = add_size(nbytes,
 						  indexRelation->rd_indam->amestimateparallelscan(indexRelation,
 																		  nkeys,
@@ -511,7 +496,7 @@ index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
 /*
  * index_parallelscan_initialize - initialize parallel scan
  *
- * We initialize both the ParallelIndexScanDesc proper and the AM-specific
+ * We initialize the ParallelIndexScanDesc proper and the AM-specific
  * information which follows it.
  *
  * This function calls access method specific initialization routine to
@@ -520,15 +505,11 @@ index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
  */
 void
 index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
-							  Snapshot snapshot, bool instrument,
-							  bool parallel_aware, int nworkers,
-							  SharedIndexScanInstrumentation **sharedinfo,
+							  Snapshot snapshot,
 							  ParallelIndexScanDesc target)
 {
 	Size		offset;
 
-	Assert(instrument || parallel_aware);
-
 	RELATION_CHECKS;
 
 	offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data),
@@ -537,29 +518,11 @@ index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
 
 	target->ps_locator = heapRelation->rd_locator;
 	target->ps_indexlocator = indexRelation->rd_locator;
-	target->ps_offset_ins = 0;
 	target->ps_offset_am = 0;
 	SerializeSnapshot(snapshot, target->ps_snapshot_data);
 
-	if (instrument)
-	{
-		Size		sharedinfosz;
-
-		target->ps_offset_ins = offset;
-		sharedinfosz = offsetof(SharedIndexScanInstrumentation, winstrument) +
-			nworkers * sizeof(IndexScanInstrumentation);
-		offset = add_size(offset, sharedinfosz);
-		offset = MAXALIGN(offset);
-
-		/* Set leader's *sharedinfo pointer, and initialize stats */
-		*sharedinfo = (SharedIndexScanInstrumentation *)
-			OffsetToPointer(target, target->ps_offset_ins);
-		memset(*sharedinfo, 0, sharedinfosz);
-		(*sharedinfo)->num_workers = nworkers;
-	}
-
 	/* aminitparallelscan is optional; assume no-op if not provided by AM */
-	if (parallel_aware && indexRelation->rd_indam->aminitparallelscan != NULL)
+	if (indexRelation->rd_indam->aminitparallelscan != NULL)
 	{
 		void	   *amtarget;
 
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 755191b51ef..10d2909892c 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -255,14 +255,20 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
 									e->pcxt);
 			break;
 		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexScanEstimate((IndexScanState *) planstate,
+									  e->pcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexScanEstimate((IndexScanState *) planstate,
-								  e->pcxt);
+			ExecIndexScanInstrumentEstimate((IndexScanState *) planstate,
+											e->pcxt);
 			break;
 		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
+										  e->pcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
-									  e->pcxt);
+			ExecIndexOnlyScanInstrumentEstimate((IndexOnlyScanState *) planstate,
+												e->pcxt);
 			break;
 		case T_BitmapIndexScanState:
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
@@ -489,13 +495,20 @@ ExecParallelInitializeDSM(PlanState *planstate,
 										 d->pcxt);
 			break;
 		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexScanInitializeDSM((IndexScanState *) planstate,
+										   d->pcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexScanInitializeDSM((IndexScanState *) planstate, d->pcxt);
+			ExecIndexScanInstrumentInitDSM((IndexScanState *) planstate,
+										   d->pcxt);
 			break;
 		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
+											   d->pcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
-										   d->pcxt);
+			ExecIndexOnlyScanInstrumentInitDSM((IndexOnlyScanState *) planstate,
+											   d->pcxt);
 			break;
 		case T_BitmapIndexScanState:
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
@@ -1367,13 +1380,20 @@ ExecParallelInitializeWorker(PlanState *planstate, ParallelWorkerContext *pwcxt)
 				ExecSeqScanInitializeWorker((SeqScanState *) planstate, pwcxt);
 			break;
 		case T_IndexScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexScanInitializeWorker((IndexScanState *) planstate,
+											  pwcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexScanInitializeWorker((IndexScanState *) planstate, pwcxt);
+			ExecIndexScanInstrumentInitWorker((IndexScanState *) planstate,
+											  pwcxt);
 			break;
 		case T_IndexOnlyScanState:
+			if (planstate->plan->parallel_aware)
+				ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate,
+												  pwcxt);
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
-			ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate,
-											  pwcxt);
+			ExecIndexOnlyScanInstrumentInitWorker((IndexOnlyScanState *) planstate,
+												  pwcxt);
 			break;
 		case T_BitmapIndexScanState:
 			/* even when not parallel-aware, for EXPLAIN ANALYZE */
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 70c55ee6d61..d0d78447f98 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -22,6 +22,7 @@
 #include "postgres.h"
 
 #include "access/genam.h"
+#include "executor/execParallel.h"
 #include "executor/executor.h"
 #include "executor/instrument.h"
 #include "executor/nodeBitmapIndexscan.h"
@@ -394,7 +395,9 @@ ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node,
 	node->biss_SharedInfo =
 		(SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc,
 															size);
-	shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
+	shm_toc_insert(pcxt->toc,
+				   node->ss.ps.plan->plan_node_id +
+				   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
 				   node->biss_SharedInfo);
 
 	/* Each per-worker area must start out as zeroes */
@@ -417,7 +420,10 @@ ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
 		return;
 
 	node->biss_SharedInfo = (SharedIndexScanInstrumentation *)
-		shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
+		shm_toc_lookup(pwcxt->toc,
+					   node->ss.ps.plan->plan_node_id +
+					   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+					   false);
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index de6154fd541..b76a1ea8110 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -36,6 +36,7 @@
 #include "access/tupdesc.h"
 #include "access/visibilitymap.h"
 #include "catalog/pg_type.h"
+#include "executor/execParallel.h"
 #include "executor/executor.h"
 #include "executor/instrument.h"
 #include "executor/nodeIndexonlyscan.h"
@@ -736,21 +737,11 @@ ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
 						  ParallelContext *pcxt)
 {
 	EState	   *estate = node->ss.ps.state;
-	bool		instrument = (node->ss.ps.instrument != NULL);
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
 													  node->ioss_NumScanKeys,
 													  node->ioss_NumOrderByKeys,
-													  estate->es_snapshot,
-													  instrument, parallel_aware,
-													  pcxt->nworkers);
+													  estate->es_snapshot);
 	shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
 	shm_toc_estimate_keys(&pcxt->estimator, 1);
 }
@@ -767,29 +758,14 @@ ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
 {
 	EState	   *estate = node->ss.ps.state;
 	ParallelIndexScanDesc piscan;
-	bool		instrument = node->ss.ps.instrument != NULL;
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
 	index_parallelscan_initialize(node->ss.ss_currentRelation,
 								  node->ioss_RelationDesc,
 								  estate->es_snapshot,
-								  instrument, parallel_aware, pcxt->nworkers,
-								  &node->ioss_SharedInfo, piscan);
+								  piscan);
 	shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
 
-	if (!parallel_aware)
-	{
-		/* Only here to initialize SharedInfo in DSM */
-		return;
-	}
-
 	node->ioss_ScanDesc =
 		index_beginscan_parallel(node->ss.ss_currentRelation,
 								 node->ioss_RelationDesc,
@@ -837,27 +813,9 @@ ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
 								  ParallelWorkerContext *pwcxt)
 {
 	ParallelIndexScanDesc piscan;
-	bool		instrument = node->ss.ps.instrument != NULL;
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
 
-	if (instrument)
-		node->ioss_SharedInfo = (SharedIndexScanInstrumentation *)
-			OffsetToPointer(piscan, piscan->ps_offset_ins);
-
-	if (!parallel_aware)
-	{
-		/* Only here to set up worker node's SharedInfo */
-		return;
-	}
-
 	node->ioss_ScanDesc =
 		index_beginscan_parallel(node->ss.ss_currentRelation,
 								 node->ioss_RelationDesc,
@@ -879,6 +837,57 @@ ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
 					 node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
 }
 
+void
+ExecIndexOnlyScanInstrumentEstimate(IndexOnlyScanState *node,
+									ParallelContext *pcxt)
+{
+	Size		size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+
+	size = offsetof(SharedIndexScanInstrumentation, winstrument) +
+		pcxt->nworkers * sizeof(IndexScanInstrumentation);
+	shm_toc_estimate_chunk(&pcxt->estimator, size);
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+void
+ExecIndexOnlyScanInstrumentInitDSM(IndexOnlyScanState *node,
+								   ParallelContext *pcxt)
+{
+	Size		size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+
+	size = offsetof(SharedIndexScanInstrumentation, winstrument) +
+		pcxt->nworkers * sizeof(IndexScanInstrumentation);
+	node->ioss_SharedInfo =
+		(SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
+
+	/* Each per-worker area must start out as zeroes */
+	memset(node->ioss_SharedInfo, 0, size);
+	node->ioss_SharedInfo->num_workers = pcxt->nworkers;
+	shm_toc_insert(pcxt->toc,
+				   node->ss.ps.plan->plan_node_id +
+				   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+				   node->ioss_SharedInfo);
+}
+
+void
+ExecIndexOnlyScanInstrumentInitWorker(IndexOnlyScanState *node,
+									  ParallelWorkerContext *pwcxt)
+{
+	node->ioss_SharedInfo = (SharedIndexScanInstrumentation *)
+		shm_toc_lookup(pwcxt->toc,
+					   node->ss.ps.plan->plan_node_id +
+					   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+					   true);
+}
+
 /* ----------------------------------------------------------------
  *		ExecIndexOnlyScanRetrieveInstrumentation
  *
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 1620d146071..9fa8f59d202 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -33,6 +33,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "catalog/pg_am.h"
+#include "executor/execParallel.h"
 #include "executor/executor.h"
 #include "executor/instrument.h"
 #include "executor/nodeIndexscan.h"
@@ -1674,21 +1675,11 @@ ExecIndexScanEstimate(IndexScanState *node,
 					  ParallelContext *pcxt)
 {
 	EState	   *estate = node->ss.ps.state;
-	bool		instrument = node->ss.ps.instrument != NULL;
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	node->iss_PscanLen = index_parallelscan_estimate(node->iss_RelationDesc,
 													 node->iss_NumScanKeys,
 													 node->iss_NumOrderByKeys,
-													 estate->es_snapshot,
-													 instrument, parallel_aware,
-													 pcxt->nworkers);
+													 estate->es_snapshot);
 	shm_toc_estimate_chunk(&pcxt->estimator, node->iss_PscanLen);
 	shm_toc_estimate_keys(&pcxt->estimator, 1);
 }
@@ -1705,29 +1696,14 @@ ExecIndexScanInitializeDSM(IndexScanState *node,
 {
 	EState	   *estate = node->ss.ps.state;
 	ParallelIndexScanDesc piscan;
-	bool		instrument = node->ss.ps.instrument != NULL;
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
 	index_parallelscan_initialize(node->ss.ss_currentRelation,
 								  node->iss_RelationDesc,
 								  estate->es_snapshot,
-								  instrument, parallel_aware, pcxt->nworkers,
-								  &node->iss_SharedInfo, piscan);
+								  piscan);
 	shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
 
-	if (!parallel_aware)
-	{
-		/* Only here to initialize SharedInfo in DSM */
-		return;
-	}
-
 	node->iss_ScanDesc =
 		index_beginscan_parallel(node->ss.ss_currentRelation,
 								 node->iss_RelationDesc,
@@ -1773,27 +1749,9 @@ ExecIndexScanInitializeWorker(IndexScanState *node,
 							  ParallelWorkerContext *pwcxt)
 {
 	ParallelIndexScanDesc piscan;
-	bool		instrument = node->ss.ps.instrument != NULL;
-	bool		parallel_aware = node->ss.ps.plan->parallel_aware;
-
-	if (!instrument && !parallel_aware)
-	{
-		/* No DSM required by the scan */
-		return;
-	}
 
 	piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
 
-	if (instrument)
-		node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
-			OffsetToPointer(piscan, piscan->ps_offset_ins);
-
-	if (!parallel_aware)
-	{
-		/* Only here to set up worker node's SharedInfo */
-		return;
-	}
-
 	node->iss_ScanDesc =
 		index_beginscan_parallel(node->ss.ss_currentRelation,
 								 node->iss_RelationDesc,
@@ -1814,6 +1772,57 @@ ExecIndexScanInitializeWorker(IndexScanState *node,
 					 node->iss_OrderByKeys, node->iss_NumOrderByKeys);
 }
 
+void
+ExecIndexScanInstrumentEstimate(IndexScanState *node,
+								ParallelContext *pcxt)
+{
+	Size		size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+
+	size = offsetof(SharedIndexScanInstrumentation, winstrument) +
+		pcxt->nworkers * sizeof(IndexScanInstrumentation);
+	shm_toc_estimate_chunk(&pcxt->estimator, size);
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+void
+ExecIndexScanInstrumentInitDSM(IndexScanState *node,
+							   ParallelContext *pcxt)
+{
+	Size		size;
+
+	/* don't need this if not instrumenting or no workers */
+	if (!node->ss.ps.instrument || pcxt->nworkers == 0)
+		return;
+
+	size = offsetof(SharedIndexScanInstrumentation, winstrument) +
+		pcxt->nworkers * sizeof(IndexScanInstrumentation);
+	node->iss_SharedInfo =
+		(SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
+
+	/* Each per-worker area must start out as zeroes */
+	memset(node->iss_SharedInfo, 0, size);
+	node->iss_SharedInfo->num_workers = pcxt->nworkers;
+	shm_toc_insert(pcxt->toc,
+				   node->ss.ps.plan->plan_node_id +
+				   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+				   node->iss_SharedInfo);
+}
+
+void
+ExecIndexScanInstrumentInitWorker(IndexScanState *node,
+								  ParallelWorkerContext *pwcxt)
+{
+	node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
+		shm_toc_lookup(pwcxt->toc,
+					   node->ss.ps.plan->plan_node_id +
+					   PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
+					   true);
+}
+
 /* ----------------------------------------------------------------
  * ExecIndexScanRetrieveInstrumentation
  *
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index b69320a7fc8..68bfe405db3 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -171,14 +171,9 @@ extern void index_endscan(IndexScanDesc scan);
 extern void index_markpos(IndexScanDesc scan);
 extern void index_restrpos(IndexScanDesc scan);
 extern Size index_parallelscan_estimate(Relation indexRelation,
-										int nkeys, int norderbys, Snapshot snapshot,
-										bool instrument, bool parallel_aware,
-										int nworkers);
+										int nkeys, int norderbys, Snapshot snapshot);
 extern void index_parallelscan_initialize(Relation heapRelation,
 										  Relation indexRelation, Snapshot snapshot,
-										  bool instrument, bool parallel_aware,
-										  int nworkers,
-										  SharedIndexScanInstrumentation **sharedinfo,
 										  ParallelIndexScanDesc target);
 extern void index_parallelrescan(IndexScanDesc scan);
 extern IndexScanDesc index_beginscan_parallel(Relation heaprel,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 960abf6c214..fd2076c582a 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -203,7 +203,6 @@ typedef struct ParallelIndexScanDescData
 {
 	RelFileLocator ps_locator;	/* physical table relation to scan */
 	RelFileLocator ps_indexlocator; /* physical index relation to scan */
-	Size		ps_offset_ins;	/* Offset to SharedIndexScanInstrumentation */
 	Size		ps_offset_am;	/* Offset to am-specific structure */
 	char		ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
 }			ParallelIndexScanDescData;
diff --git a/src/include/executor/execParallel.h b/src/include/executor/execParallel.h
index 5a2034811d5..f889034a5f5 100644
--- a/src/include/executor/execParallel.h
+++ b/src/include/executor/execParallel.h
@@ -19,6 +19,14 @@
 #include "nodes/plannodes.h"
 #include "utils/dsa.h"
 
+/*
+ * Offset added to plan_node_id to create a second TOC key for per-worker scan
+ * instrumentation. This allows scan nodes to store instrumentation in a
+ * separate DSM allocation from their parallel scan descriptor, avoiding the
+ * need to pack both into a single allocation with offset arithmetic.
+ */
+#define PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET	UINT64CONST(0xD000000000000000)
+
 typedef struct SharedExecutorInstrumentation SharedExecutorInstrumentation;
 
 typedef struct ParallelExecutorInfo
diff --git a/src/include/executor/nodeIndexonlyscan.h b/src/include/executor/nodeIndexonlyscan.h
index ba807b8d8d9..b686244eb91 100644
--- a/src/include/executor/nodeIndexonlyscan.h
+++ b/src/include/executor/nodeIndexonlyscan.h
@@ -32,6 +32,12 @@ extern void ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
 											 ParallelContext *pcxt);
 extern void ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
 											  ParallelWorkerContext *pwcxt);
+extern void ExecIndexOnlyScanInstrumentEstimate(IndexOnlyScanState *node,
+												ParallelContext *pcxt);
+extern void ExecIndexOnlyScanInstrumentInitDSM(IndexOnlyScanState *node,
+											   ParallelContext *pcxt);
+extern void ExecIndexOnlyScanInstrumentInitWorker(IndexOnlyScanState *node,
+												  ParallelWorkerContext *pwcxt);
 extern void ExecIndexOnlyScanRetrieveInstrumentation(IndexOnlyScanState *node);
 
 #endif							/* NODEINDEXONLYSCAN_H */
diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h
index 803be5b08df..c60cbc9a94b 100644
--- a/src/include/executor/nodeIndexscan.h
+++ b/src/include/executor/nodeIndexscan.h
@@ -28,6 +28,12 @@ extern void ExecIndexScanInitializeDSM(IndexScanState *node, ParallelContext *pc
 extern void ExecIndexScanReInitializeDSM(IndexScanState *node, ParallelContext *pcxt);
 extern void ExecIndexScanInitializeWorker(IndexScanState *node,
 										  ParallelWorkerContext *pwcxt);
+extern void ExecIndexScanInstrumentEstimate(IndexScanState *node,
+											ParallelContext *pcxt);
+extern void ExecIndexScanInstrumentInitDSM(IndexScanState *node,
+										   ParallelContext *pcxt);
+extern void ExecIndexScanInstrumentInitWorker(IndexScanState *node,
+											  ParallelWorkerContext *pwcxt);
 extern void ExecIndexScanRetrieveInstrumentation(IndexScanState *node);
 
 /*
-- 
2.43.0

