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