Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4feba03

Browse files
committed
Rework handling of pending data for backend statistics
9aea73f has added support for backend statistics, relying on PgStat_EntryRef->pending for its data pending for flush. This design lacks in flexibility, because the pending list does some memory allocation, making it unsuitable if incrementing counters in critical sections. Pending data of backend statistics is reworked so the implementation does not depend on PgStat_EntryRef->pending anymore, relying on a static area of memory to store the counters that are flushed when stats are reported to the pgstats dshash. An advantage of this approach is to allow the pending data to be manipulated in critical sections; some patches are under discussion and require that. The pending data is tracked by PendingBackendStats, local to pgstat_backend.c. Two routines are introduced to allow IO statistics to update the backend-side counters. have_static_pending_cb and flush_static_cb are used for the flush, instead of flush_pending_cb. Author: Bertrand Drouvot, Michael Paquier Discussion: https://postgr.es/m/66efowskppsns35v5u2m7k4sdnl7yoz5bo64tdjwq7r5lhplrz@y7dme5xwh2r5
1 parent 28de66c commit 4feba03

File tree

6 files changed

+116
-73
lines changed

6 files changed

+116
-73
lines changed

src/backend/utils/activity/pgstat.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,9 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
370370
.shared_size = sizeof(PgStatShared_Backend),
371371
.shared_data_off = offsetof(PgStatShared_Backend, stats),
372372
.shared_data_len = sizeof(((PgStatShared_Backend *) 0)->stats),
373-
.pending_size = sizeof(PgStat_BackendPending),
374373

375-
.flush_pending_cb = pgstat_backend_flush_cb,
374+
.have_static_pending_cb = pgstat_backend_have_pending_cb,
375+
.flush_static_cb = pgstat_backend_flush_cb,
376376
.reset_timestamp_cb = pgstat_backend_reset_timestamp_cb,
377377
},
378378

src/backend/utils/activity/pgstat_backend.c

Lines changed: 92 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
* This statistics kind uses a proc number as object ID for the hash table
1212
* of pgstats. Entries are created each time a process is spawned, and are
1313
* dropped when the process exits. These are not written to the pgstats file
14-
* on disk.
14+
* on disk. Pending statistics are managed without direct interactions with
15+
* PgStat_EntryRef->pending, relying on PendingBackendStats instead so as it
16+
* is possible to report data within critical sections.
1517
*
1618
* Copyright (c) 2001-2025, PostgreSQL Global Development Group
1719
*
@@ -22,8 +24,49 @@
2224

2325
#include "postgres.h"
2426

27+
#include "storage/bufmgr.h"
28+
#include "utils/memutils.h"
2529
#include "utils/pgstat_internal.h"
2630

31+
/*
32+
* Backend statistics counts waiting to be flushed out. These counters may be
33+
* reported within critical sections so we use static memory in order to avoid
34+
* memory allocation.
35+
*/
36+
static PgStat_BackendPending PendingBackendStats;
37+
38+
/*
39+
* Utility routines to report I/O stats for backends, kept here to avoid
40+
* exposing PendingBackendStats to the outside world.
41+
*/
42+
void
43+
pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
44+
IOOp io_op, instr_time io_time)
45+
{
46+
Assert(track_io_timing);
47+
48+
if (!pgstat_tracks_backend_bktype(MyBackendType))
49+
return;
50+
51+
Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
52+
53+
INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
54+
io_time);
55+
}
56+
57+
void
58+
pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
59+
IOOp io_op, uint32 cnt, uint64 bytes)
60+
{
61+
if (!pgstat_tracks_backend_bktype(MyBackendType))
62+
return;
63+
64+
Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
65+
66+
PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
67+
PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
68+
}
69+
2770
/*
2871
* Returns statistics of a backend by proc number.
2972
*/
@@ -46,14 +89,21 @@ static void
4689
pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
4790
{
4891
PgStatShared_Backend *shbackendent;
49-
PgStat_BackendPending *pendingent;
5092
PgStat_BktypeIO *bktype_shstats;
51-
PgStat_PendingIO *pending_io;
93+
PgStat_PendingIO pending_io;
94+
95+
/*
96+
* This function can be called even if nothing at all has happened for IO
97+
* statistics. In this case, avoid unnecessarily modifying the stats
98+
* entry.
99+
*/
100+
if (pg_memory_is_all_zeros(&PendingBackendStats.pending_io,
101+
sizeof(struct PgStat_PendingIO)))
102+
return;
52103

53104
shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
54-
pendingent = (PgStat_BackendPending *) entry_ref->pending;
55105
bktype_shstats = &shbackendent->stats.io_stats;
56-
pending_io = &pendingent->pending_io;
106+
pending_io = PendingBackendStats.pending_io;
57107

58108
for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
59109
{
@@ -64,68 +114,74 @@ pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
64114
instr_time time;
65115

66116
bktype_shstats->counts[io_object][io_context][io_op] +=
67-
pending_io->counts[io_object][io_context][io_op];
117+
pending_io.counts[io_object][io_context][io_op];
68118
bktype_shstats->bytes[io_object][io_context][io_op] +=
69-
pending_io->bytes[io_object][io_context][io_op];
70-
71-
time = pending_io->pending_times[io_object][io_context][io_op];
119+
pending_io.bytes[io_object][io_context][io_op];
120+
time = pending_io.pending_times[io_object][io_context][io_op];
72121

73122
bktype_shstats->times[io_object][io_context][io_op] +=
74123
INSTR_TIME_GET_MICROSEC(time);
75124
}
76125
}
77126
}
127+
128+
/*
129+
* Clear out the statistics buffer, so it can be re-used.
130+
*/
131+
MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
78132
}
79133

