summaryrefslogtreecommitdiffstats
path: root/src/include/utils/pgstat_internal.h
blob: 4b65dfef7d419689f7c255a9c3d2ce642e906475 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
/* ----------
 * pgstat_internal.h
 *
 * Definitions for the PostgreSQL cumulative statistics system that should
 * only be needed by files implementing statistics support (rather than ones
 * reporting / querying stats).
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * src/include/utils/pgstat_internal.h
 * ----------
 */
#ifndef PGSTAT_INTERNAL_H
#define PGSTAT_INTERNAL_H


#include "common/hashfn.h"
#include "lib/dshash.h"
#include "lib/ilist.h"
#include "pgstat.h"
#include "storage/lwlock.h"
#include "utils/dsa.h"


/*
 * Types related to shared memory storage of statistics.
 *
 * Per-object statistics are stored in the "shared stats" hashtable. That
 * table's entries (PgStatShared_HashEntry) contain a pointer to the actual stats
 * data for the object (the size of the stats data varies depending on the
 * kind of stats). The table is keyed by PgStat_HashKey.
 *
 * Once a backend has a reference to a shared stats entry, it increments the
 * entry's refcount. Even after stats data is dropped (e.g., due to a DROP
 * TABLE), the entry itself can only be deleted once all references have been
 * released.
 *
 * These refcounts, in combination with a backend local hashtable
 * (pgStatEntryRefHash, with entries pointing to PgStat_EntryRef) in front of
 * the shared hash table, mean that most stats work can happen without
 * touching the shared hash table, reducing contention.
 *
 * Once there are pending stats updates for a table PgStat_EntryRef->pending
 * is allocated to contain a working space for as-of-yet-unapplied stats
 * updates. Once the stats are flushed, PgStat_EntryRef->pending is freed.
 *
 * Each stat kind in the shared hash table has a fixed member
 * PgStatShared_Common as the first element.
 */

/* struct for shared statistics hash entry key. */
typedef struct PgStat_HashKey
{
	PgStat_Kind kind;			/* statistics entry kind */
	Oid			dboid;			/* database ID. InvalidOid for shared objects. */
	Oid			objoid;			/* object ID, either table or function. */
} PgStat_HashKey;

/*
 * Shared statistics hash entry. Doesn't itself contain any stats, but points
 * to them (with ->body). That allows the stats entries themselves to be of
 * variable size.
 */
typedef struct PgStatShared_HashEntry
{
	PgStat_HashKey key;			/* hash key */

	/*
	 * If dropped is set, backends need to release their references so that
	 * the memory for the entry can be freed. No new references may be made
	 * once marked as dropped.
	 */
	bool		dropped;

	/*
	 * Refcount managing lifetime of the entry itself (as opposed to the
	 * dshash entry pointing to it). The stats lifetime has to be separate
	 * from the hash table entry lifetime because we allow backends to point
	 * to a stats entry without holding a hash table lock (and some other
	 * reasons).
	 *
	 * As long as the entry is not dropped, 1 is added to the refcount
	 * representing that the entry should not be dropped. In addition each
	 * backend that has a reference to the entry needs to increment the
	 * refcount as long as it does.
	 *
	 * May only be incremented / decremented while holding at least a shared
	 * lock on the dshash partition containing the entry. It needs to be an
	 * atomic variable because multiple backends can increment the refcount
	 * with just a shared lock.
	 *
	 * When the refcount reaches 0 the entry needs to be freed.
	 */
	pg_atomic_uint32 refcount;

	/*
	 * Pointer to shared stats. The stats entry always starts with
	 * PgStatShared_Common, embedded in a larger struct containing the
	 * PgStat_Kind specific stats fields.
	 */
	dsa_pointer body;
} PgStatShared_HashEntry;

/*
 * Common header struct for PgStatShm_Stat*Entry.
 */
typedef struct PgStatShared_Common
{
	uint32		magic;			/* just a validity cross-check */
	/* lock protecting stats contents (i.e. data following the header) */
	LWLock		lock;
} PgStatShared_Common;

/*
 * A backend local reference to a shared stats entry. As long as at least one
 * such reference exists, the shared stats entry will not be released.
 *
 * If there are pending stats update to the shared stats, these are stored in
 * ->pending.
 */
