PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgstat_shmem.c File Reference
#include "postgres.h"
#include "pgstat.h"
#include "storage/shmem.h"
#include "storage/subsystems.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"
#include "lib/simplehash.h"
Include dependency graph for pgstat_shmem.c:

Go to the source code of this file.

Data Structures

struct  PgStat_EntryRefHashEntry
 

Macros

#define PGSTAT_ENTRY_REF_HASH_SIZE   128
 
#define SH_PREFIX   pgstat_entry_ref_hash
 
#define SH_ELEMENT_TYPE   PgStat_EntryRefHashEntry
 
#define SH_KEY_TYPE   PgStat_HashKey
 
#define SH_KEY   key
 
#define SH_HASH_KEY(tb, key)    pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
 
#define SH_EQUAL(tb, a, b)    pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
 
#define SH_SCOPE   static inline
 
#define SH_DEFINE
 
#define SH_DECLARE
 

Typedefs

typedef struct PgStat_EntryRefHashEntry PgStat_EntryRefHashEntry
 
typedef bool(* ReleaseMatchCB) (PgStat_EntryRefHashEntry *, Datum data)
 

Functions

static void pgstat_drop_database_and_contents (Oid dboid)
 
static void pgstat_free_entry (PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
 
static void pgstat_release_entry_ref (PgStat_HashKey key, PgStat_EntryRef *entry_ref, bool discard_pending)
 
static bool pgstat_need_entry_refs_gc (void)
 
static void pgstat_gc_entry_refs (void)
 
static void pgstat_release_all_entry_refs (bool discard_pending)
 
static void pgstat_release_matching_entry_refs (bool discard_pending, ReleaseMatchCB match, Datum match_data)
 
static void pgstat_setup_memcxt (void)
 
static void StatsShmemRequest (void *arg)
 
static void StatsShmemInit (void *arg)
 
static Size pgstat_dsa_init_size (void)
 
static Size StatsShmemSize (void)
 
void pgstat_attach_shmem (void)
 
void pgstat_detach_shmem (void)
 
PgStatShared_Commonpgstat_init_entry (PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
 
static PgStatShared_Commonpgstat_reinit_entry (PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
 
static void pgstat_setup_shared_refs (void)
 
static void pgstat_acquire_entry_ref (PgStat_EntryRef *entry_ref, PgStatShared_HashEntry *shhashent, PgStatShared_Common *shheader)
 
static bool pgstat_get_entry_ref_cached (PgStat_HashKey key, PgStat_EntryRef **entry_ref_p)
 
PgStat_EntryRefpgstat_get_entry_ref (PgStat_Kind kind, Oid dboid, uint64 objid, bool create, bool *created_entry)
 
bool pgstat_lock_entry (PgStat_EntryRef *entry_ref, bool nowait)
 
bool pgstat_lock_entry_shared (PgStat_EntryRef *entry_ref, bool nowait)
 
void pgstat_unlock_entry (PgStat_EntryRef *entry_ref)
 
PgStat_EntryRefpgstat_get_entry_ref_locked (PgStat_Kind kind, Oid dboid, uint64 objid, bool nowait)
 
void pgstat_request_entry_refs_gc (void)
 
static bool match_db (PgStat_EntryRefHashEntry *ent, Datum match_data)
 
static void pgstat_release_db_entry_refs (Oid dboid)
 
static bool pgstat_drop_entry_internal (PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
 
bool pgstat_drop_entry (PgStat_Kind kind, Oid dboid, uint64 objid, bool missing_ok)
 
void pgstat_drop_matching_entries (bool(*do_drop)(PgStatShared_HashEntry *, Datum), Datum match_data)
 
void pgstat_drop_all_entries (void)
 
static void shared_stat_reset_contents (PgStat_Kind kind, PgStatShared_Common *header, TimestampTz ts)
 
void pgstat_reset_entry (PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts)
 
void pgstat_reset_matching_entries (bool(*do_reset)(PgStatShared_HashEntry *, Datum), Datum match_data, TimestampTz ts)
 
static bool match_kind (PgStatShared_HashEntry *p, Datum match_data)
 
void pgstat_reset_entries_of_kind (PgStat_Kind kind, TimestampTz ts)
 

Variables

const ShmemCallbacks StatsShmemCallbacks
 
static const dshash_parameters dsh_params
 
static pgstat_entry_ref_hash_hashpgStatEntryRefHash = NULL
 
static int pgStatSharedRefAge = 0
 
static MemoryContext pgStatSharedRefContext = NULL
 
static MemoryContext pgStatEntryRefHashContext = NULL
 

Macro Definition Documentation

◆ PGSTAT_ENTRY_REF_HASH_SIZE

#define PGSTAT_ENTRY_REF_HASH_SIZE   128

Definition at line 22 of file pgstat_shmem.c.

◆ SH_DECLARE

#define SH_DECLARE

Definition at line 44 of file pgstat_shmem.c.

◆ SH_DEFINE

#define SH_DEFINE

Definition at line 43 of file pgstat_shmem.c.

◆ SH_ELEMENT_TYPE

#define SH_ELEMENT_TYPE   PgStat_EntryRefHashEntry

Definition at line 35 of file pgstat_shmem.c.

◆ SH_EQUAL

#define SH_EQUAL (   tb,
  a,
  b 
)     pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0

Definition at line 40 of file pgstat_shmem.c.

64 {
65 .request_fn = StatsShmemRequest,
66 .init_fn = StatsShmemInit,
67};
68
69/* parameter for the shared hash */
70static const dshash_parameters dsh_params = {
71 sizeof(PgStat_HashKey),
77};
78
79
80/*
81 * Backend local references to shared stats entries. If there are pending
82 * updates to a stats entry, the PgStat_EntryRef is added to the pgStatPending
83 * list.
84 *
85 * When a stats entry is dropped each backend needs to release its reference
86 * to it before the memory can be released. To trigger that
87 * pgStatLocal.shmem->gc_request_count is incremented - which each backend
88 * compares to their copy of pgStatSharedRefAge on a regular basis.
89 */
91static int pgStatSharedRefAge = 0; /* cache age of pgStatLocal.shmem */
92
93/*
94 * Memory contexts containing the pgStatEntryRefHash table and the
95 * pgStatSharedRef entries respectively. Kept separate to make it easier to
96 * track / attribute memory usage.
97 */
100
101
102/* ------------------------------------------------------------
103 * Public functions called from postmaster follow
104 * ------------------------------------------------------------
105 */
106
107/*
108 * The size of the shared memory allocation for stats stored in the shared
109 * stats hash table. This allocation will be done as part of the main shared
110 * memory, rather than dynamic shared memory, allowing it to be initialized in
111 * postmaster.
112 */
113static Size
115{
116 Size sz;
117
118 /*
119 * The dshash header / initial buckets array needs to fit into "plain"
120 * shared memory, but it's beneficial to not need dsm segments
121 * immediately. A size of 256kB seems works well and is not
122 * disproportional compared to other constant sized shared memory
123 * allocations. NB: To avoid DSMs further, the user can configure
124 * min_dynamic_shared_memory.
125 */
126 sz = 256 * 1024;
128 return MAXALIGN(sz);
129}
130
131/*
132 * Compute shared memory space needed for cumulative statistics
133 */
134static Size
135StatsShmemSize(void)
136{
137 Size sz;
138
141
142 /* Add shared memory for all the custom fixed-numbered statistics */
143 for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
144 {
146
147 if (!kind_info)
148 continue;
149 if (!kind_info->fixed_amount)
150 continue;
151
152 Assert(kind_info->shared_size != 0);
153 sz = add_size(sz, MAXALIGN(kind_info->shared_size));
154 }
155
156 return sz;
157}
158
159/*
160 * Register shared memory area for cumulative statistics
161 */
162static void
164{
165 ShmemRequestStruct(.name = "Shared Memory Stats",
166 .size = StatsShmemSize(),
167 .ptr = (void **) &pgStatLocal.shmem,
168 );
169}
170
171/*
172 * Initialize cumulative statistics system during startup
173 */
174static void
175StatsShmemInit(void *arg)
176{
177 dsa_area *dsa;
178 dshash_table *dsh;
180 char *p = (char *) ctl;
181
182 /* the allocation of pgStatLocal.shmem itself */
183 p += MAXALIGN(sizeof(PgStat_ShmemControl));
184
185 /*
186 * Create a small dsa allocation in plain shared memory. This is required
187 * because postmaster cannot use dsm segments. It also provides a small
188 * efficiency win.
189 */
190 ctl->raw_dsa_area = p;
192 dsa = dsa_create_in_place(ctl->raw_dsa_area,
195 dsa_pin(dsa);
196
197 /*
198 * To ensure dshash is created in "plain" shared memory, temporarily limit
199 * size of dsa to the initial size of the dsa.
200 */
202
203 /*
204 * With the limit in place, create the dshash table. XXX: It'd be nice if
205 * there were dshash_create_in_place().
206 */
207 dsh = dshash_create(dsa, &dsh_params, NULL);
208 ctl->hash_handle = dshash_get_hash_table_handle(dsh);
209
210 /* lift limit set above */
211 dsa_set_size_limit(dsa, -1);
212
213 /*
214 * Postmaster will never access these again, thus free the local
215 * dsa/dshash references.
216 */
217 dshash_detach(dsh);
218 dsa_detach(dsa);
219
220 pg_atomic_init_u64(&ctl->gc_request_count, 1);
221
222 /* Do the per-kind initialization */
223 for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
224 {
226 char *ptr;
227
228 if (!kind_info)
229 continue;
230
231 /* initialize entry count tracking */
232 if (kind_info->track_entry_count)
233 pg_atomic_init_u64(&ctl->entry_counts[kind - 1], 0);
234
235 /* initialize fixed-numbered stats */
236 if (kind_info->fixed_amount)
237 {
238 if (pgstat_is_kind_builtin(kind))
239 ptr = ((char *) ctl) + kind_info->shared_ctl_off;
240 else
241 {
242 int idx = kind - PGSTAT_KIND_CUSTOM_MIN;
243
244 Assert(kind_info->shared_size != 0);
245 ctl->custom_data[idx] = p;
246 p += MAXALIGN(kind_info->shared_size);
247 ptr = ctl->custom_data[idx];
248 }
249
250 kind_info->init_shmem_cb(ptr);
251 }
252 }
253}
254
255void
257{
258 MemoryContext oldcontext;
259
261
262 /* stats shared memory persists for the backend lifetime */
264
266 NULL);
268
271 NULL);
272
273 MemoryContextSwitchTo(oldcontext);
274}
275
276void
278{
280
281 /* we shouldn't leave references to shared stats */
283
286
288
289 /*
290 * dsa_detach() does not decrement the DSA reference count as no segment
291 * was provided to dsa_attach_in_place(), causing no cleanup callbacks to
292 * be registered. Hence, release it manually now.
293 */
295
297}
298
299
300/* ------------------------------------------------------------
301 * Maintenance of shared memory stats entries
302 * ------------------------------------------------------------
303 */
304
305/*
306 * Initialize entry newly-created.
307 *
308 * Returns NULL in the event of an allocation failure, so as callers can
309 * take cleanup actions as the entry initialized is already inserted in the
310 * shared hashtable.
311 */
315{
316 /* Create new stats entry. */
317 dsa_pointer chunk;
320
321 /*
322 * Initialize refcount to 1, marking it as valid / not dropped. The entry
323 * can't be freed before the initialization because it can't be found as
324 * long as we hold the dshash partition lock. Caller needs to increase
325 * further if a longer lived reference is needed.
326 */
327 pg_atomic_init_u32(&shhashent->refcount, 1);
328
329 /*
330 * Initialize "generation" to 0, as freshly created.
331 */
332 pg_atomic_init_u32(&shhashent->generation, 0);
333 shhashent->dropped = false;
334
336 kind_info->shared_size,
338 if (chunk == InvalidDsaPointer)
339 return NULL;
340
342 shheader->magic = 0xdeadbeef;
343
344 /* Link the new entry from the hash entry. */
345 shhashent->body = chunk;
346
347 /* Increment entry count, if required. */
348 if (kind_info->track_entry_count)
350
352
353 return shheader;
354}
355
356static PgStatShared_Common *
358{
360
362
363 /* mark as not dropped anymore */
364 pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
365
366 /*
367 * Increment "generation", to let any backend with local references know
368 * that what they point to is outdated.
369 */
370 pg_atomic_fetch_add_u32(&shhashent->generation, 1);
371 shhashent->dropped = false;
372
373 /* reinitialize content */
374 Assert(shheader->magic == 0xdeadbeef);
377
378 return shheader;
379}
380
381static void
383{
385 return;
386
392}
393
394/*
395 * Helper function for pgstat_get_entry_ref().
396 */
397static void
401{
402 Assert(shheader->magic == 0xdeadbeef);
403 Assert(pg_atomic_read_u32(&shhashent->refcount) > 0);
404
405 pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
406
408
409 entry_ref->shared_stats = shheader;
410 entry_ref->shared_entry = shhashent;
411 entry_ref->generation = pg_atomic_read_u32(&shhashent->generation);
412}
413
414/*
415 * Helper function for pgstat_get_entry_ref().
416 */
417static bool
419{
420 bool found;
422
423 /*
424 * We immediately insert a cache entry, because it avoids 1) multiple
425 * hashtable lookups in case of a cache miss 2) having to deal with
426 * out-of-memory errors after incrementing PgStatShared_Common->refcount.
427 */
428
430
431 if (!found || !cache_entry->entry_ref)
432 {
433 PgStat_EntryRef *entry_ref;
434
436 sizeof(PgStat_EntryRef),
438 if (unlikely(entry_ref == NULL))
439 {
440 /*
441 * Clean the hash entry to keep the table consistent in the
442 * backend.
443 */
445
448 errmsg("out of memory")));
449 }
450
451 cache_entry->entry_ref = entry_ref;
452 entry_ref->shared_stats = NULL;
453 entry_ref->shared_entry = NULL;
454 entry_ref->pending = NULL;
455
456 found = false;
457 }
458 else if (cache_entry->entry_ref->shared_stats == NULL)
459 {
460 Assert(cache_entry->entry_ref->pending == NULL);
461 found = false;
462 }
463 else
464 {
466
467 entry_ref = cache_entry->entry_ref;
468 Assert(entry_ref->shared_entry != NULL);
469 Assert(entry_ref->shared_stats != NULL);
470
471 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
472 /* should have at least our reference */
474 }
475
476 *entry_ref_p = cache_entry->entry_ref;
477 return found;
478}
479
480/*
481 * Get a shared stats reference. If create is true, the shared stats object is
482 * created if it does not exist.
483 *
484 * When create is true, and created_entry is non-NULL, it'll be set to true
485 * if the entry is newly created, false otherwise.
486 */
488pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create,
489 bool *created_entry)
490{
491 PgStat_HashKey key = {0};
494 PgStat_EntryRef *entry_ref;
495
496 key.kind = kind;
497 key.dboid = dboid;
498 key.objid = objid;
499
500 /*
501 * passing in created_entry only makes sense if we possibly could create
502 * entry.
503 */
504 Assert(create || created_entry == NULL);
508
511
512 if (created_entry != NULL)
513 *created_entry = false;
514
515 /*
516 * Check if other backends dropped stats that could not be deleted because
517 * somebody held references to it. If so, check this backend's references.
518 * This is not expected to happen often. The location of the check is a
519 * bit random, but this is a relatively frequently called path, so better
520 * than most.
521 */
524
525 /*
526 * First check the lookup cache hashtable in local memory. If we find a
527 * match here we can avoid taking locks / causing contention.
528 */
529 if (pgstat_get_entry_ref_cached(key, &entry_ref))
530 return entry_ref;
531
532 Assert(entry_ref != NULL);
533
534 /*
535 * Do a lookup in the hash table first - it's quite likely that the entry
536 * already exists, and that way we only need a shared lock.
537 */
539
540 if (create && !shhashent)
541 {
542 bool shfound;
543
544 /*
545 * It's possible that somebody created the entry since the above
546 * lookup. If so, fall through to the same path as if we'd have if it
547 * already had been created before the dshash_find() calls.
548 */
550 if (!shfound)
551 {
553 if (shheader == NULL)
554 {
555 /*
556 * Failed the allocation of a new entry, so clean up the
557 * shared hashtable before giving up.
558 */
560
563 errmsg("out of memory"),
564 errdetail("Failed while allocating entry %u/%u/%" PRIu64 ".",
565 key.kind, key.dboid, key.objid)));
566 }
568
569 if (created_entry != NULL)
570 *created_entry = true;
571
572 return entry_ref;
573 }
574 }
575
576 if (!shhashent)
577 {
578 /*
579 * If we're not creating, delete the reference again. In all
580 * likelihood it's just a stats lookup - no point wasting memory for a
581 * shared ref to nothing...
582 */
583 pgstat_release_entry_ref(key, entry_ref, false);
584
585 return NULL;
586 }
587 else
588 {
589 /*
590 * Can get here either because dshash_find() found a match, or if
591 * dshash_find_or_insert() found a concurrently inserted entry.
592 */
593
594 if (shhashent->dropped && create)
595 {
596 /*
597 * There are legitimate cases where the old stats entry might not
598 * yet have been dropped by the time it's reused. The most obvious
599 * case are replication slot stats, where a new slot can be
600 * created with the same index just after dropping. But oid
601 * wraparound can lead to other cases as well. We just reset the
602 * stats to their plain state, while incrementing its "generation"
603 * in the shared entry for any remaining local references.
604 */
607
608 if (created_entry != NULL)
609 *created_entry = true;
610
611 return entry_ref;
612 }
613 else if (shhashent->dropped)
614 {
616 pgstat_release_entry_ref(key, entry_ref, false);
617
618 return NULL;
619 }
620 else
621 {
624
625 return entry_ref;
626 }
627 }
628}
629
630static void
632 bool discard_pending)
633{
634 if (entry_ref && entry_ref->pending)
635 {
636 if (discard_pending)
638 else
639 elog(ERROR, "releasing ref with pending data");
640 }
641
642 if (entry_ref && entry_ref->shared_stats)
643 {
644 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
645 Assert(entry_ref->pending == NULL);
646
647 /*
648 * This can't race with another backend looking up the stats entry and
649 * increasing the refcount because it is not "legal" to create
650 * additional references to dropped entries.
651 */
652 if (pg_atomic_fetch_sub_u32(&entry_ref->shared_entry->refcount, 1) == 1)
653 {
655
656 /*
657 * We're the last referrer to this entry, try to drop the shared
658 * entry.
659 */
660
661 /* only dropped entries can reach a 0 refcount */
662 Assert(entry_ref->shared_entry->dropped);
663
665 &entry_ref->shared_entry->key,
666 true);
667 if (!shent)
668 elog(ERROR, "could not find just referenced shared stats entry");
669
670 /*
671 * This entry may have been reinitialized while trying to release
672 * it, so double-check that it has not been reused while holding a
673 * lock on its shared entry.
674 */
675 if (pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
676 entry_ref->generation)
677 {
678 /* Same "generation", so we're OK with the removal */
680 Assert(entry_ref->shared_entry == shent);
682 }
683 else
684 {
685 /*
686 * Shared stats entry has been reinitialized, so do not drop
687 * its shared entry, only release its lock.
688 */
690 }
691 }
692 }
693
695 elog(ERROR, "entry ref vanished before deletion");
696
697 if (entry_ref)
698 pfree(entry_ref);
699}
700
701/*
702 * Acquire exclusive lock on the entry.
703 *
704 * If nowait is true, it's just a conditional acquire, and the result
705 * *must* be checked to verify success.
706 * If nowait is false, waits as necessary, always returning true.
707 */
708bool
709pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
710{
711 LWLock *lock = &entry_ref->shared_stats->lock;
712
713 if (nowait)
715
717 return true;
718}
719
720/*
721 * Acquire shared lock on the entry.
722 *
723 * Separate from pgstat_lock_entry() as most callers will need to lock
724 * exclusively. The wait semantics are identical.
725 */
726bool
727pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait)
728{
729 LWLock *lock = &entry_ref->shared_stats->lock;
730
731 if (nowait)
733
735 return true;
736}
737
738void
740{
741 LWLockRelease(&entry_ref->shared_stats->lock);
742}
743
744/*
745 * Helper function to fetch and lock shared stats.
746 */
749 bool nowait)
750{
751 PgStat_EntryRef *entry_ref;
752
753 /* find shared table stats entry corresponding to the local entry */
754 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, true, NULL);
755
756 /* lock the shared entry to protect the content, skip if failed */
757 if (!pgstat_lock_entry(entry_ref, nowait))
758 return NULL;
759
760 return entry_ref;
761}
762
763void
765{
767}
768
769static bool
771{
773
775 return false;
776
777 /* should have been initialized when creating pgStatEntryRefHash */
779
781
782 return pgStatSharedRefAge != curage;
783}
784
785static void
787{
791
793 Assert(curage != 0);
794
795 /*
796 * Some entries have been dropped or reinitialized. Invalidate cache
797 * pointer to them.
798 */
801 {
802 PgStat_EntryRef *entry_ref = ent->entry_ref;
803
804 Assert(!entry_ref->shared_stats ||
805 entry_ref->shared_stats->magic == 0xdeadbeef);
806
807 /*
808 * "generation" checks for the case of entries being reinitialized,
809 * and "dropped" for the case where these are.. dropped.
810 */
811 if (!entry_ref->shared_entry->dropped &&
813 entry_ref->generation)
814 continue;
815
816 /* cannot gc shared ref that has pending data */
817 if (entry_ref->pending != NULL)
818 continue;
819
820 pgstat_release_entry_ref(ent->key, entry_ref, false);
821 }
822
824}
825
826static void
829{
832
834 return;
835
837
839 != NULL)
840 {
841 Assert(ent->entry_ref != NULL);
842
843 if (match && !match(ent, match_data))
844 continue;
845
847 }
848}
849
850/*
851 * Release all local references to shared stats entries.
852 *
853 * When a process exits it cannot do so while still holding references onto
854 * stats entries, otherwise the shared stats entries could never be freed.
855 */
856static void
858{
860 return;
861
863 Assert(pgStatEntryRefHash->members == 0);
866}
867
868static bool
870{
872
873 return ent->key.dboid == dboid;
874}
875
876static void
878{
879 pgstat_release_matching_entry_refs( /* discard pending = */ true,
880 match_db,
881 ObjectIdGetDatum(dboid));
882}
883
884
885/* ------------------------------------------------------------
886 * Dropping and resetting of stats entries
887 * ------------------------------------------------------------
888 */
889
890static void
892{
894 PgStat_Kind kind = shent->key.kind;
895
896 /*
897 * Fetch dsa pointer before deleting entry - that way we can free the
898 * memory after releasing the lock.
899 */
900 pdsa = shent->body;
901
902 if (!hstat)
904 else
906
908
909 /* Decrement entry count, if required. */
910 if (pgstat_get_kind_info(kind)->track_entry_count)
912}
913
914/*
915 * Helper for both pgstat_drop_database_and_contents() and
916 * pgstat_drop_entry(). If hstat is non-null delete the shared entry using
917 * dshash_delete_current(), otherwise use dshash_delete_entry(). In either
918 * case the entry needs to be already locked.
919 */
920static bool
923{
925
926 /* should already have released local reference */
929
930 /*
931 * Signal that the entry is dropped - this will eventually cause other
932 * backends to release their references.
933 */
934 Assert(!shent->dropped);
935 shent->dropped = true;
936
937 /* release refcount marking entry as not dropped */
938 if (pg_atomic_sub_fetch_u32(&shent->refcount, 1) == 0)
939 {
941 return true;
942 }
943 else
944 {
945 if (!hstat)
947 return false;
948 }
949}
950
951/*
952 * Drop stats for the database and all the objects inside that database.
953 */
954static void
956{
960
961 Assert(OidIsValid(dboid));
962
964
965 /*
966 * This backend might very well be the only backend holding a reference to
967 * about-to-be-dropped entries. Ensure that we're not preventing it from
968 * being cleaned up till later.
969 *
970 * Doing this separately from the dshash iteration below avoids having to
971 * do so while holding a partition lock on the shared hashtable.
972 */
974
975 /* some of the dshash entries are to be removed, take exclusive lock. */
977 while ((p = dshash_seq_next(&hstat)) != NULL)
978 {
979 if (p->dropped)
980 continue;
981
982 if (p->key.dboid != dboid)
983 continue;
984
986 {
987 /*
988 * Even statistics for a dropped database might currently be
989 * accessed (consider e.g. database stats for pg_stat_database).
990 */
992 }
993 }
995
996 /*
997 * If some of the stats data could not be freed, signal the reference
998 * holders to run garbage collection of their cached pgStatLocal.shmem.
999 */
1000 if (not_freed_count > 0)
1002}
1003
1004/*
1005 * Drop a single stats entry.
1006 *
1007 * This routine returns false if the stats entry of the dropped object could
1008 * not be freed, true otherwise.
1009 *
1010 * If missing_ok is true, skip entries that have been concurrently dropped.
1011 *
1012 * The callers of this function should call pgstat_request_entry_refs_gc()
1013 * if the stats entry could not be freed, to ensure that this entry's memory
1014 * can be reclaimed later by a different backend calling
1015 * pgstat_gc_entry_refs().
1016 */
1017bool
1018pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid,
1019 bool missing_ok)
1020{
1021 PgStat_HashKey key = {0};
1023 bool freed = true;
1024
1025 key.kind = kind;
1026 key.dboid = dboid;
1027 key.objid = objid;
1028
1029 /* delete local reference */
1031 {
1034
1035 if (lohashent)
1037 true);
1038 }
1039
1040 /* mark entry in shared hashtable as deleted, drop if possible */
1042 if (shent)
1043 {
1044 if (shent->dropped)
1045 {
1046 if (!missing_ok)
1047 elog(ERROR,
1048 "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%" PRIu64 " refcount=%u generation=%u",
1049 pgstat_get_kind_info(shent->key.kind)->name,
1050 shent->key.dboid,
1051 shent->key.objid,
1052 pg_atomic_read_u32(&shent->refcount),
1053 pg_atomic_read_u32(&shent->generation));
1055 return true;
1056 }
1057
1059
1060 /*
1061 * Database stats contain other stats. Drop those as well when
1062 * dropping the database. XXX: Perhaps this should be done in a
1063 * slightly more principled way? But not obvious what that'd look
1064 * like, and so far this is the only case...
1065 */
1066 if (key.kind == PGSTAT_KIND_DATABASE)
1068 }
1069
1070 return freed;
1071}
1072
1073/*
1074 * Scan through the shared hashtable of stats, dropping statistics if
1075 * approved by the optional do_drop() function.
1076 */
1077void
1080{
1084
1085 /* entries are removed, take an exclusive lock */
1087 while ((ps = dshash_seq_next(&hstat)) != NULL)
1088 {
1089 if (ps->dropped)
1090 continue;
1091
1092 if (do_drop != NULL && !do_drop(ps, match_data))
1093 continue;
1094
1095 /* delete local reference */
1097 {
1100
1101 if (lohashent)
1103 true);
1104 }
1105
1108 }
1110
1111 if (not_freed_count > 0)
1113}
1114
1115/*
1116 * Scan through the shared hashtable of stats and drop all entries.
1117 */
1118void
1120{
1122}
1123
1124static void
1126 TimestampTz ts)
1127{
1129
1130 memset(pgstat_get_entry_data(kind, header), 0,
1131 pgstat_get_entry_len(kind));
1132
1133 if (kind_info->reset_timestamp_cb)
1134 kind_info->reset_timestamp_cb(header, ts);
1135}
1136
1137/*
1138 * Reset one variable-numbered stats entry.
1139 */
1140void
1141pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts)
1142{
1143 PgStat_EntryRef *entry_ref;
1144
1145 Assert(!pgstat_get_kind_info(kind)->fixed_amount);
1146
1147 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1148 if (!entry_ref || entry_ref->shared_entry->dropped)
1149 return;
1150
1151 (void) pgstat_lock_entry(entry_ref, false);
1152 shared_stat_reset_contents(kind, entry_ref->shared_stats, ts);
1153 pgstat_unlock_entry(entry_ref);
1154}
1155
1156/*
1157 * Scan through the shared hashtable of stats, resetting statistics if
1158 * approved by the provided do_reset() function.
1159 */
1160void
1163{
1166
1167 /* dshash entry is not modified, take shared lock */
1169 while ((p = dshash_seq_next(&hstat)) != NULL)
1170 {
1171 PgStatShared_Common *header;
1172
1173 if (p->dropped)
1174 continue;
1175
1176 if (!do_reset(p, match_data))
1177 continue;
1178
1179 header = dsa_get_address(pgStatLocal.dsa, p->body);
1180
1181 LWLockAcquire(&header->lock, LW_EXCLUSIVE);
1182
1183 shared_stat_reset_contents(p->key.kind, header, ts);
1184
1185 LWLockRelease(&header->lock);
1186 }
1188}
1189
1190static bool
1192{
1193 return p->key.kind == DatumGetInt32(match_data);
1194}
1195
1196void
1198{
1200}
1201
1202static void
1204{
1208 "PgStat Shared Ref",
1213 "PgStat Shared Ref Hash",
1215}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition atomics.h:434
static uint32 pg_atomic_fetch_sub_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition atomics.h:376
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition atomics.h:214
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition atomics.h:361
static uint64 pg_atomic_fetch_add_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
Definition atomics.h:527
static uint64 pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_)
Definition atomics.h:573
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition atomics.h:232
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:448
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:462
#define likely(x)
Definition c.h:496
#define MAXALIGN(LEN)
Definition c.h:955
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define Assert(condition)
Definition c.h:1002
uint64_t uint64
Definition c.h:684
#define unlikely(x)
Definition c.h:497
#define OidIsValid(objectId)
Definition c.h:917
size_t Size
Definition c.h:748
int64 TimestampTz
Definition timestamp.h:39
dsa_area * dsa_attach_in_place(void *place, dsm_segment *segment)
Definition dsa.c:560
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:954
dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags)
Definition dsa.c:683
void dsa_release_in_place(void *place)
Definition dsa.c:620
void dsa_set_size_limit(dsa_area *area, size_t limit)
Definition dsa.c:1030
void dsa_pin_mapping(dsa_area *area)
Definition dsa.c:649
void dsa_detach(dsa_area *area)
Definition dsa.c:1998
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:838
size_t dsa_minimum_size(void)
Definition dsa.c:1243
void dsa_pin(dsa_area *area)
Definition dsa.c:987
uint64 dsa_pointer
Definition dsa.h:62
#define dsa_create_in_place(place, size, tranche_id, segment)
Definition dsa.h:122
#define InvalidDsaPointer
Definition dsa.h:78
#define DSA_ALLOC_NO_OOM
Definition dsa.h:74
#define DSA_ALLOC_ZERO
Definition dsa.h:75
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:611
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition dshash.c:562
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
void dshash_detach(dshash_table *hash_table)
Definition dshash.c:311
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:394
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:371
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:274
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition dshash.c:210
void dshash_delete_current(dshash_seq_status *status)
Definition dshash.c:778
#define dshash_find_or_insert(hash_table, key, found)
Definition dshash.h:109
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define MCXT_ALLOC_NO_OOM
Definition fe_memutils.h:29
struct parser_state ps
int i
Definition isn.c:77
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:670
bool LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1321
@ LW_SHARED
Definition lwlock.h:105
@ LW_EXCLUSIVE
Definition lwlock.h:104
Size add_size(Size s1, Size s2)
Definition mcxt.c:1733
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext TopMemoryContext
Definition mcxt.c:167
void * MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
Definition mcxt.c:1292
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
void pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref)
Definition pgstat.c:1360
const PgStat_KindInfo * pgstat_get_kind_info(PgStat_Kind kind)
Definition pgstat.c:1479
PgStat_LocalState pgStatLocal
Definition pgstat.c:213
static uint32 pgstat_hash_hash_key(const void *d, size_t size, void *arg)
static int pgstat_cmp_hash_key(const void *a, const void *b, size_t size, void *arg)
static void * pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common *entry)
#define pgstat_assert_is_up()
static size_t pgstat_get_entry_len(PgStat_Kind kind)
#define PGSTAT_KIND_CUSTOM_MAX
Definition pgstat_kind.h:51
static bool pgstat_is_kind_builtin(PgStat_Kind kind)
Definition pgstat_kind.h:62
#define PgStat_Kind
Definition pgstat_kind.h:17
#define PGSTAT_KIND_MAX
Definition pgstat_kind.h:21
#define PGSTAT_KIND_CUSTOM_MIN
Definition pgstat_kind.h:50
#define PGSTAT_KIND_DATABASE
Definition pgstat_kind.h:27
#define PGSTAT_KIND_MIN
Definition pgstat_kind.h:20
static bool match_db(PgStat_EntryRefHashEntry *ent, Datum match_data)
static Size StatsShmemSize(void)
static void pgstat_setup_memcxt(void)
void pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts)
void pgstat_request_entry_refs_gc(void)
static void pgstat_release_db_entry_refs(Oid dboid)
static Size pgstat_dsa_init_size(void)
static bool match_kind(PgStatShared_HashEntry *p, Datum match_data)
PgStat_EntryRef * pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create, bool *created_entry)
bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool missing_ok)
static MemoryContext pgStatEntryRefHashContext
bool pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait)
void pgstat_attach_shmem(void)
static void pgstat_free_entry(PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
static int pgStatSharedRefAge
#define PGSTAT_ENTRY_REF_HASH_SIZE
void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts)
static void shared_stat_reset_contents(PgStat_Kind kind, PgStatShared_Common *header, TimestampTz ts)
static void pgstat_setup_shared_refs(void)
static void StatsShmemRequest(void *arg)
static void pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref, bool discard_pending)
static PgStatShared_Common * pgstat_reinit_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
void pgstat_drop_matching_entries(bool(*do_drop)(PgStatShared_HashEntry *, Datum), Datum match_data)
static void pgstat_drop_database_and_contents(Oid dboid)
static pgstat_entry_ref_hash_hash * pgStatEntryRefHash
static void StatsShmemInit(void *arg)
static void pgstat_acquire_entry_ref(PgStat_EntryRef *entry_ref, PgStatShared_HashEntry *shhashent, PgStatShared_Common *shheader)
static MemoryContext pgStatSharedRefContext
PgStatShared_Common * pgstat_init_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
void pgstat_reset_matching_entries(bool(*do_reset)(PgStatShared_HashEntry *, Datum), Datum match_data, TimestampTz ts)
void pgstat_drop_all_entries(void)
static const dshash_parameters dsh_params
static bool pgstat_get_entry_ref_cached(PgStat_HashKey key, PgStat_EntryRef **entry_ref_p)
void pgstat_unlock_entry(PgStat_EntryRef *entry_ref)
static void pgstat_release_all_entry_refs(bool discard_pending)
static void pgstat_release_matching_entry_refs(bool discard_pending, ReleaseMatchCB match, Datum match_data)
bool(* ReleaseMatchCB)(PgStat_EntryRefHashEntry *, Datum data)
bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
PgStat_EntryRef * pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, uint64 objid, bool nowait)
static void pgstat_gc_entry_refs(void)
static bool pgstat_need_entry_refs_gc(void)
static bool pgstat_drop_entry_internal(PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
void pgstat_detach_shmem(void)
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static int32 DatumGetInt32(Datum X)
Definition postgres.h:202
unsigned int Oid
static int fb(int x)
tree ctl
Definition radixtree.h:1838
#define ShmemRequestStruct(...)
Definition shmem.h:176
pg_atomic_uint32 refcount
pg_atomic_uint32 generation
PgStatShared_Common * shared_stats
PgStatShared_HashEntry * shared_entry
PgStat_Kind kind
const char *const name
dshash_table * shared_hash
PgStat_ShmemControl * shmem
dshash_table_handle hash_handle
pg_atomic_uint64 gc_request_count
pg_atomic_uint64 entry_counts[PGSTAT_KIND_MAX]
const char * name

◆ SH_HASH_KEY

#define SH_HASH_KEY (   tb,
  key 
)     pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)

Definition at line 38 of file pgstat_shmem.c.

◆ SH_KEY

#define SH_KEY   key

Definition at line 37 of file pgstat_shmem.c.

◆ SH_KEY_TYPE

#define SH_KEY_TYPE   PgStat_HashKey

Definition at line 36 of file pgstat_shmem.c.

◆ SH_PREFIX

#define SH_PREFIX   pgstat_entry_ref_hash

Definition at line 34 of file pgstat_shmem.c.

◆ SH_SCOPE

#define SH_SCOPE   static inline

Definition at line 42 of file pgstat_shmem.c.

Typedef Documentation

◆ PgStat_EntryRefHashEntry

◆ ReleaseMatchCB

Definition at line 56 of file pgstat_shmem.c.

Function Documentation

◆ match_db()

static bool match_db ( PgStat_EntryRefHashEntry ent,
Datum  match_data 
)
static

Definition at line 869 of file pgstat_shmem.c.

870{
872
873 return ent->key.dboid == dboid;
874}

References DatumGetObjectId(), and fb().

Referenced by pgstat_release_db_entry_refs().

◆ match_kind()

static bool match_kind ( PgStatShared_HashEntry p,
Datum  match_data 
)
static

Definition at line 1191 of file pgstat_shmem.c.

1192{
1193 return p->key.kind == DatumGetInt32(match_data);
1194}

