@@ -258,6 +258,9 @@ static int pgStatXactCommit = 0;
258
258
static int pgStatXactRollback = 0 ;
259
259
PgStat_Counter pgStatBlockReadTime = 0 ;
260
260
PgStat_Counter pgStatBlockWriteTime = 0 ;
261
+ static PgStat_Counter pgStatActiveTime = 0 ;
262
+ static PgStat_Counter pgStatTransactionIdleTime = 0 ;
263
+ SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL ;
261
264
262
265
/* Record that's written to 2PC state file when pgstat state is persisted */
263
266
typedef struct TwoPhasePgStatRecord
@@ -343,6 +346,7 @@ static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
343
346
static void pgstat_send_funcstats (void );
344
347
static void pgstat_send_slru (void );
345
348
static HTAB * pgstat_collect_oids (Oid catalogid , AttrNumber anum_oid );
349
+ static void pgstat_send_connstats (bool disconnect , TimestampTz last_report );
346
350
347
351
static PgStat_TableStatus * get_tabstat_entry (Oid rel_id , bool isshared );
348
352
@@ -378,6 +382,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
378
382
static void pgstat_recv_recoveryconflict (PgStat_MsgRecoveryConflict * msg , int len );
379
383
static void pgstat_recv_deadlock (PgStat_MsgDeadlock * msg , int len );
380
384
static void pgstat_recv_checksum_failure (PgStat_MsgChecksumFailure * msg , int len );
385
+ static void pgstat_recv_connstat (PgStat_MsgConn * msg , int len );
381
386
static void pgstat_recv_replslot (PgStat_MsgReplSlot * msg , int len );
382
387
static void pgstat_recv_tempfile (PgStat_MsgTempFile * msg , int len );
383
388
@@ -855,10 +860,14 @@ allow_immediate_pgstat_restart(void)
855
860
* per-table and function usage statistics to the collector. Note that this
856
861
* is called only when not within a transaction, so it is fair to use
857
862
* 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.
858
867
* ----------
859
868
*/
860
869
void
861
- pgstat_report_stat (bool force )
870
+ pgstat_report_stat (bool disconnect )
862
871
{
863
872
/* we assume this inits to all zeroes: */
864
873
static const PgStat_TableCounts all_zeroes ;
@@ -873,17 +882,22 @@ pgstat_report_stat(bool force)
873
882
/* Don't expend a clock check if nothing to do */
874
883
if ((pgStatTabList == NULL || pgStatTabList -> tsa_used == 0 ) &&
875
884
pgStatXactCommit == 0 && pgStatXactRollback == 0 &&
876
- !have_function_stats )
885
+ !have_function_stats && ! disconnect )
877
886
return ;
878
887
879
888
/*
880
889
* 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 .
882
891
*/
883
892
now = GetCurrentTransactionStopTimestamp ();
884
- if (!force &&
893
+ if (!disconnect &&
885
894
!TimestampDifferenceExceeds (last_report , now , PGSTAT_STAT_INTERVAL ))
886
895
return ;
896
+
897
+ /* for backends, send connection statistics */
898
+ if (MyBackendType == B_BACKEND )
899
+ pgstat_send_connstats (disconnect , last_report );
900
+
887
901
last_report = now ;
888
902
889
903
/*
@@ -1351,6 +1365,48 @@ pgstat_drop_relation(Oid relid)
1351
1365
#endif /* NOT_USED */
1352
1366
1353
1367
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
+
1354
1410
/* ----------
1355
1411
* pgstat_reset_counters() -
1356
1412
*
@@ -3348,6 +3404,30 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
3348
3404
}
3349
3405
current_timestamp = GetCurrentTimestamp ();
3350
3406
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
+
3351
3431
/*
3352
3432
* Now update the status entry
3353
3433
*/
@@ -4919,6 +4999,10 @@ PgstatCollectorMain(int argc, char *argv[])
4919
4999
pgstat_recv_replslot (& msg .msg_replslot , len );
4920
5000
break ;
4921
5001
5002
+ case PGSTAT_MTYPE_CONNECTION :
5003
+ pgstat_recv_connstat (& msg .msg_conn , len );
5004
+ break ;
5005
+
4922
5006
default :
4923
5007
break ;
4924
5008
}
@@ -4993,6 +5077,13 @@ reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
4993
5077
dbentry -> last_checksum_failure = 0 ;
4994
5078
dbentry -> n_block_read_time = 0 ;
4995
5079
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 ;
4996
5087
4997
5088
dbentry -> stat_reset_timestamp = GetCurrentTimestamp ();
4998
5089
dbentry -> stats_timestamp = 0 ;
@@ -6944,6 +7035,41 @@ pgstat_recv_replslot(PgStat_MsgReplSlot *msg, int len)
6944
7035
}
6945
7036
}
6946
7037
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
+
6947
7073
/* ----------
6948
7074
* pgstat_recv_tempfile() -
6949
7075
*
0 commit comments