typedef struct PgStat_EntryRef
{
	/*
	 * Pointer to the PgStatShared_HashEntry entry in the shared stats
	 * hashtable.
	 */
	PgStatShared_HashEntry *shared_entry;

	/*
	 * Pointer to the stats data (i.e. PgStatShared_HashEntry->body), resolved
	 * as a local pointer, to avoid repeated dsa_get_address() calls.
	 */
	PgStatShared_Common *shared_stats;

	/*
	 * Pending statistics data that will need to be flushed to shared memory
	 * stats eventually. Each stats kind utilizing pending data defines what
	 * format its pending data has and needs to provide a
	 * PgStat_KindInfo->flush_pending_cb callback to merge pending into shared
	 * stats.
	 */
	void	   *pending;
	dlist_node	pending_node;	/* membership in pgStatPending list */
} PgStat_EntryRef;


/*
 * Some stats changes are transactional. To maintain those, a stack of
 * PgStat_SubXactStatus entries is maintained, which contain data pertaining
 * to the current transaction and its active subtransactions.
 */
typedef struct PgStat_SubXactStatus
{
	int			nest_level;		/* subtransaction nest level */

	struct PgStat_SubXactStatus *prev;	/* higher-level subxact if any */

	/*
	 * Statistics for transactionally dropped objects need to be
	 * transactionally dropped as well. Collect the stats dropped in the
	 * current (sub-)transaction and only execute the stats drop when we know
	 * if the transaction commits/aborts. To handle replicas and crashes,
	 * stats drops are included in commit / abort records.
	 */
	dlist_head	pending_drops;
	int			pending_drops_count;

	/*
	 * Tuple insertion/deletion counts for an open transaction can't be
	 * propagated into PgStat_TableStatus counters until we know if it is
	 * going to commit or abort.  Hence, we keep these counts in per-subxact
	 * structs that live in TopTransactionContext.  This data structure is
	 * designed on the assumption that subxacts won't usually modify very many
	 * tables.
	 */
	PgStat_TableXactStatus *first;	/* head of list for this subxact */
} PgStat_SubXactStatus;


/*
 * Metadata for a specific kind of statistics.
 */
typedef struct PgStat_KindInfo
{
	/*
	 * Do a fixed number of stats objects exist for this kind of stats (e.g.
	 * bgwriter stats) or not (e.g. tables).
	 */
	bool		fixed_amount:1;

	/*
	 * Can stats of this kind be accessed from another database? Determines
	 * whether a stats object gets included in stats snapshots.
	 */
	bool		accessed_across_databases:1;

	/*
	 * For variable-numbered stats: Identified on-disk using a name, rather
	 * than PgStat_HashKey. Probably only needed for replication slot stats.
	 */
	bool		named_on_disk:1;

	/*
	 * The size of an entry in the shared stats hash table (pointed to by
	 * PgStatShared_HashEntry->body).
	 */
	uint32		shared_size;

	/*
	 * The offset/size of statistics inside the shared stats entry. Used when
	 * [de-]serializing statistics to / from disk respectively. Separate from
	 * shared_size because [de-]serialization may not include in-memory state
	 * like lwlocks.
	 */
	uint32		shared_data_off;
	uint32		shared_data_len;

	/*
	 * The size of the pending data for this kind. E.g. how large
	 * PgStat_EntryRef->pending is. Used for allocations.
	 *
	 * 0 signals that an entry of this kind should never have a pending entry.
	 */
	uint32		pending_size;

	/*
	 * For variable-numbered stats: flush pending stats. Required if pending
	 * data is used.
	 */
	bool		(*flush_pending_cb) (PgStat_EntryRef *sr, bool nowait);

	/*
	 * For variable-numbered stats: delete pending stats. Optional.
	 */
	void		(*delete_pending_cb) (PgStat_EntryRef *sr);

	/*
	 * For variable-numbered stats: reset the reset timestamp. Optional.
	 */
	void		(*reset_timestamp_cb) (PgStatShared_Common *header, TimestampTz ts);

	/*
	 * For variable-numbered stats with named_on_disk. Optional.
	 */
	void		(*to_serialized_name) (const PgStat_HashKey *key,
									   const PgStatShared_Common *header, NameData *name);
	bool		(*from_serialized_name) (const NameData *name, PgStat_HashKey *key);

	/*
	 * For fixed-numbered statistics: Reset All.
	 */
	void		(*reset_all_cb) (TimestampTz ts);

	/*
	 * For fixed-numbered statistics: Build snapshot for entry
	 */
	void		(*snapshot_cb) (void);

	/* name of the kind of stats */
	const char *const name;
} PgStat_KindInfo;


/*
 * List of SLRU names that we keep stats for.  There is no central registry of
 * SLRUs, so we use this fixed list instead.  The "other" entry is used for
 * all SLRUs without an explicit entry (e.g. SLRUs in extensions).
 *
 * This is only defined here so that SLRU_NUM_ELEMENTS is known for later type
 * definitions.
 */
