Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 960869d

Browse files
committed
Add pg_stat_database counters for sessions and session time
This add counters for number of sessions, the different kind of session termination types, and timers for how much time is spent in active vs idle in a database to pg_stat_database. Internally this also renames the parameter "force" to disconnect. This was the only use-case for the parameter before, so repurposing it to this mroe narrow usecase makes things cleaner than inventing something new. Author: Laurenz Albe Reviewed-By: Magnus Hagander, Soumyadeep Chakraborty, Masahiro Ikeda Discussion: https://postgr.es/m/b07e1f9953701b90c66ed368656f2aef40cac4fb.camel@cybertec.at
1 parent 891a1d0 commit 960869d

File tree

10 files changed

+405
-6
lines changed

10 files changed

+405
-6
lines changed

doc/src/sgml/monitoring.sgml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3737,6 +3737,83 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
37373737
</para></entry>
37383738
</row>
37393739

3740+
<row>
3741+
<entry role="catalog_table_entry"><para role="column_definition">
3742+
<structfield>session_time</structfield> <type>double precision</type>
3743+
</para>
3744+
<para>
3745+
Time spent by database sessions in this database, in milliseconds
3746+
(note that statistics are only updated when the state of a session
3747+
changes, so if sessions have been idle for a long time, this idle time
3748+
won't be included)
3749+
</para></entry>
3750+
</row>
3751+
3752+
<row>
3753+
<entry role="catalog_table_entry"><para role="column_definition">
3754+
<structfield>active_time</structfield> <type>double precision</type>
3755+
</para>
3756+
<para>
3757+
Time spent executing SQL statements in this database, in milliseconds
3758+
(this corresponds to the states <literal>active</literal> and
3759+
<literal>fastpath function call</literal> in
3760+
<link linkend="monitoring-pg-stat-activity-view">
3761+
<structname>pg_stat_activity</structname></link>)
3762+
</para></entry>
3763+
</row>
3764+
3765+
<row>
3766+
<entry role="catalog_table_entry"><para role="column_definition">
3767+
<structfield>idle_in_transaction_time</structfield> <type>double precision</type>
3768+
</para>
3769+
<para>
3770+
Time spent idling while in a transaction in this database, in milliseconds
3771+
(this corresponds to the states <literal>idle in transaction</literal> and
3772+
<literal>idle in transaction (aborted)</literal> in
3773+
<link linkend="monitoring-pg-stat-activity-view">
3774+
<structname>pg_stat_activity</structname></link>)
3775+
</para></entry>
3776+
</row>
3777+
3778+
<row>
3779+
<entry role="catalog_table_entry"><para role="column_definition">
3780+
<structfield>sessions</structfield> <type>bigint</type>
3781+
</para>
3782+
<para>
3783+
Total number of sessions established to this database
3784+
</para></entry>
3785+
</row>
3786+
3787+
<row>
3788+
<entry role="catalog_table_entry"><para role="column_definition">
3789+
<structfield>sessions_abandoned</structfield> <type>bigint</type>
3790+
</para>
3791+
<para>
3792+
Number of database sessions to this database that were terminated
3793+
because connection to the client was lost
3794+
</para></entry>
3795+
</row>
3796+
3797+
<row>
3798+
<entry role="catalog_table_entry"><para role="column_definition">
3799+
<structfield>sessions_fatal</structfield> <type>bigint</type>
3800+
</para>
3801+
<para>
3802+
Number of database sessions to this database that were terminated
3803+
by fatal errors
3804+
</para></entry>
3805+
</row>
3806+
3807+
<row>
3808+
<entry role="catalog_table_entry"><para role="column_definition">
3809+
<structfield>sessions_killed</structfield> <type>bigint</type>
3810+
</para>
3811+
<para>
3812+
Number of database sessions to this database that were terminated
3813+
by operator intervention
3814+
</para></entry>
3815+
</row>
3816+
37403817
<row>
37413818
<entry role="catalog_table_entry"><para role="column_definition">
37423819
<structfield>stats_reset</structfield> <type>timestamp with time zone</type>

src/backend/catalog/system_views.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,13 @@ CREATE VIEW pg_stat_database AS
924924
pg_stat_get_db_checksum_last_failure(D.oid) AS checksum_last_failure,
925925
pg_stat_get_db_blk_read_time(D.oid) AS blk_read_time,
926926
pg_stat_get_db_blk_write_time(D.oid) AS blk_write_time,
927+
pg_stat_get_db_session_time(D.oid) AS session_time,
928+
pg_stat_get_db_active_time(D.oid) AS active_time,
929+
pg_stat_get_db_idle_in_transaction_time(D.oid) AS idle_in_transaction_time,
930+
pg_stat_get_db_sessions(D.oid) AS sessions,
931+
pg_stat_get_db_sessions_abandoned(D.oid) AS sessions_abandoned,
932+
pg_stat_get_db_sessions_fatal(D.oid) AS sessions_fatal,
933+
pg_stat_get_db_sessions_killed(D.oid) AS sessions_killed,
927934
pg_stat_get_db_stat_reset_time(D.oid) AS stats_reset
928935
FROM (
929936
SELECT 0 AS oid, NULL::name AS datname

src/backend/postmaster/pgstat.c

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ static int pgStatXactCommit = 0;
258258
static int pgStatXactRollback = 0;
259259
PgStat_Counter pgStatBlockReadTime = 0;
260260
PgStat_Counter pgStatBlockWriteTime = 0;
261+
static PgStat_Counter pgStatActiveTime = 0;
262+
static PgStat_Counter pgStatTransactionIdleTime = 0;
263+
SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL;
261264

262265
/* Record that's written to 2PC state file when pgstat state is persisted */
263266
typedef struct TwoPhasePgStatRecord
@@ -343,6 +346,7 @@ static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
343346
static void pgstat_send_funcstats(void);
344347
static void pgstat_send_slru(void);
345348
static HTAB *pgstat_collect_oids(Oid catalogid, AttrNumber anum_oid);
349+
static void pgstat_send_connstats(bool disconnect, TimestampTz last_report);
346350

347351
static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
348352

@@ -378,6 +382,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
378382
static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
379383
static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
380384
static void pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len);
385+
static void pgstat_recv_connstat(PgStat_MsgConn *msg, int len);
381386
static void pgstat_recv_replslot(PgStat_MsgReplSlot *msg, int len);
382387
static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
383388

@@ -855,10 +860,14 @@ allow_immediate_pgstat_restart(void)
855860
* per-table and function usage statistics to the collector. Note that this
856861
* is called only when not within a transaction, so it is fair to use
857862
* transaction stop time as an approximation of current time.
863+
*
864+
* "disconnect" is "true" only for the last call before the backend
865+
* exits. This makes sure that no data is lost and that interrupted
866+
* sessions are reported correctly.
858867
* ----------
859868
*/
860869
void
861-
pgstat_report_stat(bool force)
870+
pgstat_report_stat(bool disconnect)
862871
{
863872
/* we assume this inits to all zeroes: */
864873
static const PgStat_TableCounts all_zeroes;
@@ -873,17 +882,22 @@ pgstat_report_stat(bool force)
873882
/* Don't expend a clock check if nothing to do */
874883
if ((pgStatTabList == NULL || pgStatTabList->tsa_used == 0) &&
875884
pgStatXactCommit == 0 && pgStatXactRollback == 0 &&
876-
!have_function_stats)
885+
!have_function_stats && !disconnect)
877886
return;
878887

879888
/*
880889
* Don't send a message unless it's been at least PGSTAT_STAT_INTERVAL
881-
* msec since we last sent one, or the caller wants to force stats out.
890+
* msec since we last sent one, or the backend is about to exit.
882891
*/
883892
now = GetCurrentTransactionStopTimestamp();
884-
if (!force &&
893+
if (!disconnect &&
885894
!TimestampDifferenceExceeds(last_report, now, PGSTAT_STAT_INTERVAL))
886895
return;
896+
897+
/* for backends, send connection statistics */
898+
if (MyBackendType == B_BACKEND)
899+
pgstat_send_connstats(disconnect, last_report);
900+
887901
last_report = now;
888902

889903
/*
@@ -1351,6 +1365,48 @@ pgstat_drop_relation(Oid relid)
13511365
#endif /* NOT_USED */
13521366

13531367

1368+
/* ----------
1369+
* pgstat_send_connstats() -
1370+
*
1371+
* Tell the collector about session statistics.
1372+
* The parameter "disconnect" will be true when the backend exits.
1373+
* "last_report" is the last time we were called (0 if never).
1374+
* ----------
1375+
*/
1376+
static void
1377+
pgstat_send_connstats(bool disconnect, TimestampTz last_report)
1378+
{
1379+
PgStat_MsgConn msg;
1380+
long secs;
1381+
int usecs;
1382+
1383+
if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
1384+
return;
1385+
1386+
pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_CONNECTION);
1387+
msg.m_databaseid = MyDatabaseId;
1388+
1389+
/* session time since the last report */
1390+
TimestampDifference(((last_report == 0) ? MyStartTimestamp : last_report),
1391+
GetCurrentTimestamp(),
1392+
&secs, &usecs);
1393+
msg.m_session_time = secs * 1000000 + usecs;
1394+
1395+
msg.m_disconnect = disconnect ? pgStatSessionEndCause : DISCONNECT_NOT_YET;
1396+
1397+
msg.m_active_time = pgStatActiveTime;
1398+
pgStatActiveTime = 0;
1399+
1400+
msg.m_idle_in_xact_time = pgStatTransactionIdleTime;
1401+
pgStatTransactionIdleTime = 0;
1402+
1403+
/* report a new session only the first time */
1404+
msg.m_count = (last_report == 0) ? 1 : 0;
1405+
1406+
pgstat_send(&msg, sizeof(PgStat_MsgConn));
1407+
}
1408+
1409+
13541410
/* ----------
13551411
* pgstat_reset_counters() -
13561412
*
@@ -3348,6 +3404,30 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
33483404
}
33493405
current_timestamp = GetCurrentTimestamp();
33503406

3407+
/*
3408+
* If the state has changed from "active" or "idle in transaction",
3409+
* calculate the duration.
3410+
*/
3411+
if ((beentry->st_state == STATE_RUNNING ||
3412+
beentry->st_state == STATE_FASTPATH ||
3413+
beentry->st_state == STATE_IDLEINTRANSACTION ||
3414+
beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) &&
3415+
state != beentry->st_state)
3416+
{
3417+
long secs;
3418+
int usecs;
3419+
3420+
TimestampDifference(beentry->st_state_start_timestamp,
3421+
current_timestamp,
3422+
&secs, &usecs);
3423+
3424+
if (beentry->st_state == STATE_RUNNING ||
3425+
beentry->st_state == STATE_FASTPATH)
3426+
pgStatActiveTime += secs * 1000000 + usecs;
3427+
else
3428+
pgStatTransactionIdleTime += secs * 1000000 + usecs;
3429+
}
3430+
33513431
/*
33523432
* Now update the status entry
33533433
*/
@@ -4919,6 +4999,10 @@ PgstatCollectorMain(int argc, char *argv[])
49194999
pgstat_recv_replslot(&msg.msg_replslot, len);
49205000
break;
49215001

5002+
case PGSTAT_MTYPE_CONNECTION:
5003+
pgstat_recv_connstat(&msg.msg_conn, len);
5004+
break;
5005+
49225006
default:
49235007
break;
49245008
}
@@ -4993,6 +5077,13 @@ reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
49935077
dbentry->last_checksum_failure = 0;
49945078
dbentry->n_block_read_time = 0;
49955079
dbentry->n_block_write_time = 0;
5080+
dbentry->n_sessions = 0;
5081+
dbentry->total_session_time = 0;
5082+
dbentry->total_active_time = 0;
5083+
dbentry->total_idle_in_xact_time = 0;
5084+
dbentry->n_sessions_abandoned = 0;
5085+
dbentry->n_sessions_fatal = 0;
5086+
dbentry->n_sessions_killed = 0;
49965087

