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