80134
/*
81-
* Wrapper routine to flush backend statistics.
135+
* Flush out locally pending backend statistics
136+
*
137+
* "flags" parameter controls which statistics to flush. Returns true
138+
* if some statistics could not be flushed due to lock contention.
82139
*/
83-
static bool
84-
pgstat_flush_backend_entry(PgStat_EntryRef *entry_ref, bool nowait,
85-
bits32 flags)
140+
bool
141+
pgstat_flush_backend(bool nowait, bits32 flags)
86142
{
143+
PgStat_EntryRef *entry_ref;
144+
87145
if (!pgstat_tracks_backend_bktype(MyBackendType))
88146
return false;
89147

90-
if (!pgstat_lock_entry(entry_ref, nowait))
148+
if (pg_memory_is_all_zeros(&PendingBackendStats,
149+
sizeof(struct PgStat_BackendPending)))
91150
return false;
92151

152+
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
153+
MyProcNumber, nowait);
154+
if (!entry_ref)
155+
return true;
156+
93157
/* Flush requested statistics */
94158
if (flags & PGSTAT_BACKEND_FLUSH_IO)
95159
pgstat_flush_backend_entry_io(entry_ref);
96160

97161
pgstat_unlock_entry(entry_ref);
98162

99-
return true;
163+
return false;
100164
}
101165

102166
/*
103-
* Callback to flush out locally pending backend statistics.
104-
*
105-
* If no stats have been recorded, this function returns false.
167+
* Check if there are any backend stats waiting for flush.
106168
*/
107169
bool
108-
pgstat_backend_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
170+
pgstat_backend_have_pending_cb(void)
109171
{
110-
return pgstat_flush_backend_entry(entry_ref, nowait, PGSTAT_BACKEND_FLUSH_ALL);
172+
return (!pg_memory_is_all_zeros(&PendingBackendStats,
173+
sizeof(struct PgStat_BackendPending)));
111174
}
112175

113176
/*
114-
* Flush out locally pending backend statistics
177+
* Callback to flush out locally pending backend statistics.
115178
*
116-
* "flags" parameter controls which statistics to flush.
179+
* If some stats could not be flushed due to lock contention, return true.
117180
*/
118-
void
119-
pgstat_flush_backend(bool nowait, bits32 flags)
181+
bool
182+
pgstat_backend_flush_cb(bool nowait)
120183
{
121-
PgStat_EntryRef *entry_ref;
122-
123-
if (!pgstat_tracks_backend_bktype(MyBackendType))
124-
return;
125-
126-
entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_BACKEND, InvalidOid,
127-
MyProcNumber, false, NULL);
128-
(void) pgstat_flush_backend_entry(entry_ref, nowait, flags);
184+
return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
129185
}
130186

131187
/*
@@ -137,30 +193,18 @@ pgstat_create_backend(ProcNumber procnum)
137193
PgStat_EntryRef *entry_ref;
138194
PgStatShared_Backend *shstatent;
139195

140-
entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_BACKEND, InvalidOid,
141-
procnum, NULL);
142-
196+
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
197+
MyProcNumber, false);
143198
shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
144199

145200
/*
146201
* NB: need to accept that there might be stats from an older backend,
147202
* e.g. if we previously used this proc number.
148203
*/
149204
memset(&shstatent->stats, 0, sizeof(shstatent->stats));
150-
}
151-
152-
/*
153-
* Find or create a local PgStat_BackendPending entry for proc number.
154-
*/
155-
PgStat_BackendPending *
156-
pgstat_prep_backend_pending(ProcNumber procnum)
157-
{
158-
PgStat_EntryRef *entry_ref;
159-
160-
entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_BACKEND, InvalidOid,
161-
procnum, NULL);
205+
pgstat_unlock_entry(entry_ref);
162206

