Age Owner Branch data TLA Line data Source code
1 : : /* -------------------------------------------------------------------------
2 : : *
3 : : * pgstat_database.c
4 : : * Implementation of database statistics.
5 : : *
6 : : * This file contains the implementation of database statistics. It is kept
7 : : * separate from pgstat.c to enforce the line between the statistics access /
8 : : * storage implementation and the details about individual types of
9 : : * statistics.
10 : : *
11 : : * Copyright (c) 2001-2026, PostgreSQL Global Development Group
12 : : *
13 : : * IDENTIFICATION
14 : : * src/backend/utils/activity/pgstat_database.c
15 : : * -------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include "storage/standby.h"
21 : : #include "utils/pgstat_internal.h"
22 : : #include "utils/timestamp.h"
23 : :
24 : :
25 : : static bool pgstat_should_report_connstat(void);
26 : :
27 : :
28 : : PgStat_Counter pgStatBlockReadTime = 0;
29 : : PgStat_Counter pgStatBlockWriteTime = 0;
30 : : PgStat_Counter pgStatActiveTime = 0;
31 : : PgStat_Counter pgStatTransactionIdleTime = 0;
32 : : SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL;
33 : :
34 : :
35 : : static int pgStatXactCommit = 0;
36 : : static int pgStatXactRollback = 0;
37 : : static PgStat_Counter pgLastSessionReportTime = 0;
38 : :
39 : :
40 : : /*
41 : : * Remove entry for the database being dropped.
42 : : */
43 : : void
1455 andres@anarazel.de 44 :CBC 47 : pgstat_drop_database(Oid databaseid)
45 : : {
1439 46 : 47 : pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
1455 47 : 47 : }
48 : :
49 : : /*
50 : : * Called from autovacuum.c to report startup of an autovacuum process.
51 : : * We are called before InitPostgres is done, so can't rely on MyDatabaseId;
52 : : * the db OID must be passed in, instead.
53 : : */
54 : : void
1439 55 : 38 : pgstat_report_autovac(Oid dboid)
56 : : {
57 : : PgStat_EntryRef *entry_ref;
58 : : PgStatShared_Database *dbentry;
59 : :
60 : : /* can't get here in single user mode */
61 [ - + ]: 38 : Assert(IsUnderPostmaster);
62 : :
63 : : /*
64 : : * End-of-vacuum is reported instantly. Report the start the same way for
65 : : * consistency. Vacuum doesn't run frequently and is a long-lasting
66 : : * operation so it doesn't matter if we get blocked here a little.
67 : : */
68 : 38 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
69 : : dboid, InvalidOid, false);
70 : :
71 : 38 : dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
72 : 38 : dbentry->stats.last_autovac_time = GetCurrentTimestamp();
73 : :
74 : 38 : pgstat_unlock_entry(entry_ref);
75 : 38 : }
76 : :
77 : : /*
78 : : * Report a Hot Standby recovery conflict.
79 : : */
80 : : void
1455 81 : 12 : pgstat_report_recovery_conflict(int reason)
82 : : {
83 : : PgStat_StatDBEntry *dbentry;
84 : :
1439 85 [ - + ]: 12 : Assert(IsUnderPostmaster);
86 [ - + ]: 12 : if (!pgstat_track_counts)
1455 andres@anarazel.de 87 :UBC 0 : return;
88 : :
1439 andres@anarazel.de 89 :CBC 12 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
90 : :
33 heikki.linnakangas@i 91 [ + + + + :GNC 12 : switch ((RecoveryConflictReason) reason)
+ + - +
- ]
92 : : {
93 : 2 : case RECOVERY_CONFLICT_DATABASE:
94 : :
95 : : /*
96 : : * Since we drop the information about the database as soon as it
97 : : * replicates, there is no point in counting these conflicts.
98 : : */
1439 andres@anarazel.de 99 :CBC 2 : break;
33 heikki.linnakangas@i 100 :GNC 1 : case RECOVERY_CONFLICT_TABLESPACE:
1194 michael@paquier.xyz 101 :CBC 1 : dbentry->conflict_tablespace++;
1439 andres@anarazel.de 102 : 1 : break;
33 heikki.linnakangas@i 103 :GNC 1 : case RECOVERY_CONFLICT_LOCK:
1194 michael@paquier.xyz 104 :CBC 1 : dbentry->conflict_lock++;
1439 andres@anarazel.de 105 : 1 : break;
33 heikki.linnakangas@i 106 :GNC 1 : case RECOVERY_CONFLICT_SNAPSHOT:
1194 michael@paquier.xyz 107 :CBC 1 : dbentry->conflict_snapshot++;
1439 andres@anarazel.de 108 : 1 : break;
33 heikki.linnakangas@i 109 :GNC 1 : case RECOVERY_CONFLICT_BUFFERPIN:
1194 michael@paquier.xyz 110 :CBC 1 : dbentry->conflict_bufferpin++;
1439 andres@anarazel.de 111 : 1 : break;
33 heikki.linnakangas@i 112 :GNC 5 : case RECOVERY_CONFLICT_LOGICALSLOT:
1073 andres@anarazel.de 113 :CBC 5 : dbentry->conflict_logicalslot++;
114 : 5 : break;
33 heikki.linnakangas@i 115 :UNC 0 : case RECOVERY_CONFLICT_STARTUP_DEADLOCK:
1194 michael@paquier.xyz 116 : 0 : dbentry->conflict_startup_deadlock++;
1439 andres@anarazel.de 117 : 0 : break;
33 heikki.linnakangas@i 118 :GNC 1 : case RECOVERY_CONFLICT_BUFFERPIN_DEADLOCK:
119 : :
120 : : /*
121 : : * The difference between RECOVERY_CONFLICT_STARTUP_DEADLOCK and
122 : : * RECOVERY_CONFLICT_BUFFERPIN_DEADLOCK is merely whether a buffer
123 : : * pin was part of the deadlock. We use the same counter for both
124 : : * reasons.
125 : : */
33 heikki.linnakangas@i 126 :CBC 1 : dbentry->conflict_startup_deadlock++;
127 : 1 : break;
128 : : }
129 : : }
130 : :
131 : : /*
132 : : * Report a detected deadlock.
133 : : */
134 : : void
1455 andres@anarazel.de 135 : 6 : pgstat_report_deadlock(void)
136 : : {
137 : : PgStat_StatDBEntry *dbent;
138 : :
1439 139 [ - + ]: 6 : if (!pgstat_track_counts)
1455 andres@anarazel.de 140 :UBC 0 : return;
141 : :
1439 andres@anarazel.de 142 :CBC 6 : dbent = pgstat_prep_database_pending(MyDatabaseId);
1194 michael@paquier.xyz 143 : 6 : dbent->deadlocks++;
144 : : }
145 : :
146 : : /*
147 : : * Allow this backend to later report checksum failures for dboid, even if in
148 : : * a critical section at the time of the report.
149 : : *
150 : : * Without this function having been called first, the backend might need to
151 : : * allocate an EntryRef or might need to map in DSM segments. Neither should
152 : : * happen in a critical section.
153 : : */
154 : : void
350 andres@anarazel.de 155 : 1340175 : pgstat_prepare_report_checksum_failure(Oid dboid)
156 : : {
157 [ - + ]: 1340175 : Assert(!CritSectionCount);
158 : :
159 : : /*
160 : : * Just need to ensure this backend has an entry ref for the database.
161 : : * That will allows us to report checksum failures without e.g. needing to
162 : : * map in DSM segments.
163 : : */
164 : 1340175 : pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
165 : : true, NULL);
166 : 1340175 : }
167 : :
168 : : /*
169 : : * Report one or more checksum failures.
170 : : *
171 : : * To be allowed to report checksum failures in critical sections, we require
172 : : * pgstat_prepare_report_checksum_failure() to have been called before this
173 : : * function is called.
174 : : */
175 : : void
1455 176 : 41 : pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
177 : : {
178 : : PgStat_EntryRef *entry_ref;
179 : : PgStatShared_Database *sharedent;
180 : :
1439 181 [ - + ]: 41 : if (!pgstat_track_counts)
1455 andres@anarazel.de 182 :UBC 0 : return;
183 : :
184 : : /*
185 : : * Update the shared stats directly - checksum failures should never be
186 : : * common enough for that to be a problem. Note that we pass create=false
187 : : * here, as we want to be sure to not require memory allocations, so this
188 : : * can be called in critical sections.
189 : : */
350 andres@anarazel.de 190 :CBC 41 : entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
191 : : false, NULL);
192 : :
193 : : /*
194 : : * Should always have been created by
195 : : * pgstat_prepare_report_checksum_failure().
196 : : *
197 : : * When not using assertions, we don't want to crash should something have
198 : : * gone wrong, so just return.
199 : : */
200 [ - + ]: 41 : Assert(entry_ref);
201 [ - + ]: 41 : if (!entry_ref)
202 : : {
30 michael@paquier.xyz 203 [ # # ]:UBC 0 : elog(WARNING, "could not report %d checksum failures for database %u",
204 : : failurecount, dboid);
350 andres@anarazel.de 205 : 0 : return;
206 : : }
207 : :
343 tgl@sss.pgh.pa.us 208 :CBC 41 : (void) pgstat_lock_entry(entry_ref, false);
209 : :
1439 andres@anarazel.de 210 : 41 : sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
1194 michael@paquier.xyz 211 : 41 : sharedent->stats.checksum_failures += failurecount;
1439 andres@anarazel.de 212 : 41 : sharedent->stats.last_checksum_failure = GetCurrentTimestamp();
213 : :
214 : 41 : pgstat_unlock_entry(entry_ref);
215 : : }
216 : :
217 : : /*
218 : : * Report creation of temporary file.
219 : : */
220 : : void
1455 221 : 3172 : pgstat_report_tempfile(size_t filesize)
222 : : {
223 : : PgStat_StatDBEntry *dbent;
224 : :
1439 225 [ - + ]: 3172 : if (!pgstat_track_counts)
1455 andres@anarazel.de 226 :UBC 0 : return;
227 : :
1439 andres@anarazel.de 228 :CBC 3172 : dbent = pgstat_prep_database_pending(MyDatabaseId);
1194 michael@paquier.xyz 229 : 3172 : dbent->temp_bytes += filesize;
230 : 3172 : dbent->temp_files++;
231 : : }
232 : :
233 : : /*
234 : : * Notify stats system of a new connection.
235 : : */
236 : : void
1455 andres@anarazel.de 237 : 13713 : pgstat_report_connect(Oid dboid)
238 : : {
239 : : PgStat_StatDBEntry *dbentry;
240 : :
241 [ + + ]: 13713 : if (!pgstat_should_report_connstat())
242 : 1314 : return;
243 : :
244 : 12399 : pgLastSessionReportTime = MyStartTimestamp;
245 : :
1439 246 : 12399 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
1194 michael@paquier.xyz 247 : 12399 : dbentry->sessions++;
248 : : }
249 : :
250 : : /*
251 : : * Notify the stats system of a disconnect.
252 : : */
253 : : void
1455 andres@anarazel.de 254 : 15402 : pgstat_report_disconnect(Oid dboid)
255 : : {
256 : : PgStat_StatDBEntry *dbentry;
257 : :
258 [ + + ]: 15402 : if (!pgstat_should_report_connstat())
259 : 3003 : return;
260 : :
1439 261 : 12399 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
262 : :
263 [ + + + + : 12399 : switch (pgStatSessionEndCause)
- ]
264 : : {
265 : 12340 : case DISCONNECT_NOT_YET:
266 : : case DISCONNECT_NORMAL:
267 : : /* we don't collect these */
268 : 12340 : break;
269 : 32 : case DISCONNECT_CLIENT_EOF:
1194 michael@paquier.xyz 270 : 32 : dbentry->sessions_abandoned++;
1439 andres@anarazel.de 271 : 32 : break;
272 : 11 : case DISCONNECT_FATAL:
1194 michael@paquier.xyz 273 : 11 : dbentry->sessions_fatal++;
1439 andres@anarazel.de 274 : 11 : break;
275 : 16 : case DISCONNECT_KILLED:
1194 michael@paquier.xyz 276 : 16 : dbentry->sessions_killed++;
1439 andres@anarazel.de 277 : 16 : break;
278 : : }
279 : : }
280 : :
281 : : /*
282 : : * Support function for the SQL-callable pgstat* functions. Returns
283 : : * the collected statistics for one database or NULL. NULL doesn't mean
284 : : * that the database doesn't exist, just that there are no statistics, so the
285 : : * caller is better off to report ZERO instead.
286 : : */
287 : : PgStat_StatDBEntry *
288 : 2013 : pgstat_fetch_stat_dbentry(Oid dboid)
289 : : {
290 : 2013 : return (PgStat_StatDBEntry *)
291 : 2013 : pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
292 : : }
293 : :
294 : : void
1455 295 : 338872 : AtEOXact_PgStat_Database(bool isCommit, bool parallel)
296 : : {
297 : : /* Don't count parallel worker transaction stats */
298 [ + + ]: 338872 : if (!parallel)
299 : : {
300 : : /*
301 : : * Count transaction commit or abort. (We use counters, not just
302 : : * bools, in case the reporting message isn't sent right away.)
303 : : */
304 [ + + ]: 337381 : if (isCommit)
305 : 310556 : pgStatXactCommit++;
306 : : else
307 : 26825 : pgStatXactRollback++;
308 : : }
309 : 338872 : }
310 : :
311 : : /*
312 : : * Notify the stats system about parallel worker information.
313 : : */
314 : : void
489 michael@paquier.xyz 315 : 366 : pgstat_update_parallel_workers_stats(PgStat_Counter workers_to_launch,
316 : : PgStat_Counter workers_launched)
317 : : {
318 : : PgStat_StatDBEntry *dbentry;
319 : :
320 [ - + ]: 366 : if (!OidIsValid(MyDatabaseId))
489 michael@paquier.xyz 321 :UBC 0 : return;
322 : :
489 michael@paquier.xyz 323 :CBC 366 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
324 : 366 : dbentry->parallel_workers_to_launch += workers_to_launch;
325 : 366 : dbentry->parallel_workers_launched += workers_launched;
326 : : }
327 : :
328 : : /*
329 : : * Subroutine for pgstat_report_stat(): Handle xact commit/rollback and I/O
330 : : * timings.
331 : : */
332 : : void
1439 andres@anarazel.de 333 : 36951 : pgstat_update_dbstats(TimestampTz ts)
334 : : {
335 : : PgStat_StatDBEntry *dbentry;
336 : :
337 : : /*
338 : : * If not connected to a database yet, don't attribute time to "shared
339 : : * state" (InvalidOid is used to track stats for shared relations, etc.).
340 : : */
923 michael@paquier.xyz 341 [ + + ]: 36951 : if (!OidIsValid(MyDatabaseId))
342 : 4356 : return;
343 : :
1439 andres@anarazel.de 344 : 32595 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
345 : :
346 : : /*
347 : : * Accumulate xact commit/rollback and I/O timings to stats entry of the
348 : : * current database.
349 : : */
1194 michael@paquier.xyz 350 : 32595 : dbentry->xact_commit += pgStatXactCommit;
351 : 32595 : dbentry->xact_rollback += pgStatXactRollback;
352 : 32595 : dbentry->blk_read_time += pgStatBlockReadTime;
353 : 32595 : dbentry->blk_write_time += pgStatBlockWriteTime;
354 : :
1439 andres@anarazel.de 355 [ + + ]: 32595 : if (pgstat_should_report_connstat())
356 : : {
357 : : long secs;
358 : : int usecs;
359 : :
360 : : /*
361 : : * pgLastSessionReportTime is initialized to MyStartTimestamp by
362 : : * pgstat_report_connect().
363 : : */
364 : 27200 : TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
365 : 27200 : pgLastSessionReportTime = ts;
1194 michael@paquier.xyz 366 : 27200 : dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
367 : 27200 : dbentry->active_time += pgStatActiveTime;
368 : 27200 : dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
369 : : }
370 : :
1439 andres@anarazel.de 371 : 32595 : pgStatXactCommit = 0;
372 : 32595 : pgStatXactRollback = 0;
373 : 32595 : pgStatBlockReadTime = 0;
374 : 32595 : pgStatBlockWriteTime = 0;
375 : 32595 : pgStatActiveTime = 0;
376 : 32595 : pgStatTransactionIdleTime = 0;
377 : : }
378 : :
379 : : /*
380 : : * We report session statistics only for normal backend processes. Parallel
381 : : * workers run in parallel, so they don't contribute to session times, even
382 : : * though they use CPU time. Walsender processes could be considered here,
383 : : * but they have different session characteristics from normal backends (for
384 : : * example, they are always "active"), so they would skew session statistics.
385 : : */
386 : : static bool
1455 387 : 61710 : pgstat_should_report_connstat(void)
388 : : {
389 : 61710 : return MyBackendType == B_BACKEND;
390 : : }
391 : :
392 : : /*
393 : : * Find or create a local PgStat_StatDBEntry entry for dboid.
394 : : */
395 : : PgStat_StatDBEntry *
1439 396 : 931689 : pgstat_prep_database_pending(Oid dboid)
397 : : {
398 : : PgStat_EntryRef *entry_ref;
399 : :
400 : : /*
401 : : * This should not report stats on database objects before having
402 : : * connected to a database.
403 : : */
923 michael@paquier.xyz 404 [ + + - + ]: 931689 : Assert(!OidIsValid(dboid) || OidIsValid(MyDatabaseId));
405 : :
1439 andres@anarazel.de 406 : 931689 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
407 : : NULL);
408 : :
409 : 931689 : return entry_ref->pending;
410 : : }
411 : :
412 : : /*
413 : : * Reset the database's reset timestamp, without resetting the contents of the
414 : : * database stats.
415 : : */
416 : : void
417 : 11 : pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
418 : : {
419 : : PgStat_EntryRef *dbref;
420 : : PgStatShared_Database *dbentry;
421 : :
422 : 11 : dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid,
423 : : false);
424 : :
425 : 11 : dbentry = (PgStatShared_Database *) dbref->shared_stats;
426 : 11 : dbentry->stats.stat_reset_timestamp = ts;
427 : :
428 : 11 : pgstat_unlock_entry(dbref);
429 : 11 : }
430 : :
431 : : /*
432 : : * Flush out pending stats for the entry
433 : : *
434 : : * If nowait is true and the lock could not be immediately acquired, returns
435 : : * false without flushing the entry. Otherwise returns true.
436 : : */
437 : : bool
438 : 59113 : pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
439 : : {
440 : : PgStatShared_Database *sharedent;
441 : : PgStat_StatDBEntry *pendingent;
442 : :
443 : 59113 : pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
444 : 59113 : sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
445 : :
446 [ + + ]: 59113 : if (!pgstat_lock_entry(entry_ref, nowait))
1439 andres@anarazel.de 447 :GBC 1 : return false;
448 : :
449 : : #define PGSTAT_ACCUM_DBCOUNT(item) \
450 : : (sharedent)->stats.item += (pendingent)->item
451 : :
1194 michael@paquier.xyz 452 :CBC 59112 : PGSTAT_ACCUM_DBCOUNT(xact_commit);
453 : 59112 : PGSTAT_ACCUM_DBCOUNT(xact_rollback);
454 : 59112 : PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
455 : 59112 : PGSTAT_ACCUM_DBCOUNT(blocks_hit);
456 : :
457 : 59112 : PGSTAT_ACCUM_DBCOUNT(tuples_returned);
458 : 59112 : PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
459 : 59112 : PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
460 : 59112 : PGSTAT_ACCUM_DBCOUNT(tuples_updated);
461 : 59112 : PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
462 : :
463 : : /* last_autovac_time is reported immediately */
1439 andres@anarazel.de 464 [ - + ]: 59112 : Assert(pendingent->last_autovac_time == 0);
465 : :
1194 michael@paquier.xyz 466 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
467 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_lock);
468 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
1073 andres@anarazel.de 469 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
1194 michael@paquier.xyz 470 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
471 : 59112 : PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
472 : :
473 : 59112 : PGSTAT_ACCUM_DBCOUNT(temp_bytes);
474 : 59112 : PGSTAT_ACCUM_DBCOUNT(temp_files);
475 : 59112 : PGSTAT_ACCUM_DBCOUNT(deadlocks);
476 : :
477 : : /* checksum failures are reported immediately */
478 [ - + ]: 59112 : Assert(pendingent->checksum_failures == 0);
1439 andres@anarazel.de 479 [ - + ]: 59112 : Assert(pendingent->last_checksum_failure == 0);
480 : :
1194 michael@paquier.xyz 481 : 59112 : PGSTAT_ACCUM_DBCOUNT(blk_read_time);
482 : 59112 : PGSTAT_ACCUM_DBCOUNT(blk_write_time);
483 : :
484 : 59112 : PGSTAT_ACCUM_DBCOUNT(sessions);
485 : 59112 : PGSTAT_ACCUM_DBCOUNT(session_time);
486 : 59112 : PGSTAT_ACCUM_DBCOUNT(active_time);
487 : 59112 : PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
488 : 59112 : PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
489 : 59112 : PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
490 : 59112 : PGSTAT_ACCUM_DBCOUNT(sessions_killed);
489 491 : 59112 : PGSTAT_ACCUM_DBCOUNT(parallel_workers_to_launch);
492 : 59112 : PGSTAT_ACCUM_DBCOUNT(parallel_workers_launched);
493 : : #undef PGSTAT_ACCUM_DBCOUNT
494 : :
1439 andres@anarazel.de 495 : 59112 : pgstat_unlock_entry(entry_ref);
496 : :
497 : 59112 : memset(pendingent, 0, sizeof(*pendingent));
498 : :
499 : 59112 : return true;
500 : : }
501 : :
502 : : void
503 : 13 : pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
504 : : {
505 : 13 : ((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
506 : 13 : }
|