static const char *const slru_names[] = {
	"CommitTs",
	"MultiXactMember",
	"MultiXactOffset",
	"Notify",
	"Serial",
	"Subtrans",
	"Xact",
	"other"						/* has to be last */
};

#define SLRU_NUM_ELEMENTS	lengthof(slru_names)


/* ----------
 * Types and definitions for different kinds of fixed-amount stats.
 *
 * Single-writer stats use the changecount mechanism to achieve low-overhead
 * writes - they're obviously more performance critical than reads. Check the
 * definition of struct PgBackendStatus for some explanation of the
 * changecount mechanism.
 *
 * Because the obvious implementation of resetting single-writer stats isn't
 * compatible with that (another backend needs to write), we don't scribble on
 * shared stats while resetting. Instead, just record the current counter
 * values in a copy of the stats data, which is protected by ->lock. See
 * pgstat_fetch_stat_(archiver|bgwriter|checkpointer) for the reader side.
 *
 * The only exception to that is the stat_reset_timestamp in these structs,
 * which is protected by ->lock, because it has to be written by another
 * backend while resetting.
 * ----------
 */

typedef struct PgStatShared_Archiver
{
	/* lock protects ->reset_offset as well as stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_ArchiverStats stats;
	PgStat_ArchiverStats reset_offset;
} PgStatShared_Archiver;

typedef struct PgStatShared_BgWriter
{
	/* lock protects ->reset_offset as well as stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_BgWriterStats stats;
	PgStat_BgWriterStats reset_offset;
} PgStatShared_BgWriter;

typedef struct PgStatShared_Checkpointer
{
	/* lock protects ->reset_offset as well as stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_CheckpointerStats stats;
	PgStat_CheckpointerStats reset_offset;
} PgStatShared_Checkpointer;

typedef struct PgStatShared_SLRU
{
	/* lock protects ->stats */
	LWLock		lock;
	PgStat_SLRUStats stats[SLRU_NUM_ELEMENTS];
} PgStatShared_SLRU;

typedef struct PgStatShared_Wal
{
	/* lock protects ->stats */
	LWLock		lock;
	PgStat_WalStats stats;
} PgStatShared_Wal;



/* ----------
 * Types and definitions for different kinds of variable-amount stats.
 *
 * Each struct has to start with PgStatShared_Common, containing information
 * common across the different types of stats. Kind-specific data follows.
 * ----------
 */

typedef struct PgStatShared_Database
{
	PgStatShared_Common header;
	PgStat_StatDBEntry stats;
} PgStatShared_Database;

typedef struct PgStatShared_Relation
{
	PgStatShared_Common header;
	PgStat_StatTabEntry stats;
} PgStatShared_Relation;

typedef struct PgStatShared_Function
{
	PgStatShared_Common header;
	PgStat_StatFuncEntry stats;
} PgStatShared_Function;

typedef struct PgStatShared_Subscription
{
	PgStatShared_Common header;
	PgStat_StatSubEntry stats;
} PgStatShared_Subscription;

typedef struct PgStatShared_ReplSlot
{
	PgStatShared_Common header;
	PgStat_StatReplSlotEntry stats;
} PgStatShared_ReplSlot;


/*
 * Central shared memory entry for the cumulative stats system.
 *
 * Fixed amount stats, the dynamic shared memory hash table for
 * non-fixed-amount stats, as well as remaining bits and pieces are all
 * reached from here.
 */
typedef struct PgStat_ShmemControl
{
	void	   *raw_dsa_area;

	/*
	 * Stats for variable-numbered objects are kept in this shared hash table.
	 * See comment above PgStat_Kind for details.
	 */
	dshash_table_handle hash_handle;	/* shared dbstat hash */

	/* Has the stats system already been shut down? Just a debugging check. */
	bool		is_shutdown;

	/*
	 * Whenever statistics for dropped objects could not be freed - because
	 * backends still have references - the dropping backend calls
	 * pgstat_request_entry_refs_gc() incrementing this counter. Eventually
	 * that causes backends to run pgstat_gc_entry_refs(), allowing memory to
	 * be reclaimed.
	 */
	pg_atomic_uint64 gc_request_count;

	/*
	 * Stats data for fixed-numbered objects.
	 */
	PgStatShared_Archiver archiver;
	PgStatShared_BgWriter bgwriter;
	PgStatShared_Checkpointer checkpointer;
	PgStatShared_SLRU slru;
	PgStatShared_Wal wal;
} PgStat_ShmemControl;