49975088
dbentry->stat_reset_timestamp = GetCurrentTimestamp();
49985089
dbentry->stats_timestamp = 0;
@@ -6944,6 +7035,41 @@ pgstat_recv_replslot(PgStat_MsgReplSlot *msg, int len)
69447035
}
69457036
}
69467037

7038+
/* ----------
7039+
* pgstat_recv_connstat() -
7040+
*
7041+
* Process connection information.
7042+
* ----------
7043+
*/
7044+
static void
7045+
pgstat_recv_connstat(PgStat_MsgConn *msg, int len)
7046+
{
7047+
PgStat_StatDBEntry *dbentry;
7048+
7049+
dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
7050+
7051+
dbentry->n_sessions += msg->m_count;
7052+
dbentry->total_session_time += msg->m_session_time;
7053+
dbentry->total_active_time += msg->m_active_time;
7054+
dbentry->total_idle_in_xact_time += msg->m_idle_in_xact_time;
7055+
switch (msg->m_disconnect)
7056+
{
7057+
case DISCONNECT_NOT_YET:
7058+
case DISCONNECT_NORMAL:
7059+
/* we don't collect these */
7060+
break;
7061+
case DISCONNECT_CLIENT_EOF:
7062+
dbentry->n_sessions_abandoned++;
7063+
break;
7064+
case DISCONNECT_FATAL:
7065+
dbentry->n_sessions_fatal++;
7066+
break;
7067+
case DISCONNECT_KILLED:
7068+
dbentry->n_sessions_killed++;
7069+
break;
7070+
}
7071+
}
7072+
69477073
/* ----------
69487074
* pgstat_recv_tempfile() -
69497075
*

src/backend/tcop/postgres.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2865,6 +2865,9 @@ die(SIGNAL_ARGS)
28652865
ProcDiePending = true;
28662866
}
28672867

2868+
/* for the statistics collector */
2869+
pgStatSessionEndCause = DISCONNECT_KILLED;
2870+
28682871
/* If we're still here, waken anything waiting on the process latch */
28692872
SetLatch(MyLatch);
28702873

@@ -4579,9 +4582,15 @@ PostgresMain(int argc, char *argv[],
45794582
* means unexpected loss of frontend connection. Either way,
45804583
* perform normal shutdown.
45814584
*/
4582-
case 'X':
45834585
case EOF:
45844586

4587+
/* for the statistics collector */
4588+
pgStatSessionEndCause = DISCONNECT_CLIENT_EOF;
4589+
4590+
/* FALLTHROUGH */
4591+
4592+
case 'X':
4593+
45854594
/*
45864595
* Reset whereToSendOutput to prevent ereport from attempting
45874596
* to send any more messages to client.

0 commit comments

Comments
 (0)