mirror of
https://github.com/postgres/postgres.git
synced 2026-10-31 05:54:36 +03:00
Add table_index_getnext_slot, a new table AM interface that serves as
the single entry point for both plain and index-only index scans that
use amgettuple. The table AM's index_scan_begin callback resolves which
implementation the scan will use and stores it in an IndexScanDesc
function pointer (xs_getnext_slot), which table_index_getnext_slot
dispatches through.
The interface intentionally avoids using fixed TableAmRoutine members,
allowing the table AM to dynamically register an implementation based on
conditions known at the start of each scan. That way a table AM can
provide specialized variants that help the compiler produce more
efficient code. heapam provides two implementations: one for plain
index scans, and one for index-only scans. An upcoming commit that adds
the amgetbatch interface will add two more. (Table AMs can opt to
provide a single generic implementation, or to provide many specialized
implementations, as requirements dictate.)
heapam's xs_getnext_slot implementations make aggressive use of forced
inlining to ensure that plain and index-only code paths are fully
specialized at compile time, without duplicating the code they have in
common. This specialization is necessary to keep icache and iTLB
pressure to a minimum.
The new interface moves visibility map checks required by index-only
scans out of the executor (and selfuncs.c) and into heapam, enabling
batching of visibility map lookups (though for now we continue to just
perform retail lookups). Using the new higher level slot-based
interface greatly simplifies nodeIndexonlyscan.c, which no longer has to
deal with the visibility map directly. More importantly, this is a
significant architectural improvement: table AMs can now implement
index-only scans that are not tied to heapam's visibility map.
Two users of the now removed table_index_fetch_tuple interface,
_bt_check_unique and unique_key_recheck, fundamentally need to pass a
TID to the table AM to perform constraint enforcement. Neither actually
performs an index scan (even though their TIDs are taken from an index),
so neither has any use for most of the index scan machinery. Switch
these callers over to fetch_tid, a new special-purpose table AM
interface for constraint enforcement code. All true index scan callers
now use the new slot-based interface. (Note that fetch_tid doesn't
perform on-access pruning, which matches how things worked prior to
Postgres 12 commits c2fe139c2 and 71bdc99d0.)
The VISITED_PAGES_LIMIT mechanism used by get_actual_variable_range to
cap scan overhead during planning is reworked to go through a new scan
descriptor field (xs_visited_pages_limit), rather than having selfuncs.c
count heap page fetches and terminate the scan itself. This is
necessary because callers that use the new slot-based interface no
longer have direct access to which heap blocks were fetched. Similarly,
nodeIndexonlyscan.c can no longer use InstrCountTuples2 to count heap
fetches during an EXPLAIN ANALYZE. EXPLAIN ANALYZE now obtains this
information from a new IndexScanInstrumentation field, which table AMs
are required to maintain during index-only scans.
Though independently useful, this commit is preparatory work for an
upcoming commit that will add an amgetbatch index AM interface, where
the table AM takes full responsibility for managing the progress of
index scans. The table AM determines when to request the next batch
from the index AM, at a time of its choosing. A follow-on commit will
rely on this to prefetch table blocks during index scans. Having a
single entry point for all index scan callers unambiguously puts the
table AM in control of the scan.
Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Reviewed-by: Rui Zhao <zhaorui126@gmail.com>
Discussion: https://postgr.es/m/CAH2-WzmYqhacBH161peAWb5eF=Ja7CFAQ+0jSEMq=qnfLVTOOg@mail.gmail.com
310 lines
8.7 KiB
C
310 lines
8.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* instrument_node.h
|
|
* Definitions for node-specific support for parallel query instrumentation
|
|
*
|
|
* These structs purposely contain no pointers because they are copied
|
|
* across processes during parallel query execution. Each worker copies its
|
|
* individual information into the container struct at executor shutdown time,
|
|
* to allow the leader to display the information in EXPLAIN ANALYZE.
|
|
*
|
|
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/executor/instrument_node.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef INSTRUMENT_NODE_H
|
|
#define INSTRUMENT_NODE_H
|
|
|
|
/*
|
|
* Offset added to plan_node_id to create a second TOC key for per-worker scan
|
|
* instrumentation. Instrumentation and parallel-awareness are independent, so
|
|
* separate DSM chunks let each be allocated and initialized only when needed.
|
|
* In the future, if nodes need more DSM allocations, we would need a more
|
|
* robust system.
|
|
*/
|
|
#define PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET UINT64CONST(0xD000000000000000)
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for aggregate function execution
|
|
* ---------------------
|
|
*/
|
|
typedef struct AggregateInstrumentation
|
|
{
|
|
Size hash_mem_peak; /* peak hash table memory usage */
|
|
uint64 hash_disk_used; /* kB of disk space used */
|
|
int hash_batches_used; /* batches used during entire execution */
|
|
} AggregateInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker aggregate information
|
|
*/
|
|
typedef struct SharedAggInfo
|
|
{
|
|
int num_workers;
|
|
AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedAggInfo;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information about read streams and I/O
|
|
* ---------------------
|
|
*/
|
|
typedef struct IOStats
|
|
{
|
|
/* number of buffers returned to consumer (for averaging distance) */
|
|
uint64 prefetch_count;
|
|
|
|
/* sum of pinned_buffers sampled at each buffer return */
|
|
uint64 distance_sum;
|
|
|
|
/* maximum actual pinned_buffers observed during the scan */
|
|
int16 distance_max;
|
|
|
|
/* maximum possible look-ahead distance (max_pinned_buffers) */
|
|
int16 distance_capacity;
|
|
|
|
/* number of waits for a read (for the I/O) */
|
|
uint64 wait_count;
|
|
|
|
/* I/O stats */
|
|
uint64 io_count; /* number of I/Os */
|
|
uint64 io_nblocks; /* sum of blocks for all I/Os */
|
|
uint64 io_in_progress; /* sum of in-progress I/Os */
|
|
} IOStats;
|
|
|
|
typedef struct TableScanInstrumentation
|
|
{
|
|
IOStats io;
|
|
} TableScanInstrumentation;
|
|
|
|
/* merge IO statistics from 'src' into 'dst' */
|
|
static inline void
|
|
AccumulateIOStats(IOStats *dst, IOStats *src)
|
|
{
|
|
dst->prefetch_count += src->prefetch_count;
|
|
dst->distance_sum += src->distance_sum;
|
|
if (src->distance_max > dst->distance_max)
|
|
dst->distance_max = src->distance_max;
|
|
if (src->distance_capacity > dst->distance_capacity)
|
|
dst->distance_capacity = src->distance_capacity;
|
|
dst->wait_count += src->wait_count;
|
|
dst->io_count += src->io_count;
|
|
dst->io_nblocks += src->io_nblocks;
|
|
dst->io_in_progress += src->io_in_progress;
|
|
}
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for index scans (used by all AM interfaces)
|
|
* ---------------------
|
|
*/
|
|
typedef struct IndexScanInstrumentation
|
|
{
|
|
/* Index search count (incremented with pgstat_count_index_scan call) */
|
|
uint64 nsearches;
|
|
|
|
/* Table tuples fetched count (incremented during index-only scans) */
|
|
uint64 ntabletuplefetches;
|
|
} IndexScanInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker information
|
|
*/
|
|
typedef struct SharedIndexScanInstrumentation
|
|
{
|
|
int num_workers;
|
|
IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedIndexScanInstrumentation;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for bitmap heap scans
|
|
*
|
|
* exact_pages total number of exact pages retrieved
|
|
* lossy_pages total number of lossy pages retrieved
|
|
* ---------------------
|
|
*/
|
|
typedef struct BitmapHeapScanInstrumentation
|
|
{
|
|
uint64 exact_pages;
|
|
uint64 lossy_pages;
|
|
TableScanInstrumentation stats;
|
|
} BitmapHeapScanInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker information
|
|
*/
|
|
typedef struct SharedBitmapHeapInstrumentation
|
|
{
|
|
int num_workers;
|
|
BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedBitmapHeapInstrumentation;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for Memoize
|
|
* ---------------------
|
|
*/
|
|
typedef struct MemoizeInstrumentation
|
|
{
|
|
uint64 cache_hits; /* number of rescans where we've found the
|
|
* scan parameters values to be cached */
|
|
uint64 cache_misses; /* number of rescans where we've not found the
|
|
* scan parameters values to be cached */
|
|
uint64 cache_evictions; /* number of cache entries removed due to
|
|
* the need to free memory */
|
|
uint64 cache_overflows; /* number of times we've had to bypass the
|
|
* cache when filling it due to not being
|
|
* able to free enough space to store the
|
|
* current scan's tuples */
|
|
uint64 mem_peak; /* peak memory usage in bytes */
|
|
} MemoizeInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker memoize information
|
|
*/
|
|
typedef struct SharedMemoizeInfo
|
|
{
|
|
int num_workers;
|
|
MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedMemoizeInfo;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for Sorts.
|
|
* ---------------------
|
|
*/
|
|
|
|
typedef enum
|
|
{
|
|
SORT_SPACE_TYPE_DISK,
|
|
SORT_SPACE_TYPE_MEMORY,
|
|
} TuplesortSpaceType;
|
|
|
|
/*
|
|
* The parallel-sort infrastructure relies on having a zero TuplesortMethod
|
|
* to indicate that a worker never did anything, so we assign zero to
|
|
* SORT_TYPE_STILL_IN_PROGRESS. The other values of this enum can be
|
|
* OR'ed together to represent a situation where different workers used
|
|
* different methods, so we need a separate bit for each one. Keep the
|
|
* NUM_TUPLESORTMETHODS constant in sync with the number of bits!
|
|
*/
|
|
typedef enum
|
|
{
|
|
SORT_TYPE_STILL_IN_PROGRESS = 0,
|
|
SORT_TYPE_TOP_N_HEAPSORT = 1 << 0,
|
|
SORT_TYPE_QUICKSORT = 1 << 1,
|
|
SORT_TYPE_EXTERNAL_SORT = 1 << 2,
|
|
SORT_TYPE_EXTERNAL_MERGE = 1 << 3,
|
|
} TuplesortMethod;
|
|
#define NUM_TUPLESORTMETHODS 4
|
|
|
|
typedef struct TuplesortInstrumentation
|
|
{
|
|
TuplesortMethod sortMethod; /* sort algorithm used */
|
|
TuplesortSpaceType spaceType; /* type of space spaceUsed represents */
|
|
int64 spaceUsed; /* space consumption, in kB */
|
|
} TuplesortInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker sort information
|
|
*/
|
|
typedef struct SharedSortInfo
|
|
{
|
|
int num_workers;
|
|
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedSortInfo;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for nodeHash.c
|
|
* ---------------------
|
|
*/
|
|
typedef struct HashInstrumentation
|
|
{
|
|
int nbuckets; /* number of buckets at end of execution */
|
|
int nbuckets_original; /* planned number of buckets */
|
|
int nbatch; /* number of batches at end of execution */
|
|
int nbatch_original; /* planned number of batches */
|
|
Size space_peak; /* peak memory usage in bytes */
|
|
} HashInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker information
|
|
*/
|
|
typedef struct SharedHashInfo
|
|
{
|
|
int num_workers;
|
|
HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedHashInfo;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for IncrementalSort
|
|
* ---------------------
|
|
*/
|
|
typedef struct IncrementalSortGroupInfo
|
|
{
|
|
int64 groupCount;
|
|
int64 maxDiskSpaceUsed;
|
|
int64 totalDiskSpaceUsed;
|
|
int64 maxMemorySpaceUsed;
|
|
int64 totalMemorySpaceUsed;
|
|
uint32 sortMethods; /* bitmask of TuplesortMethod */
|
|
} IncrementalSortGroupInfo;
|
|
|
|
typedef struct IncrementalSortInfo
|
|
{
|
|
IncrementalSortGroupInfo fullsortGroupInfo;
|
|
IncrementalSortGroupInfo prefixsortGroupInfo;
|
|
} IncrementalSortInfo;
|
|
|
|
/* Shared memory container for per-worker incremental sort information */
|
|
typedef struct SharedIncrementalSortInfo
|
|
{
|
|
int num_workers;
|
|
IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedIncrementalSortInfo;
|
|
|
|
|
|
/* ---------------------
|
|
* Instrumentation information for sequential scans
|
|
* ---------------------
|
|
*/
|
|
typedef struct SeqScanInstrumentation
|
|
{
|
|
TableScanInstrumentation stats;
|
|
} SeqScanInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker information
|
|
*/
|
|
typedef struct SharedSeqScanInstrumentation
|
|
{
|
|
int num_workers;
|
|
SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedSeqScanInstrumentation;
|
|
|
|
|
|
/*
|
|
* Instrumentation information for TID range scans
|
|
*/
|
|
typedef struct TidRangeScanInstrumentation
|
|
{
|
|
TableScanInstrumentation stats;
|
|
} TidRangeScanInstrumentation;
|
|
|
|
/*
|
|
* Shared memory container for per-worker information
|
|
*/
|
|
typedef struct SharedTidRangeScanInstrumentation
|
|
{
|
|
int num_workers;
|
|
TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
|
} SharedTidRangeScanInstrumentation;
|
|
|
|
#endif /* INSTRUMENT_NODE_H */
|