PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgstat_shmem.c
Go to the documentation of this file.
1/* -------------------------------------------------------------------------
2 *
3 * pgstat_shmem.c
4 * Storage of stats entries in shared memory
5 *
6 * Copyright (c) 2001-2026, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/backend/utils/activity/pgstat_shmem.c
10 * -------------------------------------------------------------------------
11 */
12
13#include "postgres.h"
14
15#include "pgstat.h"
16#include "storage/shmem.h"
17#include "storage/subsystems.h"
18#include "utils/memutils.h"
20
21
22#define PGSTAT_ENTRY_REF_HASH_SIZE 128
23
24/* hash table entry for finding the PgStat_EntryRef for a key */
26{
27 PgStat_HashKey key; /* hash key */
28 char status; /* for simplehash use */
31
32
33/* for references to shared statistics entries */
34#define SH_PREFIX pgstat_entry_ref_hash
35#define SH_ELEMENT_TYPE PgStat_EntryRefHashEntry
36#define SH_KEY_TYPE PgStat_HashKey
37#define SH_KEY key
38#define SH_HASH_KEY(tb, key) \
39 pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
40#define SH_EQUAL(tb, a, b) \
41 pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
42#define SH_SCOPE static inline
43#define SH_DEFINE
44#define SH_DECLARE
45#include "lib/simplehash.h"
46
47
48static void pgstat_drop_database_and_contents(Oid dboid);
49
51
53static bool pgstat_need_entry_refs_gc(void);
54static void pgstat_gc_entry_refs(void);
58
59static void pgstat_setup_memcxt(void);
60
61static void StatsShmemRequest(void *arg);
62static void StatsShmemInit(void *arg);
63
68
69/* parameter for the shared hash */
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
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
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
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
435 cache_entry->entry_ref = entry_ref =
437 sizeof(PgStat_EntryRef));
438 entry_ref->shared_stats = NULL;
439 entry_ref->shared_entry = NULL;
440 entry_ref->pending = NULL;
441
442 found = false;
443 }
444 else if (cache_entry->entry_ref->shared_stats == NULL)
445 {
446 Assert(cache_entry->entry_ref->pending == NULL);
447 found = false;
448 }
449 else
450 {
452
453 entry_ref = cache_entry->entry_ref;
454 Assert(entry_ref->shared_entry != NULL);
455 Assert(entry_ref->shared_stats != NULL);
456
457 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
458 /* should have at least our reference */
459 Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) > 0);
460 }
461
462 *entry_ref_p = cache_entry->entry_ref;
463 return found;
464}
465
466/*
467 * Get a shared stats reference. If create is true, the shared stats object is
468 * created if it does not exist.
469 *
470 * When create is true, and created_entry is non-NULL, it'll be set to true
471 * if the entry is newly created, false otherwise.
472 */
474pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create,
475 bool *created_entry)
476{
477 PgStat_HashKey key = {0};
480 PgStat_EntryRef *entry_ref;
481
482 key.kind = kind;
483 key.dboid = dboid;
484 key.objid = objid;
485
486 /*
487 * passing in created_entry only makes sense if we possibly could create
488 * entry.
489 */
490 Assert(create || created_entry == NULL);
494
497
498 if (created_entry != NULL)
499 *created_entry = false;
500
501 /*
502 * Check if other backends dropped stats that could not be deleted because
503 * somebody held references to it. If so, check this backend's references.
504 * This is not expected to happen often. The location of the check is a
505 * bit random, but this is a relatively frequently called path, so better
506 * than most.
507 */
510
511 /*
512 * First check the lookup cache hashtable in local memory. If we find a
513 * match here we can avoid taking locks / causing contention.
514 */
515 if (pgstat_get_entry_ref_cached(key, &entry_ref))
516 return entry_ref;
517
518 Assert(entry_ref != NULL);
519
520 /*
521 * Do a lookup in the hash table first - it's quite likely that the entry
522 * already exists, and that way we only need a shared lock.
523 */
525
526 if (create && !shhashent)
527 {
528 bool shfound;
529
530 /*
531 * It's possible that somebody created the entry since the above
532 * lookup. If so, fall through to the same path as if we'd have if it
533 * already had been created before the dshash_find() calls.
534 */
536 if (!shfound)
537 {
539 if (shheader == NULL)
540 {
541 /*
542 * Failed the allocation of a new entry, so clean up the
543 * shared hashtable before giving up.
544 */
546
549 errmsg("out of memory"),
550 errdetail("Failed while allocating entry %u/%u/%" PRIu64 ".",
551 key.kind, key.dboid, key.objid)));
552 }
554
555 if (created_entry != NULL)
556 *created_entry = true;
557
558 return entry_ref;
559 }
560 }
561
562 if (!shhashent)
563 {
564 /*
565 * If we're not creating, delete the reference again. In all
566 * likelihood it's just a stats lookup - no point wasting memory for a
567 * shared ref to nothing...
568 */
569 pgstat_release_entry_ref(key, entry_ref, false);
570
571 return NULL;
572 }
573 else
574 {
575 /*
576 * Can get here either because dshash_find() found a match, or if
577 * dshash_find_or_insert() found a concurrently inserted entry.
578 */
579
580 if (shhashent->dropped && create)
581 {
582 /*
583 * There are legitimate cases where the old stats entry might not
584 * yet have been dropped by the time it's reused. The most obvious
585 * case are replication slot stats, where a new slot can be
586 * created with the same index just after dropping. But oid
587 * wraparound can lead to other cases as well. We just reset the
588 * stats to their plain state, while incrementing its "generation"
589 * in the shared entry for any remaining local references.
590 */
593
594 if (created_entry != NULL)
595 *created_entry = true;
596
597 return entry_ref;
598 }
599 else if (shhashent->dropped)
600 {
602 pgstat_release_entry_ref(key, entry_ref, false);
603
604 return NULL;
605 }
606 else
607 {
610
611 return entry_ref;
612 }
613 }
614}
615
616static void
618 bool discard_pending)
619{
620 if (entry_ref && entry_ref->pending)
621 {
622 if (discard_pending)
624 else
625 elog(ERROR, "releasing ref with pending data");
626 }
627
628 if (entry_ref && entry_ref->shared_stats)
629 {
630 Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
631 Assert(entry_ref->pending == NULL);
632
633 /*
634 * This can't race with another backend looking up the stats entry and
635 * increasing the refcount because it is not "legal" to create
636 * additional references to dropped entries.
637 */
638 if (pg_atomic_fetch_sub_u32(&entry_ref->shared_entry->refcount, 1) == 1)
639 {
641
642 /*
643 * We're the last referrer to this entry, try to drop the shared
644 * entry.
645 */
646
647 /* only dropped entries can reach a 0 refcount */
648 Assert(entry_ref->shared_entry->dropped);
649
651 &entry_ref->shared_entry->key,
652 true);
653 if (!shent)
654 elog(ERROR, "could not find just referenced shared stats entry");
655
656 /*
657 * This entry may have been reinitialized while trying to release
658 * it, so double-check that it has not been reused while holding a
659 * lock on its shared entry.
660 */
661 if (pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
662 entry_ref->generation)
663 {
664 /* Same "generation", so we're OK with the removal */
666 Assert(entry_ref->shared_entry == shent);
668 }
669 else
670 {
671 /*
672 * Shared stats entry has been reinitialized, so do not drop
673 * its shared entry, only release its lock.
674 */
676 }
677 }
678 }
679
681 elog(ERROR, "entry ref vanished before deletion");
682
683 if (entry_ref)
684 pfree(entry_ref);
685}
686
687/*
688 * Acquire exclusive lock on the entry.
689 *
690 * If nowait is true, it's just a conditional acquire, and the result
691 * *must* be checked to verify success.
692 * If nowait is false, waits as necessary, always returning true.
693 */
694bool
695pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
696{
697 LWLock *lock = &entry_ref->shared_stats->lock;
698
699 if (nowait)
701
703 return true;
704}
705
706/*
707 * Acquire shared lock on the entry.
708 *
709 * Separate from pgstat_lock_entry() as most callers will need to lock
710 * exclusively. The wait semantics are identical.
711 */
712bool
714{
715 LWLock *lock = &entry_ref->shared_stats->lock;
716
717 if (nowait)
719
721 return true;
722}
723
724void
726{
727 LWLockRelease(&entry_ref->shared_stats->lock);
728}
729
730/*
731 * Helper function to fetch and lock shared stats.
732 */
735 bool nowait)
736{
737 PgStat_EntryRef *entry_ref;
738
739 /* find shared table stats entry corresponding to the local entry */
740 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, true, NULL);
741
742 /* lock the shared entry to protect the content, skip if failed */
743 if (!pgstat_lock_entry(entry_ref, nowait))
744 return NULL;
745
746 return entry_ref;
747}
748
749void
754
755static bool
757{
759
761 return false;
762
763 /* should have been initialized when creating pgStatEntryRefHash */
765
767
768 return pgStatSharedRefAge != curage;
769}
770
771static void
773{
777
779 Assert(curage != 0);
780
781 /*
782 * Some entries have been dropped or reinitialized. Invalidate cache
783 * pointer to them.
784 */
787 {
788 PgStat_EntryRef *entry_ref = ent->entry_ref;
789
790 Assert(!entry_ref->shared_stats ||
791 entry_ref->shared_stats->magic == 0xdeadbeef);
792
793 /*
794 * "generation" checks for the case of entries being reinitialized,
795 * and "dropped" for the case where these are.. dropped.
796 */
797 if (!entry_ref->shared_entry->dropped &&
799 entry_ref->generation)
800 continue;
801
802 /* cannot gc shared ref that has pending data */
803 if (entry_ref->pending != NULL)
804 continue;
805
806 pgstat_release_entry_ref(ent->key, entry_ref, false);
807 }
808
810}
811
812static void
815{
818
820 return;
821
823
825 != NULL)
826 {
827 Assert(ent->entry_ref != NULL);
828
829 if (match && !match(ent, match_data))
830 continue;
831
833 }
834}
835
836/*
837 * Release all local references to shared stats entries.
838 *
839 * When a process exits it cannot do so while still holding references onto
840 * stats entries, otherwise the shared stats entries could never be freed.
841 */
842static void
853
854static bool
856{
858
859 return ent->key.dboid == dboid;
860}
861
862static void
864{
865 pgstat_release_matching_entry_refs( /* discard pending = */ true,
866 match_db,
867 ObjectIdGetDatum(dboid));
868}
869
870
871/* ------------------------------------------------------------
872 * Dropping and resetting of stats entries
873 * ------------------------------------------------------------
874 */
875
876static void
878{
880 PgStat_Kind kind = shent->key.kind;
881
882 /*
883 * Fetch dsa pointer before deleting entry - that way we can free the
884 * memory after releasing the lock.
885 */
886 pdsa = shent->body;
887
888 if (!hstat)
890 else
892
894
895 /* Decrement entry count, if required. */
896 if (pgstat_get_kind_info(kind)->track_entry_count)
898}
899
900/*
901 * Helper for both pgstat_drop_database_and_contents() and
902 * pgstat_drop_entry(). If hstat is non-null delete the shared entry using
903 * dshash_delete_current(), otherwise use dshash_delete_entry(). In either
904 * case the entry needs to be already locked.
905 */
906static bool
909{
911
912 /* should already have released local reference */
915
916 /*
917 * Signal that the entry is dropped - this will eventually cause other
918 * backends to release their references.
919 */
920 if (shent->dropped)
921 elog(ERROR,
922 "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%" PRIu64 " refcount=%u generation=%u",
923 pgstat_get_kind_info(shent->key.kind)->name,
924 shent->key.dboid,
925 shent->key.objid,
926 pg_atomic_read_u32(&shent->refcount),
927 pg_atomic_read_u32(&shent->generation));
928 shent->dropped = true;
929
930 /* release refcount marking entry as not dropped */
931 if (pg_atomic_sub_fetch_u32(&shent->refcount, 1) == 0)
932 {
934 return true;
935 }
936 else
937 {
938 if (!hstat)
940 return false;
941 }
942}
943
944/*
945 * Drop stats for the database and all the objects inside that database.
946 */
947static void
949{
953
954 Assert(OidIsValid(dboid));
955
957
958 /*
959 * This backend might very well be the only backend holding a reference to
960 * about-to-be-dropped entries. Ensure that we're not preventing it from
961 * being cleaned up till later.
962 *
963 * Doing this separately from the dshash iteration below avoids having to
964 * do so while holding a partition lock on the shared hashtable.
965 */
967
968 /* some of the dshash entries are to be removed, take exclusive lock. */
970 while ((p = dshash_seq_next(&hstat)) != NULL)
971 {
972 if (p->dropped)
973 continue;
974
975 if (p->key.dboid != dboid)
976 continue;
977
979 {
980 /*
981 * Even statistics for a dropped database might currently be
982 * accessed (consider e.g. database stats for pg_stat_database).
983 */
985 }
986 }
988
989 /*
990 * If some of the stats data could not be freed, signal the reference
991 * holders to run garbage collection of their cached pgStatLocal.shmem.
992 */
993 if (not_freed_count > 0)
995}
996
997/*
998 * Drop a single stats entry.
999 *
1000 * This routine returns false if the stats entry of the dropped object could
1001 * not be freed, true otherwise.
1002 *
1003 * The callers of this function should call pgstat_request_entry_refs_gc()
1004 * if the stats entry could not be freed, to ensure that this entry's memory
1005 * can be reclaimed later by a different backend calling
1006 * pgstat_gc_entry_refs().
1007 */
1008bool
1010{
1011 PgStat_HashKey key = {0};
1013 bool freed = true;
1014
1015 key.kind = kind;
1016 key.dboid = dboid;
1017 key.objid = objid;
1018
1019 /* delete local reference */
1021 {
1024
1025 if (lohashent)
1027 true);
1028 }
1029
1030 /* mark entry in shared hashtable as deleted, drop if possible */
1032 if (shent)
1033 {
1035
1036 /*
1037 * Database stats contain other stats. Drop those as well when
1038 * dropping the database. XXX: Perhaps this should be done in a
1039 * slightly more principled way? But not obvious what that'd look
1040 * like, and so far this is the only case...
1041 */
1042 if (key.kind == PGSTAT_KIND_DATABASE)
1044 }
1045
1046 return freed;
1047}
1048
1049/*
1050 * Scan through the shared hashtable of stats, dropping statistics if
1051 * approved by the optional do_drop() function.
1052 */
1053void
1056{
1060
1061 /* entries are removed, take an exclusive lock */
1063 while ((ps = dshash_seq_next(&hstat)) != NULL)
1064 {
1065 if (ps->dropped)
1066 continue;
1067
1068 if (do_drop != NULL && !do_drop(ps, match_data))
1069 continue;
1070
1071 /* delete local reference */
1073 {
1076
1077 if (lohashent)
1079 true);
1080 }
1081
1084 }
1086
1087 if (not_freed_count > 0)
1089}
1090
1091/*
1092 * Scan through the shared hashtable of stats and drop all entries.
1093 */
1094void
1099
1100static void
1102 TimestampTz ts)
1103{
1105
1106 memset(pgstat_get_entry_data(kind, header), 0,
1107 pgstat_get_entry_len(kind));
1108
1109 if (kind_info->reset_timestamp_cb)
1110 kind_info->reset_timestamp_cb(header, ts);
1111}
1112
1113/*
1114 * Reset one variable-numbered stats entry.
1115 */
1116void
1118{
1119 PgStat_EntryRef *entry_ref;
1120
1121 Assert(!pgstat_get_kind_info(kind)->fixed_amount);
1122
1123 entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1124 if (!entry_ref || entry_ref->shared_entry->dropped)
1125 return;
1126
1127 (void) pgstat_lock_entry(entry_ref, false);
1128 shared_stat_reset_contents(kind, entry_ref->shared_stats, ts);
1129 pgstat_unlock_entry(entry_ref);
1130}
1131
1132/*
1133 * Scan through the shared hashtable of stats, resetting statistics if
1134 * approved by the provided do_reset() function.
1135 */
1136void
1139{
1142
1143 /* dshash entry is not modified, take shared lock */
1145 while ((p = dshash_seq_next(&hstat)) != NULL)
1146 {
1147 PgStatShared_Common *header;
1148
1149 if (p->dropped)
1150 continue;
1151
1152 if (!do_reset(p, match_data))
1153 continue;
1154
1155 header = dsa_get_address(pgStatLocal.dsa, p->body);
1156
1157 LWLockAcquire(&header->lock, LW_EXCLUSIVE);
1158
1159 shared_stat_reset_contents(p->key.kind, header, ts);
1160
1161 LWLockRelease(&header->lock);
1162 }
1164}
1165
1166static bool
1171
1172void
1177
1178static void
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:262
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition atomics.h:439
static uint32 pg_atomic_fetch_sub_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition atomics.h:381
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition atomics.h:219
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition atomics.h:366
static uint64 pg_atomic_fetch_add_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
Definition atomics.h:532
static uint64 pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_)
Definition atomics.h:578
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition atomics.h:237
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:453
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
#define likely(x)
Definition c.h:437
#define MAXALIGN(LEN)
Definition c.h:896
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
#define Assert(condition)
Definition c.h:943
uint64_t uint64
Definition c.h:625
#define unlikely(x)
Definition c.h:438
#define OidIsValid(objectId)
Definition c.h:858
size_t Size
Definition c.h:689
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:957
dsa_pointer dsa_allocate_extended(dsa_area *area, size_t size, int flags)
Definition dsa.c:686
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:1033
void dsa_pin_mapping(dsa_area *area)
Definition dsa.c:650
void dsa_detach(dsa_area *area)
Definition dsa.c:2002
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:841
size_t dsa_minimum_size(void)
Definition dsa.c:1246
void dsa_pin(dsa_area *area)
Definition dsa.c:990
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:1322
int errcode(int sqlerrcode)
Definition elog.c:874
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
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
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
void pfree(void *pointer)
Definition mcxt.c:1616
MemoryContext TopMemoryContext
Definition mcxt.c:166
#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:124
const void * data
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)
const ShmemCallbacks StatsShmemCallbacks
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)
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
bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
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
Size add_size(Size s1, Size s2)
Definition shmem.c:1048
#define ShmemRequestStruct(...)
Definition shmem.h:176
pg_atomic_uint32 refcount
pg_atomic_uint32 generation
PgStat_EntryRef * entry_ref
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]
ShmemRequestCallback request_fn
Definition shmem.h:133
const char * name