User Connections (MSSQL) (#21348)
thiagoftsm committed
Nov 25, 2025 at 18:19 UTC
d137eb9562e00eb3057efc15e28833cd04e9e949
4 files changed
+83
-16
src/collectors/windows.plugin/MonitorSQL.c
+64
-9
@@ -739,7 +739,7 @@ enddrunquery:
739
return 1;
740
}
741
742
-long metdata_mssql_check_permission(struct mssql_instance *mi)
742
+long netdata_mssql_check_permission(struct mssql_instance *mi)
743
{
744
static int next_try = NETDATA_MSSQL_NEXT_TRY - 1;
745
long perm = 0;
@@ -779,7 +779,7 @@ endperm:
779
return perm;
780
}
781
782
-void metdata_mssql_fill_mssql_status(struct mssql_instance *mi)
782
+void netdata_mssql_fill_mssql_status(struct mssql_instance *mi)
783
{
784
char dbname[SQLSERVER_MAX_NAME_LENGTH + 1];
785
int readonly = 0;
@@ -831,7 +831,7 @@ enddbstate:
831
netdata_MSSQL_release_results(mi->conn->dbSQLState);
832
}
833
834
-void metdata_mssql_fill_job_status(struct mssql_instance *mi)
834
+void netdata_mssql_fill_job_status(struct mssql_instance *mi)
835
{
836
char job[SQLSERVER_MAX_NAME_LENGTH + 1];
837
BYTE state = 0;
@@ -882,7 +882,42 @@ enddbjobs:
882
netdata_MSSQL_release_results(mi->conn->dbSQLJobs);
883
}
884
885
-void metdata_mssql_fill_dictionary_from_db(struct mssql_instance *mi)
885
+void netdata_mssql_fill_user_connection(struct mssql_instance *mi)
886
+{
887
+ if (unlikely(!mi->conn->collect_user_connections))
888
+ return;
889
+
890
+ collected_number connections = 0;
891
+ SQLLEN col_user_connections_len = 0;
892
+
893
+ SQLRETURN ret;
894
+
895
+ ret = SQLExecDirect(mi->conn->dbSQLUserConnections, (SQLCHAR *)NETDATA_QUERY_USER_CONNECTIONS, SQL_NTS);
896
+ if (likely(netdata_mssql_check_result(ret))) {
897
+ netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn->dbSQLUserConnections, NETDATA_MSSQL_ODBC_QUERY, mi->instanceID);
898
+ goto enduserconn;
899
+ }
900
+
901
+ ret = SQLBindCol(mi->conn->dbSQLUserConnections, 1, SQL_C_LONG, &connections, sizeof(connections), &col_user_connections_len);
902
+ if (likely(netdata_mssql_check_result(ret))) {
903
+ netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn->dbSQLUserConnections, NETDATA_MSSQL_ODBC_PREPARE, mi->instanceID);
904
+ goto enduserconn;
905
+ }
906
+
907
+ do {
908
+ ret = SQLFetch(mi->conn->dbSQLUserConnections);
909
+ if (likely(netdata_mssql_check_result(ret))) {
910
+ goto enduserconn;
911
+ }
912
+
913
+ mi->MSSQLUserConnections.current.Data = (ULONGLONG)connections;
914
+ } while (true);
915
+
916
+enduserconn:
917
+ netdata_MSSQL_release_results(mi->conn->dbSQLUserConnections);
918
+}
919
+
920
+void netdata_mssql_fill_dictionary_from_db(struct mssql_instance *mi)
921
{
922
char dbname[SQLSERVER_MAX_NAME_LENGTH + 1];
923
SQLLEN col_data_len = 0;
@@ -1043,6 +1078,12 @@ static bool netdata_MSSQL_initialize_connection(struct netdata_mssql_conn *nmc)
1078
goto endMSSQLInitializationConnection;
1079
}
1080
1081
+ ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->dbSQLUserConnections);
1082
+ if (likely(netdata_mssql_check_result(ret))) {
1083
+ retConn = FALSE;
1084
+ goto endMSSQLInitializationConnection;
1085
+ }
1086
+
1087
ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->dbReplicationPublisher);
1088
if (likely(netdata_mssql_check_result(ret))) {
1089
retConn = FALSE;
@@ -1091,6 +1132,8 @@ static void initialize_mssql_objects(struct mssql_instance *mi, const char *inst
1132
strncpyz(&name[length], "SystemJobs", sizeof(name) - length);
1133
mi->objectName[NETDATA_MSSQL_JOBS] = strdupz(name);
1134
1135
+ mi->objectName[NETDATA_USER_CONNECTIONS] = NULL;
1136
+
1137
strncpyz(&name[length], "Memory Manager", sizeof(name) - length);
1138
mi->objectName[NETDATA_MSSQL_MEMORY] = strdupz(name);
1139
@@ -1280,6 +1323,8 @@ static void netdata_read_config_options()
1323
&netdata_config, section_name, "collect buffer stats", true);
1324
dbconn->collect_data_size = inicfg_get_boolean(
1325
&netdata_config, section_name, "collect database size", true);
1326
+ dbconn->collect_user_connections = inicfg_get_boolean(
1327
+ &netdata_config, section_name, "collect user connections", true);
1328
dbconn->is_connected = FALSE;
1329
1330
netdata_mount_mssql_connection_string(dbconn);
@@ -1426,7 +1471,7 @@ int dict_mssql_query_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value,
1471
static long collecting = 1;
1472
1473
if (likely(mi->conn && mi->conn->is_connected && collecting)) {
1429
- collecting = metdata_mssql_check_permission(mi);
1474
+ collecting = netdata_mssql_check_permission(mi);
1475
if (!collecting) {
1476
nd_log(
1477
NDLS_COLLECTORS,
@@ -1435,9 +1480,10 @@ int dict_mssql_query_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value,
1480
mi->conn->username,
1481
mi->instanceID);
1482
} else {
1438
- metdata_mssql_fill_dictionary_from_db(mi);
1439
- metdata_mssql_fill_mssql_status(mi);
1440
- metdata_mssql_fill_job_status(mi);
1483
+ netdata_mssql_fill_dictionary_from_db(mi);
1484
+ netdata_mssql_fill_mssql_status(mi);
1485
+ netdata_mssql_fill_job_status(mi);
1486
+ netdata_mssql_fill_user_connection(mi);
1487
dictionary_sorted_walkthrough_read(mi->databases, dict_mssql_databases_run_queries, NULL);
1488
}
1489
@@ -2135,6 +2181,14 @@ static void do_mssql_job_status_sql(PERF_DATA_BLOCK *pDataBlock, struct mssql_in
2181
dictionary_sorted_walkthrough_read(mi->sysjobs, dict_mssql_sysjobs_chart_cb, mi);
2182
}
2183
2184
+static void do_mssql_user_connection(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every)
2185
+{
2186
+ if (unlikely(!mi->conn->collect_user_connections))
2187
+ return;
2188
+
2189
+ do_mssql_user_connections(mi, update_every);
2190
+}
2191
+
2192
void dict_mssql_replication_status(struct mssql_publisher_publication *mpp, int update_every)
2193
{
2194
if (unlikely(!mpp->st_publisher_status)) {
@@ -3004,13 +3058,14 @@ int dict_mssql_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value
3058
do_mssql_waits,
3059
do_mssql_bufferman_stats_sql,
3060
do_mssql_job_status_sql,
3061
+ do_mssql_user_connection,
3062
3063
NULL};
3064
3065
DWORD i;
3066
PERF_DATA_BLOCK *pDataBlock;
3067
static bool collect_perflib[NETDATA_MSSQL_METRICS_END] = {
3013
- true, true, true, true, true, true, true, true, true, true};
3068
+ true, true, true, true, true, true, true, true, true, true, false};
3069
for (i = 0; i < NETDATA_MSSQL_ACCESS_METHODS; i++) {
3070
if (unlikely(!collect_perflib[i]))
3071
continue;
src/collectors/windows.plugin/metadata.yaml
+4
@@ -2281,6 +2281,10 @@ modules:
2281
description: Run queries to collect information from the server's database size.
2282
default_value: yes
2283
required: false
2284
+ - name: collect user connections
2285
+ description: Run queries to collect information from the server's connection.
2286
+ default_value: yes
2287
+ required: false
2288
examples:
2289
folding:
2290
enabled: true
src/collectors/windows.plugin/perflib-mssql-queries.h
+11
-5
@@ -37,6 +37,8 @@
37
38
#define NETDATA_QUERY_JOBS_STATUS "SELECT name, enabled FROM msdb.dbo.sysjobs;"
39
40
+#define NETDATA_QUERY_USER_CONNECTIONS "SELECT COUNT(*) AS user_connections FROM sys.dm_exec_sessions WHERE is_user_process = 1;"
41
+
42
// https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-os-wait-stats-transact-sql?view=sql-server-ver16
43
#define NETDATA_QUERY_CHECK_WAITS \
44
"SELECT \
@@ -670,6 +672,7 @@ struct netdata_mssql_conn {
672
SQLHSTMT dbLocksSTMT;
673
SQLHSTMT dbSQLState;
674
SQLHSTMT dbSQLJobs;
675
+ SQLHSTMT dbSQLUserConnections;
676
SQLHSTMT dbReplicationPublisher;
677
678
BOOL collect_transactions;
@@ -679,6 +682,7 @@ struct netdata_mssql_conn {
682
BOOL collect_jobs;
683
BOOL collect_buffer;
684
BOOL collect_data_size;
685
+ BOOL collect_user_connections;
686
687
BOOL is_connected;
688
};
@@ -695,6 +699,7 @@ enum netdata_mssql_metrics {
699
NETDATA_MSSQL_WAITS,
700
NETDATA_MSSQL_BUFFER_MANAGEMENT,
701
NETDATA_MSSQL_JOBS,
702
+ NETDATA_USER_CONNECTIONS,
703
704
NETDATA_MSSQL_METRICS_END
705
};
@@ -982,9 +987,10 @@ netdata_mssql_get_perf_data_block(bool *collect_perflib, struct mssql_instance *
987
return pDataBlock;
988
}
989
985
-void do_mssql_general_stats(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
986
-void do_mssql_errors(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
987
-void do_mssql_memory_mgr(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
988
-void do_mssql_statistics_perflib(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
989
-void do_mssql_access_methods(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
990
+extern void do_mssql_general_stats(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
991
+extern void do_mssql_errors(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
992
+extern void do_mssql_memory_mgr(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
993
+extern void do_mssql_statistics_perflib(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
994
+extern void do_mssql_access_methods(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every);
995
+extern void do_mssql_user_connections(struct mssql_instance *mi, int update_every);
996
#endif
src/collectors/windows.plugin/perflib-mssql.c
+4
-2
@@ -420,8 +420,10 @@ void do_mssql_general_stats(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *
420
if (unlikely(!pObjectType))
421
return;
422
423
- if (likely(perflibGetObjectCounter(pDataBlock, pObjectType, &mi->MSSQLUserConnections))) {
424
- do_mssql_user_connections(mi, update_every);
423
+ if (unlikely(!mi->conn) || unlikely(!mi->conn->collect_user_connections)) {
424
+ if (likely(perflibGetObjectCounter(pDataBlock, pObjectType, &mi->MSSQLUserConnections))) {
425
+ do_mssql_user_connections(mi, update_every);
426
+ }
427
}
428
429
if (likely(perflibGetObjectCounter(pDataBlock, pObjectType, &mi->MSSQLBlockedProcesses))) {