Age Owner 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-2023, 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 "utils/pgstat_internal.h"
21 : #include "utils/timestamp.h"
22 : #include "storage/procsignal.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
384 andres 44 CBC 20 : pgstat_drop_database(Oid databaseid)
45 : {
368 46 20 : pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
384 47 20 : }
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
368 55 16 : 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 16 : 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 16 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
69 : dboid, InvalidOid, false);
70 :
71 16 : dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
72 16 : dbentry->stats.last_autovac_time = GetCurrentTimestamp();
73 :
74 16 : pgstat_unlock_entry(entry_ref);
75 16 : }
76 :
77 : /*
78 : * Report a Hot Standby recovery conflict.
79 : */
80 : void
384 81 12 : pgstat_report_recovery_conflict(int reason)
82 : {
83 : PgStat_StatDBEntry *dbentry;
84 :
368 85 12 : Assert(IsUnderPostmaster);
86 12 : if (!pgstat_track_counts)
384 andres 87 UBC 0 : return;
88 :
368 andres 89 CBC 12 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
90 :
91 12 : switch (reason)
92 : {
93 2 : case PROCSIG_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 : */
99 2 : break;
100 1 : case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
123 michael 101 GNC 1 : dbentry->conflict_tablespace++;
368 andres 102 CBC 1 : break;
103 1 : case PROCSIG_RECOVERY_CONFLICT_LOCK:
123 michael 104 GNC 1 : dbentry->conflict_lock++;
368 andres 105 CBC 1 : break;
106 1 : case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
123 michael 107 GNC 1 : dbentry->conflict_snapshot++;
368 andres 108 CBC 1 : break;
109 1 : case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
123 michael 110 GNC 1 : dbentry->conflict_bufferpin++;
368 andres 111 1 : break;
2 112 5 : case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
113 5 : dbentry->conflict_logicalslot++;
2 andres 114 CBC 5 : break;
368 115 1 : case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
123 michael 116 GNC 1 : dbentry->conflict_startup_deadlock++;
368 andres 117 CBC 1 : break;
368 andres 118 ECB : }
384 119 : }
120 :
121 : /*
122 : * Report a detected deadlock.
123 : */
124 : void
384 andres 125 GIC 5 : pgstat_report_deadlock(void)
126 : {
127 : PgStat_StatDBEntry *dbent;
384 andres 128 ECB :
368 andres 129 GIC 5 : if (!pgstat_track_counts)
384 andres 130 UIC 0 : return;
131 :
368 andres 132 CBC 5 : dbent = pgstat_prep_database_pending(MyDatabaseId);
123 michael 133 GNC 5 : dbent->deadlocks++;
134 : }
384 andres 135 ECB :
370 136 : /*
137 : * Report one or more checksum failures.
138 : */
139 : void
384 andres 140 GIC 2 : pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
141 : {
142 : PgStat_EntryRef *entry_ref;
368 andres 143 ECB : PgStatShared_Database *sharedent;
144 :
368 andres 145 GIC 2 : if (!pgstat_track_counts)
384 andres 146 UIC 0 : return;
147 :
368 andres 148 ECB : /*
368 andres 149 EUB : * Update the shared stats directly - checksum failures should never be
150 : * common enough for that to be a problem.
151 : */
152 : entry_ref =
368 andres 153 GIC 2 : pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, dboid, InvalidOid, false);
154 :
155 2 : sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
123 michael 156 GNC 2 : sharedent->stats.checksum_failures += failurecount;
368 andres 157 GIC 2 : sharedent->stats.last_checksum_failure = GetCurrentTimestamp();
384 andres 158 ECB :
368 andres 159 CBC 2 : pgstat_unlock_entry(entry_ref);
384 andres 160 ECB : }
161 :
370 162 : /*
163 : * Report one checksum failure in the current database.
164 : */
165 : void
384 andres 166 UIC 0 : pgstat_report_checksum_failure(void)
167 : {
168 0 : pgstat_report_checksum_failures_in_db(MyDatabaseId, 1);
384 andres 169 UBC 0 : }
170 :
370 andres 171 EUB : /*
368 172 : * Report creation of temporary file.
173 : */
174 : void
384 andres 175 GIC 3222 : pgstat_report_tempfile(size_t filesize)
176 : {
177 : PgStat_StatDBEntry *dbent;
384 andres 178 ECB :
368 andres 179 GIC 3222 : if (!pgstat_track_counts)
384 andres 180 UIC 0 : return;
181 :
368 andres 182 CBC 3222 : dbent = pgstat_prep_database_pending(MyDatabaseId);
123 michael 183 GNC 3222 : dbent->temp_bytes += filesize;
184 3222 : dbent->temp_files++;
384 andres 185 ECB : }
186 :
370 187 : /*
188 : * Notify stats system of a new connection.
189 : */
190 : void
384 andres 191 GIC 8866 : pgstat_report_connect(Oid dboid)
192 : {
193 : PgStat_StatDBEntry *dbentry;
384 andres 194 ECB :
384 andres 195 GIC 8866 : if (!pgstat_should_report_connstat())
196 1143 : return;
197 :
384 andres 198 CBC 7723 : pgLastSessionReportTime = MyStartTimestamp;
384 andres 199 ECB :
368 andres 200 GIC 7723 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
123 michael 201 GNC 7723 : dbentry->sessions++;
202 : }
384 andres 203 ECB :
370 204 : /*
205 : * Notify the stats system of a disconnect.
206 : */
207 : void
384 andres 208 GIC 10456 : pgstat_report_disconnect(Oid dboid)
209 : {
210 : PgStat_StatDBEntry *dbentry;
384 andres 211 ECB :
384 andres 212 GIC 10456 : if (!pgstat_should_report_connstat())
213 2733 : return;
214 :
368 andres 215 CBC 7723 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
368 andres 216 ECB :
368 andres 217 GIC 7723 : switch (pgStatSessionEndCause)
368 andres 218 ECB : {
368 andres 219 GIC 7684 : case DISCONNECT_NOT_YET:
368 andres 220 ECB : case DISCONNECT_NORMAL:
221 : /* we don't collect these */
368 andres 222 CBC 7684 : break;
368 andres 223 GIC 23 : case DISCONNECT_CLIENT_EOF:
123 michael 224 GNC 23 : dbentry->sessions_abandoned++;
368 andres 225 CBC 23 : break;
226 5 : case DISCONNECT_FATAL:
123 michael 227 GNC 5 : dbentry->sessions_fatal++;
368 andres 228 CBC 5 : break;
229 11 : case DISCONNECT_KILLED:
123 michael 230 GNC 11 : dbentry->sessions_killed++;
368 andres 231 CBC 11 : break;
368 andres 232 ECB : }
233 : }
234 :
235 : /*
236 : * Support function for the SQL-callable pgstat* functions. Returns
237 : * the collected statistics for one database or NULL. NULL doesn't mean
238 : * that the database doesn't exist, just that there are no statistics, so the
239 : * caller is better off to report ZERO instead.
240 : */
241 : PgStat_StatDBEntry *
368 andres 242 GIC 1396 : pgstat_fetch_stat_dbentry(Oid dboid)
243 : {
244 1396 : return (PgStat_StatDBEntry *)
368 andres 245 CBC 1396 : pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
246 : }
384 andres 247 ECB :
248 : void
384 andres 249 GIC 486199 : AtEOXact_PgStat_Database(bool isCommit, bool parallel)
250 : {
251 : /* Don't count parallel worker transaction stats */
384 andres 252 CBC 486199 : if (!parallel)
253 : {
254 : /*
384 andres 255 ECB : * Count transaction commit or abort. (We use counters, not just
256 : * bools, in case the reporting message isn't sent right away.)
257 : */
384 andres 258 GIC 484901 : if (isCommit)
259 464737 : pgStatXactCommit++;
260 : else
384 andres 261 CBC 20164 : pgStatXactRollback++;
384 andres 262 ECB : }
384 andres 263 GIC 486199 : }
384 andres 264 ECB :
265 : /*
368 266 : * Subroutine for pgstat_report_stat(): Handle xact commit/rollback and I/O
267 : * timings.
268 : */
269 : void
368 andres 270 GIC 24455 : pgstat_update_dbstats(TimestampTz ts)
271 : {
272 : PgStat_StatDBEntry *dbentry;
384 andres 273 ECB :
368 andres 274 GIC 24455 : dbentry = pgstat_prep_database_pending(MyDatabaseId);
275 :
276 : /*
368 andres 277 ECB : * Accumulate xact commit/rollback and I/O timings to stats entry of the
278 : * current database.
279 : */
123 michael 280 GNC 24455 : dbentry->xact_commit += pgStatXactCommit;
281 24455 : dbentry->xact_rollback += pgStatXactRollback;
282 24455 : dbentry->blk_read_time += pgStatBlockReadTime;
283 24455 : dbentry->blk_write_time += pgStatBlockWriteTime;
368 andres 284 ECB :
368 andres 285 CBC 24455 : if (pgstat_should_report_connstat())
384 andres 286 ECB : {
287 : long secs;
368 288 : int usecs;
289 :
290 : /*
291 : * pgLastSessionReportTime is initialized to MyStartTimestamp by
292 : * pgstat_report_connect().
293 : */
368 andres 294 GIC 16572 : TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
295 16572 : pgLastSessionReportTime = ts;
123 michael 296 GNC 16572 : dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
297 16572 : dbentry->active_time += pgStatActiveTime;
298 16572 : dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
384 andres 299 ECB : }
368 300 :
368 andres 301 CBC 24455 : pgStatXactCommit = 0;
368 andres 302 GIC 24455 : pgStatXactRollback = 0;
303 24455 : pgStatBlockReadTime = 0;
368 andres 304 CBC 24455 : pgStatBlockWriteTime = 0;
305 24455 : pgStatActiveTime = 0;
306 24455 : pgStatTransactionIdleTime = 0;
384 307 24455 : }
384 andres 308 ECB :
370 309 : /*
310 : * We report session statistics only for normal backend processes. Parallel
311 : * workers run in parallel, so they don't contribute to session times, even
312 : * though they use CPU time. Walsender processes could be considered here,
313 : * but they have different session characteristics from normal backends (for
314 : * example, they are always "active"), so they would skew session statistics.
315 : */
316 : static bool
384 andres 317 GIC 43777 : pgstat_should_report_connstat(void)
318 : {
319 43777 : return MyBackendType == B_BACKEND;
384 andres 320 ECB : }
321 :
368 322 : /*
323 : * Find or create a local PgStat_StatDBEntry entry for dboid.
324 : */
325 : PgStat_StatDBEntry *
368 andres 326 GIC 751190 : pgstat_prep_database_pending(Oid dboid)
327 : {
328 : PgStat_EntryRef *entry_ref;
368 andres 329 ECB :
368 andres 330 GIC 751190 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
331 : NULL);
332 :
368 andres 333 CBC 751190 : return entry_ref->pending;
334 : }
335 :
368 andres 336 ECB : /*
337 : * Reset the database's reset timestamp, without resetting the contents of the
338 : * database stats.
339 : */
340 : void
368 andres 341 GIC 5 : pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
342 : {
343 : PgStat_EntryRef *dbref;
368 andres 344 ECB : PgStatShared_Database *dbentry;
345 :
368 andres 346 GIC 5 : dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid,
347 : false);
348 :
368 andres 349 CBC 5 : dbentry = (PgStatShared_Database *) dbref->shared_stats;
368 andres 350 GIC 5 : dbentry->stats.stat_reset_timestamp = ts;
351 :
368 andres 352 CBC 5 : pgstat_unlock_entry(dbref);
353 5 : }
354 :
368 andres 355 ECB : /*
356 : * Flush out pending stats for the entry
357 : *
358 : * If nowait is true, this function returns false if lock could not
359 : * immediately acquired, otherwise true is returned.
360 : */
361 : bool
368 andres 362 GIC 41606 : pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
363 : {
364 : PgStatShared_Database *sharedent;
368 andres 365 ECB : PgStat_StatDBEntry *pendingent;
366 :
368 andres 367 GIC 41606 : pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
368 41606 : sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
369 :
368 andres 370 CBC 41606 : if (!pgstat_lock_entry(entry_ref, nowait))
371 2 : return false;
372 :
368 andres 373 ECB : #define PGSTAT_ACCUM_DBCOUNT(item) \
374 : (sharedent)->stats.item += (pendingent)->item
375 :
123 michael 376 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(xact_commit);
377 41604 : PGSTAT_ACCUM_DBCOUNT(xact_rollback);
378 41604 : PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
379 41604 : PGSTAT_ACCUM_DBCOUNT(blocks_hit);
368 andres 380 ECB :
123 michael 381 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(tuples_returned);
382 41604 : PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
383 41604 : PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
384 41604 : PGSTAT_ACCUM_DBCOUNT(tuples_updated);
385 41604 : PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
368 andres 386 ECB :
387 : /* last_autovac_time is reported immediately */
368 andres 388 CBC 41604 : Assert(pendingent->last_autovac_time == 0);
389 :
123 michael 390 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
391 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_lock);
392 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
2 andres 393 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
123 michael 394 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
395 41604 : PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
368 andres 396 ECB :
123 michael 397 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(temp_bytes);
398 41604 : PGSTAT_ACCUM_DBCOUNT(temp_files);
399 41604 : PGSTAT_ACCUM_DBCOUNT(deadlocks);
400 :
368 andres 401 ECB : /* checksum failures are reported immediately */
123 michael 402 GNC 41604 : Assert(pendingent->checksum_failures == 0);
368 andres 403 CBC 41604 : Assert(pendingent->last_checksum_failure == 0);
404 :
123 michael 405 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(blk_read_time);
406 41604 : PGSTAT_ACCUM_DBCOUNT(blk_write_time);
368 andres 407 ECB :
123 michael 408 GNC 41604 : PGSTAT_ACCUM_DBCOUNT(sessions);
409 41604 : PGSTAT_ACCUM_DBCOUNT(session_time);
410 41604 : PGSTAT_ACCUM_DBCOUNT(active_time);
411 41604 : PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
412 41604 : PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
413 41604 : PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
414 41604 : PGSTAT_ACCUM_DBCOUNT(sessions_killed);
368 andres 415 ECB : #undef PGSTAT_ACCUM_DBCOUNT
416 :
368 andres 417 CBC 41604 : pgstat_unlock_entry(entry_ref);
368 andres 418 ECB :
368 andres 419 GIC 41604 : memset(pendingent, 0, sizeof(*pendingent));
420 :
368 andres 421 CBC 41604 : return true;
422 : }
368 andres 423 ECB :
424 : void
368 andres 425 CBC 13 : pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
426 : {
368 andres 427 GIC 13 : ((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
428 13 : }
|