summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
authorPeter Geoghegan2025-03-11 13:20:50 +0000
committerPeter Geoghegan2025-03-11 13:20:50 +0000
commit0fbceae841cb5a31b13d3f284ac8fdd19822eceb (patch)
tree0a4de79065d137bc86c1455adc317373b490a880 /src/include
parent12c5f797ea6a8e96de661e3838410b9775061796 (diff)
Show index search count in EXPLAIN ANALYZE, take 2.
Expose the count of index searches/index descents in EXPLAIN ANALYZE's output for index scan/index-only scan/bitmap index scan nodes. This information is particularly useful with scans that use ScalarArrayOp quals, where the number of index searches can be unpredictable due to implementation details that interact with physical index characteristics (at least with nbtree SAOP scans, since Postgres 17 commit 5bf748b8). The information shown also provides useful context when EXPLAIN ANALYZE runs a plan with an index scan node that successfully applied the skip scan optimization (set to be added to nbtree by an upcoming patch). The instrumentation works by teaching all index AMs to increment a new nsearches counter whenever a new index search begins. The counter is incremented at exactly the same point that index AMs already increment the pg_stat_*_indexes.idx_scan counter (we're counting the same event, but at the scan level rather than the relation level). Parallel queries have workers copy their local counter struct into shared memory when an index scan node ends -- even when it isn't a parallel aware scan node. An earlier version of this patch that only worked with parallel aware scans became commit 5ead85fb (though that was quickly reverted by commit d00107cd following "debug_parallel_query=regress" buildfarm failures). Our approach doesn't match the approach used when tracking other index scan related costs (e.g., "Rows Removed by Filter:"). It is comparable to the approach used in similar cases involving costs that are only readily accessible inside an access method, not from the executor proper (e.g., "Heap Blocks:" output for a Bitmap Heap Scan, which was recently enhanced to show per-worker costs by commit 5a1e6df3, using essentially the same scheme as the one used here). It is necessary for index AMs to have direct responsibility for maintaining the new counter, since the counter might need to be incremented multiple times per amgettuple call (or per amgetbitmap call). But it is also necessary for the executor proper to manage the shared memory now used to transfer each worker's counter struct to the leader. Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Robert Haas <robertmhaas@gmail.com> Reviewed-By: Tomas Vondra <tomas@vondra.me> Reviewed-By: Masahiro Ikeda <ikedamsh@oss.nttdata.com> Reviewed-By: Matthias van de Meent <boekewurm+postgres@gmail.com> Discussion: https://postgr.es/m/CAH2-WzkRqvaqR2CTNqTZP0z6FuL4-3ED6eQB0yx38XBNj1v-4Q@mail.gmail.com Discussion: https://postgr.es/m/CAH2-Wz=PKR6rB7qbx+Vnd7eqeB5VTcrW=iJvAsTsKbdG+kW_UA@mail.gmail.com
Diffstat (limited to 'src/include')
-rw-r--r--src/include/access/genam.h34
-rw-r--r--src/include/access/relscan.h11
-rw-r--r--src/include/executor/nodeBitmapIndexscan.h6
-rw-r--r--src/include/executor/nodeIndexonlyscan.h1
-rw-r--r--src/include/executor/nodeIndexscan.h1
-rw-r--r--src/include/nodes/execnodes.h12
6 files changed, 62 insertions, 3 deletions
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 1be8739573f..5b2ab181b5f 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -27,6 +27,27 @@
struct IndexInfo;
/*
+ * Struct for statistics maintained by amgettuple and amgetbitmap
+ *
+ * Note: IndexScanInstrumentation can't contain any pointers, since it is
+ * copied into a SharedIndexScanInstrumentation during parallel scans
+ */
+typedef struct IndexScanInstrumentation
+{
+ /* Index search count (incremented with pgstat_count_index_scan call) */
+ uint64 nsearches;
+} IndexScanInstrumentation;
+
+/*
+ * Struct for every worker's IndexScanInstrumentation, stored in shared memory
+ */
+typedef struct SharedIndexScanInstrumentation
+{
+ int num_workers;
+ IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER];
+} SharedIndexScanInstrumentation;
+
+/*
* Struct for statistics returned by ambuild
*/
typedef struct IndexBuildResult
@@ -157,9 +178,11 @@ extern void index_insert_cleanup(Relation indexRelation,
extern IndexScanDesc index_beginscan(Relation heapRelation,
Relation indexRelation,
Snapshot snapshot,
+ IndexScanInstrumentation *instrument,
int nkeys, int norderbys);
extern IndexScanDesc index_beginscan_bitmap(Relation indexRelation,
Snapshot snapshot,
+ IndexScanInstrumentation *instrument,
int nkeys);
extern void index_rescan(IndexScanDesc scan,
ScanKey keys, int nkeys,
@@ -168,13 +191,20 @@ 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);
+ int nkeys, int norderbys, Snapshot snapshot,
+ bool instrument, bool parallel_aware,
+ int nworkers);
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,
- Relation indexrel, int nkeys, int norderbys,
+ Relation indexrel,
+ IndexScanInstrumentation *instrument,
+ int nkeys, int norderbys,
ParallelIndexScanDesc pscan);
extern ItemPointer index_getnext_tid(IndexScanDesc scan,
ScanDirection direction);
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index dc6e0184284..b5e0fb386c0 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -123,6 +123,8 @@ typedef struct IndexFetchTableData
Relation rel;
} IndexFetchTableData;
+struct IndexScanInstrumentation;
+
/*
* We use the same IndexScanDescData structure for both amgettuple-based
* and amgetbitmap-based index scans. Some fields are only relevant in
@@ -151,6 +153,12 @@ typedef struct IndexScanDescData
void *opaque; /* access-method-specific info */
/*
+ * Instrumentation counters maintained by all index AMs during both
+ * amgettuple calls and amgetbitmap calls (unless field remains NULL)
+ */
+ struct IndexScanInstrumentation *instrument;
+
+ /*
* In an index-only scan, a successful amgettuple call must fill either
* xs_itup (and xs_itupdesc) or xs_hitup (and xs_hitupdesc) to provide the
* data returned by the scan. It can fill both, in which case the heap
@@ -188,7 +196,8 @@ typedef struct ParallelIndexScanDescData
{
RelFileLocator ps_locator; /* physical table relation to scan */
RelFileLocator ps_indexlocator; /* physical index relation to scan */
- Size ps_offset; /* Offset in bytes of am specific structure */
+ 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/nodeBitmapIndexscan.h b/src/include/executor/nodeBitmapIndexscan.h
index b51cb184e0d..b6a5ae25ed1 100644
--- a/src/include/executor/nodeBitmapIndexscan.h
+++ b/src/include/executor/nodeBitmapIndexscan.h
@@ -14,11 +14,17 @@
#ifndef NODEBITMAPINDEXSCAN_H
#define NODEBITMAPINDEXSCAN_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
extern BitmapIndexScanState *ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags);
extern Node *MultiExecBitmapIndexScan(BitmapIndexScanState *node);
extern void ExecEndBitmapIndexScan(BitmapIndexScanState *node);
extern void ExecReScanBitmapIndexScan(BitmapIndexScanState *node);
+extern void ExecBitmapIndexScanEstimate(BitmapIndexScanState *node, ParallelContext *pcxt);
+extern void ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node, ParallelContext *pcxt);
+extern void ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
+ ParallelWorkerContext *pwcxt);
+extern void ExecBitmapIndexScanRetrieveInstrumentation(BitmapIndexScanState *node);
#endif /* NODEBITMAPINDEXSCAN_H */
diff --git a/src/include/executor/nodeIndexonlyscan.h b/src/include/executor/nodeIndexonlyscan.h
index c27d8eb6d4d..ae85dee6d8f 100644
--- a/src/include/executor/nodeIndexonlyscan.h
+++ b/src/include/executor/nodeIndexonlyscan.h
@@ -32,5 +32,6 @@ extern void ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
ParallelContext *pcxt);
extern void ExecIndexOnlyScanInitializeWorker(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 1c63d0615fd..08f0a148db6 100644
--- a/src/include/executor/nodeIndexscan.h
+++ b/src/include/executor/nodeIndexscan.h
@@ -28,6 +28,7 @@ extern void ExecIndexScanInitializeDSM(IndexScanState *node, ParallelContext *pc
extern void ExecIndexScanReInitializeDSM(IndexScanState *node, ParallelContext *pcxt);
extern void ExecIndexScanInitializeWorker(IndexScanState *node,
ParallelWorkerContext *pwcxt);
+extern void ExecIndexScanRetrieveInstrumentation(IndexScanState *node);
/*
* These routines are exported to share code with nodeIndexonlyscan.c and
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a323fa98bbb..575b0b1bd24 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1680,6 +1680,8 @@ typedef struct
* RuntimeContext expr context for evaling runtime Skeys
* RelationDesc index relation descriptor
* ScanDesc index scan descriptor
+ * Instrument local index scan instrumentation
+ * SharedInfo parallel worker instrumentation (no leader entry)
*
* ReorderQueue tuples that need reordering due to re-check
* ReachedEnd have we fetched all tuples from index already?
@@ -1706,6 +1708,8 @@ typedef struct IndexScanState
ExprContext *iss_RuntimeContext;
Relation iss_RelationDesc;
struct IndexScanDescData *iss_ScanDesc;
+ IndexScanInstrumentation iss_Instrument;
+ SharedIndexScanInstrumentation *iss_SharedInfo;
/* These are needed for re-checking ORDER BY expr ordering */
pairingheap *iss_ReorderQueue;
@@ -1732,6 +1736,8 @@ typedef struct IndexScanState
* RuntimeContext expr context for evaling runtime Skeys
* RelationDesc index relation descriptor
* ScanDesc index scan descriptor
+ * Instrument local index scan instrumentation
+ * SharedInfo parallel worker instrumentation (no leader entry)
* TableSlot slot for holding tuples fetched from the table
* VMBuffer buffer in use for visibility map testing, if any
* PscanLen size of parallel index-only scan descriptor
@@ -1753,6 +1759,8 @@ typedef struct IndexOnlyScanState
ExprContext *ioss_RuntimeContext;
Relation ioss_RelationDesc;
struct IndexScanDescData *ioss_ScanDesc;
+ IndexScanInstrumentation ioss_Instrument;
+ SharedIndexScanInstrumentation *ioss_SharedInfo;
TupleTableSlot *ioss_TableSlot;
Buffer ioss_VMBuffer;
Size ioss_PscanLen;
@@ -1774,6 +1782,8 @@ typedef struct IndexOnlyScanState
* RuntimeContext expr context for evaling runtime Skeys
* RelationDesc index relation descriptor
* ScanDesc index scan descriptor
+ * Instrument local index scan instrumentation
+ * SharedInfo parallel worker instrumentation (no leader entry)
* ----------------
*/
typedef struct BitmapIndexScanState
@@ -1790,6 +1800,8 @@ typedef struct BitmapIndexScanState
ExprContext *biss_RuntimeContext;
Relation biss_RelationDesc;
struct IndexScanDescData *biss_ScanDesc;
+ IndexScanInstrumentation biss_Instrument;
+ SharedIndexScanInstrumentation *biss_SharedInfo;
} BitmapIndexScanState;
/* ----------------