Age Owner Branch data TLA Line data Source code
1 : : /* -------------------------------------------------------------------------
2 : : *
3 : : * pgstat_io.c
4 : : * Implementation of IO statistics.
5 : : *
6 : : * This file contains the implementation of IO statistics. It is kept separate
7 : : * from pgstat.c to enforce the line between the statistics access / storage
8 : : * implementation and the details about individual types of statistics.
9 : : *
10 : : * Copyright (c) 2021-2024, PostgreSQL Global Development Group
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/utils/activity/pgstat_io.c
14 : : * -------------------------------------------------------------------------
15 : : */
16 : :
17 : : #include "postgres.h"
18 : :
19 : : #include "executor/instrument.h"
20 : : #include "storage/bufmgr.h"
21 : : #include "utils/pgstat_internal.h"
22 : :
23 : :
24 : : typedef struct PgStat_PendingIO
25 : : {
26 : : PgStat_Counter counts[IOOBJECT_NUM_TYPES][IOCONTEXT_NUM_TYPES][IOOP_NUM_TYPES];
27 : : instr_time pending_times[IOOBJECT_NUM_TYPES][IOCONTEXT_NUM_TYPES][IOOP_NUM_TYPES];
28 : : } PgStat_PendingIO;
29 : :
30 : :
31 : : static PgStat_PendingIO PendingIOStats;
32 : : bool have_iostats = false;
33 : :
34 : :
35 : : /*
36 : : * Check that stats have not been counted for any combination of IOObject,
37 : : * IOContext, and IOOp which are not tracked for the passed-in BackendType. If
38 : : * stats are tracked for this combination and IO times are non-zero, counts
39 : : * should be non-zero.
40 : : *
41 : : * The passed-in PgStat_BktypeIO must contain stats from the BackendType
42 : : * specified by the second parameter. Caller is responsible for locking the
43 : : * passed-in PgStat_BktypeIO, if needed.
44 : : */
45 : : bool
431 andres@anarazel.de 46 :CBC 122639 : pgstat_bktype_io_stats_valid(PgStat_BktypeIO *backend_io,
47 : : BackendType bktype)
48 : : {
412 tgl@sss.pgh.pa.us 49 [ + + ]: 367917 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
50 : : {
51 [ + + ]: 1226390 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
52 : : {
53 [ + + ]: 8830008 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
54 : : {
55 : : /* we do track it */
373 andres@anarazel.de 56 [ + + ]: 7848896 : if (pgstat_tracks_io_op(bktype, io_object, io_context, io_op))
57 : : {
58 : : /* ensure that if IO times are non-zero, counts are > 0 */
59 [ + + ]: 2834656 : if (backend_io->times[io_object][io_context][io_op] != 0 &&
60 [ - + ]: 140 : backend_io->counts[io_object][io_context][io_op] <= 0)
373 andres@anarazel.de 61 :UBC 0 : return false;
62 : :
431 andres@anarazel.de 63 :CBC 2834656 : continue;
64 : : }
65 : :
66 : : /* we don't track it, and it is not 0 */
373 67 [ - + ]: 5014240 : if (backend_io->counts[io_object][io_context][io_op] != 0)
431 andres@anarazel.de 68 :UBC 0 : return false;
69 : : }
70 : : }
71 : : }
72 : :
431 andres@anarazel.de 73 :CBC 122639 : return true;
74 : : }
75 : :
76 : : void
77 : 51478800 : pgstat_count_io_op(IOObject io_object, IOContext io_context, IOOp io_op)
78 : : {
375 79 : 51478800 : pgstat_count_io_op_n(io_object, io_context, io_op, 1);
80 : 51478800 : }
81 : :
82 : : void
83 : 53319002 : pgstat_count_io_op_n(IOObject io_object, IOContext io_context, IOOp io_op, uint32 cnt)
84 : : {
411 tgl@sss.pgh.pa.us 85 [ - + ]: 53319002 : Assert((unsigned int) io_object < IOOBJECT_NUM_TYPES);
86 [ - + ]: 53319002 : Assert((unsigned int) io_context < IOCONTEXT_NUM_TYPES);
87 [ - + ]: 53319002 : Assert((unsigned int) io_op < IOOP_NUM_TYPES);
431 andres@anarazel.de 88 [ - + ]: 53319002 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
89 : :
373 90 : 53319002 : PendingIOStats.counts[io_object][io_context][io_op] += cnt;
91 : :
431 92 : 53319002 : have_iostats = true;
93 : 53319002 : }
94 : :
95 : : /*
96 : : * Initialize the internal timing for an IO operation, depending on an
97 : : * IO timing GUC.
98 : : */
99 : : instr_time
120 michael@paquier.xyz 100 :GNC 1840217 : pgstat_prepare_io_time(bool track_io_guc)
101 : : {
102 : : instr_time io_start;
103 : :
104 [ + + ]: 1840217 : if (track_io_guc)
373 andres@anarazel.de 105 :CBC 1 : INSTR_TIME_SET_CURRENT(io_start);
106 : : else
107 : : {
108 : : /*
109 : : * There is no need to set io_start when an IO timing GUC is disabled,
110 : : * still initialize it to zero to avoid compiler warnings.
111 : : */
112 : 1840216 : INSTR_TIME_SET_ZERO(io_start);
113 : : }
114 : :
115 : 1840217 : return io_start;
116 : : }
117 : :
118 : : /*
119 : : * Like pgstat_count_io_op_n() except it also accumulates time.
120 : : */
121 : : void
367 pg@bowt.ie 122 : 1840202 : pgstat_count_io_op_time(IOObject io_object, IOContext io_context, IOOp io_op,
123 : : instr_time start_time, uint32 cnt)
124 : : {
373 andres@anarazel.de 125 [ + + ]: 1840202 : if (track_io_timing)
126 : : {
127 : : instr_time io_time;
128 : :
129 : 1 : INSTR_TIME_SET_CURRENT(io_time);
130 : 1 : INSTR_TIME_SUBTRACT(io_time, start_time);
131 : :
179 michael@paquier.xyz 132 [ + - - + ]: 1 : if (io_op == IOOP_WRITE || io_op == IOOP_EXTEND)
133 : : {
373 andres@anarazel.de 134 :UBC 0 : pgstat_count_buffer_write_time(INSTR_TIME_GET_MICROSEC(io_time));
367 pg@bowt.ie 135 [ # # ]: 0 : if (io_object == IOOBJECT_RELATION)
178 michael@paquier.xyz 136 :UNC 0 : INSTR_TIME_ADD(pgBufferUsage.shared_blk_write_time, io_time);
137 [ # # ]: 0 : else if (io_object == IOOBJECT_TEMP_RELATION)
138 : 0 : INSTR_TIME_ADD(pgBufferUsage.local_blk_write_time, io_time);
139 : : }
373 andres@anarazel.de 140 [ + - ]:CBC 1 : else if (io_op == IOOP_READ)
141 : : {
142 : 1 : pgstat_count_buffer_read_time(INSTR_TIME_GET_MICROSEC(io_time));
367 pg@bowt.ie 143 [ + - ]: 1 : if (io_object == IOOBJECT_RELATION)
178 michael@paquier.xyz 144 :GNC 1 : INSTR_TIME_ADD(pgBufferUsage.shared_blk_read_time, io_time);
178 michael@paquier.xyz 145 [ # # ]:UNC 0 : else if (io_object == IOOBJECT_TEMP_RELATION)
146 : 0 : INSTR_TIME_ADD(pgBufferUsage.local_blk_read_time, io_time);
147 : : }
148 : :
367 pg@bowt.ie 149 :CBC 1 : INSTR_TIME_ADD(PendingIOStats.pending_times[io_object][io_context][io_op],
150 : : io_time);
151 : : }
152 : :
153 : 1840202 : pgstat_count_io_op_n(io_object, io_context, io_op, cnt);
373 andres@anarazel.de 154 : 1840202 : }
155 : :
156 : : PgStat_IO *
431 157 : 56 : pgstat_fetch_stat_io(void)
158 : : {
159 : 56 : pgstat_snapshot_fixed(PGSTAT_KIND_IO);
160 : :
161 : 56 : return &pgStatLocal.snapshot.io;
162 : : }
163 : :
164 : : /*
165 : : * Flush out locally pending IO statistics
166 : : *
167 : : * If no stats have been recorded, this function returns false.
168 : : *
169 : : * If nowait is true, this function returns true if the lock could not be
170 : : * acquired. Otherwise, return false.
171 : : */
172 : : bool
173 : 169461 : pgstat_flush_io(bool nowait)
174 : : {
175 : : LWLock *bktype_lock;
176 : : PgStat_BktypeIO *bktype_shstats;
177 : :
178 [ + + ]: 169461 : if (!have_iostats)
179 : 47698 : return false;
180 : :
181 : 121763 : bktype_lock = &pgStatLocal.shmem->io.locks[MyBackendType];
182 : 121763 : bktype_shstats =
183 : 121763 : &pgStatLocal.shmem->io.stats.stats[MyBackendType];
184 : :
185 [ + + ]: 121763 : if (!nowait)
186 : 109270 : LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
187 [ + + ]: 12493 : else if (!LWLockConditionalAcquire(bktype_lock, LW_EXCLUSIVE))
188 : 20 : return true;
189 : :
412 tgl@sss.pgh.pa.us 190 [ + + ]: 365229 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
191 : : {
192 [ + + ]: 1217430 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
193 : : {
194 [ + + ]: 8765496 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
195 : : {
196 : : instr_time time;
197 : :
373 andres@anarazel.de 198 : 7791552 : bktype_shstats->counts[io_object][io_context][io_op] +=
199 : 7791552 : PendingIOStats.counts[io_object][io_context][io_op];
200 : :
201 : 7791552 : time = PendingIOStats.pending_times[io_object][io_context][io_op];
202 : :
203 : 7791552 : bktype_shstats->times[io_object][io_context][io_op] +=
204 : 7791552 : INSTR_TIME_GET_MICROSEC(time);
205 : : }
206 : : }
207 : : }
208 : :
431 209 [ - + ]: 121743 : Assert(pgstat_bktype_io_stats_valid(bktype_shstats, MyBackendType));
210 : :
211 : 121743 : LWLockRelease(bktype_lock);
212 : :
213 : 121743 : memset(&PendingIOStats, 0, sizeof(PendingIOStats));
214 : :
215 : 121743 : have_iostats = false;
216 : :
217 : 121743 : return false;
218 : : }
219 : :
220 : : const char *
221 : 4480 : pgstat_get_io_context_name(IOContext io_context)
222 : : {
223 [ + + + + : 4480 : switch (io_context)
- ]
224 : : {
225 : 1120 : case IOCONTEXT_BULKREAD:
226 : 1120 : return "bulkread";
227 : 1120 : case IOCONTEXT_BULKWRITE:
228 : 1120 : return "bulkwrite";
229 : 1120 : case IOCONTEXT_NORMAL:
230 : 1120 : return "normal";
231 : 1120 : case IOCONTEXT_VACUUM:
232 : 1120 : return "vacuum";
233 : : }
234 : :
431 andres@anarazel.de 235 [ # # ]:UBC 0 : elog(ERROR, "unrecognized IOContext value: %d", io_context);
236 : : pg_unreachable();
237 : : }
238 : :
239 : : const char *
431 andres@anarazel.de 240 :CBC 1120 : pgstat_get_io_object_name(IOObject io_object)
241 : : {
242 [ + + - ]: 1120 : switch (io_object)
243 : : {
244 : 560 : case IOOBJECT_RELATION:
245 : 560 : return "relation";
246 : 560 : case IOOBJECT_TEMP_RELATION:
247 : 560 : return "temp relation";
248 : : }
249 : :
431 andres@anarazel.de 250 [ # # ]:UBC 0 : elog(ERROR, "unrecognized IOObject value: %d", io_object);
251 : : pg_unreachable();
252 : : }
253 : :
254 : : void
431 andres@anarazel.de 255 :CBC 246 : pgstat_io_reset_all_cb(TimestampTz ts)
256 : : {
257 [ + + ]: 4182 : for (int i = 0; i < BACKEND_NUM_TYPES; i++)
258 : : {
259 : 3936 : LWLock *bktype_lock = &pgStatLocal.shmem->io.locks[i];
260 : 3936 : PgStat_BktypeIO *bktype_shstats = &pgStatLocal.shmem->io.stats.stats[i];
261 : :
262 : 3936 : LWLockAcquire(bktype_lock, LW_EXCLUSIVE);
263 : :
264 : : /*
265 : : * Use the lock in the first BackendType's PgStat_BktypeIO to protect
266 : : * the reset timestamp as well.
267 : : */
268 [ + + ]: 3936 : if (i == 0)
269 : 246 : pgStatLocal.shmem->io.stats.stat_reset_timestamp = ts;
270 : :
271 : 3936 : memset(bktype_shstats, 0, sizeof(*bktype_shstats));
272 : 3936 : LWLockRelease(bktype_lock);
273 : : }
274 : 246 : }
275 : :
276 : : void
277 : 593 : pgstat_io_snapshot_cb(void)
278 : : {
279 [ + + ]: 10081 : for (int i = 0; i < BACKEND_NUM_TYPES; i++)
280 : : {
281 : 9488 : LWLock *bktype_lock = &pgStatLocal.shmem->io.locks[i];
282 : 9488 : PgStat_BktypeIO *bktype_shstats = &pgStatLocal.shmem->io.stats.stats[i];
283 : 9488 : PgStat_BktypeIO *bktype_snap = &pgStatLocal.snapshot.io.stats[i];
284 : :
285 : 9488 : LWLockAcquire(bktype_lock, LW_SHARED);
286 : :
287 : : /*
288 : : * Use the lock in the first BackendType's PgStat_BktypeIO to protect
289 : : * the reset timestamp as well.
290 : : */
291 [ + + ]: 9488 : if (i == 0)
292 : 593 : pgStatLocal.snapshot.io.stat_reset_timestamp =
293 : 593 : pgStatLocal.shmem->io.stats.stat_reset_timestamp;
294 : :
295 : : /* using struct assignment due to better type safety */
296 : 9488 : *bktype_snap = *bktype_shstats;
297 : 9488 : LWLockRelease(bktype_lock);
298 : : }
299 : 593 : }
300 : :
301 : : /*
302 : : * IO statistics are not collected for all BackendTypes.
303 : : *
304 : : * The following BackendTypes do not participate in the cumulative stats
305 : : * subsystem or do not perform IO on which we currently track:
306 : : * - Syslogger because it is not connected to shared memory
307 : : * - Archiver because most relevant archiving IO is delegated to a
308 : : * specialized command or module
309 : : * - WAL Receiver, WAL Writer, and WAL Summarizer IO are not tracked in
310 : : * pg_stat_io for now
311 : : *
312 : : * Function returns true if BackendType participates in the cumulative stats
313 : : * subsystem for IO and false if it does not.
314 : : *
315 : : * When adding a new BackendType, also consider adding relevant restrictions to
316 : : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
317 : : */
318 : : bool
319 : 61188954 : pgstat_tracks_io_bktype(BackendType bktype)
320 : : {
321 : : /*
322 : : * List every type so that new backend types trigger a warning about
323 : : * needing to adjust this switch.
324 : : */
325 [ + + - ]: 61188954 : switch (bktype)
326 : : {
327 : 21840 : case B_INVALID:
328 : : case B_ARCHIVER:
329 : : case B_LOGGER:
330 : : case B_WAL_RECEIVER:
331 : : case B_WAL_WRITER:
332 : : case B_WAL_SUMMARIZER:
333 : 21840 : return false;
334 : :
335 : 61167114 : case B_AUTOVAC_LAUNCHER:
336 : : case B_AUTOVAC_WORKER:
337 : : case B_BACKEND:
338 : : case B_BG_WORKER:
339 : : case B_BG_WRITER:
340 : : case B_CHECKPOINTER:
341 : : case B_SLOTSYNC_WORKER:
342 : : case B_STANDALONE_BACKEND:
343 : : case B_STARTUP:
344 : : case B_WAL_SENDER:
345 : 61167114 : return true;
346 : : }
347 : :
431 andres@anarazel.de 348 :UBC 0 : return false;
349 : : }
350 : :
351 : : /*
352 : : * Some BackendTypes do not perform IO on certain IOObjects or in certain
353 : : * IOContexts. Some IOObjects are never operated on in some IOContexts. Check
354 : : * that the given BackendType is expected to do IO in the given IOContext and
355 : : * on the given IOObject and that the given IOObject is expected to be operated
356 : : * on in the given IOContext.
357 : : */
358 : : bool
431 andres@anarazel.de 359 :CBC 61188058 : pgstat_tracks_io_object(BackendType bktype, IOObject io_object,
360 : : IOContext io_context)
361 : : {
362 : : bool no_temp_rel;
363 : :
364 : : /*
365 : : * Some BackendTypes should never track IO statistics.
366 : : */
367 [ + + ]: 61188058 : if (!pgstat_tracks_io_bktype(bktype))
368 : 21504 : return false;
369 : :
370 : : /*
371 : : * Currently, IO on temporary relations can only occur in the
372 : : * IOCONTEXT_NORMAL IOContext.
373 : : */
374 [ + + + + ]: 61166554 : if (io_context != IOCONTEXT_NORMAL &&
375 : : io_object == IOOBJECT_TEMP_RELATION)
376 : 2936952 : return false;
377 : :
378 : : /*
379 : : * In core Postgres, only regular backends and WAL Sender processes
380 : : * executing queries will use local buffers and operate on temporary
381 : : * relations. Parallel workers will not use local buffers (see
382 : : * InitLocalBuffers()); however, extensions leveraging background workers
383 : : * have no such limitation, so track IO on IOOBJECT_TEMP_RELATION for
384 : : * BackendType B_BG_WORKER.
385 : : */
386 [ + + + + ]: 58182189 : no_temp_rel = bktype == B_AUTOVAC_LAUNCHER || bktype == B_BG_WRITER ||
387 [ + + + + ]: 57777199 : bktype == B_CHECKPOINTER || bktype == B_AUTOVAC_WORKER ||
388 [ + + + + ]: 116411791 : bktype == B_STANDALONE_BACKEND || bktype == B_STARTUP;
389 : :
390 [ + + + + : 58229602 : if (no_temp_rel && io_context == IOCONTEXT_NORMAL &&
+ + ]
391 : : io_object == IOOBJECT_TEMP_RELATION)
392 : 701448 : return false;
393 : :
394 : : /*
395 : : * Some BackendTypes do not currently perform any IO in certain
396 : : * IOContexts, and, while it may not be inherently incorrect for them to
397 : : * do so, excluding those rows from the view makes the view easier to use.
398 : : */
399 [ + + + + : 57528154 : if ((bktype == B_CHECKPOINTER || bktype == B_BG_WRITER) &&
+ + ]
400 [ + + ]: 340382 : (io_context == IOCONTEXT_BULKREAD ||
401 [ + + ]: 308078 : io_context == IOCONTEXT_BULKWRITE ||
402 : : io_context == IOCONTEXT_VACUUM))
403 : 96912 : return false;
404 : :
405 [ + + + + ]: 57431242 : if (bktype == B_AUTOVAC_LAUNCHER && io_context == IOCONTEXT_VACUUM)
406 : 9000 : return false;
407 : :
408 [ + + + + : 57422242 : if ((bktype == B_AUTOVAC_WORKER || bktype == B_AUTOVAC_LAUNCHER) &&
+ + ]
409 : : io_context == IOCONTEXT_BULKWRITE)
410 : 611912 : return false;
411 : :
412 : 56810330 : return true;
413 : : }
414 : :
415 : : /*
416 : : * Some BackendTypes will never do certain IOOps and some IOOps should not
417 : : * occur in certain IOContexts or on certain IOObjects. Check that the given
418 : : * IOOp is valid for the given BackendType in the given IOContext and on the
419 : : * given IOObject. Note that there are currently no cases of an IOOp being
420 : : * invalid for a particular BackendType only within a certain IOContext and/or
421 : : * only on a certain IOObject.
422 : : */
423 : : bool
424 : 61183578 : pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
425 : : IOContext io_context, IOOp io_op)
426 : : {
427 : : bool strategy_io_context;
428 : :
429 : : /* if (io_context, io_object) will never collect stats, we're done */
430 [ + + ]: 61183578 : if (!pgstat_tracks_io_object(bktype, io_object, io_context))
431 : 4375208 : return false;
432 : :
433 : : /*
434 : : * Some BackendTypes will not do certain IOOps.
435 : : */
436 [ + + + + : 56808370 : if ((bktype == B_BG_WRITER || bktype == B_CHECKPOINTER) &&
+ + ]
381 437 [ + + + + ]: 271526 : (io_op == IOOP_READ || io_op == IOOP_EVICT || io_op == IOOP_HIT))
431 438 : 12408 : return false;
439 : :
440 [ + + + + : 56795962 : if ((bktype == B_AUTOVAC_LAUNCHER || bktype == B_BG_WRITER ||
+ + ]
441 [ + + ]: 283555 : bktype == B_CHECKPOINTER) && io_op == IOOP_EXTEND)
442 : 6484 : return false;
443 : :
444 : : /*
445 : : * Temporary tables are not logged and thus do not require fsync'ing.
446 : : * Writeback is not requested for temporary tables.
447 : : */
333 448 [ + + + + ]: 56789478 : if (io_object == IOOBJECT_TEMP_RELATION &&
449 [ + + ]: 1505646 : (io_op == IOOP_FSYNC || io_op == IOOP_WRITEBACK))
450 : 69776 : return false;
451 : :
452 : : /*
453 : : * Some IOOps are not valid in certain IOContexts and some IOOps are only
454 : : * valid in certain contexts.
455 : : */
431 456 [ + + + + ]: 56719702 : if (io_context == IOCONTEXT_BULKREAD && io_op == IOOP_EXTEND)
457 : 117553 : return false;
458 : :
459 [ + + ]: 54923627 : strategy_io_context = io_context == IOCONTEXT_BULKREAD ||
460 [ + + + + ]: 111525776 : io_context == IOCONTEXT_BULKWRITE || io_context == IOCONTEXT_VACUUM;
461 : :
462 : : /*
463 : : * IOOP_REUSE is only relevant when a BufferAccessStrategy is in use.
464 : : */
465 [ + + + + ]: 56602149 : if (!strategy_io_context && io_op == IOOP_REUSE)
466 : 157751 : return false;
467 : :
468 : : /*
469 : : * IOOP_FSYNC IOOps done by a backend using a BufferAccessStrategy are
470 : : * counted in the IOCONTEXT_NORMAL IOContext. See comment in
471 : : * register_dirty_segment() for more details.
472 : : */
473 [ + + + + ]: 56444398 : if (strategy_io_context && io_op == IOOP_FSYNC)
474 : 278420 : return false;
475 : :
476 : :
477 : 56165978 : return true;
478 : : }
|