References DatumGetInt32(), fb(), PgStatShared_HashEntry::key, and PgStat_HashKey::kind.

Referenced by pgstat_reset_entries_of_kind().

◆ pgstat_acquire_entry_ref()

static void pgstat_acquire_entry_ref ( PgStat_EntryRef entry_ref,
PgStatShared_HashEntry shhashent,
PgStatShared_Common shheader 
)
static

◆ pgstat_attach_shmem()

◆ pgstat_detach_shmem()

void pgstat_detach_shmem ( void  )

Definition at line 277 of file pgstat_shmem.c.

278{
280
281 /* we shouldn't leave references to shared stats */
283
286
288
289 /*
290 * dsa_detach() does not decrement the DSA reference count as no segment
291 * was provided to dsa_attach_in_place(), causing no cleanup callbacks to
292 * be registered. Hence, release it manually now.
293 */
295
297}

References Assert, PgStat_LocalState::dsa, dsa_detach(), dsa_release_in_place(), dshash_detach(), fb(), pgstat_release_all_entry_refs(), pgStatLocal, PgStat_ShmemControl::raw_dsa_area, PgStat_LocalState::shared_hash, and PgStat_LocalState::shmem.

Referenced by pgstat_shutdown_hook().

◆ pgstat_drop_all_entries()