/*
 * Cached statistics snapshot
 */
typedef struct PgStat_Snapshot
{
	PgStat_FetchConsistency mode;

	/* time at which snapshot was taken */
	TimestampTz snapshot_timestamp;

	bool		fixed_valid[PGSTAT_NUM_KINDS];

	PgStat_ArchiverStats archiver;

	PgStat_BgWriterStats bgwriter;

	PgStat_CheckpointerStats checkpointer;

	PgStat_SLRUStats slru[SLRU_NUM_ELEMENTS];

	PgStat_WalStats wal;

	/* to free snapshot in bulk */
	MemoryContext context;
	struct pgstat_snapshot_hash *stats;
} PgStat_Snapshot;


/*
 * Collection of backend-local stats state.
 */
typedef struct PgStat_LocalState
{
	PgStat_ShmemControl *shmem;
	dsa_area   *dsa;
	dshash_table *shared_hash;

	/* the current statistics snapshot */
	PgStat_Snapshot snapshot;
} PgStat_LocalState;


/*
 * Inline functions defined further below.
 */

static inline void pgstat_begin_changecount_write(uint32 *cc);
static inline void pgstat_end_changecount_write(uint32 *cc);
static inline uint32 pgstat_begin_changecount_read(uint32 *cc);
static inline bool pgstat_end_changecount_read(uint32 *cc, uint32 cc_before);

static inline void pgstat_copy_changecounted_stats(void *dst, void *src, size_t len,
												   uint32 *cc);

static inline int pgstat_cmp_hash_key(const void *a, const void *b, size_t size, void *arg);
static inline uint32 pgstat_hash_hash_key(const void *d, size_t size, void *arg);
static inline size_t pgstat_get_entry_len(PgStat_Kind kind);
static inline void *pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common *entry);


/*
 * Functions in pgstat.c
 */

extern const PgStat_KindInfo *pgstat_get_kind_info(PgStat_Kind kind);

#ifdef USE_ASSERT_CHECKING
extern void pgstat_assert_is_up(void);
#else
#define pgstat_assert_is_up() ((void)true)
#endif

extern void pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref);
extern PgStat_EntryRef *pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid, bool *created_entry);
extern PgStat_EntryRef *pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid);

extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_snapshot_fixed(PgStat_Kind kind);


/*
 * Functions in pgstat_archiver.c
 */

extern void pgstat_archiver_reset_all_cb(TimestampTz ts);
extern void pgstat_archiver_snapshot_cb(void);


/*
 * Functions in pgstat_bgwriter.c
 */

extern void pgstat_bgwriter_reset_all_cb(TimestampTz ts);
extern void pgstat_bgwriter_snapshot_cb(void);


/*
 * Functions in pgstat_checkpointer.c
 */

extern void pgstat_checkpointer_reset_all_cb(TimestampTz ts);
extern void pgstat_checkpointer_snapshot_cb(void);


/*
 * Functions in pgstat_database.c
 */

extern void pgstat_report_disconnect(Oid dboid);
extern void pgstat_update_dbstats(TimestampTz ts);
extern void AtEOXact_PgStat_Database(bool isCommit, bool parallel);

extern PgStat_StatDBEntry *pgstat_prep_database_pending(Oid dboid);
extern void pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts);
extern bool pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);


/*
 * Functions in pgstat_function.c
 */

extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);


/*
 * Functions in pgstat_relation.c
 */

extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);

extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref);


/*
 * Functions in pgstat_replslot.c
 */

extern void pgstat_replslot_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);
extern void pgstat_replslot_to_serialized_name_cb(const PgStat_HashKey *key, const PgStatShared_Common *header, NameData *name);
extern bool pgstat_replslot_from_serialized_name_cb(const NameData *name, PgStat_HashKey *key);


/*
 * Functions in pgstat_shmem.c
 */

extern void pgstat_attach_shmem(void);
extern void pgstat_detach_shmem(void);

extern PgStat_EntryRef *pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, Oid objoid,
											 bool create, bool *found);
extern bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait);
extern bool pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_unlock_entry(PgStat_EntryRef *entry_ref);
extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_drop_all_entries(void);
extern PgStat_EntryRef *pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, Oid objoid,
													bool nowait);
extern void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, Oid objoid, TimestampTz ts);
extern void pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts);
extern void pgstat_reset_matching_entries(bool (*do_reset) (PgStatShared_HashEntry *, Datum),
										  Datum match_data,
										  TimestampTz ts);

