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