void pgstat_drop_all_entries ( void  )

Definition at line 1119 of file pgstat_shmem.c.

1120{
1122}

References fb(), and pgstat_drop_matching_entries().

Referenced by pgstat_reset_after_failure().

◆ pgstat_drop_database_and_contents()

static void pgstat_drop_database_and_contents ( Oid  dboid)
static

Definition at line 955 of file pgstat_shmem.c.

956{
960
961 Assert(OidIsValid(dboid));
962
964
965 /*
966 * This backend might very well be the only backend holding a reference to
967 * about-to-be-dropped entries. Ensure that we're not preventing it from
968 * being cleaned up till later.
969 *
970 * Doing this separately from the dshash iteration below avoids having to
971 * do so while holding a partition lock on the shared hashtable.
972 */
974
975 /* some of the dshash entries are to be removed, take exclusive lock. */
977 while ((p = dshash_seq_next(&hstat)) != NULL)
978 {
979 if (p->dropped)
980 continue;
981
982 if (p->key.dboid != dboid)
983 continue;
984
986 {
987 /*
988 * Even statistics for a dropped database might currently be
989 * accessed (consider e.g. database stats for pg_stat_database).
990 */
992 }
993 }
995
996 /*
997 * If some of the stats data could not be freed, signal the reference
998 * holders to run garbage collection of their cached pgStatLocal.shmem.
999 */
1000 if (not_freed_count > 0)
1002}