extern void pgstat_request_entry_refs_gc(void);
extern PgStatShared_Common *pgstat_init_entry(PgStat_Kind kind,
											  PgStatShared_HashEntry *shhashent);


/*
 * Functions in pgstat_slru.c
 */

extern bool pgstat_slru_flush(bool nowait);
extern void pgstat_slru_reset_all_cb(TimestampTz ts);
extern void pgstat_slru_snapshot_cb(void);


/*
 * Functions in pgstat_wal.c
 */

extern bool pgstat_flush_wal(bool nowait);
extern void pgstat_init_wal(void);
extern bool pgstat_have_pending_wal(void);

extern void pgstat_wal_reset_all_cb(TimestampTz ts);
extern void pgstat_wal_snapshot_cb(void);


/*
 * Functions in pgstat_subscription.c
 */

extern bool pgstat_subscription_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_subscription_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);

/*
 * Functions in pgstat_xact.c
 */

extern PgStat_SubXactStatus *pgstat_get_xact_stack_level(int nest_level);
extern void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid);


/*
 * Variables in pgstat.c
 */

extern PGDLLIMPORT PgStat_LocalState pgStatLocal;


/*
 * Variables in pgstat_slru.c
 */

extern PGDLLIMPORT bool have_slrustats;


/*
 * Implementation of inline functions declared above.
 */

/*
 * Helpers for changecount manipulation. See comments around struct
 * PgBackendStatus for details.
 */

static inline void
pgstat_begin_changecount_write(uint32 *cc)
{
	Assert((*cc & 1) == 0);

	START_CRIT_SECTION();
	(*cc)++;
	pg_write_barrier();
}

static inline void
pgstat_end_changecount_write(uint32 *cc)
{
	Assert((*cc & 1) == 1);

	pg_write_barrier();

	(*cc)++;

	END_CRIT_SECTION();
}

static inline uint32
pgstat_begin_changecount_read(uint32 *cc)
{
	uint32		before_cc = *cc;

	CHECK_FOR_INTERRUPTS();

	pg_read_barrier();

	return before_cc;
}

/*
 * Returns true if the read succeeded, false if it needs to be repeated.
 */
static inline bool
pgstat_end_changecount_read(uint32 *cc, uint32 before_cc)
{
	uint32		after_cc;

	pg_read_barrier();

	after_cc = *cc;

	/* was a write in progress when we started? */
	if (before_cc & 1)
		return false;

	/* did writes start and complete while we read? */
	return before_cc == after_cc;
}


/*
 * helper function for PgStat_KindInfo->snapshot_cb
 * PgStat_KindInfo->reset_all_cb callbacks.
 *
 * Copies out the specified memory area following change-count protocol.
 */
static inline void
pgstat_copy_changecounted_stats(void *dst, void *src, size_t len,
								uint32 *cc)
{
	uint32		cc_before;

	do
	{
		cc_before = pgstat_begin_changecount_read(cc);

		memcpy(dst, src, len);
	}
	while (!pgstat_end_changecount_read(cc, cc_before));
}

/* helpers for dshash / simplehash hashtables */
static inline int
pgstat_cmp_hash_key(const void *a, const void *b, size_t size, void *arg)
{
	AssertArg(size == sizeof(PgStat_HashKey) && arg == NULL);
	return memcmp(a, b, sizeof(PgStat_HashKey));
}

static inline uint32
pgstat_hash_hash_key(const void *d, size_t size, void *arg)
{
	const PgStat_HashKey *key = (PgStat_HashKey *) d;
	uint32		hash;

	AssertArg(size == sizeof(PgStat_HashKey) && arg == NULL);

	hash = murmurhash32(key->kind);
	hash = hash_combine(hash, murmurhash32(key->dboid));
	hash = hash_combine(hash, murmurhash32(key->objoid));

	return hash;
}

/*
 * The length of the data portion of a shared memory stats entry (i.e. without
 * transient data such as refcounts, lwlocks, ...).
 */
static inline size_t
pgstat_get_entry_len(PgStat_Kind kind)
{
	return pgstat_get_kind_info(kind)->shared_data_len;
}

/*
 * Returns a pointer to the data portion of a shared memory stats entry.
 */
static inline void *
pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common *entry)
{
	size_t		off = pgstat_get_kind_info(kind)->shared_data_off;

	Assert(off != 0 && off < PG_UINT32_MAX);

	return ((char *) (entry)) + off;
}

#endif							/* PGSTAT_INTERNAL_H */