PostgreSQL Source Code  git master
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-2023, 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 "utils/memutils.h"
18 #include "utils/pgstat_internal.h"
19 
20 
21 #define PGSTAT_ENTRY_REF_HASH_SIZE 128
22 
23 /* hash table entry for finding the PgStat_EntryRef for a key */
25 {
26  PgStat_HashKey key; /* hash key */
27  char status; /* for simplehash use */
30 
31 
32 /* for references to shared statistics entries */
33 #define SH_PREFIX pgstat_entry_ref_hash
34 #define SH_ELEMENT_TYPE PgStat_EntryRefHashEntry
35 #define SH_KEY_TYPE PgStat_HashKey
36 #define SH_KEY key
37 #define SH_HASH_KEY(tb, key) \
38  pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
39 #define SH_EQUAL(tb, a, b) \
40  pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
41 #define SH_SCOPE static inline
42 #define SH_DEFINE
43 #define SH_DECLARE
44 #include "lib/simplehash.h"
45 
46 
47 static void pgstat_drop_database_and_contents(Oid dboid);
48 
50 
51 static void pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref, bool discard_pending);
52 static bool pgstat_need_entry_refs_gc(void);
53 static void pgstat_gc_entry_refs(void);
54 static void pgstat_release_all_entry_refs(bool discard_pending);
56 static void pgstat_release_matching_entry_refs(bool discard_pending, ReleaseMatchCB match, Datum match_data);
57 
58 static void pgstat_setup_memcxt(void);
59 
60 
61 /* parameter for the shared hash */
62 static const dshash_parameters dsh_params = {
63  sizeof(PgStat_HashKey),
64  sizeof(PgStatShared_HashEntry),
68 };
69 
70 
71 /*
72  * Backend local references to shared stats entries. If there are pending
73  * updates to a stats entry, the PgStat_EntryRef is added to the pgStatPending
74  * list.
75  *
76  * When a stats entry is dropped each backend needs to release its reference
77  * to it before the memory can be released. To trigger that
78  * pgStatLocal.shmem->gc_request_count is incremented - which each backend
79  * compares to their copy of pgStatSharedRefAge on a regular basis.
80  */
81 static pgstat_entry_ref_hash_hash *pgStatEntryRefHash = NULL;
82 static int pgStatSharedRefAge = 0; /* cache age of pgStatShmLookupCache */
83 
84 /*
85  * Memory contexts containing the pgStatEntryRefHash table and the
86  * pgStatSharedRef entries respectively. Kept separate to make it easier to
87  * track / attribute memory usage.
88  */
91 
92 
93 /* ------------------------------------------------------------
94  * Public functions called from postmaster follow
95  * ------------------------------------------------------------
96  */
97 
98 /*
99  * The size of the shared memory allocation for stats stored in the shared
100  * stats hash table. This allocation will be done as part of the main shared
101  * memory, rather than dynamic shared memory, allowing it to be initialized in
102  * postmaster.
103  */
104 static Size
106 {
107  Size sz;
108 
109  /*
110  * The dshash header / initial buckets array needs to fit into "plain"
111  * shared memory, but it's beneficial to not need dsm segments
112  * immediately. A size of 256kB seems works well and is not
113  * disproportional compared to other constant sized shared memory
114  * allocations. NB: To avoid DSMs further, the user can configure
115  * min_dynamic_shared_memory.
116  */
117  sz = 256 * 1024;
118  Assert(dsa_minimum_size() <= sz);
119  return MAXALIGN(sz);
120 }
121 
122 /*
123  * Compute shared memory space needed for cumulative statistics
124  */
125 Size
127 {
128  Size sz;
129 
130  sz = MAXALIGN(sizeof(PgStat_ShmemControl));
131  sz = add_size(sz, pgstat_dsa_init_size());
132 
133  return sz;
134 }
135 
136 /*
137  * Initialize cumulative statistics system during startup
138  */
139 void
141 {
142  bool found;
143  Size sz;
144 
145  sz = StatsShmemSize();
147  ShmemInitStruct("Shared Memory Stats", sz, &found);
148 
149  if (!IsUnderPostmaster)
150  {
151  dsa_area *dsa;
152  dshash_table *dsh;
154  char *p = (char *) ctl;
155 
156  Assert(!found);
157 
158  /* the allocation of pgStatLocal.shmem itself */
159  p += MAXALIGN(sizeof(PgStat_ShmemControl));
160 
161  /*
162  * Create a small dsa allocation in plain shared memory. This is
163  * required because postmaster cannot use dsm segments. It also
164  * provides a small efficiency win.
165  */
166  ctl->raw_dsa_area = p;
171  dsa_pin(dsa);
172 
173  /*
174  * To ensure dshash is created in "plain" shared memory, temporarily
175  * limit size of dsa to the initial size of the dsa.
176  */
178 
179  /*
180  * With the limit in place, create the dshash table. XXX: It'd be nice
181  * if there were dshash_create_in_place().
182  */
183  dsh = dshash_create(dsa, &dsh_params, 0);
185 
186  /* lift limit set above */
187  dsa_set_size_limit(dsa, -1);
188 
189  /*
190  * Postmaster will never access these again, thus free the local
191  * dsa/dshash references.
192  */
193  dshash_detach(dsh);
194  dsa_detach(dsa);
195 
197 
198 
199  /* initialize fixed-numbered stats */
205 
206  for (int i = 0; i < BACKEND_NUM_TYPES; i++)
207  LWLockInitialize(&ctl->io.locks[i],
209  }
210  else
211  {
212  Assert(found);
213  }
214 }
215 
216 void
218 {
219  MemoryContext oldcontext;
220 
221  Assert(pgStatLocal.dsa == NULL);
222 
223  /* stats shared memory persists for the backend lifetime */
225 
227  NULL);
229 
232 
233  MemoryContextSwitchTo(oldcontext);
234 }
235 
236 void
238 {
240 
241  /* we shouldn't leave references to shared stats */
243 
245  pgStatLocal.shared_hash = NULL;
246 
248  pgStatLocal.dsa = NULL;
249 }
250 
251 
252 /* ------------------------------------------------------------
253  * Maintenance of shared memory stats entries
254  * ------------------------------------------------------------
255  */
256 
259  PgStatShared_HashEntry *shhashent)
260 {
261  /* Create new stats entry. */
262  dsa_pointer chunk;
263  PgStatShared_Common *shheader;
264 
265  /*
266  * Initialize refcount to 1, marking it as valid / not dropped. The entry
267  * can't be freed before the initialization because it can't be found as
268  * long as we hold the dshash partition lock. Caller needs to increase
269  * further if a longer lived reference is needed.
270  */
271  pg_atomic_init_u32(&shhashent->refcount, 1);
272  shhashent->dropped = false;
273 
274  chunk = dsa_allocate0(pgStatLocal.dsa, pgstat_get_kind_info(kind)->shared_size);
275  shheader = dsa_get_address(pgStatLocal.dsa, chunk);
276  shheader->magic = 0xdeadbeef;
277 
278  /* Link the new entry from the hash entry. */
279  shhashent->body = chunk;
280 
282 
283  return shheader;
284 }
285 
286 static PgStatShared_Common *
288 {
289  PgStatShared_Common *shheader;
290 
291  shheader = dsa_get_address(pgStatLocal.dsa, shhashent->body);
292 
293  /* mark as not dropped anymore */
294  pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
295  shhashent->dropped = false;
296 
297  /* reinitialize content */
298  Assert(shheader->magic == 0xdeadbeef);
299  memset(pgstat_get_entry_data(kind, shheader), 0,
300  pgstat_get_entry_len(kind));
301 
302  return shheader;
303 }
304 
305 static void
307 {
308  if (likely(pgStatEntryRefHash != NULL))
309  return;
310 
312  pgstat_entry_ref_hash_create(pgStatEntryRefHashContext,
316 }
317 
318 /*
319  * Helper function for pgstat_get_entry_ref().
320  */
321 static void
323  PgStatShared_HashEntry *shhashent,
324  PgStatShared_Common *shheader)
325 {
326  Assert(shheader->magic == 0xdeadbeef);
327  Assert(pg_atomic_read_u32(&shhashent->refcount) > 0);
328 
329  pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
330 
332 
333  entry_ref->shared_stats = shheader;
334  entry_ref->shared_entry = shhashent;
335 }
336 
337 /*
338  * Helper function for pgstat_get_entry_ref().
339  */
340 static bool
342 {
343  bool found;
344  PgStat_EntryRefHashEntry *cache_entry;
345 
346  /*
347  * We immediately insert a cache entry, because it avoids 1) multiple
348  * hashtable lookups in case of a cache miss 2) having to deal with
349  * out-of-memory errors after incrementing PgStatShared_Common->refcount.
350  */
351 
352  cache_entry = pgstat_entry_ref_hash_insert(pgStatEntryRefHash, key, &found);
353 
354  if (!found || !cache_entry->entry_ref)
355  {
356  PgStat_EntryRef *entry_ref;
357 
358  cache_entry->entry_ref = entry_ref =
360  sizeof(PgStat_EntryRef));
361  entry_ref->shared_stats = NULL;
362  entry_ref->shared_entry = NULL;
363  entry_ref->pending = NULL;
364 
365  found = false;
366  }
367  else if (cache_entry->entry_ref->shared_stats == NULL)
368  {
369  Assert(cache_entry->entry_ref->pending == NULL);
370  found = false;
371  }
372  else
373  {
375 
376  entry_ref = cache_entry->entry_ref;
377  Assert(entry_ref->shared_entry != NULL);
378  Assert(entry_ref->shared_stats != NULL);
379 
380  Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
381  /* should have at least our reference */
382  Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) > 0);
383  }
384 
385  *entry_ref_p = cache_entry->entry_ref;
386  return found;
387 }
388 
389 /*
390  * Get a shared stats reference. If create is true, the shared stats object is
391  * created if it does not exist.
392  *
393  * When create is true, and created_entry is non-NULL, it'll be set to true
394  * if the entry is newly created, false otherwise.
395  */
397 pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, Oid objoid, bool create,
398  bool *created_entry)
399 {
400  PgStat_HashKey key = {.kind = kind,.dboid = dboid,.objoid = objoid};
401  PgStatShared_HashEntry *shhashent;
402  PgStatShared_Common *shheader = NULL;
403  PgStat_EntryRef *entry_ref;
404 
405  /*
406  * passing in created_entry only makes sense if we possibly could create
407  * entry.
408  */
409  Assert(create || created_entry == NULL);
411  Assert(pgStatLocal.shared_hash != NULL);
413 
416 
417  if (created_entry != NULL)
418  *created_entry = false;
419 
420  /*
421  * Check if other backends dropped stats that could not be deleted because
422  * somebody held references to it. If so, check this backend's references.
423  * This is not expected to happen often. The location of the check is a
424  * bit random, but this is a relatively frequently called path, so better
425  * than most.
426  */
429 
430  /*
431  * First check the lookup cache hashtable in local memory. If we find a
432  * match here we can avoid taking locks / causing contention.
433  */
434  if (pgstat_get_entry_ref_cached(key, &entry_ref))
435  return entry_ref;
436 
437  Assert(entry_ref != NULL);
438 
439  /*
440  * Do a lookup in the hash table first - it's quite likely that the entry
441  * already exists, and that way we only need a shared lock.
442  */
443  shhashent = dshash_find(pgStatLocal.shared_hash, &key, false);
444 
445  if (create && !shhashent)
446  {
447  bool shfound;
448 
449  /*
450  * It's possible that somebody created the entry since the above
451  * lookup. If so, fall through to the same path as if we'd have if it
452  * already had been created before the dshash_find() calls.
453  */
454  shhashent = dshash_find_or_insert(pgStatLocal.shared_hash, &key, &shfound);
455  if (!shfound)
456  {
457  shheader = pgstat_init_entry(kind, shhashent);
458  pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
459 
460  if (created_entry != NULL)
461  *created_entry = true;
462 
463  return entry_ref;
464  }
465  }
466 
467  if (!shhashent)
468  {
469  /*
470  * If we're not creating, delete the reference again. In all
471  * likelihood it's just a stats lookup - no point wasting memory for a
472  * shared ref to nothing...
473  */
474  pgstat_release_entry_ref(key, entry_ref, false);
475 
476  return NULL;
477  }
478  else
479  {
480  /*
481  * Can get here either because dshash_find() found a match, or if
482  * dshash_find_or_insert() found a concurrently inserted entry.
483  */
484 
485  if (shhashent->dropped && create)
486  {
487  /*
488  * There are legitimate cases where the old stats entry might not
489  * yet have been dropped by the time it's reused. The most obvious
490  * case are replication slot stats, where a new slot can be
491  * created with the same index just after dropping. But oid
492  * wraparound can lead to other cases as well. We just reset the
493  * stats to their plain state.
494  */
495  shheader = pgstat_reinit_entry(kind, shhashent);
496  pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
497 
498  if (created_entry != NULL)
499  *created_entry = true;
500 
501  return entry_ref;
502  }
503  else if (shhashent->dropped)
504  {
506  pgstat_release_entry_ref(key, entry_ref, false);
507 
508  return NULL;
509  }
510  else
511  {
512  shheader = dsa_get_address(pgStatLocal.dsa, shhashent->body);
513  pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
514 
515  return entry_ref;
516  }
517  }
518 }
519 
520 static void
522  bool discard_pending)
523 {
524  if (entry_ref && entry_ref->pending)
525  {
526  if (discard_pending)
527  pgstat_delete_pending_entry(entry_ref);
528  else
529  elog(ERROR, "releasing ref with pending data");
530  }
531 
532  if (entry_ref && entry_ref->shared_stats)
533  {
534  Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
535  Assert(entry_ref->pending == NULL);
536 
537  /*
538  * This can't race with another backend looking up the stats entry and
539  * increasing the refcount because it is not "legal" to create
540  * additional references to dropped entries.
541  */
542  if (pg_atomic_fetch_sub_u32(&entry_ref->shared_entry->refcount, 1) == 1)
543  {
544  PgStatShared_HashEntry *shent;
545 
546  /*
547  * We're the last referrer to this entry, try to drop the shared
548  * entry.
549  */
550 
551  /* only dropped entries can reach a 0 refcount */
552  Assert(entry_ref->shared_entry->dropped);
553 
555  &entry_ref->shared_entry->key,
556  true);
557  if (!shent)
558  elog(ERROR, "could not find just referenced shared stats entry");
559 
560  Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) == 0);
561  Assert(entry_ref->shared_entry == shent);
562 
563  pgstat_free_entry(shent, NULL);
564  }
565  }
566 
567  if (!pgstat_entry_ref_hash_delete(pgStatEntryRefHash, key))
568  elog(ERROR, "entry ref vanished before deletion");
569 
570  if (entry_ref)
571  pfree(entry_ref);
572 }
573 
574 bool
575 pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
576 {
577  LWLock *lock = &entry_ref->shared_stats->lock;
578 
579  if (nowait)
581 
583  return true;
584 }
585 
586 /*
587  * Separate from pgstat_lock_entry() as most callers will need to lock
588  * exclusively.
589  */
590 bool
592 {
593  LWLock *lock = &entry_ref->shared_stats->lock;
594 
595  if (nowait)
596  return LWLockConditionalAcquire(lock, LW_SHARED);
597 
598  LWLockAcquire(lock, LW_SHARED);
599  return true;
600 }
601 
602 void
604 {
605  LWLockRelease(&entry_ref->shared_stats->lock);
606 }
607 
608 /*
609  * Helper function to fetch and lock shared stats.
610  */
613  bool nowait)
614 {
615  PgStat_EntryRef *entry_ref;
616 
617  /* find shared table stats entry corresponding to the local entry */
618  entry_ref = pgstat_get_entry_ref(kind, dboid, objoid, true, NULL);
619 
620  /* lock the shared entry to protect the content, skip if failed */
621  if (!pgstat_lock_entry(entry_ref, nowait))
622  return NULL;
623 
624  return entry_ref;
625 }
626 
627 void
629 {
631 }
632 
633 static bool
635 {
636  uint64 curage;
637 
638  if (!pgStatEntryRefHash)
639  return false;
640 
641  /* should have been initialized when creating pgStatEntryRefHash */
643 
645 
646  return pgStatSharedRefAge != curage;
647 }
648 
649 static void
651 {
652  pgstat_entry_ref_hash_iterator i;
654  uint64 curage;
655 
657  Assert(curage != 0);
658 
659  /*
660  * Some entries have been dropped. Invalidate cache pointer to them.
661  */
662  pgstat_entry_ref_hash_start_iterate(pgStatEntryRefHash, &i);
663  while ((ent = pgstat_entry_ref_hash_iterate(pgStatEntryRefHash, &i)) != NULL)
664  {
665  PgStat_EntryRef *entry_ref = ent->entry_ref;
666 
667  Assert(!entry_ref->shared_stats ||
668  entry_ref->shared_stats->magic == 0xdeadbeef);
669 
670  if (!entry_ref->shared_entry->dropped)
671  continue;
672 
673  /* cannot gc shared ref that has pending data */
674  if (entry_ref->pending != NULL)
675  continue;
676 
677  pgstat_release_entry_ref(ent->key, entry_ref, false);
678  }
679 
680  pgStatSharedRefAge = curage;
681 }
682 
683 static void
685  Datum match_data)
686 {
687  pgstat_entry_ref_hash_iterator i;
689 
690  if (pgStatEntryRefHash == NULL)
691  return;
692 
693  pgstat_entry_ref_hash_start_iterate(pgStatEntryRefHash, &i);
694 
695  while ((ent = pgstat_entry_ref_hash_iterate(pgStatEntryRefHash, &i))
696  != NULL)
697  {
698  Assert(ent->entry_ref != NULL);
699 
700  if (match && !match(ent, match_data))
701  continue;
702 
703  pgstat_release_entry_ref(ent->key, ent->entry_ref, discard_pending);
704  }
705 }
706 
707 /*
708  * Release all local references to shared stats entries.
709  *
710  * When a process exits it cannot do so while still holding references onto
711  * stats entries, otherwise the shared stats entries could never be freed.
712  */
713 static void
714 pgstat_release_all_entry_refs(bool discard_pending)
715 {
716  if (pgStatEntryRefHash == NULL)
717  return;
718 
719  pgstat_release_matching_entry_refs(discard_pending, NULL, 0);
720  Assert(pgStatEntryRefHash->members == 0);
721  pgstat_entry_ref_hash_destroy(pgStatEntryRefHash);
722  pgStatEntryRefHash = NULL;
723 }
724 
725 static bool
727 {
728  Oid dboid = DatumGetObjectId(match_data);
729 
730  return ent->key.dboid == dboid;
731 }
732 
733 static void
735 {
736  pgstat_release_matching_entry_refs( /* discard pending = */ true,
737  match_db,
738  ObjectIdGetDatum(dboid));
739 }
740 
741 
742 /* ------------------------------------------------------------
743  * Dropping and resetting of stats entries
744  * ------------------------------------------------------------
745  */
746 
747 static void
749 {
750  dsa_pointer pdsa;
751 
752  /*
753  * Fetch dsa pointer before deleting entry - that way we can free the
754  * memory after releasing the lock.
755  */
756  pdsa = shent->body;
757 
758  if (!hstat)
760  else
761  dshash_delete_current(hstat);
762 
763  dsa_free(pgStatLocal.dsa, pdsa);
764 }
765 
766 /*
767  * Helper for both pgstat_drop_database_and_contents() and
768  * pgstat_drop_entry(). If hstat is non-null delete the shared entry using
769  * dshash_delete_current(), otherwise use dshash_delete_entry(). In either
770  * case the entry needs to be already locked.
771  */
772 static bool
774  dshash_seq_status *hstat)
775 {
776  Assert(shent->body != InvalidDsaPointer);
777 
778  /* should already have released local reference */
779  if (pgStatEntryRefHash)
780  Assert(!pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, shent->key));
781 
782  /*
783  * Signal that the entry is dropped - this will eventually cause other
784  * backends to release their references.
785  */
786  if (shent->dropped)
787  elog(ERROR, "can only drop stats once");
788  shent->dropped = true;
789 
790  /* release refcount marking entry as not dropped */
791  if (pg_atomic_sub_fetch_u32(&shent->refcount, 1) == 0)
792  {
793  pgstat_free_entry(shent, hstat);
794  return true;
795  }
796  else
797  {
798  if (!hstat)
800  return false;
801  }
802 }
803 
804 /*
805  * Drop stats for the database and all the objects inside that database.
806  */
807 static void
809 {
810  dshash_seq_status hstat;
812  uint64 not_freed_count = 0;
813 
814  Assert(OidIsValid(dboid));
815 
816  Assert(pgStatLocal.shared_hash != NULL);
817 
818  /*
819  * This backend might very well be the only backend holding a reference to
820  * about-to-be-dropped entries. Ensure that we're not preventing it from
821  * being cleaned up till later.
822  *
823  * Doing this separately from the dshash iteration below avoids having to
824  * do so while holding a partition lock on the shared hashtable.
825  */
827 
828  /* some of the dshash entries are to be removed, take exclusive lock. */
829  dshash_seq_init(&hstat, pgStatLocal.shared_hash, true);
830  while ((p = dshash_seq_next(&hstat)) != NULL)
831  {
832  if (p->dropped)
833  continue;
834 
835  if (p->key.dboid != dboid)
836  continue;
837 
838  if (!pgstat_drop_entry_internal(p, &hstat))
839  {
840  /*
841  * Even statistics for a dropped database might currently be
842  * accessed (consider e.g. database stats for pg_stat_database).
843  */
844  not_freed_count++;
845  }
846  }
847  dshash_seq_term(&hstat);
848 
849  /*
850  * If some of the stats data could not be freed, signal the reference
851  * holders to run garbage collection of their cached pgStatShmLookupCache.
852  */
853  if (not_freed_count > 0)
855 }
856 
857 bool
858 pgstat_drop_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
859 {
860  PgStat_HashKey key = {.kind = kind,.dboid = dboid,.objoid = objoid};
861  PgStatShared_HashEntry *shent;
862  bool freed = true;
863 
864  /* delete local reference */
865  if (pgStatEntryRefHash)
866  {
867  PgStat_EntryRefHashEntry *lohashent =
868  pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, key);
869 
870  if (lohashent)
871  pgstat_release_entry_ref(lohashent->key, lohashent->entry_ref,
872  true);
873  }
874 
875  /* mark entry in shared hashtable as deleted, drop if possible */
876  shent = dshash_find(pgStatLocal.shared_hash, &key, true);
877  if (shent)
878  {
879  freed = pgstat_drop_entry_internal(shent, NULL);
880 
881  /*
882  * Database stats contain other stats. Drop those as well when
883  * dropping the database. XXX: Perhaps this should be done in a
884  * slightly more principled way? But not obvious what that'd look
885  * like, and so far this is the only case...
886  */
887  if (key.kind == PGSTAT_KIND_DATABASE)
889  }
890 
891  return freed;
892 }
893 
894 void
896 {
897  dshash_seq_status hstat;
899  uint64 not_freed_count = 0;
900 
901  dshash_seq_init(&hstat, pgStatLocal.shared_hash, true);
902  while ((ps = dshash_seq_next(&hstat)) != NULL)
903  {
904  if (ps->dropped)
905  continue;
906 
907  if (!pgstat_drop_entry_internal(ps, &hstat))
908  not_freed_count++;
909  }
910  dshash_seq_term(&hstat);
911 
912  if (not_freed_count > 0)
914 }
915 
916 static void
918  TimestampTz ts)
919 {
920  const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
921 
922  memset(pgstat_get_entry_data(kind, header), 0,
923  pgstat_get_entry_len(kind));
924 
925  if (kind_info->reset_timestamp_cb)
926  kind_info->reset_timestamp_cb(header, ts);
927 }
928 
929 /*
930  * Reset one variable-numbered stats entry.
931  */
932 void
934 {
935  PgStat_EntryRef *entry_ref;
936 
937  Assert(!pgstat_get_kind_info(kind)->fixed_amount);
938 
939  entry_ref = pgstat_get_entry_ref(kind, dboid, objoid, false, NULL);
940  if (!entry_ref || entry_ref->shared_entry->dropped)
941  return;
942 
943  (void) pgstat_lock_entry(entry_ref, false);
944  shared_stat_reset_contents(kind, entry_ref->shared_stats, ts);
945  pgstat_unlock_entry(entry_ref);
946 }
947 
948 /*
949  * Scan through the shared hashtable of stats, resetting statistics if
950  * approved by the provided do_reset() function.
951  */
952 void
954  Datum match_data, TimestampTz ts)
955 {
956  dshash_seq_status hstat;
958 
959  /* dshash entry is not modified, take shared lock */
960  dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
961  while ((p = dshash_seq_next(&hstat)) != NULL)
962  {
963  PgStatShared_Common *header;
964 
965  if (p->dropped)
966  continue;
967 
968  if (!do_reset(p, match_data))
969  continue;
970 
971  header = dsa_get_address(pgStatLocal.dsa, p->body);
972 
973  LWLockAcquire(&header->lock, LW_EXCLUSIVE);
974 
975  shared_stat_reset_contents(p->key.kind, header, ts);
976 
977  LWLockRelease(&header->lock);
978  }
979  dshash_seq_term(&hstat);
980 }
981 
982 static bool
984 {
985  return p->key.kind == DatumGetInt32(match_data);
986 }
987 
988 void
990 {
992 }
993 
994 static void
996 {
1000  "PgStat Shared Ref",
1005  "PgStat Shared Ref Hash",
1007 }
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:396
static uint32 pg_atomic_fetch_sub_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:338
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:218
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:323
static uint64 pg_atomic_fetch_add_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
Definition: atomics.h:462
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:236
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition: atomics.h:410
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition: atomics.h:424
#define likely(x)
Definition: c.h:299
#define MAXALIGN(LEN)
Definition: c.h:800
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:171
unsigned char bool
Definition: c.h:445
#define unlikely(x)
Definition: c.h:300
#define OidIsValid(objectId)
Definition: c.h:764
size_t Size
Definition: c.h:594
int64 TimestampTz
Definition: timestamp.h:39
dsa_area * dsa_create_in_place(void *place, size_t size, int tranche_id, dsm_segment *segment)
Definition: dsa.c:488
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition: dsa.c:957
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:1942
dsa_area * dsa_attach_in_place(void *place, dsm_segment *segment)
Definition: dsa.c:560
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition: dsa.c:841
size_t dsa_minimum_size(void)
Definition: dsa.c:1194
void dsa_pin(dsa_area *area)
Definition: dsa.c:990
#define dsa_allocate0(area, size)
Definition: dsa.h:88
uint64 dsa_pointer
Definition: dsa.h:62
#define InvalidDsaPointer
Definition: dsa.h:78
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition: dshash.c:541
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition: dshash.c:558
void dshash_detach(dshash_table *hash_table)
Definition: dshash.c:307
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition: dshash.c:595
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition: dshash.c:390
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition: dshash.c:367
void dshash_seq_term(dshash_seq_status *status)
Definition: dshash.c:704
void * dshash_find_or_insert(dshash_table *hash_table, const void *key, bool *found)
Definition: dshash.c:433
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition: dshash.c:270
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition: dshash.c:206
void * dshash_seq_next(dshash_seq_status *status)
Definition: dshash.c:614
void dshash_delete_current(dshash_seq_status *status)
Definition: dshash.c:714
#define ERROR
Definition: elog.h:39
bool IsUnderPostmaster
Definition: globals.c:115
struct parser_state ps
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1195
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition: lwlock.c:730
bool LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1366
@ LWTRANCHE_PGSTATS_HASH
Definition: lwlock.h:206
@ LWTRANCHE_PGSTATS_DSA
Definition: lwlock.h:205
@ LWTRANCHE_PGSTATS_DATA
Definition: lwlock.h:207
@ LW_SHARED
Definition: lwlock.h:117
@ LW_EXCLUSIVE
Definition: lwlock.h:116
void pfree(void *pointer)
Definition: mcxt.c:1456
MemoryContext TopMemoryContext
Definition: mcxt.c:141
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1021
#define AllocSetContextCreate
Definition: memutils.h:126
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:160
#define BACKEND_NUM_TYPES
Definition: miscadmin.h:346
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
const void * data
const PgStat_KindInfo * pgstat_get_kind_info(PgStat_Kind kind)
Definition: pgstat.c:1263
void pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref)
Definition: pgstat.c:1156
PgStat_LocalState pgStatLocal
Definition: pgstat.c:196
PgStat_Kind
Definition: pgstat.h:36
@ PGSTAT_KIND_DATABASE
Definition: pgstat.h:41
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()
struct PgStat_HashKey PgStat_HashKey
static size_t pgstat_get_entry_len(PgStat_Kind kind)
static bool match_db(PgStat_EntryRefHashEntry *ent, Datum match_data)
Definition: pgstat_shmem.c:726
static void pgstat_setup_memcxt(void)
Definition: pgstat_shmem.c:995
void pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts)
Definition: pgstat_shmem.c:989
void pgstat_request_entry_refs_gc(void)
Definition: pgstat_shmem.c:628
static void pgstat_release_db_entry_refs(Oid dboid)
Definition: pgstat_shmem.c:734
PgStatShared_Common * pgstat_init_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
Definition: pgstat_shmem.c:258
static Size pgstat_dsa_init_size(void)
Definition: pgstat_shmem.c:105
static bool match_kind(PgStatShared_HashEntry *p, Datum match_data)
Definition: pgstat_shmem.c:983
static MemoryContext pgStatEntryRefHashContext
Definition: pgstat_shmem.c:90
void StatsShmemInit(void)
Definition: pgstat_shmem.c:140
bool pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait)
Definition: pgstat_shmem.c:591
void pgstat_attach_shmem(void)
Definition: pgstat_shmem.c:217
static void pgstat_free_entry(PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
Definition: pgstat_shmem.c:748
static int pgStatSharedRefAge
Definition: pgstat_shmem.c:82
#define PGSTAT_ENTRY_REF_HASH_SIZE
Definition: pgstat_shmem.c:21
static void shared_stat_reset_contents(PgStat_Kind kind, PgStatShared_Common *header, TimestampTz ts)
Definition: pgstat_shmem.c:917
static void pgstat_setup_shared_refs(void)
Definition: pgstat_shmem.c:306
static void pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref, bool discard_pending)
Definition: pgstat_shmem.c:521
static void pgstat_drop_database_and_contents(Oid dboid)
Definition: pgstat_shmem.c:808
static PgStatShared_Common * pgstat_reinit_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
Definition: pgstat_shmem.c:287
static pgstat_entry_ref_hash_hash * pgStatEntryRefHash
Definition: pgstat_shmem.c:81
void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, Oid objoid, TimestampTz ts)
Definition: pgstat_shmem.c:933
Size StatsShmemSize(void)
Definition: pgstat_shmem.c:126
static void pgstat_acquire_entry_ref(PgStat_EntryRef *entry_ref, PgStatShared_HashEntry *shhashent, PgStatShared_Common *shheader)
Definition: pgstat_shmem.c:322
static MemoryContext pgStatSharedRefContext
Definition: pgstat_shmem.c:89
void pgstat_reset_matching_entries(bool(*do_reset)(PgStatShared_HashEntry *, Datum), Datum match_data, TimestampTz ts)
Definition: pgstat_shmem.c:953
void pgstat_drop_all_entries(void)
Definition: pgstat_shmem.c:895
static const dshash_parameters dsh_params
Definition: pgstat_shmem.c:62
PgStat_EntryRef * pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, Oid objoid, bool create, bool *created_entry)
Definition: pgstat_shmem.c:397
static bool pgstat_get_entry_ref_cached(PgStat_HashKey key, PgStat_EntryRef **entry_ref_p)
Definition: pgstat_shmem.c:341
void pgstat_unlock_entry(PgStat_EntryRef *entry_ref)
Definition: pgstat_shmem.c:603
static void pgstat_release_all_entry_refs(bool discard_pending)
Definition: pgstat_shmem.c:714
struct PgStat_EntryRefHashEntry PgStat_EntryRefHashEntry
static void pgstat_release_matching_entry_refs(bool discard_pending, ReleaseMatchCB match, Datum match_data)
Definition: pgstat_shmem.c:684
bool(* ReleaseMatchCB)(PgStat_EntryRefHashEntry *, Datum data)
Definition: pgstat_shmem.c:55
bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
Definition: pgstat_shmem.c:858
bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
Definition: pgstat_shmem.c:575
static void pgstat_gc_entry_refs(void)
Definition: pgstat_shmem.c:650
static bool pgstat_need_entry_refs_gc(void)
Definition: pgstat_shmem.c:634
static bool pgstat_drop_entry_internal(PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
Definition: pgstat_shmem.c:773
void pgstat_detach_shmem(void)
Definition: pgstat_shmem.c:237
PgStat_EntryRef * pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, Oid objoid, bool nowait)
Definition: pgstat_shmem.c:612
uintptr_t Datum
Definition: postgres.h:64
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:202
unsigned int Oid
Definition: postgres_ext.h:31
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:396
Definition: lwlock.h:41
pg_atomic_uint32 refcount
LWLock locks[BACKEND_NUM_TYPES]
PgStat_EntryRef * entry_ref
Definition: pgstat_shmem.c:28
PgStatShared_Common * shared_stats
PgStatShared_HashEntry * shared_entry
PgStat_Kind kind
void(* reset_timestamp_cb)(PgStatShared_Common *header, TimestampTz ts)
dshash_table * shared_hash
PgStat_ShmemControl * shmem
dshash_table_handle hash_handle
PgStatShared_SLRU slru
PgStatShared_IO io
pg_atomic_uint64 gc_request_count
PgStatShared_Wal wal
PgStatShared_Archiver archiver
PgStatShared_Checkpointer checkpointer
PgStatShared_BgWriter bgwriter
Definition: dsa.c:368