References Assert, PgStat_HashKey::dboid, PgStatShared_HashEntry::dropped, dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), fb(), PgStatShared_HashEntry::key, OidIsValid, pgstat_drop_entry_internal(), pgstat_release_db_entry_refs(), pgstat_request_entry_refs_gc(), pgStatLocal, and PgStat_LocalState::shared_hash.

Referenced by pgstat_drop_entry().

◆ pgstat_drop_entry()

bool pgstat_drop_entry ( PgStat_Kind  kind,
Oid  dboid,
uint64  objid,
bool  missing_ok 
)

Definition at line 1018 of file pgstat_shmem.c.

1020{
1021 PgStat_HashKey key = {0};
1023 bool freed = true;
1024
1025 key.kind = kind;
1026 key.dboid = dboid;
1027 key.objid = objid;
1028
1029 /* delete local reference */
1031 {
1034
1035 if (lohashent)
1037 true);
1038 }
1039
1040 /* mark entry in shared hashtable as deleted, drop if possible */
1042 if (shent)
1043 {
1044 if (shent->dropped)
1045 {
1046 if (!missing_ok)
1047 elog(ERROR,
1048 "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%" PRIu64 " refcount=%u generation=%u",
1049 pgstat_get_kind_info(shent->key.kind)->name,
1050 shent->key.dboid,
1051 shent->key.objid,
1052 pg_atomic_read_u32(&shent->refcount),
1053 pg_atomic_read_u32(&shent->generation));
1055 return true;
1056 }
1057
1059
1060 /*
1061 * Database stats contain other stats. Drop those as well when
1062 * dropping the database. XXX: Perhaps this should be done in a
1063 * slightly more principled way? But not obvious what that'd look
1064 * like, and so far this is the only case...
1065 */
1066 if (key.kind == PGSTAT_KIND_DATABASE)
1068 }
1069
1070 return freed;
1071}

References dshash_find(), dshash_release_lock(), elog, ERROR, fb(), PgStat_KindInfo::name, pg_atomic_read_u32(), pgstat_drop_database_and_contents(), pgstat_drop_entry_internal(), pgstat_get_kind_info(), PGSTAT_KIND_DATABASE, pgstat_release_entry_ref(), pgStatEntryRefHash, pgStatLocal, and PgStat_LocalState::shared_hash.

Referenced by AtEOSubXact_PgStat_DroppedStats(), AtEOXact_PgStat_DroppedStats(), pgstat_drop_replslot(), pgstat_execute_transactional_drops(), pgstat_init_function_usage(), pgstat_shutdown_hook(), and test_custom_stats_var_drop().

◆ pgstat_drop_entry_internal()

static bool pgstat_drop_entry_internal ( PgStatShared_HashEntry shent,
dshash_seq_status hstat 
)
static

Definition at line 921 of file pgstat_shmem.c.

923{
925
926 /* should already have released local reference */
929
930 /*
931 * Signal that the entry is dropped - this will eventually cause other
932 * backends to release their references.
933 */
934 Assert(!shent->dropped);
935 shent->dropped = true;
936
937 /* release refcount marking entry as not dropped */
938 if (pg_atomic_sub_fetch_u32(&shent->refcount, 1) == 0)
939 {
941 return true;
942 }
943 else
944 {
945 if (!hstat)
947 return false;
948 }
949}

References Assert, dshash_release_lock(), fb(), InvalidDsaPointer, pg_atomic_sub_fetch_u32(), pgstat_free_entry(), pgStatEntryRefHash, pgStatLocal, and PgStat_LocalState::shared_hash.

Referenced by pgstat_drop_database_and_contents(), pgstat_drop_entry(), and pgstat_drop_matching_entries().

◆ pgstat_drop_matching_entries()

void pgstat_drop_matching_entries ( bool(*)(PgStatShared_HashEntry *, Datum do_drop,
Datum  match_data 
)

Definition at line 1078 of file pgstat_shmem.c.

1080{
1084
1085 /* entries are removed, take an exclusive lock */
1087 while ((ps = dshash_seq_next(&hstat)) != NULL)
1088 {
1089 if (ps->dropped)
1090 continue;
1091
1092 if (do_drop != NULL && !do_drop(ps, match_data))
1093 continue;
1094
1095 /* delete local reference */
1097 {
1100
1101 if (lohashent)
1103 true);
1104 }
1105
1108 }
1110
1111 if (not_freed_count > 0)
1113}

References dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), fb(), pgstat_drop_entry_internal(), pgstat_release_entry_ref(), pgstat_request_entry_refs_gc(), pgStatEntryRefHash, pgStatLocal, ps, and PgStat_LocalState::shared_hash.

Referenced by pgstat_drop_all_entries().

◆ pgstat_dsa_init_size()

static Size pgstat_dsa_init_size ( void  )
static

Definition at line 114 of file pgstat_shmem.c.

115{
116 Size sz;
117
118 /*
119 * The dshash header / initial buckets array needs to fit into "plain"
120 * shared memory, but it's beneficial to not need dsm segments
121 * immediately. A size of 256kB seems works well and is not
122 * disproportional compared to other constant sized shared memory
123 * allocations. NB: To avoid DSMs further, the user can configure
124 * min_dynamic_shared_memory.
125 */
126 sz = 256 * 1024;
128 return MAXALIGN(sz);
129}

References Assert, dsa_minimum_size(), fb(), and MAXALIGN.

Referenced by StatsShmemInit(), and StatsShmemSize().

◆ pgstat_free_entry()

static void pgstat_free_entry ( PgStatShared_HashEntry shent,
dshash_seq_status hstat 
)
static

Definition at line 891 of file pgstat_shmem.c.

892{
894 PgStat_Kind kind = shent->key.kind;
895
896 /*
897 * Fetch dsa pointer before deleting entry - that way we can free the
898 * memory after releasing the lock.
899 */
900 pdsa = shent->body;
901
902 if (!hstat)
904 else
906
908
909 /* Decrement entry count, if required. */
910 if (pgstat_get_kind_info(kind)->track_entry_count)
912}

References PgStat_LocalState::dsa, dsa_free(), dshash_delete_current(), dshash_delete_entry(), PgStat_ShmemControl::entry_counts, fb(), pg_atomic_sub_fetch_u64(), pgstat_get_kind_info(), PgStat_Kind, pgStatLocal, PgStat_LocalState::shared_hash, and PgStat_LocalState::shmem.

Referenced by pgstat_drop_entry_internal(), and pgstat_release_entry_ref().

◆ pgstat_gc_entry_refs()

static void pgstat_gc_entry_refs ( void  )
static

Definition at line 786 of file pgstat_shmem.c.

787{
791
793 Assert(curage != 0);
794
795 /*
796 * Some entries have been dropped or reinitialized. Invalidate cache
797 * pointer to them.
798 */
801 {
802 PgStat_EntryRef *entry_ref = ent->entry_ref;
803
804 Assert(!entry_ref->shared_stats ||
805 entry_ref->shared_stats->magic == 0xdeadbeef);
806
807 /*
808 * "generation" checks for the case of entries being reinitialized,
809 * and "dropped" for the case where these are.. dropped.
810 */
811 if (!entry_ref->shared_entry->dropped &&
813 entry_ref->generation)
814 continue;
815
816 /* cannot gc shared ref that has pending data */
817 if (entry_ref->pending != NULL)
818 continue;
819
820 pgstat_release_entry_ref(ent->key, entry_ref, false);
821 }
822
824}

References Assert, PgStatShared_HashEntry::dropped, fb(), PgStat_ShmemControl::gc_request_count, PgStatShared_HashEntry::generation, PgStat_EntryRef::generation, i, PgStatShared_Common::magic, PgStat_EntryRef::pending, pg_atomic_read_u32(), pg_atomic_read_u64(), pgstat_release_entry_ref(), pgStatEntryRefHash, pgStatLocal, pgStatSharedRefAge, PgStat_EntryRef::shared_entry, PgStat_EntryRef::shared_stats, and PgStat_LocalState::shmem.

Referenced by pgstat_get_entry_ref().

◆ pgstat_get_entry_ref()

PgStat_EntryRef * pgstat_get_entry_ref ( PgStat_Kind  kind,
Oid  dboid,
uint64  objid,
bool  create,
bool created_entry 
)

Definition at line 488 of file pgstat_shmem.c.

490{
491 PgStat_HashKey key = {0};
494 PgStat_EntryRef *entry_ref;
495
496 key.kind = kind;
497 key.dboid = dboid;
498 key.objid = objid;
499
500 /*
501 * passing in created_entry only makes sense if we possibly could create
502 * entry.
503 */
504 Assert(create || created_entry == NULL);
508
511
512 if (created_entry != NULL)
513 *created_entry = false;
514
515 /*
516 * Check if other backends dropped stats that could not be deleted because
517 * somebody held references to it. If so, check this backend's references.
518 * This is not expected to happen often. The location of the check is a
519 * bit random, but this is a relatively frequently called path, so better
520 * than most.
521 */
524
525 /*
526 * First check the lookup cache hashtable in local memory. If we find a
527 * match here we can avoid taking locks / causing contention.
528 */
529 if (pgstat_get_entry_ref_cached(key, &entry_ref))
530 return entry_ref;
531
532 Assert(entry_ref != NULL);
533
534 /*
535 * Do a lookup in the hash table first - it's quite likely that the entry
536 * already exists, and that way we only need a shared lock.
537 */
539
540 if (create && !shhashent)
541 {
542 bool shfound;
543
544 /*
545 * It's possible that somebody created the entry since the above
546 * lookup. If so, fall through to the same path as if we'd have if it
547 * already had been created before the dshash_find() calls.
548 */
550 if (!shfound)
551 {
553 if (shheader == NULL)
554 {
555 /*
556 * Failed the allocation of a new entry, so clean up the
557 * shared hashtable before giving up.
558 */
560
563 errmsg("out of memory"),
564 errdetail("Failed while allocating entry %u/%u/%" PRIu64 ".",
565 key.kind, key.dboid, key.objid)));
566 }
568
569 if (created_entry != NULL)
570 *created_entry = true;
571
572 return entry_ref;
573 }
574 }
575
576 if (!shhashent)
577 {
578 /*
579 * If we're not creating, delete the reference again. In all
580 * likelihood it's just a stats lookup - no point wasting memory for a
581 * shared ref to nothing...
582 */
583 pgstat_release_entry_ref(key, entry_ref, false);
584
585 return NULL;
586 }
587 else
588 {
589 /*
590 * Can get here either because dshash_find() found a match, or if
591 * dshash_find_or_insert() found a concurrently inserted entry.
592 */
593
594 if (shhashent->dropped && create)
595 {
596 /*
597 * There are legitimate cases where the old stats entry might not
598 * yet have been dropped by the time it's reused. The most obvious
599 * case are replication slot stats, where a new slot can be
600 * created with the same index just after dropping. But oid
601 * wraparound can lead to other cases as well. We just reset the
602 * stats to their plain state, while incrementing its "generation"
603 * in the shared entry for any remaining local references.
604 */
607
608 if (created_entry != NULL)
609 *created_entry = true;
610
611 return entry_ref;
612 }
613 else if (shhashent->dropped)
614 {
616 pgstat_release_entry_ref(key, entry_ref, false);
617
618 return NULL;
619 }
620 else
621 {
624
625 return entry_ref;
626 }
627 }
628}

References Assert, PgStat_LocalState::dsa, dsa_get_address(), dshash_delete_entry(), dshash_find(), dshash_find_or_insert, dshash_release_lock(), ereport, errcode(), errdetail(), errmsg, ERROR, fb(), PgStat_ShmemControl::is_shutdown, pgstat_acquire_entry_ref(), pgstat_assert_is_up, pgstat_gc_entry_refs(), pgstat_get_entry_ref_cached(), pgstat_init_entry(), pgstat_need_entry_refs_gc(), pgstat_reinit_entry(), pgstat_release_entry_ref(), pgstat_setup_memcxt(), pgstat_setup_shared_refs(), pgStatLocal, PgStat_LocalState::shared_hash, and PgStat_LocalState::shmem.

Referenced by pgstat_acquire_replslot(), pgstat_create_subscription(), pgstat_create_transactional(), pgstat_fetch_entry(), pgstat_fetch_pending_entry(), pgstat_get_entry_ref_locked(), pgstat_have_entry(), pgstat_prep_pending_entry(), pgstat_prepare_report_checksum_failure(), pgstat_report_checksum_failures_in_db(), pgstat_reset_entry(), and test_custom_stats_var_report().

◆ pgstat_get_entry_ref_cached()

static bool pgstat_get_entry_ref_cached ( PgStat_HashKey  key,
PgStat_EntryRef **  entry_ref_p 
)
static

Definition at line 418 of file pgstat_shmem.c.

419{
420 bool found;
422
423 /*
424 * We immediately insert a cache entry, because it avoids 1) multiple
425 * hashtable lookups in case of a cache miss 2) having to deal with
426 * out-of-memory errors after incrementing PgStatShared_Common->refcount.
427 */
428
430
431 if (!found || !cache_entry->entry_ref)
432 {
433 PgStat_EntryRef *entry_ref;
434
436 sizeof(PgStat_EntryRef),
438 if (unlikely(entry_ref == NULL))
439 {
440 /*
441 * Clean the hash entry to keep the table consistent in the
442 * backend.
443 */
445
448 errmsg("out of memory")));
449 }
450
451 cache_entry->entry_ref = entry_ref;
452 entry_ref->shared_stats = NULL;
453 entry_ref->shared_entry = NULL;
454 entry_ref->pending = NULL;
455
456 found = false;
457 }
458 else if (cache_entry->entry_ref->shared_stats == NULL)
459 {
460 Assert(cache_entry->entry_ref->pending == NULL);
461 found = false;
462 }
463 else
464 {
466
467 entry_ref = cache_entry->entry_ref;
468 Assert(entry_ref->shared_entry != NULL);
469 Assert(entry_ref->shared_stats != NULL);
470
471 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
472 /* should have at least our reference */
474 }
475
476 *entry_ref_p = cache_entry->entry_ref;
477 return found;
478}

References Assert, ereport, errcode(), errmsg, ERROR, fb(), MCXT_ALLOC_NO_OOM, MemoryContextAllocExtended(), PgStat_EntryRef::pending, pg_atomic_read_u32(), PG_USED_FOR_ASSERTS_ONLY, pgStatEntryRefHash, pgStatSharedRefContext, PgStat_EntryRef::shared_entry, PgStat_EntryRef::shared_stats, and unlikely.

Referenced by pgstat_get_entry_ref().

◆ pgstat_get_entry_ref_locked()

PgStat_EntryRef * pgstat_get_entry_ref_locked ( PgStat_Kind  kind,
Oid  dboid,
uint64  objid,
bool  nowait 
)

Definition at line 748 of file pgstat_shmem.c.

750{
751 PgStat_EntryRef *entry_ref;
752
753 /* find shared table stats entry corresponding to the local entry */
754 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, true, NULL);
755
756 /* lock the shared entry to protect the content, skip if failed */
757 if (!pgstat_lock_entry(entry_ref, nowait))
758 return NULL;
759
760 return entry_ref;
761}

References fb(), pgstat_get_entry_ref(), and pgstat_lock_entry().

Referenced by pgstat_copy_relation_stats(), pgstat_create_backend(), pgstat_create_replslot(), pgstat_flush_backend(), pgstat_report_analyze(), pgstat_report_autovac(), pgstat_report_replslot(), pgstat_report_replslotsync(), pgstat_report_vacuum(), pgstat_reset_database_timestamp(), and test_custom_stats_var_create().

◆ pgstat_init_entry()

PgStatShared_Common * pgstat_init_entry ( PgStat_Kind  kind,
PgStatShared_HashEntry shhashent 
)

Definition at line 313 of file pgstat_shmem.c.

315{
316 /* Create new stats entry. */
317 dsa_pointer chunk;
320
321 /*
322 * Initialize refcount to 1, marking it as valid / not dropped. The entry
323 * can't be freed before the initialization because it can't be found as
324 * long as we hold the dshash partition lock. Caller needs to increase
325 * further if a longer lived reference is needed.
326 */
327 pg_atomic_init_u32(&shhashent->refcount, 1);
328
329 /*
330 * Initialize "generation" to 0, as freshly created.
331 */
332 pg_atomic_init_u32(&shhashent->generation, 0);
333 shhashent->dropped = false;
334
336 kind_info->shared_size,
338 if (chunk == InvalidDsaPointer)
339 return NULL;
340
342 shheader->magic = 0xdeadbeef;
343
344 /* Link the new entry from the hash entry. */
345 shhashent->body = chunk;
346
347 /* Increment entry count, if required. */
348 if (kind_info->track_entry_count)
350
352
353 return shheader;
354}

References PgStat_LocalState::dsa, DSA_ALLOC_NO_OOM, DSA_ALLOC_ZERO, dsa_allocate_extended(), dsa_get_address(), PgStat_ShmemControl::entry_counts, fb(), InvalidDsaPointer, LWLockInitialize(), pg_atomic_fetch_add_u64(), pg_atomic_init_u32(), pgstat_get_kind_info(), pgStatLocal, and PgStat_LocalState::shmem.

Referenced by pgstat_get_entry_ref(), and pgstat_read_statsfile().

◆ pgstat_lock_entry()

◆ pgstat_lock_entry_shared()

bool pgstat_lock_entry_shared ( PgStat_EntryRef entry_ref,
bool  nowait 
)

Definition at line 727 of file pgstat_shmem.c.

728{
729 LWLock *lock = &entry_ref->shared_stats->lock;
730
731 if (nowait)
733
735 return true;
736}

References PgStatShared_Common::lock, LW_SHARED, LWLockAcquire(), LWLockConditionalAcquire(), and PgStat_EntryRef::shared_stats.

Referenced by pgstat_fetch_entry().

◆ pgstat_need_entry_refs_gc()

static bool pgstat_need_entry_refs_gc ( void  )
static

Definition at line 770 of file pgstat_shmem.c.

771{
773
775 return false;
776
777 /* should have been initialized when creating pgStatEntryRefHash */
779
781
782 return pgStatSharedRefAge != curage;
783}

References Assert, fb(), PgStat_ShmemControl::gc_request_count, pg_atomic_read_u64(), pgStatEntryRefHash, pgStatLocal, pgStatSharedRefAge, and PgStat_LocalState::shmem.

Referenced by pgstat_get_entry_ref().

◆ pgstat_reinit_entry()

static PgStatShared_Common * pgstat_reinit_entry ( PgStat_Kind  kind,
PgStatShared_HashEntry shhashent 
)
static

Definition at line 357 of file pgstat_shmem.c.

358{
360
362
363 /* mark as not dropped anymore */
364 pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
365
366 /*
367 * Increment "generation", to let any backend with local references know
368 * that what they point to is outdated.
369 */
370 pg_atomic_fetch_add_u32(&shhashent->generation, 1);
371 shhashent->dropped = false;
372
373 /* reinitialize content */
374 Assert(shheader->magic == 0xdeadbeef);
377
378 return shheader;
379}

References Assert, PgStat_LocalState::dsa, dsa_get_address(), fb(), pg_atomic_fetch_add_u32(), pgstat_get_entry_data(), pgstat_get_entry_len(), and pgStatLocal.

Referenced by pgstat_get_entry_ref().

◆ pgstat_release_all_entry_refs()

static void pgstat_release_all_entry_refs ( bool  discard_pending)
static

◆ pgstat_release_db_entry_refs()

static void pgstat_release_db_entry_refs ( Oid  dboid)
static

Definition at line 877 of file pgstat_shmem.c.

878{
879 pgstat_release_matching_entry_refs( /* discard pending = */ true,
880 match_db,
881 ObjectIdGetDatum(dboid));
882}

References match_db(), ObjectIdGetDatum(), and pgstat_release_matching_entry_refs().

Referenced by pgstat_drop_database_and_contents().

◆ pgstat_release_entry_ref()

static void pgstat_release_entry_ref ( PgStat_HashKey  key,
PgStat_EntryRef entry_ref,
bool  discard_pending 
)
static

Definition at line 631 of file pgstat_shmem.c.

633{
634 if (entry_ref && entry_ref->pending)
635 {
636 if (discard_pending)
638 else
639 elog(ERROR, "releasing ref with pending data");
640 }
641
642 if (entry_ref && entry_ref->shared_stats)
643 {
644 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
645 Assert(entry_ref->pending == NULL);
646
647 /*
648 * This can't race with another backend looking up the stats entry and
649 * increasing the refcount because it is not "legal" to create
650 * additional references to dropped entries.
651 */
652 if (pg_atomic_fetch_sub_u32(&entry_ref->shared_entry->refcount, 1) == 1)
653 {
655
656 /*
657 * We're the last referrer to this entry, try to drop the shared
658 * entry.
659 */
660
661 /* only dropped entries can reach a 0 refcount */
662 Assert(entry_ref->shared_entry->dropped);
663
665 &entry_ref->shared_entry->key,
666 true);
667 if (!shent)
668 elog(ERROR, "could not find just referenced shared stats entry");
669
670 /*
671 * This entry may have been reinitialized while trying to release
672 * it, so double-check that it has not been reused while holding a
673 * lock on its shared entry.
674 */
675 if (pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
676 entry_ref->generation)
677 {
678 /* Same "generation", so we're OK with the removal */
680 Assert(entry_ref->shared_entry == shent);
682 }
683 else
684 {
685 /*
686 * Shared stats entry has been reinitialized, so do not drop
687 * its shared entry, only release its lock.
688 */
690 }
691 }
692 }
693
695 elog(ERROR, "entry ref vanished before deletion");
696
697 if (entry_ref)
698 pfree(entry_ref);
699}

