Age Owner Branch data TLA Line data Source code
1 : : /* -------------------------------------------------------------------------
2 : : *
3 : : * pgstat_shmem.c
4 : : * Storage of stats entries in shared memory
5 : : *
6 : : * Copyright (c) 2001-2025, 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 */
24 : : typedef struct PgStat_EntryRefHashEntry
25 : : {
26 : : PgStat_HashKey key; /* hash key */
27 : : char status; /* for simplehash use */
28 : : PgStat_EntryRef *entry_ref;
29 : : } PgStat_EntryRefHashEntry;
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 : :
49 : : static void pgstat_free_entry(PgStatShared_HashEntry *shent, dshash_seq_status *hstat);
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);
55 : : typedef bool (*ReleaseMatchCB) (PgStat_EntryRefHashEntry *, Datum data);
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),
65 : : pgstat_cmp_hash_key,
66 : : pgstat_hash_hash_key,
67 : : dshash_memcpy,
68 : : LWTRANCHE_PGSTATS_HASH
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 pgStatLocal.shmem */
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 : : */
90 : : static MemoryContext pgStatSharedRefContext = NULL;
91 : : static MemoryContext pgStatEntryRefHashContext = NULL;
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
1249 andres@anarazel.de 106 :CBC 4996 : pgstat_dsa_init_size(void)
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 : 4996 : sz = 256 * 1024;
119 [ - + ]: 4996 : Assert(dsa_minimum_size() <= sz);
120 : 4996 : return MAXALIGN(sz);
121 : : }
122 : :
123 : : /*
124 : : * Compute shared memory space needed for cumulative statistics
125 : : */
126 : : Size
127 : 2938 : StatsShmemSize(void)
128 : : {
129 : : Size sz;
130 : :
131 : 2938 : sz = MAXALIGN(sizeof(PgStat_ShmemControl));
132 : 2938 : sz = add_size(sz, pgstat_dsa_init_size());
133 : :
134 : : /* Add shared memory for all the custom fixed-numbered statistics */
398 michael@paquier.xyz 135 [ + + ]: 29380 : for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
136 : : {
137 : 26442 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
138 : :
139 [ + - ]: 26442 : if (!kind_info)
140 : 26442 : continue;
398 michael@paquier.xyz 141 [ # # ]:UBC 0 : if (!kind_info->fixed_amount)
142 : 0 : continue;
143 : :
144 [ # # ]: 0 : Assert(kind_info->shared_size != 0);
145 : :
146 : 0 : sz += MAXALIGN(kind_info->shared_size);
147 : : }
148 : :
1249 andres@anarazel.de 149 :CBC 2938 : return sz;
150 : : }
151 : :
152 : : /*
153 : : * Initialize cumulative statistics system during startup
154 : : */
155 : : void
156 : 1029 : StatsShmemInit(void)
157 : : {
158 : : bool found;
159 : : Size sz;
160 : :
161 : 1029 : sz = StatsShmemSize();
162 : 1029 : pgStatLocal.shmem = (PgStat_ShmemControl *)
163 : 1029 : ShmemInitStruct("Shared Memory Stats", sz, &found);
164 : :
165 [ + - ]: 1029 : if (!IsUnderPostmaster)
166 : : {
167 : : dsa_area *dsa;
168 : : dshash_table *dsh;
169 : 1029 : PgStat_ShmemControl *ctl = pgStatLocal.shmem;
170 : 1029 : char *p = (char *) ctl;
171 : :
172 [ - + ]: 1029 : Assert(!found);
173 : :
174 : : /* the allocation of pgStatLocal.shmem itself */
175 : 1029 : p += MAXALIGN(sizeof(PgStat_ShmemControl));
176 : :
177 : : /*
178 : : * Create a small dsa allocation in plain shared memory. This is
179 : : * required because postmaster cannot use dsm segments. It also
180 : : * provides a small efficiency win.
181 : : */
182 : 1029 : ctl->raw_dsa_area = p;
183 : 1029 : dsa = dsa_create_in_place(ctl->raw_dsa_area,
184 : : pgstat_dsa_init_size(),
185 : : LWTRANCHE_PGSTATS_DSA, NULL);
186 : 1029 : dsa_pin(dsa);
187 : :
188 : : /*
189 : : * To ensure dshash is created in "plain" shared memory, temporarily
190 : : * limit size of dsa to the initial size of the dsa.
191 : : */
192 : 1029 : dsa_set_size_limit(dsa, pgstat_dsa_init_size());
193 : :
194 : : /*
195 : : * With the limit in place, create the dshash table. XXX: It'd be nice
196 : : * if there were dshash_create_in_place().
197 : : */
558 nathan@postgresql.or 198 : 1029 : dsh = dshash_create(dsa, &dsh_params, NULL);
1249 andres@anarazel.de 199 : 1029 : ctl->hash_handle = dshash_get_hash_table_handle(dsh);
200 : :
201 : : /* lift limit set above */
202 : 1029 : dsa_set_size_limit(dsa, -1);
203 : :
204 : : /*
205 : : * Postmaster will never access these again, thus free the local
206 : : * dsa/dshash references.
207 : : */
208 : 1029 : dshash_detach(dsh);
209 : 1029 : dsa_detach(dsa);
210 : :
211 : 1029 : pg_atomic_init_u64(&ctl->gc_request_count, 1);
212 : :
213 : : /* initialize fixed-numbered stats */
398 michael@paquier.xyz 214 [ + + ]: 33957 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
215 : : {
422 216 : 32928 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
217 : : char *ptr;
218 : :
398 219 [ + + + + ]: 32928 : if (!kind_info || !kind_info->fixed_amount)
422 220 : 26754 : continue;
221 : :
398 222 [ + - ]: 6174 : if (pgstat_is_kind_builtin(kind))
223 : 6174 : ptr = ((char *) ctl) + kind_info->shared_ctl_off;
224 : : else
225 : : {
398 michael@paquier.xyz 226 :UBC 0 : int idx = kind - PGSTAT_KIND_CUSTOM_MIN;
227 : :
228 [ # # ]: 0 : Assert(kind_info->shared_size != 0);
229 : 0 : ctl->custom_data[idx] = ShmemAlloc(kind_info->shared_size);
230 : 0 : ptr = ctl->custom_data[idx];
231 : : }
232 : :
422 michael@paquier.xyz 233 :CBC 6174 : kind_info->init_shmem_cb(ptr);
234 : : }
235 : : }
236 : : else
237 : : {
1249 andres@anarazel.de 238 [ # # ]:UBC 0 : Assert(found);
239 : : }
1249 andres@anarazel.de 240 :CBC 1029 : }
241 : :
242 : : void
243 : 18772 : pgstat_attach_shmem(void)
244 : : {
245 : : MemoryContext oldcontext;
246 : :
247 [ - + ]: 18772 : Assert(pgStatLocal.dsa == NULL);
248 : :
249 : : /* stats shared memory persists for the backend lifetime */
250 : 18772 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
251 : :
252 : 18772 : pgStatLocal.dsa = dsa_attach_in_place(pgStatLocal.shmem->raw_dsa_area,
253 : : NULL);
254 : 18772 : dsa_pin_mapping(pgStatLocal.dsa);
255 : :
256 : 37544 : pgStatLocal.shared_hash = dshash_attach(pgStatLocal.dsa, &dsh_params,
92 nathan@postgresql.or 257 : 18772 : pgStatLocal.shmem->hash_handle,
258 : : NULL);
259 : :
1249 andres@anarazel.de 260 : 18772 : MemoryContextSwitchTo(oldcontext);
261 : 18772 : }
262 : :
263 : : void
264 : 18772 : pgstat_detach_shmem(void)
265 : : {
266 [ - + ]: 18772 : Assert(pgStatLocal.dsa);
267 : :
268 : : /* we shouldn't leave references to shared stats */
269 : 18772 : pgstat_release_all_entry_refs(false);
270 : :
271 : 18772 : dshash_detach(pgStatLocal.shared_hash);
272 : 18772 : pgStatLocal.shared_hash = NULL;
273 : :
274 : 18772 : dsa_detach(pgStatLocal.dsa);
275 : :
276 : : /*
277 : : * dsa_detach() does not decrement the DSA reference count as no segment
278 : : * was provided to dsa_attach_in_place(), causing no cleanup callbacks to
279 : : * be registered. Hence, release it manually now.
280 : : */
436 michael@paquier.xyz 281 : 18772 : dsa_release_in_place(pgStatLocal.shmem->raw_dsa_area);
282 : :
1249 andres@anarazel.de 283 : 18772 : pgStatLocal.dsa = NULL;
284 : 18772 : }
285 : :
286 : :
287 : : /* ------------------------------------------------------------
288 : : * Maintenance of shared memory stats entries
289 : : * ------------------------------------------------------------
290 : : */
291 : :
292 : : PgStatShared_Common *
293 : 298812 : pgstat_init_entry(PgStat_Kind kind,
294 : : PgStatShared_HashEntry *shhashent)
295 : : {
296 : : /* Create new stats entry. */
297 : : dsa_pointer chunk;
298 : : PgStatShared_Common *shheader;
299 : :
300 : : /*
301 : : * Initialize refcount to 1, marking it as valid / not dropped. The entry
302 : : * can't be freed before the initialization because it can't be found as
303 : : * long as we hold the dshash partition lock. Caller needs to increase
304 : : * further if a longer lived reference is needed.
305 : : */
306 : 298812 : pg_atomic_init_u32(&shhashent->refcount, 1);
307 : :
308 : : /*
309 : : * Initialize "generation" to 0, as freshly created.
310 : : */
295 michael@paquier.xyz 311 : 298812 : pg_atomic_init_u32(&shhashent->generation, 0);
1249 andres@anarazel.de 312 : 298812 : shhashent->dropped = false;
313 : :
314 : 298812 : chunk = dsa_allocate0(pgStatLocal.dsa, pgstat_get_kind_info(kind)->shared_size);
315 : 298812 : shheader = dsa_get_address(pgStatLocal.dsa, chunk);
316 : 298812 : shheader->magic = 0xdeadbeef;
317 : :
318 : : /* Link the new entry from the hash entry. */
319 : 298812 : shhashent->body = chunk;
320 : :
321 : 298812 : LWLockInitialize(&shheader->lock, LWTRANCHE_PGSTATS_DATA);
322 : :
323 : 298812 : return shheader;
324 : : }
325 : :
326 : : static PgStatShared_Common *
327 : 30 : pgstat_reinit_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
328 : : {
329 : : PgStatShared_Common *shheader;
330 : :
331 : 30 : shheader = dsa_get_address(pgStatLocal.dsa, shhashent->body);
332 : :
333 : : /* mark as not dropped anymore */
334 : 30 : pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
335 : :
336 : : /*
337 : : * Increment "generation", to let any backend with local references know
338 : : * that what they point to is outdated.
339 : : */
295 michael@paquier.xyz 340 : 30 : pg_atomic_fetch_add_u32(&shhashent->generation, 1);
1249 andres@anarazel.de 341 : 30 : shhashent->dropped = false;
342 : :
343 : : /* reinitialize content */
344 [ - + ]: 30 : Assert(shheader->magic == 0xdeadbeef);
345 : 30 : memset(pgstat_get_entry_data(kind, shheader), 0,
346 : : pgstat_get_entry_len(kind));
347 : :
348 : 30 : return shheader;
349 : : }
350 : :
351 : : static void
352 : 3092771 : pgstat_setup_shared_refs(void)
353 : : {
354 [ + + ]: 3092771 : if (likely(pgStatEntryRefHash != NULL))
355 : 3077186 : return;
356 : :
357 : 15585 : pgStatEntryRefHash =
358 : 15585 : pgstat_entry_ref_hash_create(pgStatEntryRefHashContext,
359 : : PGSTAT_ENTRY_REF_HASH_SIZE, NULL);
360 : 15585 : pgStatSharedRefAge = pg_atomic_read_u64(&pgStatLocal.shmem->gc_request_count);
361 [ - + ]: 15585 : Assert(pgStatSharedRefAge != 0);
362 : : }
363 : :
364 : : /*
365 : : * Helper function for pgstat_get_entry_ref().
366 : : */
367 : : static void
368 : 678042 : pgstat_acquire_entry_ref(PgStat_EntryRef *entry_ref,
369 : : PgStatShared_HashEntry *shhashent,
370 : : PgStatShared_Common *shheader)
371 : : {
372 [ - + ]: 678042 : Assert(shheader->magic == 0xdeadbeef);
373 [ - + ]: 678042 : Assert(pg_atomic_read_u32(&shhashent->refcount) > 0);
374 : :
375 : 678042 : pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
376 : :
377 : 678042 : dshash_release_lock(pgStatLocal.shared_hash, shhashent);
378 : :
379 : 678042 : entry_ref->shared_stats = shheader;
380 : 678042 : entry_ref->shared_entry = shhashent;
295 michael@paquier.xyz 381 : 678042 : entry_ref->generation = pg_atomic_read_u32(&shhashent->generation);
1249 andres@anarazel.de 382 : 678042 : }
383 : :
384 : : /*
385 : : * Helper function for pgstat_get_entry_ref().
386 : : */
387 : : static bool
388 : 3092771 : pgstat_get_entry_ref_cached(PgStat_HashKey key, PgStat_EntryRef **entry_ref_p)
389 : : {
390 : : bool found;
391 : : PgStat_EntryRefHashEntry *cache_entry;
392 : :
393 : : /*
394 : : * We immediately insert a cache entry, because it avoids 1) multiple
395 : : * hashtable lookups in case of a cache miss 2) having to deal with
396 : : * out-of-memory errors after incrementing PgStatShared_Common->refcount.
397 : : */
398 : :
399 : 3092771 : cache_entry = pgstat_entry_ref_hash_insert(pgStatEntryRefHash, key, &found);
400 : :
401 [ + + - + ]: 3092771 : if (!found || !cache_entry->entry_ref)
402 : 755955 : {
403 : : PgStat_EntryRef *entry_ref;
404 : :
405 : 755955 : cache_entry->entry_ref = entry_ref =
406 : 755955 : MemoryContextAlloc(pgStatSharedRefContext,
407 : : sizeof(PgStat_EntryRef));
408 : 755955 : entry_ref->shared_stats = NULL;
409 : 755955 : entry_ref->shared_entry = NULL;
410 : 755955 : entry_ref->pending = NULL;
411 : :
412 : 755955 : found = false;
413 : : }
414 [ - + ]: 2336816 : else if (cache_entry->entry_ref->shared_stats == NULL)
415 : : {
1249 andres@anarazel.de 416 [ # # ]:UBC 0 : Assert(cache_entry->entry_ref->pending == NULL);
417 : 0 : found = false;
418 : : }
419 : : else
420 : : {
421 : : PgStat_EntryRef *entry_ref PG_USED_FOR_ASSERTS_ONLY;
422 : :
1249 andres@anarazel.de 423 :CBC 2336816 : entry_ref = cache_entry->entry_ref;
424 [ - + ]: 2336816 : Assert(entry_ref->shared_entry != NULL);
425 [ - + ]: 2336816 : Assert(entry_ref->shared_stats != NULL);
426 : :
427 [ - + ]: 2336816 : Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
428 : : /* should have at least our reference */
429 [ - + ]: 2336816 : Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) > 0);
430 : : }
431 : :
432 : 3092771 : *entry_ref_p = cache_entry->entry_ref;
433 : 3092771 : return found;
434 : : }
435 : :
436 : : /*
437 : : * Get a shared stats reference. If create is true, the shared stats object is
438 : : * created if it does not exist.
439 : : *
440 : : * When create is true, and created_entry is non-NULL, it'll be set to true
441 : : * if the entry is newly created, false otherwise.
442 : : */
443 : : PgStat_EntryRef *
353 michael@paquier.xyz 444 : 3092771 : pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create,
445 : : bool *created_entry)
446 : : {
447 : : PgStat_HashKey key;
448 : : PgStatShared_HashEntry *shhashent;
1249 andres@anarazel.de 449 : 3092771 : PgStatShared_Common *shheader = NULL;
450 : : PgStat_EntryRef *entry_ref;
451 : :
452 : : /* clear padding */
305 michael@paquier.xyz 453 : 3092771 : memset(&key, 0, sizeof(struct PgStat_HashKey));
454 : :
455 : 3092771 : key.kind = kind;
456 : 3092771 : key.dboid = dboid;
457 : 3092771 : key.objid = objid;
458 : :
459 : : /*
460 : : * passing in created_entry only makes sense if we possibly could create
461 : : * entry.
462 : : */
1044 peter@eisentraut.org 463 [ + + - + ]: 3092771 : Assert(create || created_entry == NULL);
1249 andres@anarazel.de 464 : 3092771 : pgstat_assert_is_up();
465 [ - + ]: 3092771 : Assert(pgStatLocal.shared_hash != NULL);
466 [ - + ]: 3092771 : Assert(!pgStatLocal.shmem->is_shutdown);
467 : :
468 : 3092771 : pgstat_setup_memcxt();
469 : 3092771 : pgstat_setup_shared_refs();
470 : :
471 [ + + ]: 3092771 : if (created_entry != NULL)
472 : 107 : *created_entry = false;
473 : :
474 : : /*
475 : : * Check if other backends dropped stats that could not be deleted because
476 : : * somebody held references to it. If so, check this backend's references.
477 : : * This is not expected to happen often. The location of the check is a
478 : : * bit random, but this is a relatively frequently called path, so better
479 : : * than most.
480 : : */
481 [ + + ]: 3092771 : if (pgstat_need_entry_refs_gc())
482 : 6089 : pgstat_gc_entry_refs();
483 : :
484 : : /*
485 : : * First check the lookup cache hashtable in local memory. If we find a
486 : : * match here we can avoid taking locks / causing contention.
487 : : */
488 [ + + ]: 3092771 : if (pgstat_get_entry_ref_cached(key, &entry_ref))
489 : 2336816 : return entry_ref;
490 : :
491 [ - + ]: 755955 : Assert(entry_ref != NULL);
492 : :
493 : : /*
494 : : * Do a lookup in the hash table first - it's quite likely that the entry
495 : : * already exists, and that way we only need a shared lock.
496 : : */
497 : 755955 : shhashent = dshash_find(pgStatLocal.shared_hash, &key, false);
498 : :
499 [ + + + + ]: 755955 : if (create && !shhashent)
500 : : {
501 : : bool shfound;
502 : :
503 : : /*
504 : : * It's possible that somebody created the entry since the above
505 : : * lookup. If so, fall through to the same path as if we'd have if it
506 : : * already had been created before the dshash_find() calls.
507 : : */
508 : 111912 : shhashent = dshash_find_or_insert(pgStatLocal.shared_hash, &key, &shfound);
509 [ + - ]: 111912 : if (!shfound)
510 : : {
511 : 111912 : shheader = pgstat_init_entry(kind, shhashent);
512 : 111912 : pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
513 : :
514 [ + + ]: 111912 : if (created_entry != NULL)
515 : 48 : *created_entry = true;
516 : :
517 : 111912 : return entry_ref;
518 : : }
519 : : }
520 : :
521 [ + + ]: 644043 : if (!shhashent)
522 : : {
523 : : /*
524 : : * If we're not creating, delete the reference again. In all
525 : : * likelihood it's just a stats lookup - no point wasting memory for a
526 : : * shared ref to nothing...
527 : : */
528 : 77876 : pgstat_release_entry_ref(key, entry_ref, false);
529 : :
530 : 77876 : return NULL;
531 : : }
532 : : else
533 : : {
534 : : /*
535 : : * Can get here either because dshash_find() found a match, or if
536 : : * dshash_find_or_insert() found a concurrently inserted entry.
537 : : */
538 : :
539 [ + + + + ]: 566167 : if (shhashent->dropped && create)
540 : : {
541 : : /*
542 : : * There are legitimate cases where the old stats entry might not
543 : : * yet have been dropped by the time it's reused. The most obvious
544 : : * case are replication slot stats, where a new slot can be
545 : : * created with the same index just after dropping. But oid
546 : : * wraparound can lead to other cases as well. We just reset the
547 : : * stats to their plain state, while incrementing its "generation"
548 : : * in the shared entry for any remaining local references.
549 : : */
550 : 30 : shheader = pgstat_reinit_entry(kind, shhashent);
551 : 30 : pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
552 : :
553 [ - + ]: 30 : if (created_entry != NULL)
1249 andres@anarazel.de 554 :UBC 0 : *created_entry = true;
555 : :
1249 andres@anarazel.de 556 :CBC 30 : return entry_ref;
557 : : }
558 [ + + ]: 566137 : else if (shhashent->dropped)
559 : : {
560 : 37 : dshash_release_lock(pgStatLocal.shared_hash, shhashent);
561 : 37 : pgstat_release_entry_ref(key, entry_ref, false);
562 : :
563 : 37 : return NULL;
564 : : }
565 : : else
566 : : {
567 : 566100 : shheader = dsa_get_address(pgStatLocal.dsa, shhashent->body);
568 : 566100 : pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
569 : :
570 : 566100 : return entry_ref;
571 : : }
572 : : }
573 : : }
574 : :
575 : : static void
576 : 755955 : pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref,
577 : : bool discard_pending)
578 : : {
579 [ + - + + ]: 755955 : if (entry_ref && entry_ref->pending)
580 : : {
581 [ + - ]: 32283 : if (discard_pending)
582 : 32283 : pgstat_delete_pending_entry(entry_ref);
583 : : else
1249 andres@anarazel.de 584 [ # # ]:UBC 0 : elog(ERROR, "releasing ref with pending data");
585 : : }
586 : :
1249 andres@anarazel.de 587 [ + - + + ]:CBC 755955 : if (entry_ref && entry_ref->shared_stats)
588 : : {
589 [ - + ]: 678042 : Assert(entry_ref->shared_stats->magic == 0xdeadbeef);
590 [ - + ]: 678042 : Assert(entry_ref->pending == NULL);
591 : :
592 : : /*
593 : : * This can't race with another backend looking up the stats entry and
594 : : * increasing the refcount because it is not "legal" to create
595 : : * additional references to dropped entries.
596 : : */
597 [ + + ]: 678042 : if (pg_atomic_fetch_sub_u32(&entry_ref->shared_entry->refcount, 1) == 1)
598 : : {
599 : : PgStatShared_HashEntry *shent;
600 : :
601 : : /*
602 : : * We're the last referrer to this entry, try to drop the shared
603 : : * entry.
604 : : */
605 : :
606 : : /* only dropped entries can reach a 0 refcount */
607 [ - + ]: 4740 : Assert(entry_ref->shared_entry->dropped);
608 : :
609 : 4740 : shent = dshash_find(pgStatLocal.shared_hash,
610 : 4740 : &entry_ref->shared_entry->key,
611 : : true);
612 [ - + ]: 4740 : if (!shent)
1249 andres@anarazel.de 613 [ # # ]:UBC 0 : elog(ERROR, "could not find just referenced shared stats entry");
614 : :
615 : : /*
616 : : * This entry may have been reinitialized while trying to release
617 : : * it, so double-check that it has not been reused while holding a
618 : : * lock on its shared entry.
619 : : */
295 michael@paquier.xyz 620 :CBC 4740 : if (pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
621 [ + - ]: 4740 : entry_ref->generation)
622 : : {
623 : : /* Same "generation", so we're OK with the removal */
624 [ - + ]: 4740 : Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) == 0);
625 [ - + ]: 4740 : Assert(entry_ref->shared_entry == shent);
626 : 4740 : pgstat_free_entry(shent, NULL);
627 : : }
628 : : else
629 : : {
630 : : /*
631 : : * Shared stats entry has been reinitialized, so do not drop
632 : : * its shared entry, only release its lock.
633 : : */
295 michael@paquier.xyz 634 :UBC 0 : dshash_release_lock(pgStatLocal.shared_hash, shent);
635 : : }
636 : : }
637 : : }
638 : :
1249 andres@anarazel.de 639 [ - + ]:CBC 755955 : if (!pgstat_entry_ref_hash_delete(pgStatEntryRefHash, key))
1249 andres@anarazel.de 640 [ # # ]:UBC 0 : elog(ERROR, "entry ref vanished before deletion");
641 : :
1249 andres@anarazel.de 642 [ + - ]:CBC 755955 : if (entry_ref)
643 : 755955 : pfree(entry_ref);
644 : 755955 : }
645 : :
646 : : /*
647 : : * Acquire exclusive lock on the entry.
648 : : *
649 : : * If nowait is true, it's just a conditional acquire, and the result
650 : : * *must* be checked to verify success.
651 : : * If nowait is false, waits as necessary, always returning true.
652 : : */
653 : : bool
654 : 907961 : pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait)
655 : : {
656 : 907961 : LWLock *lock = &entry_ref->shared_stats->lock;
657 : :
658 [ + + ]: 907961 : if (nowait)
659 : 351031 : return LWLockConditionalAcquire(lock, LW_EXCLUSIVE);
660 : :
661 : 556930 : LWLockAcquire(lock, LW_EXCLUSIVE);
662 : 556930 : return true;
663 : : }
664 : :
665 : : /*
666 : : * Acquire shared lock on the entry.
667 : : *
668 : : * Separate from pgstat_lock_entry() as most callers will need to lock
669 : : * exclusively. The wait semantics are identical.
670 : : */
671 : : bool
1111 672 : 8962 : pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait)
673 : : {
674 : 8962 : LWLock *lock = &entry_ref->shared_stats->lock;
675 : :
676 [ - + ]: 8962 : if (nowait)
1111 andres@anarazel.de 677 :UBC 0 : return LWLockConditionalAcquire(lock, LW_SHARED);
678 : :
1111 andres@anarazel.de 679 :CBC 8962 : LWLockAcquire(lock, LW_SHARED);
680 : 8962 : return true;
681 : : }
682 : :
683 : : void
1249 684 : 916920 : pgstat_unlock_entry(PgStat_EntryRef *entry_ref)
685 : : {
686 : 916920 : LWLockRelease(&entry_ref->shared_stats->lock);
687 : 916920 : }
688 : :
689 : : /*
690 : : * Helper function to fetch and lock shared stats.
691 : : */
692 : : PgStat_EntryRef *
353 michael@paquier.xyz 693 : 84460 : pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, uint64 objid,
694 : : bool nowait)
695 : : {
696 : : PgStat_EntryRef *entry_ref;
697 : :
698 : : /* find shared table stats entry corresponding to the local entry */
699 : 84460 : entry_ref = pgstat_get_entry_ref(kind, dboid, objid, true, NULL);
700 : :
701 : : /* lock the shared entry to protect the content, skip if failed */
1249 andres@anarazel.de 702 [ - + ]: 84460 : if (!pgstat_lock_entry(entry_ref, nowait))
1249 andres@anarazel.de 703 :UBC 0 : return NULL;
704 : :
1249 andres@anarazel.de 705 :CBC 84460 : return entry_ref;
706 : : }
707 : :
708 : : void
709 : 1877 : pgstat_request_entry_refs_gc(void)
710 : : {
711 : 1877 : pg_atomic_fetch_add_u64(&pgStatLocal.shmem->gc_request_count, 1);
712 : 1877 : }
713 : :
714 : : static bool
715 : 3092771 : pgstat_need_entry_refs_gc(void)
716 : : {
717 : : uint64 curage;
718 : :
719 [ - + ]: 3092771 : if (!pgStatEntryRefHash)
1249 andres@anarazel.de 720 :UBC 0 : return false;
721 : :
722 : : /* should have been initialized when creating pgStatEntryRefHash */
1249 andres@anarazel.de 723 [ - + ]:CBC 3092771 : Assert(pgStatSharedRefAge != 0);
724 : :
725 : 3092771 : curage = pg_atomic_read_u64(&pgStatLocal.shmem->gc_request_count);
726 : :
727 : 3092771 : return pgStatSharedRefAge != curage;
728 : : }
729 : :
730 : : static void
731 : 6089 : pgstat_gc_entry_refs(void)
732 : : {
733 : : pgstat_entry_ref_hash_iterator i;
734 : : PgStat_EntryRefHashEntry *ent;
735 : : uint64 curage;
736 : :
737 : 6089 : curage = pg_atomic_read_u64(&pgStatLocal.shmem->gc_request_count);
738 [ - + ]: 6089 : Assert(curage != 0);
739 : :
740 : : /*
741 : : * Some entries have been dropped or reinitialized. Invalidate cache
742 : : * pointer to them.
743 : : */
744 : 6089 : pgstat_entry_ref_hash_start_iterate(pgStatEntryRefHash, &i);
745 [ + + ]: 414858 : while ((ent = pgstat_entry_ref_hash_iterate(pgStatEntryRefHash, &i)) != NULL)
746 : : {
747 : 408769 : PgStat_EntryRef *entry_ref = ent->entry_ref;
748 : :
749 [ + - - + ]: 408769 : Assert(!entry_ref->shared_stats ||
750 : : entry_ref->shared_stats->magic == 0xdeadbeef);
751 : :
752 : : /*
753 : : * "generation" checks for the case of entries being reinitialized,
754 : : * and "dropped" for the case where these are.. dropped.
755 : : */
271 michael@paquier.xyz 756 [ + + ]: 408769 : if (!entry_ref->shared_entry->dropped &&
757 : 328517 : pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
758 [ + + ]: 328517 : entry_ref->generation)
1249 andres@anarazel.de 759 : 328487 : continue;
760 : :
761 : : /* cannot gc shared ref that has pending data */
762 [ + + ]: 80282 : if (entry_ref->pending != NULL)
763 : 74571 : continue;
764 : :
765 : 5711 : pgstat_release_entry_ref(ent->key, entry_ref, false);
766 : : }
767 : :
768 : 6089 : pgStatSharedRefAge = curage;
769 : 6089 : }
770 : :
771 : : static void
772 : 15619 : pgstat_release_matching_entry_refs(bool discard_pending, ReleaseMatchCB match,
773 : : Datum match_data)
774 : : {
775 : : pgstat_entry_ref_hash_iterator i;
776 : : PgStat_EntryRefHashEntry *ent;
777 : :
778 [ + + ]: 15619 : if (pgStatEntryRefHash == NULL)
779 : 1 : return;
780 : :
781 : 15618 : pgstat_entry_ref_hash_start_iterate(pgStatEntryRefHash, &i);
782 : :
783 : 641110 : while ((ent = pgstat_entry_ref_hash_iterate(pgStatEntryRefHash, &i))
784 [ + + ]: 641110 : != NULL)
785 : : {
786 [ - + ]: 625492 : Assert(ent->entry_ref != NULL);
787 : :
788 [ + + + - ]: 625492 : if (match && !match(ent, match_data))
789 : 1023 : continue;
790 : :
791 : 624469 : pgstat_release_entry_ref(ent->key, ent->entry_ref, discard_pending);
792 : : }
793 : : }
794 : :
795 : : /*
796 : : * Release all local references to shared stats entries.
797 : : *
798 : : * When a process exits it cannot do so while still holding references onto
799 : : * stats entries, otherwise the shared stats entries could never be freed.
800 : : */
801 : : static void
802 : 18772 : pgstat_release_all_entry_refs(bool discard_pending)
803 : : {
804 [ + + ]: 18772 : if (pgStatEntryRefHash == NULL)
805 : 3187 : return;
806 : :
807 : 15585 : pgstat_release_matching_entry_refs(discard_pending, NULL, 0);
808 [ - + ]: 15585 : Assert(pgStatEntryRefHash->members == 0);
809 : 15585 : pgstat_entry_ref_hash_destroy(pgStatEntryRefHash);
810 : 15585 : pgStatEntryRefHash = NULL;
811 : : }
812 : :
813 : : static bool
814 : 1023 : match_db(PgStat_EntryRefHashEntry *ent, Datum match_data)
815 : : {
816 : 1023 : Oid dboid = DatumGetObjectId(match_data);
817 : :
818 : 1023 : return ent->key.dboid == dboid;
819 : : }
820 : :
821 : : static void
822 : 34 : pgstat_release_db_entry_refs(Oid dboid)
823 : : {
824 : 34 : pgstat_release_matching_entry_refs( /* discard pending = */ true,
825 : : match_db,
826 : : ObjectIdGetDatum(dboid));
827 : 34 : }
828 : :
829 : :
830 : : /* ------------------------------------------------------------
831 : : * Dropping and resetting of stats entries
832 : : * ------------------------------------------------------------
833 : : */
834 : :
835 : : static void
836 : 52267 : pgstat_free_entry(PgStatShared_HashEntry *shent, dshash_seq_status *hstat)
837 : : {
838 : : dsa_pointer pdsa;
839 : :
840 : : /*
841 : : * Fetch dsa pointer before deleting entry - that way we can free the
842 : : * memory after releasing the lock.
843 : : */
844 : 52267 : pdsa = shent->body;
845 : :
846 [ + + ]: 52267 : if (!hstat)
847 : 47969 : dshash_delete_entry(pgStatLocal.shared_hash, shent);
848 : : else
849 : 4298 : dshash_delete_current(hstat);
850 : :
851 : 52267 : dsa_free(pgStatLocal.dsa, pdsa);
852 : 52267 : }
853 : :
854 : : /*
855 : : * Helper for both pgstat_drop_database_and_contents() and
856 : : * pgstat_drop_entry(). If hstat is non-null delete the shared entry using
857 : : * dshash_delete_current(), otherwise use dshash_delete_entry(). In either
858 : : * case the entry needs to be already locked.
859 : : */
860 : : static bool
861 : 52297 : pgstat_drop_entry_internal(PgStatShared_HashEntry *shent,
862 : : dshash_seq_status *hstat)
863 : : {
864 [ - + ]: 52297 : Assert(shent->body != InvalidDsaPointer);
865 : :
866 : : /* should already have released local reference */
867 [ + + ]: 52297 : if (pgStatEntryRefHash)
868 [ - + ]: 52221 : Assert(!pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, shent->key));
869 : :
870 : : /*
871 : : * Signal that the entry is dropped - this will eventually cause other
872 : : * backends to release their references.
873 : : */
874 [ - + ]: 52297 : if (shent->dropped)
456 michael@paquier.xyz 875 [ # # ]:UBC 0 : elog(ERROR,
876 : : "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%" PRIu64 " refcount=%u generation=%u",
877 : : pgstat_get_kind_info(shent->key.kind)->name,
878 : : shent->key.dboid,
879 : : shent->key.objid,
880 : : pg_atomic_read_u32(&shent->refcount),
881 : : pg_atomic_read_u32(&shent->generation));
1249 andres@anarazel.de 882 :CBC 52297 : shent->dropped = true;
883 : :
884 : : /* release refcount marking entry as not dropped */
885 [ + + ]: 52297 : if (pg_atomic_sub_fetch_u32(&shent->refcount, 1) == 0)
886 : : {
887 : 47527 : pgstat_free_entry(shent, hstat);
888 : 47527 : return true;
889 : : }
890 : : else
891 : : {
892 [ + - ]: 4770 : if (!hstat)
893 : 4770 : dshash_release_lock(pgStatLocal.shared_hash, shent);
894 : 4770 : return false;
895 : : }
896 : : }
897 : :
898 : : /*
899 : : * Drop stats for the database and all the objects inside that database.
900 : : */
901 : : static void
902 : 34 : pgstat_drop_database_and_contents(Oid dboid)
903 : : {
904 : : dshash_seq_status hstat;
905 : : PgStatShared_HashEntry *p;
906 : 34 : uint64 not_freed_count = 0;
907 : :
908 [ - + ]: 34 : Assert(OidIsValid(dboid));
909 : :
910 [ - + ]: 34 : Assert(pgStatLocal.shared_hash != NULL);
911 : :
912 : : /*
913 : : * This backend might very well be the only backend holding a reference to
914 : : * about-to-be-dropped entries. Ensure that we're not preventing it from
915 : : * being cleaned up till later.
916 : : *
917 : : * Doing this separately from the dshash iteration below avoids having to
918 : : * do so while holding a partition lock on the shared hashtable.
919 : : */
920 : 34 : pgstat_release_db_entry_refs(dboid);
921 : :
922 : : /* some of the dshash entries are to be removed, take exclusive lock. */
923 : 34 : dshash_seq_init(&hstat, pgStatLocal.shared_hash, true);
924 [ + + ]: 12843 : while ((p = dshash_seq_next(&hstat)) != NULL)
925 : : {
926 [ + + ]: 12809 : if (p->dropped)
927 : 1 : continue;
928 : :
929 [ + + ]: 12808 : if (p->key.dboid != dboid)
930 : 8566 : continue;
931 : :
932 [ - + ]: 4242 : if (!pgstat_drop_entry_internal(p, &hstat))
933 : : {
934 : : /*
935 : : * Even statistics for a dropped database might currently be
936 : : * accessed (consider e.g. database stats for pg_stat_database).
937 : : */
1249 andres@anarazel.de 938 :UBC 0 : not_freed_count++;
939 : : }
940 : : }
1249 andres@anarazel.de 941 :CBC 34 : dshash_seq_term(&hstat);
942 : :
943 : : /*
944 : : * If some of the stats data could not be freed, signal the reference
945 : : * holders to run garbage collection of their cached pgStatLocal.shmem.
946 : : */
947 [ - + ]: 34 : if (not_freed_count > 0)
1249 andres@anarazel.de 948 :UBC 0 : pgstat_request_entry_refs_gc();
1249 andres@anarazel.de 949 :CBC 34 : }
950 : :
951 : : /*
952 : : * Drop a single stats entry.
953 : : *
954 : : * This routine returns false if the stats entry of the dropped object could
955 : : * not be freed, true otherwise.
956 : : *
957 : : * The callers of this function should call pgstat_request_entry_refs_gc()
958 : : * if the stats entry could not be freed, to ensure that this entry's memory
959 : : * can be reclaimed later by a different backend calling
960 : : * pgstat_gc_entry_refs().
961 : : */
962 : : bool
353 michael@paquier.xyz 963 : 68360 : pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
964 : : {
965 : : PgStat_HashKey key;
966 : : PgStatShared_HashEntry *shent;
1249 andres@anarazel.de 967 : 68360 : bool freed = true;
968 : :
969 : : /* clear padding */
305 michael@paquier.xyz 970 : 68360 : memset(&key, 0, sizeof(struct PgStat_HashKey));
971 : :
972 : 68360 : key.kind = kind;
973 : 68360 : key.dboid = dboid;
974 : 68360 : key.objid = objid;
975 : :
976 : : /* delete local reference */
1249 andres@anarazel.de 977 [ + + ]: 68360 : if (pgStatEntryRefHash)
978 : : {
979 : : PgStat_EntryRefHashEntry *lohashent =
841 tgl@sss.pgh.pa.us 980 : 65168 : pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, key);
981 : :
1249 andres@anarazel.de 982 [ + + ]: 65168 : if (lohashent)
983 : 47862 : pgstat_release_entry_ref(lohashent->key, lohashent->entry_ref,
984 : : true);
985 : : }
986 : :
987 : : /* mark entry in shared hashtable as deleted, drop if possible */
988 : 68360 : shent = dshash_find(pgStatLocal.shared_hash, &key, true);
989 [ + + ]: 68360 : if (shent)
990 : : {
991 : 47999 : freed = pgstat_drop_entry_internal(shent, NULL);
992 : :
993 : : /*
994 : : * Database stats contain other stats. Drop those as well when
995 : : * dropping the database. XXX: Perhaps this should be done in a
996 : : * slightly more principled way? But not obvious what that'd look
997 : : * like, and so far this is the only case...
998 : : */
999 [ + + ]: 47999 : if (key.kind == PGSTAT_KIND_DATABASE)
1000 : 34 : pgstat_drop_database_and_contents(key.dboid);
1001 : : }
1002 : :
1003 : 68360 : return freed;
1004 : : }
1005 : :
1006 : : /*
1007 : : * Scan through the shared hashtable of stats, dropping statistics if
1008 : : * approved by the optional do_drop() function.
1009 : : */
1010 : : void
218 michael@paquier.xyz 1011 : 222 : pgstat_drop_matching_entries(bool (*do_drop) (PgStatShared_HashEntry *, Datum),
1012 : : Datum match_data)
1013 : : {
1014 : : dshash_seq_status hstat;
1015 : : PgStatShared_HashEntry *ps;
1249 andres@anarazel.de 1016 : 222 : uint64 not_freed_count = 0;
1017 : :
1018 : : /* entries are removed, take an exclusive lock */
1239 1019 : 222 : dshash_seq_init(&hstat, pgStatLocal.shared_hash, true);
1249 1020 [ + + ]: 278 : while ((ps = dshash_seq_next(&hstat)) != NULL)
1021 : : {
1022 [ - + ]: 56 : if (ps->dropped)
1249 andres@anarazel.de 1023 :UBC 0 : continue;
1024 : :
218 michael@paquier.xyz 1025 [ - + - - ]:CBC 56 : if (do_drop != NULL && !do_drop(ps, match_data))
218 michael@paquier.xyz 1026 :UBC 0 : continue;
1027 : :
1028 : : /* delete local reference */
218 michael@paquier.xyz 1029 [ - + ]:CBC 56 : if (pgStatEntryRefHash)
1030 : : {
1031 : : PgStat_EntryRefHashEntry *lohashent =
218 michael@paquier.xyz 1032 :UBC 0 : pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, ps->key);
1033 : :
1034 [ # # ]: 0 : if (lohashent)
1035 : 0 : pgstat_release_entry_ref(lohashent->key, lohashent->entry_ref,
1036 : : true);
1037 : : }
1038 : :
1249 andres@anarazel.de 1039 [ - + ]:CBC 56 : if (!pgstat_drop_entry_internal(ps, &hstat))
1249 andres@anarazel.de 1040 :UBC 0 : not_freed_count++;
1041 : : }
1249 andres@anarazel.de 1042 :CBC 222 : dshash_seq_term(&hstat);
1043 : :
1044 [ - + ]: 222 : if (not_freed_count > 0)
1249 andres@anarazel.de 1045 :UBC 0 : pgstat_request_entry_refs_gc();
1249 andres@anarazel.de 1046 :CBC 222 : }
1047 : :
1048 : : /*
1049 : : * Scan through the shared hashtable of stats and drop all entries.
1050 : : */
1051 : : void
218 michael@paquier.xyz 1052 : 222 : pgstat_drop_all_entries(void)
1053 : : {
1054 : 222 : pgstat_drop_matching_entries(NULL, 0);
1055 : 222 : }
1056 : :
1057 : : static void
1249 andres@anarazel.de 1058 : 8513 : shared_stat_reset_contents(PgStat_Kind kind, PgStatShared_Common *header,
1059 : : TimestampTz ts)
1060 : : {
1061 : 8513 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1062 : :
1063 : 8513 : memset(pgstat_get_entry_data(kind, header), 0,
1064 : : pgstat_get_entry_len(kind));
1065 : :
1066 [ + + ]: 8513 : if (kind_info->reset_timestamp_cb)
1067 : 193 : kind_info->reset_timestamp_cb(header, ts);
1068 : 8513 : }
1069 : :
1070 : : /*
1071 : : * Reset one variable-numbered stats entry.
1072 : : */
1073 : : void
353 michael@paquier.xyz 1074 : 185 : pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts)
1075 : : {
1076 : : PgStat_EntryRef *entry_ref;
1077 : :
1249 andres@anarazel.de 1078 [ - + ]: 185 : Assert(!pgstat_get_kind_info(kind)->fixed_amount);
1079 : :
353 michael@paquier.xyz 1080 : 185 : entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1249 andres@anarazel.de 1081 [ + + - + ]: 185 : if (!entry_ref || entry_ref->shared_entry->dropped)
1082 : 1 : return;
1083 : :
1244 tgl@sss.pgh.pa.us 1084 : 184 : (void) pgstat_lock_entry(entry_ref, false);
1249 andres@anarazel.de 1085 : 184 : shared_stat_reset_contents(kind, entry_ref->shared_stats, ts);
1086 : 184 : pgstat_unlock_entry(entry_ref);
1087 : : }
1088 : :
1089 : : /*
1090 : : * Scan through the shared hashtable of stats, resetting statistics if
1091 : : * approved by the provided do_reset() function.
1092 : : */
1093 : : void
1094 : 11 : pgstat_reset_matching_entries(bool (*do_reset) (PgStatShared_HashEntry *, Datum),
1095 : : Datum match_data, TimestampTz ts)
1096 : : {
1097 : : dshash_seq_status hstat;
1098 : : PgStatShared_HashEntry *p;
1099 : :
1100 : : /* dshash entry is not modified, take shared lock */
1101 : 11 : dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
1102 [ + + ]: 12169 : while ((p = dshash_seq_next(&hstat)) != NULL)
1103 : : {
1104 : : PgStatShared_Common *header;
1105 : :
1106 [ + + ]: 12158 : if (p->dropped)
1107 : 1 : continue;
1108 : :
1109 [ + + ]: 12157 : if (!do_reset(p, match_data))
1110 : 3828 : continue;
1111 : :
1112 : 8329 : header = dsa_get_address(pgStatLocal.dsa, p->body);
1113 : :
1114 : 8329 : LWLockAcquire(&header->lock, LW_EXCLUSIVE);
1115 : :
1116 : 8329 : shared_stat_reset_contents(p->key.kind, header, ts);
1117 : :
1118 : 8329 : LWLockRelease(&header->lock);
1119 : : }
1120 : 11 : dshash_seq_term(&hstat);
1121 : 11 : }
1122 : :
1123 : : static bool
1124 : 1482 : match_kind(PgStatShared_HashEntry *p, Datum match_data)
1125 : : {
1126 : 1482 : return p->key.kind == DatumGetInt32(match_data);
1127 : : }
1128 : :
1129 : : void
1130 : 4 : pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts)
1131 : : {
1132 : 4 : pgstat_reset_matching_entries(match_kind, Int32GetDatum(kind), ts);
1133 : 4 : }
1134 : :
1135 : : static void
1136 : 3092771 : pgstat_setup_memcxt(void)
1137 : : {
1138 [ + + ]: 3092771 : if (unlikely(!pgStatSharedRefContext))
1139 : 15585 : pgStatSharedRefContext =
1086 1140 : 15585 : AllocSetContextCreate(TopMemoryContext,
1141 : : "PgStat Shared Ref",
1142 : : ALLOCSET_SMALL_SIZES);
1249 1143 [ + + ]: 3092771 : if (unlikely(!pgStatEntryRefHashContext))
1144 : 15585 : pgStatEntryRefHashContext =
1086 1145 : 15585 : AllocSetContextCreate(TopMemoryContext,
1146 : : "PgStat Shared Ref Hash",
1147 : : ALLOCSET_SMALL_SIZES);
1249 1148 : 3092771 : }
|