LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_database.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 160 168 95.2 %
Date: 2025-04-01 15:15:16 Functions: 18 18 100.0 %
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-2025, 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          88 : pgstat_drop_database(Oid databaseid)
      45             : {
      46          88 :     pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
      47          88 : }
      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        2386 : 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        2386 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
      69             :                                             dboid, InvalidOid, false);
      70             : 
      71        2386 :     dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
      72        2386 :     dbentry->stats.last_autovac_time = GetCurrentTimestamp();
      73             : 
      74        2386 :     pgstat_unlock_entry(entry_ref);
      75        2386 : }
      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             :  * Allow this backend to later report checksum failures for dboid, even if in
     138             :  * a critical section at the time of the report.
     139             :  *
     140             :  * Without this function having been called first, the backend might need to
     141             :  * allocate an EntryRef or might need to map in DSM segments. Neither should
     142             :  * happen in a critical section.
     143             :  */
     144             : void
     145     2428600 : pgstat_prepare_report_checksum_failure(Oid dboid)
     146             : {
     147             :     Assert(!CritSectionCount);
     148             : 
     149             :     /*
     150             :      * Just need to ensure this backend has an entry ref for the database.
     151             :      * That will allows us to report checksum failures without e.g. needing to
     152             :      * map in DSM segments.
     153             :      */
     154     2428600 :     pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     155             :                          true, NULL);
     156     2428600 : }
     157             : 
     158             : /*
     159             :  * Report one or more checksum failures.
     160             :  *
     161             :  * To be allowed to report checksum failures in critical sections, we require
     162             :  * pgstat_prepare_report_checksum_failure() to have been called before this
     163             :  * function is called.
     164             :  */
     165             : void
     166           4 : pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
     167             : {
     168             :     PgStat_EntryRef *entry_ref;
     169             :     PgStatShared_Database *sharedent;
     170             : 
     171           4 :     if (!pgstat_track_counts)
     172           0 :         return;
     173             : 
     174             :     /*
     175             :      * Update the shared stats directly - checksum failures should never be
     176             :      * common enough for that to be a problem. Note that we pass create=false
     177             :      * here, as we want to be sure to not require memory allocations, so this
     178             :      * can be called in critical sections.
     179             :      */
     180           4 :     entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     181             :                                      false, NULL);
     182             : 
     183             :     /*
     184             :      * Should always have been created by
     185             :      * pgstat_prepare_report_checksum_failure().
     186             :      *
     187             :      * When not using assertions, we don't want to crash should something have
     188             :      * gone wrong, so just return.
     189             :      */
     190             :     Assert(entry_ref);
     191           4 :     if (!entry_ref)
     192             :     {
     193           0 :         elog(WARNING, "could not report %d conflicts for DB %u",
     194             :              failurecount, dboid);
     195           0 :         return;
     196             :     }
     197             : 
     198           4 :     pgstat_lock_entry(entry_ref, false);
     199             : 
     200           4 :     sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
     201           4 :     sharedent->stats.checksum_failures += failurecount;
     202           4 :     sharedent->stats.last_checksum_failure = GetCurrentTimestamp();
     203             : 
     204           4 :     pgstat_unlock_entry(entry_ref);
     205             : }
     206             : 
     207             : /*
     208             :  * Report creation of temporary file.
     209             :  */
     210             : void
     211        3686 : pgstat_report_tempfile(size_t filesize)
     212             : {
     213             :     PgStat_StatDBEntry *dbent;
     214             : 
     215        3686 :     if (!pgstat_track_counts)
     216           0 :         return;
     217             : 
     218        3686 :     dbent = pgstat_prep_database_pending(MyDatabaseId);
     219        3686 :     dbent->temp_bytes += filesize;
     220        3686 :     dbent->temp_files++;
     221             : }
     222             : 
     223             : /*
     224             :  * Notify stats system of a new connection.
     225             :  */
     226             : void
     227       26682 : pgstat_report_connect(Oid dboid)
     228             : {
     229             :     PgStat_StatDBEntry *dbentry;
     230             : 
     231       26682 :     if (!pgstat_should_report_connstat())
     232        2304 :         return;
     233             : 
     234       24378 :     pgLastSessionReportTime = MyStartTimestamp;
     235             : 
     236       24378 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     237       24378 :     dbentry->sessions++;
     238             : }
     239             : 
     240             : /*
     241             :  * Notify the stats system of a disconnect.
     242             :  */
     243             : void
     244       31836 : pgstat_report_disconnect(Oid dboid)
     245             : {
     246             :     PgStat_StatDBEntry *dbentry;
     247             : 
     248       31836 :     if (!pgstat_should_report_connstat())
     249        7458 :         return;
     250             : 
     251       24378 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     252             : 
     253       24378 :     switch (pgStatSessionEndCause)
     254             :     {
     255       24254 :         case DISCONNECT_NOT_YET:
     256             :         case DISCONNECT_NORMAL:
     257             :             /* we don't collect these */
     258       24254 :             break;
     259          74 :         case DISCONNECT_CLIENT_EOF:
     260          74 :             dbentry->sessions_abandoned++;
     261          74 :             break;
     262          24 :         case DISCONNECT_FATAL:
     263          24 :             dbentry->sessions_fatal++;
     264          24 :             break;
     265          26 :         case DISCONNECT_KILLED:
     266          26 :             dbentry->sessions_killed++;
     267          26 :             break;
     268             :     }
     269             : }
     270             : 
     271             : /*
     272             :  * Support function for the SQL-callable pgstat* functions. Returns
     273             :  * the collected statistics for one database or NULL. NULL doesn't mean
     274             :  * that the database doesn't exist, just that there are no statistics, so the
     275             :  * caller is better off to report ZERO instead.
     276             :  */
     277             : PgStat_StatDBEntry *
     278        8754 : pgstat_fetch_stat_dbentry(Oid dboid)
     279             : {
     280        8754 :     return (PgStat_StatDBEntry *)
     281        8754 :         pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
     282             : }
     283             : 
     284             : void
     285      818122 : AtEOXact_PgStat_Database(bool isCommit, bool parallel)
     286             : {
     287             :     /* Don't count parallel worker transaction stats */
     288      818122 :     if (!parallel)
     289             :     {
     290             :         /*
     291             :          * Count transaction commit or abort.  (We use counters, not just
     292             :          * bools, in case the reporting message isn't sent right away.)
     293             :          */
     294      815386 :         if (isCommit)
     295      766960 :             pgStatXactCommit++;
     296             :         else
     297       48426 :             pgStatXactRollback++;
     298             :     }
     299      818122 : }
     300             : 
     301             : /*
     302             :  * Notify the stats system about parallel worker information.
     303             :  */
     304             : void
     305         682 : pgstat_update_parallel_workers_stats(PgStat_Counter workers_to_launch,
     306             :                                      PgStat_Counter workers_launched)
     307             : {
     308             :     PgStat_StatDBEntry *dbentry;
     309             : 
     310         682 :     if (!OidIsValid(MyDatabaseId))
     311           0 :         return;
     312             : 
     313         682 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     314         682 :     dbentry->parallel_workers_to_launch += workers_to_launch;
     315         682 :     dbentry->parallel_workers_launched += workers_launched;
     316             : }
     317             : 
     318             : /*
     319             :  * Subroutine for pgstat_report_stat(): Handle xact commit/rollback and I/O
     320             :  * timings.
     321             :  */
     322             : void
     323       68690 : pgstat_update_dbstats(TimestampTz ts)
     324             : {
     325             :     PgStat_StatDBEntry *dbentry;
     326             : 
     327             :     /*
     328             :      * If not connected to a database yet, don't attribute time to "shared
     329             :      * state" (InvalidOid is used to track stats for shared relations, etc.).
     330             :      */
     331       68690 :     if (!OidIsValid(MyDatabaseId))
     332        6792 :         return;
     333             : 
     334       61898 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     335             : 
     336             :     /*
     337             :      * Accumulate xact commit/rollback and I/O timings to stats entry of the
     338             :      * current database.
     339             :      */
     340       61898 :     dbentry->xact_commit += pgStatXactCommit;
     341       61898 :     dbentry->xact_rollback += pgStatXactRollback;
     342       61898 :     dbentry->blk_read_time += pgStatBlockReadTime;
     343       61898 :     dbentry->blk_write_time += pgStatBlockWriteTime;
     344             : 
     345       61898 :     if (pgstat_should_report_connstat())
     346             :     {
     347             :         long        secs;
     348             :         int         usecs;
     349             : 
     350             :         /*
     351             :          * pgLastSessionReportTime is initialized to MyStartTimestamp by
     352             :          * pgstat_report_connect().
     353             :          */
     354       50862 :         TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
     355       50862 :         pgLastSessionReportTime = ts;
     356       50862 :         dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
     357       50862 :         dbentry->active_time += pgStatActiveTime;
     358       50862 :         dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
     359             :     }
     360             : 
     361       61898 :     pgStatXactCommit = 0;
     362       61898 :     pgStatXactRollback = 0;
     363       61898 :     pgStatBlockReadTime = 0;
     364       61898 :     pgStatBlockWriteTime = 0;
     365       61898 :     pgStatActiveTime = 0;
     366       61898 :     pgStatTransactionIdleTime = 0;
     367             : }
     368             : 
     369             : /*
     370             :  * We report session statistics only for normal backend processes.  Parallel
     371             :  * workers run in parallel, so they don't contribute to session times, even
     372             :  * though they use CPU time. Walsender processes could be considered here,
     373             :  * but they have different session characteristics from normal backends (for
     374             :  * example, they are always "active"), so they would skew session statistics.
     375             :  */
     376             : static bool
     377      120416 : pgstat_should_report_connstat(void)
     378             : {
     379      120416 :     return MyBackendType == B_BACKEND;
     380             : }
     381             : 
     382             : /*
     383             :  * Find or create a local PgStat_StatDBEntry entry for dboid.
     384             :  */
     385             : PgStat_StatDBEntry *
     386     1848554 : pgstat_prep_database_pending(Oid dboid)
     387             : {
     388             :     PgStat_EntryRef *entry_ref;
     389             : 
     390             :     /*
     391             :      * This should not report stats on database objects before having
     392             :      * connected to a database.
     393             :      */
     394             :     Assert(!OidIsValid(dboid) || OidIsValid(MyDatabaseId));
     395             : 
     396     1848554 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     397             :                                           NULL);
     398             : 
     399     1848554 :     return entry_ref->pending;
     400             : }
     401             : 
     402             : /*
     403             :  * Reset the database's reset timestamp, without resetting the contents of the
     404             :  * database stats.
     405             :  */
     406             : void
     407          16 : pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
     408             : {
     409             :     PgStat_EntryRef *dbref;
     410             :     PgStatShared_Database *dbentry;
     411             : 
     412          16 :     dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid,
     413             :                                         false);
     414             : 
     415          16 :     dbentry = (PgStatShared_Database *) dbref->shared_stats;
     416          16 :     dbentry->stats.stat_reset_timestamp = ts;
     417             : 
     418          16 :     pgstat_unlock_entry(dbref);
     419          16 : }
     420             : 
     421             : /*
     422             :  * Flush out pending stats for the entry
     423             :  *
     424             :  * If nowait is true, this function returns false if lock could not
     425             :  * immediately acquired, otherwise true is returned.
     426             :  */
     427             : bool
     428      114694 : pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     429             : {
     430             :     PgStatShared_Database *sharedent;
     431             :     PgStat_StatDBEntry *pendingent;
     432             : 
     433      114694 :     pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
     434      114694 :     sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
     435             : 
     436      114694 :     if (!pgstat_lock_entry(entry_ref, nowait))
     437           0 :         return false;
     438             : 
     439             : #define PGSTAT_ACCUM_DBCOUNT(item)      \
     440             :     (sharedent)->stats.item += (pendingent)->item
     441             : 
     442      114694 :     PGSTAT_ACCUM_DBCOUNT(xact_commit);
     443      114694 :     PGSTAT_ACCUM_DBCOUNT(xact_rollback);
     444      114694 :     PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
     445      114694 :     PGSTAT_ACCUM_DBCOUNT(blocks_hit);
     446             : 
     447      114694 :     PGSTAT_ACCUM_DBCOUNT(tuples_returned);
     448      114694 :     PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
     449      114694 :     PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
     450      114694 :     PGSTAT_ACCUM_DBCOUNT(tuples_updated);
     451      114694 :     PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
     452             : 
     453             :     /* last_autovac_time is reported immediately */
     454             :     Assert(pendingent->last_autovac_time == 0);
     455             : 
     456      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
     457      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_lock);
     458      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
     459      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
     460      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
     461      114694 :     PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
     462             : 
     463      114694 :     PGSTAT_ACCUM_DBCOUNT(temp_bytes);
     464      114694 :     PGSTAT_ACCUM_DBCOUNT(temp_files);
     465      114694 :     PGSTAT_ACCUM_DBCOUNT(deadlocks);
     466             : 
     467             :     /* checksum failures are reported immediately */
     468             :     Assert(pendingent->checksum_failures == 0);
     469             :     Assert(pendingent->last_checksum_failure == 0);
     470             : 
     471      114694 :     PGSTAT_ACCUM_DBCOUNT(blk_read_time);
     472      114694 :     PGSTAT_ACCUM_DBCOUNT(blk_write_time);
     473             : 
     474      114694 :     PGSTAT_ACCUM_DBCOUNT(sessions);
     475      114694 :     PGSTAT_ACCUM_DBCOUNT(session_time);
     476      114694 :     PGSTAT_ACCUM_DBCOUNT(active_time);
     477      114694 :     PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
     478      114694 :     PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
     479      114694 :     PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
     480      114694 :     PGSTAT_ACCUM_DBCOUNT(sessions_killed);
     481      114694 :     PGSTAT_ACCUM_DBCOUNT(parallel_workers_to_launch);
     482      114694 :     PGSTAT_ACCUM_DBCOUNT(parallel_workers_launched);
     483             : #undef PGSTAT_ACCUM_DBCOUNT
     484             : 
     485      114694 :     pgstat_unlock_entry(entry_ref);
     486             : 
     487      114694 :     memset(pendingent, 0, sizeof(*pendingent));
     488             : 
     489      114694 :     return true;
     490             : }
     491             : 
     492             : void
     493          26 : pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     494             : {
     495          26 :     ((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
     496          26 : }

Generated by: LCOV version 1.14