References Assert, PgStatShared_HashEntry::dropped, dshash_find(), dshash_release_lock(), elog, ERROR, fb(), PgStatShared_HashEntry::generation, PgStat_EntryRef::generation, PgStatShared_HashEntry::key, PgStatShared_Common::magic, PgStat_EntryRef::pending, pfree(), pg_atomic_fetch_sub_u32(), pg_atomic_read_u32(), pgstat_delete_pending_entry(), pgstat_free_entry(), pgStatEntryRefHash, pgStatLocal, PgStatShared_HashEntry::refcount, PgStat_EntryRef::shared_entry, PgStat_LocalState::shared_hash, and PgStat_EntryRef::shared_stats.

Referenced by pgstat_drop_entry(), pgstat_drop_matching_entries(), pgstat_gc_entry_refs(), pgstat_get_entry_ref(), and pgstat_release_matching_entry_refs().

◆ pgstat_release_matching_entry_refs()

static void pgstat_release_matching_entry_refs ( bool  discard_pending,
ReleaseMatchCB  match,
Datum  match_data 
)
static

Definition at line 827 of file pgstat_shmem.c.

829{
832
834 return;
835
837
839 != NULL)
840 {
841 Assert(ent->entry_ref != NULL);
842
843 if (match && !match(ent, match_data))
844 continue;
845
847 }
848}

References Assert, fb(), i, pgstat_release_entry_ref(), and pgStatEntryRefHash.

Referenced by pgstat_release_all_entry_refs(), and pgstat_release_db_entry_refs().

◆ pgstat_request_entry_refs_gc()

◆ pgstat_reset_entries_of_kind()

void pgstat_reset_entries_of_kind ( PgStat_Kind  kind,
TimestampTz  ts 
)

Definition at line 1197 of file pgstat_shmem.c.

References Int32GetDatum(), match_kind(), and pgstat_reset_matching_entries().

Referenced by pgstat_reset_of_kind().

◆ pgstat_reset_entry()

void pgstat_reset_entry ( PgStat_Kind  kind,
Oid  dboid,
uint64  objid,
TimestampTz  ts 
)

Definition at line 1141 of file pgstat_shmem.c.

1142{
1143 PgStat_EntryRef *entry_ref;
1144
1145 Assert(!pgstat_get_kind_info(kind)->fixed_amount);
1146
1147 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1148 if (!entry_ref || entry_ref->shared_entry->dropped)
1149 return;
1150
1151 (void) pgstat_lock_entry(entry_ref, false);
1152 shared_stat_reset_contents(kind, entry_ref->shared_stats, ts);
1153 pgstat_unlock_entry(entry_ref);
1154}

References Assert, PgStatShared_HashEntry::dropped, fb(), pgstat_get_entry_ref(), pgstat_get_kind_info(), pgstat_lock_entry(), pgstat_unlock_entry(), PgStat_EntryRef::shared_entry, shared_stat_reset_contents(), and PgStat_EntryRef::shared_stats.

Referenced by pgstat_create_subscription(), and pgstat_reset().

◆ pgstat_reset_matching_entries()

void pgstat_reset_matching_entries ( bool(*)(PgStatShared_HashEntry *, Datum do_reset,
Datum  match_data,
TimestampTz  ts 
)

◆ pgstat_setup_memcxt()

◆ pgstat_setup_shared_refs()

◆ pgstat_unlock_entry()

◆ shared_stat_reset_contents()

static void shared_stat_reset_contents ( PgStat_Kind  kind,
PgStatShared_Common header,
TimestampTz  ts 
)
static

Definition at line 1125 of file pgstat_shmem.c.

1127{
1129
1130 memset(pgstat_get_entry_data(kind, header), 0,
1131 pgstat_get_entry_len(kind));
1132
1133 if (kind_info->reset_timestamp_cb)
1134 kind_info->reset_timestamp_cb(header, ts);
1135}

References fb(), pgstat_get_entry_data(), pgstat_get_entry_len(), and pgstat_get_kind_info().

Referenced by pgstat_reset_entry(), and pgstat_reset_matching_entries().

◆ StatsShmemInit()

static void StatsShmemInit ( void arg)
static

Definition at line 175 of file pgstat_shmem.c.

176{
177 dsa_area *dsa;
178 dshash_table *dsh;
180 char *p = (char *) ctl;
181
182 /* the allocation of pgStatLocal.shmem itself */
183 p += MAXALIGN(sizeof(PgStat_ShmemControl));
184
185 /*
186 * Create a small dsa allocation in plain shared memory. This is required
187 * because postmaster cannot use dsm segments. It also provides a small
188 * efficiency win.
189 */
190 ctl->raw_dsa_area = p;
192 dsa = dsa_create_in_place(ctl->raw_dsa_area,
195 dsa_pin(dsa);
196
197 /*
198 * To ensure dshash is created in "plain" shared memory, temporarily limit
199 * size of dsa to the initial size of the dsa.
200 */
202
203 /*
204 * With the limit in place, create the dshash table. XXX: It'd be nice if
205 * there were dshash_create_in_place().
206 */
207 dsh = dshash_create(dsa, &dsh_params, NULL);
208 ctl->hash_handle = dshash_get_hash_table_handle(dsh);
209
210 /* lift limit set above */
211 dsa_set_size_limit(dsa, -1);
212
213 /*
214 * Postmaster will never access these again, thus free the local
215 * dsa/dshash references.
216 */
217 dshash_detach(dsh);
218 dsa_detach(dsa);
219
220 pg_atomic_init_u64(&ctl->gc_request_count, 1);
221
222 /* Do the per-kind initialization */
223 for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
224 {
226 char *ptr;
227
228 if (!kind_info)
229 continue;
230
231 /* initialize entry count tracking */
232 if (kind_info->track_entry_count)
233 pg_atomic_init_u64(&ctl->entry_counts[kind - 1], 0);
234
235 /* initialize fixed-numbered stats */
236 if (kind_info->fixed_amount)
237 {
238 if (pgstat_is_kind_builtin(kind))
239 ptr = ((char *) ctl) + kind_info->shared_ctl_off;
240 else
241 {
242 int idx = kind - PGSTAT_KIND_CUSTOM_MIN;
243
244 Assert(kind_info->shared_size != 0);
245 ctl->custom_data[idx] = p;
246 p += MAXALIGN(kind_info->shared_size);
247 ptr = ctl->custom_data[idx];
248 }
249
250 kind_info->init_shmem_cb(ptr);
251 }
252 }
253}

References Assert, ctl, dsa_create_in_place, dsa_detach(), dsa_pin(), dsa_set_size_limit(), dsh_params, dshash_create(), dshash_detach(), dshash_get_hash_table_handle(), fb(), idx(), MAXALIGN, pg_atomic_init_u64(), pgstat_dsa_init_size(), pgstat_get_kind_info(), pgstat_is_kind_builtin(), PgStat_Kind, PGSTAT_KIND_CUSTOM_MIN, PGSTAT_KIND_MAX, PGSTAT_KIND_MIN, pgStatLocal, and PgStat_LocalState::shmem.

◆ StatsShmemRequest()

static void StatsShmemRequest ( void arg)
static

Definition at line 163 of file pgstat_shmem.c.

164{
165 ShmemRequestStruct(.name = "Shared Memory Stats",
166 .size = StatsShmemSize(),
167 .ptr = (void **) &pgStatLocal.shmem,
168 );
169}

References name, pgStatLocal, PgStat_LocalState::shmem, ShmemRequestStruct, and StatsShmemSize().

◆ StatsShmemSize()

static Size StatsShmemSize ( void  )
static

Definition at line 135 of file pgstat_shmem.c.

136{
137 Size sz;
138
141
142 /* Add shared memory for all the custom fixed-numbered statistics */
143 for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
144 {
146
147 if (!kind_info)
148 continue;
149 if (!kind_info->fixed_amount)
150 continue;
151
152 Assert(kind_info->shared_size != 0);
153 sz = add_size(sz, MAXALIGN(kind_info->shared_size));
154 }
155
156 return sz;
157}

References add_size(), Assert, fb(), MAXALIGN, pgstat_dsa_init_size(), pgstat_get_kind_info(), PgStat_Kind, PGSTAT_KIND_CUSTOM_MAX, and PGSTAT_KIND_CUSTOM_MIN.

Referenced by StatsShmemRequest().

Variable Documentation

◆ dsh_params

◆ pgStatEntryRefHash

◆ pgStatEntryRefHashContext

MemoryContext pgStatEntryRefHashContext = NULL
static

Definition at line 99 of file pgstat_shmem.c.

Referenced by pgstat_setup_memcxt(), and pgstat_setup_shared_refs().

◆ pgStatSharedRefAge

int pgStatSharedRefAge = 0
static

◆ pgStatSharedRefContext

MemoryContext pgStatSharedRefContext = NULL
static

Definition at line 98 of file pgstat_shmem.c.

Referenced by pgstat_get_entry_ref_cached(), and pgstat_setup_memcxt().

◆ StatsShmemCallbacks

const ShmemCallbacks StatsShmemCallbacks
Initial value:
= {
.request_fn = StatsShmemRequest,
.init_fn = StatsShmemInit,
}

Definition at line 64 of file pgstat_shmem.c.

64 {
65 .request_fn = StatsShmemRequest,
66 .init_fn = StatsShmemInit,
67};