@@ -258,6 +258,9 @@ static int pgStatXactCommit = 0;
258258static int pgStatXactRollback = 0 ;
259259PgStat_Counter pgStatBlockReadTime = 0 ;
260260PgStat_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 */
263266typedef struct TwoPhasePgStatRecord
@@ -343,6 +346,7 @@ static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
343346static void pgstat_send_funcstats (void );
344347static void pgstat_send_slru (void );
345348static HTAB * pgstat_collect_oids (Oid catalogid , AttrNumber anum_oid );
349+ static void pgstat_send_connstats (bool disconnect , TimestampTz last_report );
346350
347351static 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);
378382static void pgstat_recv_recoveryconflict (PgStat_MsgRecoveryConflict * msg , int len );
379383static void pgstat_recv_deadlock (PgStat_MsgDeadlock * msg , int len );
380384static void pgstat_recv_checksum_failure (PgStat_MsgChecksumFailure * msg , int len );
385+ static void pgstat_recv_connstat (PgStat_MsgConn * msg , int len );
381386static void pgstat_recv_replslot (PgStat_MsgReplSlot * msg , int len );
382387static 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 */
860869void
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 *
0 commit comments