agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v2 07/15] heapam: Add batch mode mvcc check and use it in page mode 6+ messages / 2 participants [nested] [flat]
* [PATCH v2 07/15] heapam: Add batch mode mvcc check and use it in page mode @ 2024-10-17 17:16 Andres Freund <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Andres Freund @ 2024-10-17 17:16 UTC (permalink / raw) There are two reasons for doing so: 1) It is generally faster to perform checks in a batched fashion and making sequential scans faster is nice. 2) We would like to stop setting hint bits while pages are being written out. The necessary locking becomes visible for page mode scans if done for every tuple. With batching the overhead can be amortized to only happen once per page. There are substantial further optimization opportunities along these lines: - Right now HeapTupleSatisfiesMVCCBatch() simply uses the single-tuple HeapTupleSatisfiesMVCC(), relying on the compiler to inline it. We could instead write an explicitly optimized version that avoids repeated xid tests. - Introduce batched version of the serializability test - Introduce batched version of HeapTupleSatisfiesVacuum Author: Reviewed-by: Discussion: https://postgr.es/m/ Backpatch: --- src/include/access/heapam.h | 28 ++++++ src/backend/access/heap/heapam.c | 104 ++++++++++++++++---- src/backend/access/heap/heapam_visibility.c | 50 ++++++++++ src/tools/pgindent/typedefs.list | 1 + 4 files changed, 163 insertions(+), 20 deletions(-) diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 96cf82f97b7..d4a790251cc 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -417,6 +417,34 @@ extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple); extern bool HeapTupleIsSurelyDead(HeapTuple htup, struct GlobalVisState *vistest); +/* + * FIXME: define to be removed + * + * Without this I see worse performance. But it's a bit ugly, so I thought + * it'd be useful to leave a way in for others to experiment with this. + */ +#define BATCHMVCC_FEWER_ARGS + +#ifdef BATCHMVCC_FEWER_ARGS +typedef struct BatchMVCCState +{ + HeapTupleData tuples[MaxHeapTuplesPerPage]; + bool visible[MaxHeapTuplesPerPage]; +} BatchMVCCState; +#endif + +extern int HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer, + int ntups, +#ifdef BATCHMVCC_FEWER_ARGS + BatchMVCCState *batchmvcc, +#else + HeapTupleData *tuples, + bool *visible, +#endif + OffsetNumber *vistuples_dense); + + + /* * To avoid leaking too much knowledge about reorderbuffer implementation * details this is implemented in reorderbuffer.c not heapam_visibility.c diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index f9086369728..1781e2c0571 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -437,42 +437,106 @@ page_collect_tuples(HeapScanDesc scan, Snapshot snapshot, BlockNumber block, int lines, bool all_visible, bool check_serializable) { + Oid relid = RelationGetRelid(scan->rs_base.rs_rd); +#ifdef BATCHMVCC_FEWER_ARGS + BatchMVCCState batchmvcc; +#else + HeapTupleData tuples[MaxHeapTuplesPerPage]; + bool visible[MaxHeapTuplesPerPage]; +#endif int ntup = 0; - OffsetNumber lineoff; + int nvis = 0; - for (lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++) + /* page at a time should have been disabled otherwise */ + Assert(IsMVCCSnapshot(snapshot)); + + /* first find all tuples on the page */ + for (OffsetNumber lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++) { ItemId lpp = PageGetItemId(page, lineoff); - HeapTupleData loctup; - bool valid; + HeapTuple tup; - if (!ItemIdIsNormal(lpp)) + if (unlikely(!ItemIdIsNormal(lpp))) continue; - loctup.t_data = (HeapTupleHeader) PageGetItem(page, lpp); - loctup.t_len = ItemIdGetLength(lpp); - loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd); - ItemPointerSet(&(loctup.t_self), block, lineoff); + /* + * If the page is not all-visible or we need to check serializability, + * maintain enough state to be able to refind the tuple efficiently, + * without again needing to extract it from the page. + */ + if (!all_visible || check_serializable) + { +#ifdef BATCHMVCC_FEWER_ARGS + tup = &batchmvcc.tuples[ntup]; +#else + tup = &tuples[ntup]; +#endif + tup->t_data = (HeapTupleHeader) PageGetItem(page, lpp); + tup->t_len = ItemIdGetLength(lpp); + tup->t_tableOid = relid; + ItemPointerSet(&(tup->t_self), block, lineoff); + } + + /* + * If the page is all visible, these fields won'otherwise wont be + * populated in loop below. + */ if (all_visible) - valid = true; - else - valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); - - if (check_serializable) - HeapCheckForSerializableConflictOut(valid, scan->rs_base.rs_rd, - &loctup, buffer, snapshot); - - if (valid) { + if (check_serializable) + { +#ifdef BATCHMVCC_FEWER_ARGS + batchmvcc.visible[ntup] = true; +#else + visible[ntup] = true; +#endif + } scan->rs_vistuples[ntup] = lineoff; - ntup++; } + + ntup++; } Assert(ntup <= MaxHeapTuplesPerPage); - return ntup; + /* unless the page is all visible, test visibility for all tuples one go */ + if (all_visible) + nvis = ntup; + else + nvis = HeapTupleSatisfiesMVCCBatch(snapshot, buffer, + ntup, +#ifdef BATCHMVCC_FEWER_ARGS + &batchmvcc, +#else + tuples, visible, +#endif + scan->rs_vistuples + ); + + /* + * So far we don't have batch API for testing serializabilty, so do so + * one-by-one. + */ + if (check_serializable) + { + for (int i = 0; i < ntup; i++) + { +#ifdef BATCHMVCC_FEWER_ARGS + HeapCheckForSerializableConflictOut(batchmvcc.visible[i], + scan->rs_base.rs_rd, + &batchmvcc.tuples[i], + buffer, snapshot); +#else + HeapCheckForSerializableConflictOut(visible[i], + scan->rs_base.rs_rd, + &tuples[i], + buffer, snapshot); +#endif + } + } + + return nvis; } /* diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c index b7aa8bb7a52..4baafc01a2c 100644 --- a/src/backend/access/heap/heapam_visibility.c +++ b/src/backend/access/heap/heapam_visibility.c @@ -1575,6 +1575,56 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, return true; } +/* + * Perform HeaptupleSatisfiesMVCC() on each passed in tuple. This is more + * efficient than doing HeapTupleSatisfiesMVCC() one-by-one. + * + * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's + * visibility is set in batchmvcc->visible[]. In addition, ->vistuples_dense + * is set to contain the offsets of visible tuples. + * + * Returns the number of visible tuples. + */ +int +HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer, + int ntups, +#ifdef BATCHMVCC_FEWER_ARGS + BatchMVCCState *batchmvcc, +#else + HeapTupleData *tuples, + bool *visible, +#endif + OffsetNumber *vistuples_dense) +{ + int nvis = 0; + + Assert(IsMVCCSnapshot(snapshot)); + + for (int i = 0; i < ntups; i++) + { + bool valid; +#ifdef BATCHMVCC_FEWER_ARGS + HeapTuple tup = &batchmvcc->tuples[i]; +#else + HeapTuple tup = &tuples[i]; +#endif + + valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer); +#ifdef BATCHMVCC_FEWER_ARGS + batchmvcc->visible[i] = valid; +#else + visible[i] = valid; +#endif + if (likely(valid)) + { + vistuples_dense[nvis] = tup->t_self.ip_posid; + nvis++; + } + } + + return nvis; +} + /* * HeapTupleSatisfiesVisibility * True iff heap tuple satisfies a time qual. diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 08521d51a9b..2d278960078 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -244,6 +244,7 @@ Barrier BaseBackupCmd BaseBackupTargetHandle BaseBackupTargetType +BatchMVCCState BeginDirectModify_function BeginForeignInsert_function BeginForeignModify_function -- 2.45.2.746.g06e570c0df.dirty --ftvuw4gllm5odk5g Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0008-bufmgr-Make-it-easier-to-change-number-of-buffer-.patch" ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v1 1/1] rework DSM registry view @ 2025-11-26 22:48 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Nathan Bossart @ 2025-11-26 22:48 UTC (permalink / raw) --- doc/src/sgml/system-views.sgml | 4 +-- src/backend/storage/ipc/dsm_registry.c | 17 ++++----- src/backend/utils/mmgr/dsa.c | 35 +++++++++++++++++++ src/include/utils/dsa.h | 1 + .../expected/test_dsm_registry.out | 4 +-- .../sql/test_dsm_registry.sql | 4 +-- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 0e623e7fb86..3d083128a4b 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -1150,8 +1150,8 @@ AND c1.path[c2.level] = c2.path[c2.level]; <structfield>size</structfield> <type>int8</type> </para> <para> - Size of the allocation in bytes. NULL for entries of type - <literal>area</literal> and <literal>hash</literal>. + Size of the allocation in bytes. NULL for entries that failed + initialization. </para></entry> </row> </tbody> diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index ef6533b1100..4e327f69789 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -463,17 +463,6 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) Datum vals[3]; bool nulls[3] = {0}; - /* Do not show partially-initialized entries. */ - if (entry->type == DSMR_ENTRY_TYPE_DSM && - entry->dsm.handle == DSM_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSA && - entry->dsa.handle == DSA_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSH && - entry->dsh.dsa_handle == DSA_HANDLE_INVALID) - continue; - vals[0] = CStringGetTextDatum(entry->name); vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]); @@ -483,6 +472,12 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) */ if (entry->type == DSMR_ENTRY_TYPE_DSM) vals[2] = Int64GetDatum(entry->dsm.size); + else if (entry->type == DSMR_ENTRY_TYPE_DSA && + entry->dsa.handle != DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle)); + else if (entry->type == DSMR_ENTRY_TYPE_DSH && + entry->dsh.dsa_handle !=DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle)); else nulls[2] = true; diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index be43e9351c3..030f9463712 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -1050,6 +1050,41 @@ dsa_get_total_size(dsa_area *area) return size; } +/* + * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have + * been created with dsa_create (not dsa_create_in_place). + */ +size_t +dsa_get_total_size_from_handle(dsa_handle handle) +{ + size_t size; + bool already_attached; + dsm_segment *segment; + dsa_area_control *control; + + already_attached = dsa_is_attached(handle); + if (already_attached) + segment = dsm_find_mapping(handle); + else + segment = dsm_attach(handle); + + if (segment == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not attach to dynamic shared area"))); + + control = (dsa_area_control *) dsm_segment_address(segment); + + LWLockAcquire(&control->lock, LW_SHARED); + size = control->total_segment_size; + LWLockRelease(&control->lock); + + if (!already_attached) + dsm_detach(segment); + + return size; +} + /* * Aggressively free all spare memory in the hope of returning DSM segments to * the operating system. diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index f2104dacbfc..42449ff22de 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -161,6 +161,7 @@ extern dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags) extern void dsa_free(dsa_area *area, dsa_pointer dp); extern void *dsa_get_address(dsa_area *area, dsa_pointer dp); extern size_t dsa_get_total_size(dsa_area *area); +extern size_t dsa_get_total_size_from_handle(dsa_handle handle); extern void dsa_trim(dsa_area *area); extern void dsa_dump(dsa_area *area); diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index ca8abbb377e..75d9eda0756 100644 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size @@ -32,7 +32,7 @@ SELECT get_val_in_hash('test'); (1 row) \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index 965a3f1ebb6..1b9ee3ebf18 100644 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; CREATE EXTENSION test_dsm_registry; @@ -8,6 +8,6 @@ SELECT set_val_in_hash('test', '1414'); SELECT get_val_in_shmem(); SELECT get_val_in_hash('test'); \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; -- 2.39.5 (Apple Git-154) --qPihe5i8c6jBTp1x-- ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v2 1/1] rework DSM registry view @ 2025-11-26 22:48 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Nathan Bossart @ 2025-11-26 22:48 UTC (permalink / raw) --- doc/src/sgml/system-views.sgml | 4 +-- src/backend/storage/ipc/dsm_registry.c | 22 ++++-------- src/backend/utils/mmgr/dsa.c | 35 +++++++++++++++++++ src/include/utils/dsa.h | 1 + .../expected/test_dsm_registry.out | 4 +-- .../sql/test_dsm_registry.sql | 4 +-- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 7db8f73eba2..162c76b729a 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -1150,8 +1150,8 @@ AND c1.path[c2.level] = c2.path[c2.level]; <structfield>size</structfield> <type>int8</type> </para> <para> - Size of the allocation in bytes. NULL for entries of type - <literal>area</literal> and <literal>hash</literal>. + Size of the allocation in bytes. NULL for entries that failed + initialization. </para></entry> </row> </tbody> diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index ef6533b1100..0f3e08c44d8 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -463,26 +463,18 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) Datum vals[3]; bool nulls[3] = {0}; - /* Do not show partially-initialized entries. */ - if (entry->type == DSMR_ENTRY_TYPE_DSM && - entry->dsm.handle == DSM_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSA && - entry->dsa.handle == DSA_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSH && - entry->dsh.dsa_handle == DSA_HANDLE_INVALID) - continue; - vals[0] = CStringGetTextDatum(entry->name); vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]); - /* - * Since we can't know the size of DSA/dshash entries without first - * attaching to them, return NULL for those. - */ + /* Be careful to only return the sizes of initialized entries. */ if (entry->type == DSMR_ENTRY_TYPE_DSM) vals[2] = Int64GetDatum(entry->dsm.size); + else if (entry->type == DSMR_ENTRY_TYPE_DSA && + entry->dsa.handle != DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle)); + else if (entry->type == DSMR_ENTRY_TYPE_DSH && + entry->dsh.dsa_handle !=DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle)); else nulls[2] = true; diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index be43e9351c3..030f9463712 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -1050,6 +1050,41 @@ dsa_get_total_size(dsa_area *area) return size; } +/* + * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have + * been created with dsa_create (not dsa_create_in_place). + */ +size_t +dsa_get_total_size_from_handle(dsa_handle handle) +{ + size_t size; + bool already_attached; + dsm_segment *segment; + dsa_area_control *control; + + already_attached = dsa_is_attached(handle); + if (already_attached) + segment = dsm_find_mapping(handle); + else + segment = dsm_attach(handle); + + if (segment == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not attach to dynamic shared area"))); + + control = (dsa_area_control *) dsm_segment_address(segment); + + LWLockAcquire(&control->lock, LW_SHARED); + size = control->total_segment_size; + LWLockRelease(&control->lock); + + if (!already_attached) + dsm_detach(segment); + + return size; +} + /* * Aggressively free all spare memory in the hope of returning DSM segments to * the operating system. diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index f2104dacbfc..42449ff22de 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -161,6 +161,7 @@ extern dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags) extern void dsa_free(dsa_area *area, dsa_pointer dp); extern void *dsa_get_address(dsa_area *area, dsa_pointer dp); extern size_t dsa_get_total_size(dsa_area *area); +extern size_t dsa_get_total_size_from_handle(dsa_handle handle); extern void dsa_trim(dsa_area *area); extern void dsa_dump(dsa_area *area); diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index ca8abbb377e..75d9eda0756 100644 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size @@ -32,7 +32,7 @@ SELECT get_val_in_hash('test'); (1 row) \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index 965a3f1ebb6..1b9ee3ebf18 100644 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; CREATE EXTENSION test_dsm_registry; @@ -8,6 +8,6 @@ SELECT set_val_in_hash('test', '1414'); SELECT get_val_in_shmem(); SELECT get_val_in_hash('test'); \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; -- 2.39.5 (Apple Git-154) --yd3uzAJlpJ8QAvQg-- ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v3 1/1] rework DSM registry view @ 2025-11-26 22:48 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Nathan Bossart @ 2025-11-26 22:48 UTC (permalink / raw) --- doc/src/sgml/system-views.sgml | 4 +-- src/backend/storage/ipc/dsm_registry.c | 22 ++++-------- src/backend/utils/mmgr/dsa.c | 35 +++++++++++++++++++ src/include/utils/dsa.h | 1 + .../expected/test_dsm_registry.out | 12 +++---- .../sql/test_dsm_registry.sql | 4 +-- 6 files changed, 53 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 7db8f73eba2..162c76b729a 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -1150,8 +1150,8 @@ AND c1.path[c2.level] = c2.path[c2.level]; <structfield>size</structfield> <type>int8</type> </para> <para> - Size of the allocation in bytes. NULL for entries of type - <literal>area</literal> and <literal>hash</literal>. + Size of the allocation in bytes. NULL for entries that failed + initialization. </para></entry> </row> </tbody> diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index ef6533b1100..0f3e08c44d8 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -463,26 +463,18 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) Datum vals[3]; bool nulls[3] = {0}; - /* Do not show partially-initialized entries. */ - if (entry->type == DSMR_ENTRY_TYPE_DSM && - entry->dsm.handle == DSM_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSA && - entry->dsa.handle == DSA_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSH && - entry->dsh.dsa_handle == DSA_HANDLE_INVALID) - continue; - vals[0] = CStringGetTextDatum(entry->name); vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]); - /* - * Since we can't know the size of DSA/dshash entries without first - * attaching to them, return NULL for those. - */ + /* Be careful to only return the sizes of initialized entries. */ if (entry->type == DSMR_ENTRY_TYPE_DSM) vals[2] = Int64GetDatum(entry->dsm.size); + else if (entry->type == DSMR_ENTRY_TYPE_DSA && + entry->dsa.handle != DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle)); + else if (entry->type == DSMR_ENTRY_TYPE_DSH && + entry->dsh.dsa_handle !=DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle)); else nulls[2] = true; diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index be43e9351c3..030f9463712 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -1050,6 +1050,41 @@ dsa_get_total_size(dsa_area *area) return size; } +/* + * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have + * been created with dsa_create (not dsa_create_in_place). + */ +size_t +dsa_get_total_size_from_handle(dsa_handle handle) +{ + size_t size; + bool already_attached; + dsm_segment *segment; + dsa_area_control *control; + + already_attached = dsa_is_attached(handle); + if (already_attached) + segment = dsm_find_mapping(handle); + else + segment = dsm_attach(handle); + + if (segment == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not attach to dynamic shared area"))); + + control = (dsa_area_control *) dsm_segment_address(segment); + + LWLockAcquire(&control->lock, LW_SHARED); + size = control->total_segment_size; + LWLockRelease(&control->lock); + + if (!already_attached) + dsm_detach(segment); + + return size; +} + /* * Aggressively free all spare memory in the hope of returning DSM segments to * the operating system. diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index f2104dacbfc..42449ff22de 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -161,6 +161,7 @@ extern dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags) extern void dsa_free(dsa_area *area, dsa_pointer dp); extern void *dsa_get_address(dsa_area *area, dsa_pointer dp); extern size_t dsa_get_total_size(dsa_area *area); +extern size_t dsa_get_total_size_from_handle(dsa_handle handle); extern void dsa_trim(dsa_area *area); extern void dsa_dump(dsa_area *area); diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index ca8abbb377e..9128e171b1b 100644 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@ -1,8 +1,8 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; - name | type | size -------+------+------ + name | type | size_ok +------+------+--------- (0 rows) CREATE EXTENSION test_dsm_registry; @@ -32,11 +32,11 @@ SELECT get_val_in_hash('test'); (1 row) \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; - name | type | size -------------------------+---------+------ + name | type | size_ok +------------------------+---------+--------- test_dsm_registry_dsa | area | t test_dsm_registry_dsm | segment | t test_dsm_registry_hash | hash | t diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index 965a3f1ebb6..a606e8872a1 100644 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; CREATE EXTENSION test_dsm_registry; @@ -8,6 +8,6 @@ SELECT set_val_in_hash('test', '1414'); SELECT get_val_in_shmem(); SELECT get_val_in_hash('test'); \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; -- 2.39.5 (Apple Git-154) --jJt2Y/LqGp3oC5LK-- ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v1 1/1] rework DSM registry view @ 2025-11-26 22:48 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Nathan Bossart @ 2025-11-26 22:48 UTC (permalink / raw) --- doc/src/sgml/system-views.sgml | 4 +-- src/backend/storage/ipc/dsm_registry.c | 17 ++++----- src/backend/utils/mmgr/dsa.c | 35 +++++++++++++++++++ src/include/utils/dsa.h | 1 + .../expected/test_dsm_registry.out | 4 +-- .../sql/test_dsm_registry.sql | 4 +-- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 0e623e7fb86..3d083128a4b 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -1150,8 +1150,8 @@ AND c1.path[c2.level] = c2.path[c2.level]; <structfield>size</structfield> <type>int8</type> </para> <para> - Size of the allocation in bytes. NULL for entries of type - <literal>area</literal> and <literal>hash</literal>. + Size of the allocation in bytes. NULL for entries that failed + initialization. </para></entry> </row> </tbody> diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index ef6533b1100..4e327f69789 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -463,17 +463,6 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) Datum vals[3]; bool nulls[3] = {0}; - /* Do not show partially-initialized entries. */ - if (entry->type == DSMR_ENTRY_TYPE_DSM && - entry->dsm.handle == DSM_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSA && - entry->dsa.handle == DSA_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSH && - entry->dsh.dsa_handle == DSA_HANDLE_INVALID) - continue; - vals[0] = CStringGetTextDatum(entry->name); vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]); @@ -483,6 +472,12 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) */ if (entry->type == DSMR_ENTRY_TYPE_DSM) vals[2] = Int64GetDatum(entry->dsm.size); + else if (entry->type == DSMR_ENTRY_TYPE_DSA && + entry->dsa.handle != DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle)); + else if (entry->type == DSMR_ENTRY_TYPE_DSH && + entry->dsh.dsa_handle !=DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle)); else nulls[2] = true; diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index be43e9351c3..030f9463712 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -1050,6 +1050,41 @@ dsa_get_total_size(dsa_area *area) return size; } +/* + * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have + * been created with dsa_create (not dsa_create_in_place). + */ +size_t +dsa_get_total_size_from_handle(dsa_handle handle) +{ + size_t size; + bool already_attached; + dsm_segment *segment; + dsa_area_control *control; + + already_attached = dsa_is_attached(handle); + if (already_attached) + segment = dsm_find_mapping(handle); + else + segment = dsm_attach(handle); + + if (segment == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not attach to dynamic shared area"))); + + control = (dsa_area_control *) dsm_segment_address(segment); + + LWLockAcquire(&control->lock, LW_SHARED); + size = control->total_segment_size; + LWLockRelease(&control->lock); + + if (!already_attached) + dsm_detach(segment); + + return size; +} + /* * Aggressively free all spare memory in the hope of returning DSM segments to * the operating system. diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index f2104dacbfc..42449ff22de 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -161,6 +161,7 @@ extern dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags) extern void dsa_free(dsa_area *area, dsa_pointer dp); extern void *dsa_get_address(dsa_area *area, dsa_pointer dp); extern size_t dsa_get_total_size(dsa_area *area); +extern size_t dsa_get_total_size_from_handle(dsa_handle handle); extern void dsa_trim(dsa_area *area); extern void dsa_dump(dsa_area *area); diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index ca8abbb377e..75d9eda0756 100644 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size @@ -32,7 +32,7 @@ SELECT get_val_in_hash('test'); (1 row) \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; name | type | size diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index 965a3f1ebb6..1b9ee3ebf18 100644 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; CREATE EXTENSION test_dsm_registry; @@ -8,6 +8,6 @@ SELECT set_val_in_hash('test', '1414'); SELECT get_val_in_shmem(); SELECT get_val_in_hash('test'); \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; -- 2.39.5 (Apple Git-154) --qPihe5i8c6jBTp1x-- ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v3 1/1] rework DSM registry view @ 2025-11-26 22:48 Nathan Bossart <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Nathan Bossart @ 2025-11-26 22:48 UTC (permalink / raw) --- doc/src/sgml/system-views.sgml | 4 +-- src/backend/storage/ipc/dsm_registry.c | 22 ++++-------- src/backend/utils/mmgr/dsa.c | 35 +++++++++++++++++++ src/include/utils/dsa.h | 1 + .../expected/test_dsm_registry.out | 12 +++---- .../sql/test_dsm_registry.sql | 4 +-- 6 files changed, 53 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 7db8f73eba2..162c76b729a 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -1150,8 +1150,8 @@ AND c1.path[c2.level] = c2.path[c2.level]; <structfield>size</structfield> <type>int8</type> </para> <para> - Size of the allocation in bytes. NULL for entries of type - <literal>area</literal> and <literal>hash</literal>. + Size of the allocation in bytes. NULL for entries that failed + initialization. </para></entry> </row> </tbody> diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index ef6533b1100..0f3e08c44d8 100644 --- a/src/backend/storage/ipc/dsm_registry.c +++ b/src/backend/storage/ipc/dsm_registry.c @@ -463,26 +463,18 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS) Datum vals[3]; bool nulls[3] = {0}; - /* Do not show partially-initialized entries. */ - if (entry->type == DSMR_ENTRY_TYPE_DSM && - entry->dsm.handle == DSM_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSA && - entry->dsa.handle == DSA_HANDLE_INVALID) - continue; - if (entry->type == DSMR_ENTRY_TYPE_DSH && - entry->dsh.dsa_handle == DSA_HANDLE_INVALID) - continue; - vals[0] = CStringGetTextDatum(entry->name); vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]); - /* - * Since we can't know the size of DSA/dshash entries without first - * attaching to them, return NULL for those. - */ + /* Be careful to only return the sizes of initialized entries. */ if (entry->type == DSMR_ENTRY_TYPE_DSM) vals[2] = Int64GetDatum(entry->dsm.size); + else if (entry->type == DSMR_ENTRY_TYPE_DSA && + entry->dsa.handle != DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsa.handle)); + else if (entry->type == DSMR_ENTRY_TYPE_DSH && + entry->dsh.dsa_handle !=DSA_HANDLE_INVALID) + vals[2] = Int64GetDatum(dsa_get_total_size_from_handle(entry->dsh.dsa_handle)); else nulls[2] = true; diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index be43e9351c3..030f9463712 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -1050,6 +1050,41 @@ dsa_get_total_size(dsa_area *area) return size; } +/* + * Same as dsa_get_total_size(), but accepts a DSA handle. The area must have + * been created with dsa_create (not dsa_create_in_place). + */ +size_t +dsa_get_total_size_from_handle(dsa_handle handle) +{ + size_t size; + bool already_attached; + dsm_segment *segment; + dsa_area_control *control; + + already_attached = dsa_is_attached(handle); + if (already_attached) + segment = dsm_find_mapping(handle); + else + segment = dsm_attach(handle); + + if (segment == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not attach to dynamic shared area"))); + + control = (dsa_area_control *) dsm_segment_address(segment); + + LWLockAcquire(&control->lock, LW_SHARED); + size = control->total_segment_size; + LWLockRelease(&control->lock); + + if (!already_attached) + dsm_detach(segment); + + return size; +} + /* * Aggressively free all spare memory in the hope of returning DSM segments to * the operating system. diff --git a/src/include/utils/dsa.h b/src/include/utils/dsa.h index f2104dacbfc..42449ff22de 100644 --- a/src/include/utils/dsa.h +++ b/src/include/utils/dsa.h @@ -161,6 +161,7 @@ extern dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags) extern void dsa_free(dsa_area *area, dsa_pointer dp); extern void *dsa_get_address(dsa_area *area, dsa_pointer dp); extern size_t dsa_get_total_size(dsa_area *area); +extern size_t dsa_get_total_size_from_handle(dsa_handle handle); extern void dsa_trim(dsa_area *area); extern void dsa_dump(dsa_area *area); diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out index ca8abbb377e..9128e171b1b 100644 --- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out +++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out @@ -1,8 +1,8 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; - name | type | size -------+------+------ + name | type | size_ok +------+------+--------- (0 rows) CREATE EXTENSION test_dsm_registry; @@ -32,11 +32,11 @@ SELECT get_val_in_hash('test'); (1 row) \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; - name | type | size -------------------------+---------+------ + name | type | size_ok +------------------------+---------+--------- test_dsm_registry_dsa | area | t test_dsm_registry_dsm | segment | t test_dsm_registry_hash | hash | t diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql index 965a3f1ebb6..a606e8872a1 100644 --- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql +++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql @@ -1,4 +1,4 @@ -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; CREATE EXTENSION test_dsm_registry; @@ -8,6 +8,6 @@ SELECT set_val_in_hash('test', '1414'); SELECT get_val_in_shmem(); SELECT get_val_in_hash('test'); \c -SELECT name, type, size IS DISTINCT FROM 0 AS size +SELECT name, type, size > 0 AS size_ok FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%' ORDER BY name; -- 2.39.5 (Apple Git-154) --jJt2Y/LqGp3oC5LK-- ^ permalink raw reply [nested|flat] 6+ messages in thread
end of thread, other threads:[~2025-11-26 22:48 UTC | newest] Thread overview: 6+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2024-10-17 17:16 [PATCH v2 07/15] heapam: Add batch mode mvcc check and use it in page mode Andres Freund <[email protected]> 2025-11-26 22:48 [PATCH v1 1/1] rework DSM registry view Nathan Bossart <[email protected]> 2025-11-26 22:48 [PATCH v2 1/1] rework DSM registry view Nathan Bossart <[email protected]> 2025-11-26 22:48 [PATCH v3 1/1] rework DSM registry view Nathan Bossart <[email protected]> 2025-11-26 22:48 [PATCH v1 1/1] rework DSM registry view Nathan Bossart <[email protected]> 2025-11-26 22:48 [PATCH v3 1/1] rework DSM registry view Nathan Bossart <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox