LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_database.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 147 155 94.8 %
Date: 2023-06-06 10:12:12 Functions: 16 17 94.1 %
Legend: Lines: hit not hit

          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
      44          40 : pgstat_drop_database(Oid databaseid)
      45             : {
      46          40 :     pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
      47          40 : }
      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          20 : 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          20 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
      69             :                                             dboid, InvalidOid, false);
      70             : 
      71          20 :     dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
      72          20 :     dbentry->stats.last_autovac_time = GetCurrentTimestamp();
      73             : 
      74          20 :     pgstat_unlock_entry(entry_ref);
      75          20 : }
      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          10 : pgstat_report_deadlock(void)
     126             : {
     127             :     PgStat_StatDBEntry *dbent;
     128             : 
     129          10 :     if (!pgstat_track_counts)
     130           0 :         return;
     131             : 
     132          10 :     dbent = pgstat_prep_database_pending(MyDatabaseId);
     133          10 :     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        5270 : pgstat_report_tempfile(size_t filesize)
     176             : {
     177             :     PgStat_StatDBEntry *dbent;
     178             : 
     179        5270 :     if (!pgstat_track_counts)
     180           0 :         return;
     181             : 
     182        5270 :     dbent = pgstat_prep_database_pending(MyDatabaseId);
     183        5270 :     dbent->temp_bytes += filesize;
     184        5270 :     dbent->temp_files++;
     185             : }
     186             : 
     187             : /*
     188             :  * Notify stats system of a new connection.
     189             :  */
     190             : void
     191       18656 : pgstat_report_connect(Oid dboid)
     192             : {
     193             :     PgStat_StatDBEntry *dbentry;
     194             : 
     195       18656 :     if (!pgstat_should_report_connstat())
     196        2276 :         return;
     197             : 
     198       16380 :     pgLastSessionReportTime = MyStartTimestamp;
     199             : 
     200       16380 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     201       16380 :     dbentry->sessions++;
     202             : }
     203             : 
     204             : /*
     205             :  * Notify the stats system of a disconnect.
     206             :  */
     207             : void
     208       21848 : pgstat_report_disconnect(Oid dboid)
     209             : {
     210             :     PgStat_StatDBEntry *dbentry;
     211             : 
     212       21848 :     if (!pgstat_should_report_connstat())
     213        5468 :         return;
     214             : 
     215       16380 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     216             : 
     217       16380 :     switch (pgStatSessionEndCause)
     218             :     {
     219       16298 :         case DISCONNECT_NOT_YET:
     220             :         case DISCONNECT_NORMAL:
     221             :             /* we don't collect these */
     222       16298 :             break;
     223          56 :         case DISCONNECT_CLIENT_EOF:
     224          56 :             dbentry->sessions_abandoned++;
     225          56 :             break;
     226          10 :         case DISCONNECT_FATAL:
     227          10 :             dbentry->sessions_fatal++;
     228          10 :             break;
     229          16 :         case DISCONNECT_KILLED:
     230          16 :             dbentry->sessions_killed++;
     231          16 :             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        2678 : pgstat_fetch_stat_dbentry(Oid dboid)
     243             : {
     244        2678 :     return (PgStat_StatDBEntry *)
     245        2678 :         pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
     246             : }
     247             : 
     248             : void
     249      975664 : AtEOXact_PgStat_Database(bool isCommit, bool parallel)
     250             : {
     251             :     /* Don't count parallel worker transaction stats */
     252      975664 :     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      973058 :         if (isCommit)
     259      932718 :             pgStatXactCommit++;
     260             :         else
     261       40340 :             pgStatXactRollback++;
     262             :     }
     263      975664 : }
     264             : 
     265             : /*
     266             :  * Subroutine for pgstat_report_stat(): Handle xact commit/rollback and I/O
     267             :  * timings.
     268             :  */
     269             : void
     270       47630 : pgstat_update_dbstats(TimestampTz ts)
     271             : {
     272             :     PgStat_StatDBEntry *dbentry;
     273             : 
     274       47630 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     275             : 
     276             :     /*
     277             :      * Accumulate xact commit/rollback and I/O timings to stats entry of the
     278             :      * current database.
     279             :      */
     280       47630 :     dbentry->xact_commit += pgStatXactCommit;
     281       47630 :     dbentry->xact_rollback += pgStatXactRollback;
     282       47630 :     dbentry->blk_read_time += pgStatBlockReadTime;
     283       47630 :     dbentry->blk_write_time += pgStatBlockWriteTime;
     284             : 
     285       47630 :     if (pgstat_should_report_connstat())
     286             :     {
     287             :         long        secs;
     288             :         int         usecs;
     289             : 
     290             :         /*
     291             :          * pgLastSessionReportTime is initialized to MyStartTimestamp by
     292             :          * pgstat_report_connect().
     293             :          */
     294       34334 :         TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
     295       34334 :         pgLastSessionReportTime = ts;
     296       34334 :         dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
     297       34334 :         dbentry->active_time += pgStatActiveTime;
     298       34334 :         dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
     299             :     }
     300             : 
     301       47630 :     pgStatXactCommit = 0;
     302       47630 :     pgStatXactRollback = 0;
     303       47630 :     pgStatBlockReadTime = 0;
     304       47630 :     pgStatBlockWriteTime = 0;
     305       47630 :     pgStatActiveTime = 0;
     306       47630 :     pgStatTransactionIdleTime = 0;
     307       47630 : }
     308             : 
     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
     317       88134 : pgstat_should_report_connstat(void)
     318             : {
     319       88134 :     return MyBackendType == B_BACKEND;
     320             : }
     321             : 
     322             : /*
     323             :  * Find or create a local PgStat_StatDBEntry entry for dboid.
     324             :  */
     325             : PgStat_StatDBEntry *
     326     1381998 : pgstat_prep_database_pending(Oid dboid)
     327             : {
     328             :     PgStat_EntryRef *entry_ref;
     329             : 
     330     1381998 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     331             :                                           NULL);
     332             : 
     333     1381998 :     return entry_ref->pending;
     334             : }
     335             : 
     336             : /*
     337             :  * Reset the database's reset timestamp, without resetting the contents of the
     338             :  * database stats.
     339             :  */
     340             : void
     341          10 : pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
     342             : {
     343             :     PgStat_EntryRef *dbref;
     344             :     PgStatShared_Database *dbentry;
     345             : 
     346          10 :     dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid,
     347             :                                         false);
     348             : 
     349          10 :     dbentry = (PgStatShared_Database *) dbref->shared_stats;
     350          10 :     dbentry->stats.stat_reset_timestamp = ts;
     351             : 
     352          10 :     pgstat_unlock_entry(dbref);
     353          10 : }
     354             : 
     355             : /*
     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
     362       80826 : pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     363             : {
     364             :     PgStatShared_Database *sharedent;
     365             :     PgStat_StatDBEntry *pendingent;
     366             : 
     367       80826 :     pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
     368       80826 :     sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
     369             : 
     370       80826 :     if (!pgstat_lock_entry(entry_ref, nowait))
     371           0 :         return false;
     372             : 
     373             : #define PGSTAT_ACCUM_DBCOUNT(item)      \
     374             :     (sharedent)->stats.item += (pendingent)->item
     375             : 
     376       80826 :     PGSTAT_ACCUM_DBCOUNT(xact_commit);
     377       80826 :     PGSTAT_ACCUM_DBCOUNT(xact_rollback);
     378       80826 :     PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
     379       80826 :     PGSTAT_ACCUM_DBCOUNT(blocks_hit);
     380             : 
     381       80826 :     PGSTAT_ACCUM_DBCOUNT(tuples_returned);
     382       80826 :     PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
     383       80826 :     PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
     384       80826 :     PGSTAT_ACCUM_DBCOUNT(tuples_updated);
     385       80826 :     PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
     386             : 
     387             :     /* last_autovac_time is reported immediately */
     388             :     Assert(pendingent->last_autovac_time == 0);
     389             : 
     390       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
     391       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_lock);
     392       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
     393       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
     394       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
     395       80826 :     PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
     396             : 
     397       80826 :     PGSTAT_ACCUM_DBCOUNT(temp_bytes);
     398       80826 :     PGSTAT_ACCUM_DBCOUNT(temp_files);
     399       80826 :     PGSTAT_ACCUM_DBCOUNT(deadlocks);
     400             : 
     401             :     /* checksum failures are reported immediately */
     402             :     Assert(pendingent->checksum_failures == 0);
     403             :     Assert(pendingent->last_checksum_failure == 0);
     404             : 
     405       80826 :     PGSTAT_ACCUM_DBCOUNT(blk_read_time);
     406       80826 :     PGSTAT_ACCUM_DBCOUNT(blk_write_time);
     407             : 
     408       80826 :     PGSTAT_ACCUM_DBCOUNT(sessions);
     409       80826 :     PGSTAT_ACCUM_DBCOUNT(session_time);
     410       80826 :     PGSTAT_ACCUM_DBCOUNT(active_time);
     411       80826 :     PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
     412       80826 :     PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
     413       80826 :     PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
     414       80826 :     PGSTAT_ACCUM_DBCOUNT(sessions_killed);
     415             : #undef PGSTAT_ACCUM_DBCOUNT
     416             : 
     417       80826 :     pgstat_unlock_entry(entry_ref);
     418             : 
     419       80826 :     memset(pendingent, 0, sizeof(*pendingent));
     420             : 
     421       80826 :     return true;
     422             : }
     423             : 
     424             : void
     425          26 : pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     426             : {
     427          26 :     ((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
     428          26 : }

Generated by: LCOV version 1.14