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