11
11
* This statistics kind uses a proc number as object ID for the hash table
12
12
* of pgstats. Entries are created each time a process is spawned, and are
13
13
* 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.
15
17
*
16
18
* Copyright (c) 2001-2025, PostgreSQL Global Development Group
17
19
*
22
24
23
25
#include "postgres.h"
24
26
27
+ #include "storage/bufmgr.h"
28
+ #include "utils/memutils.h"
25
29
#include "utils/pgstat_internal.h"
26
30
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
+
27
70
/*
28
71
* Returns statistics of a backend by proc number.
29
72
*/
@@ -46,14 +89,21 @@ static void
46
89
pgstat_flush_backend_entry_io (PgStat_EntryRef * entry_ref )
47
90
{
48
91
PgStatShared_Backend * shbackendent ;
49
- PgStat_BackendPending * pendingent ;
50
92
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 ;
52
103
53
104
shbackendent = (PgStatShared_Backend * ) entry_ref -> shared_stats ;
54
- pendingent = (PgStat_BackendPending * ) entry_ref -> pending ;
55
105
bktype_shstats = & shbackendent -> stats .io_stats ;
56
- pending_io = & pendingent -> pending_io ;
106
+ pending_io = PendingBackendStats . pending_io ;
57
107
58
108
for (int io_object = 0 ; io_object < IOOBJECT_NUM_TYPES ; io_object ++ )
59
109
{
@@ -64,68 +114,74 @@ pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
64
114
instr_time time ;
65
115
66
116
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 ];
68
118
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 ];
72
121
73
122
bktype_shstats -> times [io_object ][io_context ][io_op ] +=
74
123
INSTR_TIME_GET_MICROSEC (time );
75
124
}
76
125
}
77
126
}
127
+
128
+ /*
129
+ * Clear out the statistics buffer, so it can be re-used.
130
+ */
131
+ MemSet (& PendingBackendStats .pending_io , 0 , sizeof (PgStat_PendingIO ));
78
132
}
79
133
80
134
/*
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.
82
139
*/
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 )
86
142
{
143
+ PgStat_EntryRef * entry_ref ;
144
+
87
145
if (!pgstat_tracks_backend_bktype (MyBackendType ))
88
146
return false;
89
147
90
- if (!pgstat_lock_entry (entry_ref , nowait ))
148
+ if (pg_memory_is_all_zeros (& PendingBackendStats ,
149
+ sizeof (struct PgStat_BackendPending )))
91
150
return false;
92
151
152
+ entry_ref = pgstat_get_entry_ref_locked (PGSTAT_KIND_BACKEND , InvalidOid ,
153
+ MyProcNumber , nowait );
154
+ if (!entry_ref )
155
+ return true;
156
+
93
157
/* Flush requested statistics */
94
158
if (flags & PGSTAT_BACKEND_FLUSH_IO )
95
159
pgstat_flush_backend_entry_io (entry_ref );
96
160
97
161
pgstat_unlock_entry (entry_ref );
98
162
99
- return true ;
163
+ return false ;
100
164
}
101
165
102
166
/*
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.
106
168
*/
107
169
bool
108
- pgstat_backend_flush_cb ( PgStat_EntryRef * entry_ref , bool nowait )
170
+ pgstat_backend_have_pending_cb ( void )
109
171
{
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 )));
111
174
}
112
175
113
176
/*
114
- * Flush out locally pending backend statistics
177
+ * Callback to flush out locally pending backend statistics.
115
178
*
116
- * "flags" parameter controls which statistics to flush .
179
+ * If some stats could not be flushed due to lock contention, return true .
117
180
*/
118
- void
119
- pgstat_flush_backend (bool nowait , bits32 flags )
181
+ bool
182
+ pgstat_backend_flush_cb (bool nowait )
120
183
{
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 );
129
185
}
130
186
131
187
/*
@@ -137,30 +193,18 @@ pgstat_create_backend(ProcNumber procnum)
137
193
PgStat_EntryRef * entry_ref ;
138
194
PgStatShared_Backend * shstatent ;
139
195
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);
143
198
shstatent = (PgStatShared_Backend * ) entry_ref -> shared_stats ;
144
199
145
200
/*
146
201
* NB: need to accept that there might be stats from an older backend,
147
202
* e.g. if we previously used this proc number.
148
203
*/
149
204
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 );
162
206
163
- return entry_ref -> pending ;
207
+ MemSet ( & PendingBackendStats , 0 , sizeof ( PgStat_BackendPending )) ;
164
208
}
165
209
166
210
/*
0 commit comments