163-
return entry_ref->pending;
207+
MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
164208
}
165209

166210
/*

src/backend/utils/activity/pgstat_io.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,12 @@ pgstat_count_io_op(IOObject io_object, IOContext io_context, IOOp io_op,
7373
Assert(pgstat_is_ioop_tracked_in_bytes(io_op) || bytes == 0);
7474
Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
7575

76-
if (pgstat_tracks_backend_bktype(MyBackendType))
77-
{
78-
PgStat_BackendPending *entry_ref;
79-
80-
entry_ref = pgstat_prep_backend_pending(MyProcNumber);
81-
entry_ref->pending_io.counts[io_object][io_context][io_op] += cnt;
82-
entry_ref->pending_io.bytes[io_object][io_context][io_op] += bytes;
83-
}
84-
8576
PendingIOStats.counts[io_object][io_context][io_op] += cnt;
8677
PendingIOStats.bytes[io_object][io_context][io_op] += bytes;
8778

79+
/* Add the per-backend counts */
80+
pgstat_count_backend_io_op(io_object, io_context, io_op, cnt, bytes);
81+
8882
have_iostats = true;
8983
}
9084

@@ -145,14 +139,9 @@ pgstat_count_io_op_time(IOObject io_object, IOContext io_context, IOOp io_op,
145139
INSTR_TIME_ADD(PendingIOStats.pending_times[io_object][io_context][io_op],
146140
io_time);
147141

148-
if (pgstat_tracks_backend_bktype(MyBackendType))
149-
{
150-
PgStat_BackendPending *entry_ref;
151-
152-
entry_ref = pgstat_prep_backend_pending(MyProcNumber);
153-
INSTR_TIME_ADD(entry_ref->pending_io.pending_times[io_object][io_context][io_op],
154-
io_time);
155-
}
142+
/* Add the per-backend count */
143+
pgstat_count_backend_io_op_time(io_object, io_context, io_op,
144+
io_time);
156145
}
157146

158147
pgstat_count_io_op(io_object, io_context, io_op, cnt, bytes);

src/backend/utils/activity/pgstat_relation.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
264264
* VACUUM command has processed all tables and committed.
265265
*/
266266
pgstat_flush_io(false);
267-
pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
267+
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
268268
}
269269

270270
/*
@@ -351,7 +351,7 @@ pgstat_report_analyze(Relation rel,
351351

352352
/* see pgstat_report_vacuum() */
353353
pgstat_flush_io(false);
354-
pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
354+
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
355355
}
356356

357357
/*

src/include/pgstat.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,15 @@ extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
540540
* Functions in pgstat_backend.c
541541
*/
542542

543+
/* used by pgstat_io.c for I/O stats tracked in backends */
544+
extern void pgstat_count_backend_io_op_time(IOObject io_object,
545+
IOContext io_context,
546+
IOOp io_op,
547+
instr_time io_time);
548+
extern void pgstat_count_backend_io_op(IOObject io_object,
549+
IOContext io_context,
550+
IOOp io_op, uint32 cnt,
551+
uint64 bytes);
543552
extern PgStat_Backend *pgstat_fetch_stat_backend(ProcNumber procNumber);
544553
extern bool pgstat_tracks_backend_bktype(BackendType bktype);
545554
extern void pgstat_create_backend(ProcNumber procnum);

src/include/utils/pgstat_internal.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,11 @@ extern void pgstat_archiver_snapshot_cb(void);
624624
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
625625
#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO)
626626

627-
extern void pgstat_flush_backend(bool nowait, bits32 flags);
628-
extern PgStat_BackendPending *pgstat_prep_backend_pending(ProcNumber procnum);
629-
extern bool pgstat_backend_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
630-
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);
627+
extern bool pgstat_flush_backend(bool nowait, bits32 flags);
628+
extern bool pgstat_backend_flush_cb(bool nowait);
629+
extern bool pgstat_backend_have_pending_cb(void);
630+
extern void pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header,
631+
TimestampTz ts);
631632

632633
/*
633634
* Functions in pgstat_bgwriter.c

0 commit comments

Comments
 (0)