/* -------------------------------------------------------------------------
*
* pgstat_database . c
* Implementation of database statistics .
*
* This file contains the implementation of database statistics . It is kept
* separate from pgstat . c to enforce the line between the statistics access /
* storage implementation and the details about individual types of
* statistics .
*
* Copyright ( c ) 2001 - 2025 , PostgreSQL Global Development Group
*
* IDENTIFICATION
* src / backend / utils / activity / pgstat_database . c
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "storage/procsignal.h"
#include "utils/pgstat_internal.h"
#include "utils/timestamp.h"
static bool pgstat_should_report_connstat(void );
PgStat_Counter pgStatBlockReadTime = 0 ;
PgStat_Counter pgStatBlockWriteTime = 0 ;
PgStat_Counter pgStatActiveTime = 0 ;
PgStat_Counter pgStatTransactionIdleTime = 0 ;
SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL;
static int pgStatXactCommit = 0 ;
static int pgStatXactRollback = 0 ;
static PgStat_Counter pgLastSessionReportTime = 0 ;
/*
* Remove entry for the database being dropped .
*/
void
pgstat_drop_database(Oid databaseid)
{
pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
}
/*
* Called from autovacuum . c to report startup of an autovacuum process .
* We are called before InitPostgres is done , so can ' t rely on MyDatabaseId ;
* the db OID must be passed in , instead .
*/
void
pgstat_report_autovac(Oid dboid)
{
PgStat_EntryRef *entry_ref;
PgStatShared_Database *dbentry;
/* can't get here in single user mode */
Assert(IsUnderPostmaster);
/*
* End - of - vacuum is reported instantly . Report the start the same way for
* consistency . Vacuum doesn ' t run frequently and is a long - lasting
* operation so it doesn ' t matter if we get blocked here a little .
*/
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
dboid, InvalidOid, false );
dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
dbentry->stats.last_autovac_time = GetCurrentTimestamp();
pgstat_unlock_entry(entry_ref);
}
/*
* Report a Hot Standby recovery conflict .
*/
void
pgstat_report_recovery_conflict(int reason)
{
PgStat_StatDBEntry *dbentry;
Assert(IsUnderPostmaster);
if (!pgstat_track_counts)
return ;
dbentry = pgstat_prep_database_pending(MyDatabaseId);
switch (reason)
{
case PROCSIG_RECOVERY_CONFLICT_DATABASE:
/*
* Since we drop the information about the database as soon as it
* replicates , there is no point in counting these conflicts .
*/
break ;
case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
dbentry->conflict_tablespace++;
break ;
case PROCSIG_RECOVERY_CONFLICT_LOCK:
dbentry->conflict_lock++;
break ;
case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
dbentry->conflict_snapshot++;
break ;
case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
dbentry->conflict_bufferpin++;
break ;
case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
dbentry->conflict_logicalslot++;
break ;
case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
dbentry->conflict_startup_deadlock++;
break ;
}
}
/*
* Report a detected deadlock .
*/
void
pgstat_report_deadlock(void )
{
PgStat_StatDBEntry *dbent;
if (!pgstat_track_counts)
return ;
dbent = pgstat_prep_database_pending(MyDatabaseId);
dbent->deadlocks++;
}
/*
* Allow this backend to later report checksum failures for dboid , even if in
* a critical section at the time of the report .
*
* Without this function having been called first , the backend might need to
* allocate an EntryRef or might need to map in DSM segments . Neither should
* happen in a critical section .
*/
void
pgstat_prepare_report_checksum_failure(Oid dboid)
{
Assert(!CritSectionCount);
/*
* Just need to ensure this backend has an entry ref for the database .
* That will allows us to report checksum failures without e . g . needing to
* map in DSM segments .
*/
pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
true , NULL);
}
/*
* Report one or more checksum failures .
*
* To be allowed to report checksum failures in critical sections , we require
* pgstat_prepare_report_checksum_failure ( ) to have been called before this
* function is called .
*/
void
pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
{
PgStat_EntryRef *entry_ref;
PgStatShared_Database *sharedent;
if (!pgstat_track_counts)
return ;
/*
* Update the shared stats directly - checksum failures should never be
* common enough for that to be a problem . Note that we pass create = false
* here , as we want to be sure to not require memory allocations , so this
* can be called in critical sections .
*/
entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
false , NULL);
/*
* Should always have been created by
* pgstat_prepare_report_checksum_failure ( ) .
*
* When not using assertions , we don ' t want to crash should something have
* gone wrong , so just return .
*/
Assert(entry_ref);
if (!entry_ref)
{
elog(WARNING, "could not report %d checksum failures for database %u" ,
failurecount, dboid);
return ;
}
(void ) pgstat_lock_entry(entry_ref, false );
sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
sharedent->stats.checksum_failures += failurecount;
sharedent->stats.last_checksum_failure = GetCurrentTimestamp();
pgstat_unlock_entry(entry_ref);
}
/*
* Report creation of temporary file .
*/
void
pgstat_report_tempfile(size_t filesize)
{
PgStat_StatDBEntry *dbent;
if (!pgstat_track_counts)
return ;
dbent = pgstat_prep_database_pending(MyDatabaseId);
dbent->temp_bytes += filesize;
dbent->temp_files++;
}
/*
* Notify stats system of a new connection .
*/
void
pgstat_report_connect(Oid dboid)
{
PgStat_StatDBEntry *dbentry;
if (!pgstat_should_report_connstat())
return ;
pgLastSessionReportTime = MyStartTimestamp;
dbentry = pgstat_prep_database_pending(dboid);
dbentry->sessions++;
}
/*
* Notify the stats system of a disconnect .
*/
void
pgstat_report_disconnect(Oid dboid)
{
PgStat_StatDBEntry *dbentry;
if (!pgstat_should_report_connstat())
return ;
dbentry = pgstat_prep_database_pending(dboid);
switch (pgStatSessionEndCause)
{
case DISCONNECT_NOT_YET:
case DISCONNECT_NORMAL:
/* we don't collect these */
break ;
case DISCONNECT_CLIENT_EOF:
dbentry->sessions_abandoned++;
break ;
case DISCONNECT_FATAL:
dbentry->sessions_fatal++;
break ;
case DISCONNECT_KILLED:
dbentry->sessions_killed++;
break ;
}
}
/*
* Support function for the SQL - callable pgstat * functions . Returns
* the collected statistics for one database or NULL . NULL doesn ' t mean
* that the database doesn ' t exist , just that there are no statistics , so the
* caller is better off to report ZERO instead .
*/
PgStat_StatDBEntry *
pgstat_fetch_stat_dbentry(Oid dboid)
{
return (PgStat_StatDBEntry *)
pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
}
void
AtEOXact_PgStat_Database(bool isCommit, bool parallel)
{
/* Don't count parallel worker transaction stats */
if (!parallel)
{
/*
* Count transaction commit or abort . ( We use counters , not just
* bools , in case the reporting message isn ' t sent right away . )
*/
if (isCommit)
pgStatXactCommit++;
else
pgStatXactRollback++;
}
}
/*
* Notify the stats system about parallel worker information .
*/
void
pgstat_update_parallel_workers_stats(PgStat_Counter workers_to_launch,
PgStat_Counter workers_launched)
{
PgStat_StatDBEntry *dbentry;
if (!OidIsValid(MyDatabaseId))
return ;
dbentry = pgstat_prep_database_pending(MyDatabaseId);
dbentry->parallel_workers_to_launch += workers_to_launch;
dbentry->parallel_workers_launched += workers_launched;
}
/*
* Subroutine for pgstat_report_stat ( ) : Handle xact commit / rollback and I / O
* timings .
*/
void
pgstat_update_dbstats(TimestampTz ts)
{
PgStat_StatDBEntry *dbentry;
/*
* If not connected to a database yet , don ' t attribute time to " shared
* state " ( InvalidOid is used to track stats for shared relations , etc . ) .
*/
if (!OidIsValid(MyDatabaseId))
return ;
dbentry = pgstat_prep_database_pending(MyDatabaseId);
/*
* Accumulate xact commit / rollback and I / O timings to stats entry of the
* current database .
*/
dbentry->xact_commit += pgStatXactCommit;
dbentry->xact_rollback += pgStatXactRollback;
dbentry->blk_read_time += pgStatBlockReadTime;
dbentry->blk_write_time += pgStatBlockWriteTime;
if (pgstat_should_report_connstat())
{
long secs;
int usecs;
/*
* pgLastSessionReportTime is initialized to MyStartTimestamp by
* pgstat_report_connect ( ) .
*/
TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
pgLastSessionReportTime = ts;
dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
dbentry->active_time += pgStatActiveTime;
dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
}
pgStatXactCommit = 0 ;
pgStatXactRollback = 0 ;
pgStatBlockReadTime = 0 ;
pgStatBlockWriteTime = 0 ;
pgStatActiveTime = 0 ;
pgStatTransactionIdleTime = 0 ;
}
/*
* We report session statistics only for normal backend processes . Parallel
* workers run in parallel , so they don ' t contribute to session times , even
* though they use CPU time . Walsender processes could be considered here ,
* but they have different session characteristics from normal backends ( for
* example , they are always " active " ) , so they would skew session statistics .
*/
static bool
pgstat_should_report_connstat(void )
{
return MyBackendType == B_BACKEND;
}
/*
* Find or create a local PgStat_StatDBEntry entry for dboid .
*/
PgStat_StatDBEntry *
pgstat_prep_database_pending(Oid dboid)
{
PgStat_EntryRef *entry_ref;
/*
* This should not report stats on database objects before having
* connected to a database .
*/
Assert(!OidIsValid(dboid) || OidIsValid(MyDatabaseId));
entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
NULL);
return entry_ref->pending;
}
/*
* Reset the database ' s reset timestamp , without resetting the contents of the
* database stats .
*/
void
pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
{
PgStat_EntryRef *dbref;
PgStatShared_Database *dbentry;
dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
false );
dbentry = (PgStatShared_Database *) dbref->shared_stats;
dbentry->stats.stat_reset_timestamp = ts;
pgstat_unlock_entry(dbref);
}
/*
* Flush out pending stats for the entry
*
* If nowait is true and the lock could not be immediately acquired , returns
* false without flushing the entry . Otherwise returns true .
*/
bool
pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
{
PgStatShared_Database *sharedent;
PgStat_StatDBEntry *pendingent;
pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
if (!pgstat_lock_entry(entry_ref, nowait))
return false ;
#define PGSTAT_ACCUM_DBCOUNT(item) \
(sharedent)->stats.item += (pendingent)->item
PGSTAT_ACCUM_DBCOUNT(xact_commit);
PGSTAT_ACCUM_DBCOUNT(xact_rollback);
PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
PGSTAT_ACCUM_DBCOUNT(blocks_hit);
PGSTAT_ACCUM_DBCOUNT(tuples_returned);
PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
PGSTAT_ACCUM_DBCOUNT(tuples_updated);
PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
/* last_autovac_time is reported immediately */
Assert(pendingent->last_autovac_time == 0 );
PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
PGSTAT_ACCUM_DBCOUNT(conflict_lock);
PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
PGSTAT_ACCUM_DBCOUNT(temp_bytes);
PGSTAT_ACCUM_DBCOUNT(temp_files);
PGSTAT_ACCUM_DBCOUNT(deadlocks);
/* checksum failures are reported immediately */
Assert(pendingent->checksum_failures == 0 );
Assert(pendingent->last_checksum_failure == 0 );
PGSTAT_ACCUM_DBCOUNT(blk_read_time);
PGSTAT_ACCUM_DBCOUNT(blk_write_time);
PGSTAT_ACCUM_DBCOUNT(sessions);
PGSTAT_ACCUM_DBCOUNT(session_time);
PGSTAT_ACCUM_DBCOUNT(active_time);
PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
PGSTAT_ACCUM_DBCOUNT(sessions_killed);
PGSTAT_ACCUM_DBCOUNT(parallel_workers_to_launch);
PGSTAT_ACCUM_DBCOUNT(parallel_workers_launched);
#undef PGSTAT_ACCUM_DBCOUNT
pgstat_unlock_entry(entry_ref);
memset(pendingent, 0 , sizeof (*pendingent));
return true ;
}
void
pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
{
((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
}
Messung V0.5 in Prozent C=94 H=100 G=96
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland