@cryptotaxi247 / netdata-1 / commits / 4b63e6cda

Improve MSSQL (Windows.plugin Part II) (#20141)

thiagoftsm committed May 2, 2025 at 17:25 UTC 4b63e6cdad29f3873e03e2e448084bbdf30b9c58
4 files changed +725 -267
src/collectors/windows.plugin/README.md
+105 -99
@@ -1,19 +1,16 @@
1 # Windows.plugin
2
3 -This internal plugin is only available for Microsoft Windows operating systems.
3 +This internal plugin is exclusively available for Microsoft Windows operating systems.
4
5 -## The Collector
5 +## Overview
6
7 -This plugin primarily collects metrics from Microsoft Windows [Performance Counters](https://learn.microsoft.com/en-us/windows/win32/perfctrs/performance-counters-what-s-new). All detected metrics are automatically displayed without requiring additional configuration.
8 -=======
9 -Most of the metrics collected by this plugin originate from Microsoft Windows
10 -[Performance Counters](https://learn.microsoft.com/en-us/windows/win32/perfctrs/performance-counters-what-s-new).
7 +The Windows plugin primarily collects metrics from Microsoft Windows [Performance Counters](https://learn.microsoft.com/en-us/windows/win32/perfctrs/performance-counters-what-s-new). All detected metrics are automatically displayed in the Netdata dashboard without requiring additional configuration.
8
12 -These metrics are always displayed when detected, requiring no additional configuration.
9 +## Default Configuration
10
14 -By default, all collector threads are enabled except for `PerflibThermalZone` and `PerflibServices`. You can enable these or disable others by modifying options in the `[plugin:windows]` section of the configuration file.
11 +By default, all collector threads are enabled except for `PerflibThermalZone` and `PerflibServices`. You can enable these disabled collectors or disable any of the currently active ones by modifying the `[plugin:windows]` section in your configuration file.
12
16 -To change a setting, remove the comment symbol (`#`) from the beginning of the line and set the value to either `yes` or `no`:
13 +To change a setting, remove the comment symbol (`#`) from the beginning of the line and set the value to either `yes` or `no`.
14
15 ```text
16 [plugin:windows]
@@ -36,155 +33,164 @@ To change a setting, remove the comment symbol (`#`) from the beginning of the l
33 # PerflibADFS = yes
34 ```
35
39 -## Configuration
36 +## Microsoft SQL Server Integration
37
41 -To collect certain metrics for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server),
42 -Netdata needs access to internal server data. To collect this data, you need to configure both your server and Netdata.
38 +To collect metrics from [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server), Netdata needs access to internal server data. This requires configuration on both the Windows system and Netdata sides.
39
44 -### Windows Defender Firewall with Advanced Security
40 +## Configuring
41
46 -To connect to your SQL Server, Netdata uses TCP port 1433 (or any other configured port).
47 -This port is typically blocked by Windows Defender, so you must allow access before proceeding:
42 +### Step 1: Configure Windows Defender Firewall
43
49 -1. Open `Windows Defender Firewall with Advanced Security` as an Adminstrator.
50 -2. Right-click `Inbound Rules` and select `New Rule...`.
51 -3. Select `Port`, then click `Next`.
52 -4. Choose `TCP`, then enter `1433` in the `Specific local ports:`. Click `Next`.
53 -5. Select an appropriate action based on your network policy. For example, `Allow the connection`.
54 - Click `Next`.
55 -6. Choose where the rule will be applied (Domain, Private, or Public).
56 -7. Finally, provide a `Name` and an optional `Description` for the new rule,
57 - then click `Finish`.
44 +Netdata connects to SQL Server using TCP port 1433 (or your custom-configured port). You'll need to create a firewall rule to allow this connection:
45
59 -You can use the same rule to allow connections to other instances. To do this,
60 -specify the ports for each instance in the `Port` field separated by commas.
46 +1. Open `Windows Defender Firewall with Advanced Security` as an Administrator
47 +2. Right-click `Inbound Rules` and select `New Rule...`
48 +3. Select `Port`, then click `Next`
49 +4. Choose `TCP`, enter `1433` in the `Specific local ports:` field, then click `Next`
50 +5. Select an appropriate action (typically `Allow the connection`), then click `Next`
51 +6. Choose where the rule applies (Domain, Private, or Public networks), then click `Next`
52 +7. Provide a name and optional description for the rule, then click `Finish`
53
62 -### Microsoft SQL Server (Configuration)
54 +For multiple SQL Server instances, you can specify multiple ports in the same rule by separating them with commas.
55
64 -After enabling access through the firewall, you will need to configure your SQL Server instance to accept TCP
65 -connections:
56 +### Step 2: Configure SQL Server Network Settings
57
67 -1. Open `SQL Server Configuration Manager`.
68 -2. Expand `SQL Server Network Configuration`.
69 -3. In the console panel, select `Protocols for <instance name>.`.
70 -4. In the details panel, double-click the `TCP` protocol, and set `Yes` for `Enabled`.
71 -5. Go to the `IP Address` tab, locate the section labeled`IPAII`.
72 - - Remove any value from the `TCP Dynamic Ports` field.
73 - - Enter a value in the `TCP Port` field. By default, SQL Server uses `1433`.
74 -6. Finally, select `SQL Server Services`, and restart your SQL Server instance.
58 +Enable SQL Server to accept TCP connections:
59
76 -You need to apply this configuration to every instance.
60 +1. Open `SQL Server Configuration Manager`
61 +2. Expand `SQL Server Network Configuration`
62 +3. Select `Protocols for <instance name>` in the console panel
63 +4. Double-click the `TCP` protocol in the details panel and set `Enabled` to `Yes`
64 +5. Go to the `IP Address` tab and locate the `IPAII` section:
65 + - Clear any value from the `TCP Dynamic Ports` field
66 + - Enter a port number in the `TCP Port` field (default is `1433`)
67 +6. Select `SQL Server Services` and restart your SQL Server instance
68
78 -### Microsoft SQL Server (User)
69 +These steps must be performed for each SQL Server instance you want to monitor.
70
80 -If you want to allow connections using SQL Server authentication, you must modify the server configuration:
71 +### Step 3: Configure SQL Server Authentication
72
82 -1. Open `Microsoft Server Management Studio`.
83 -2. Right-click your server, and select `Properties`.
84 -3. In the left panel, select `Security`.
85 -4. Under `Server authentication`, choose `SQL Server and Windows Authentication mode`.
86 -5. Click `OK`.
87 -6. Finally, right-click your server, and select`Restart`.
73 +If you're using SQL Server authentication (rather than Windows authentication):
74
89 -After that, you need to create a user with the `VIEW SERVER STATE` permission to collect data from the server.
90 -Once the user is created, it must also be granted access to the databases.
75 +1. Open `Microsoft Server Management Studio`
76 +2. Right-click your server and select `Properties`
77 +3. Select `Security` in the left panel
78 +4. Choose `SQL Server and Windows Authentication mode` under `Server authentication`
79 +5. Click `OK`
80 +6. Right-click your server and select `Restart`
81 +
82 +### Step 4: Create a Monitoring User
83 +
84 +Create an SQL Server user with the necessary permissions to collect monitoring data:
85
86 ```tsql
87 USE master;
94 -CREATE LOGIN netdata_user WITH PASSWORD = 'netdata';
88 +CREATE LOGIN netdata_user WITH PASSWORD = '1ReallyStrongPasswordShouldBeInsertedHere';
89 CREATE USER netdata_user FOR LOGIN netdata_user;
90 GRANT CONNECT SQL TO netdata_user;
91 GRANT VIEW SERVER STATE TO netdata_user;
92 GO
93 ```
94
101 -In addition to creating the user, you must enable the
102 -[query store](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store?view=sql-server-ver16),
103 -on each database you wish to monitor.
95 +Additionally, enable the [Query Store](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store?view=sql-server-ver16) on each database you want to monitor:
96
97 ```tsql
98 DECLARE @dbname NVARCHAR(max)
107 -DECLARE nd_user_cursor CURSOR FOR SELECT name FROM master.dbo.sysdatabases WHERE NAME NOT IN ('master','msdb','tempdb','model')
99 +DECLARE nd_user_cursor CURSOR FOR SELECT name
100 + FROM master.dbo.sysdatabases
101 + WHERE name NOT IN ('master', 'tempdb')
102
103 OPEN nd_user_cursor
110 -FETCH NEXT FROM nd_user_cursor INTO @dbname WHILE @@FETCH_STATUS = 0
111 -BEGIN
104 +FETCH NEXT FROM nd_user_cursor INTO @dbname
105 +WHILE @@FETCH_STATUS = 0
106 + BEGIN
107 EXECUTE ("USE "+ @dbname+"; CREATE USER netdata_user FOR LOGIN netdata_user; ALTER DATABASE "+@dbname+" SET QUERY_STORE = ON ( QUERY_CAPTURE_MODE = ALL, DATA_FLUSH_INTERVAL_SECONDS = 900 )");
108 FETCH next FROM nd_user_cursor INTO @dbname;
114 -END
109 + END
110 CLOSE nd_user_cursor
111 DEALLOCATE nd_user_cursor
112 GO
113 ```
114
120 -You need to apply this configuration to every instance.
115 +Apply this configuration to each SQL Server instance you wish to monitor.
116
122 -### Netdata Configuration
117 +### Step 5: Configure Netdata
118
124 -Now that the user has been created inside your server, update `netdata.conf` by adding the following section:
119 +Add the SQL Server connection details to your `netdata.conf` file:
120
126 -```text
121 +```
122 [plugin:windows:PerflibMSSQL]
123 driver = SQL Server
124 server = 127.0.0.1\\Dev, 1433
125 #address = [protocol:]Address[,port |\pipe\pipename]
126 uid = netdata_user
132 - pwd = AReallyStrongPasswordShouldBeInsertedHere
127 + pwd = 1ReallyStrongPasswordShouldBeInsertedHere
128 # additional instances = 0
129 #windows authentication = no
130 ```
131
137 -In next table, we give a short description about them:
138 -
139 -| Option | Description |
140 -|--------------------------|-----------------------------------------------------------------------------------|
141 -|`driver` | ODBC driver used to connect to the MSSQL server. |
142 -|`server` | Server address or instance name to connect to. |
143 -|`address` | Similar to `server`. You can also use named pipes if your server supports them. |
144 -|`uid` | User identifier (`uid`) created on your server. |
145 -|`pwd` | Password for the specified user. |
146 -|`additional instances` | Number of additional MSSQL instances to monitor. |
147 -|`windows authentications` | Use Windows credentials to connect to the MSSQL server. |
132 +Configuration options:
133
149 -For additional information on how to set these parameters, refer to the
150 -[Microsoft Official Documentation](https://learn.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client?view=sql-server-ver15&viewFallbackFrom=sql-server-ver16)
134 +| Option | Description |
135 +|--------------------------|------------------------------------------------------------------------------|
136 +| `driver` | ODBC driver used to connect to the SQL Server |
137 +| `server` | Server address or instance name |
138 +| `address` | Alternative to `server`; supports named pipes if the server supports them |
139 +| `uid` | SQL Server user identifier |
140 +| `pwd` | Password for the specified user |
141 +| `additional instances` | Number of additional SQL Server instances to monitor |
142 +| `windows authentication` | Set to `yes` to use Windows credentials instead of SQL Server authentication |
143
152 -#### Additional Instance
144 +For more information on connection parameters, see the [Microsoft Official Documentation](https://learn.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client?view=sql-server-ver15&viewFallbackFrom=sql-server-ver16).
145
154 -Microsoft SQL Server can host multiple instances. To connect to them, you need to create additional sections in your
155 -`netdata.conf` file, specifying their respective connection options.
146 +## Monitoring Multiple SQL Server Instances
147
157 -Let’s suppose you have an additional instance named `Production`. To enable Netdata to monitor both the `Production`
158 -and `Dev` instances, you’ll need to add the following configuration section:
148 +SQL Server can host multiple instances on the same machine. To monitor additional instances:
149
160 -```text
161 -[plugin:windows:PerflibMSSQL1]
150 +1. Update the main section to specify the number of additional instances:
151 + ```text
152 + [plugin:windows:PerflibMSSQL]
153 + additional instances = 1
154 + ```
155 +2. Add a new configuration section for each additional instance, using sequential numbering:
156 + ```text
157 + [plugin:windows:PerflibMSSQL1]
158 driver = SQL Server
159 server = 127.0.0.1\\Production, 1434
160 uid = netdata_user
165 - pwd = AnotherReallyStrongPasswordShouldBeInsertedHere
166 -```
161 + pwd = AnotherReallyStrongPasswordShouldBeInsertedHere2$
162 + ```
163
168 -You must also update the main section to indicate how many additional instances you want to monitor:
164 +Each additional instance section must follow the naming pattern `plugin:windows:PerflibMSSQL` with a sequential number from 1 to 65535.
165
170 -```text
171 -[plugin:windows:PerflibMSSQL]
172 - additional instances = 1
173 -```
166 +## Troubleshooting Common Issues
167 +
168 +### Data source name is not found and no default driver
169 +
170 +This error occurs when the specified ODBC driver is incorrect. To check available drivers:
171 +
172 +1. Open `ODBC Data Sources`
173 +2. Go to the `Drivers` tab
174 +3. Look for the correct name of the `ODBC` or SQL `Server driver`
175
175 -Each additional instance section must follow the naming pattern `plugin:windows:PerflibMSSQL`, with a sequential number
176 -from 1 to 65535, based on the number of instances you want to monitor.
176 +### Database Metrics Not Visible
177
178 -### Errors
178 +If a database isn't appearing on the Netdata dashboard, statistics collection might not be enabled:
179
180 -When configuring Netdata to access SQL Server, some errors may occur. You can check your configuration using the
181 -Microsoft Event Viewer by looking for entries that begin with `MSSQL server error using the handle`.
180 +1. Open `SQL Server Configuration Manager`
181 +2. Right-click the database and select `Properties`
182 +3. Select `Options` in the left pane
183 +4. Set `Auto Create Statistics` to `True`
184 +
185 +### Login Failed
186 +
187 +If authentication fails, check the SQL Server error log at:
188 +
189 +```
190 +C:\Program Files\Microsoft SQL Server\VERSION\MSSQL\Log\ERRORLOG
191 +```
192
183 -#### Data source name not found and no default driver
193 +Where `VERSION` corresponds to your SQL Server version.
194
185 -This error occurs when the driver is not specified correctly. You can check the available drivers by following
186 -these steps:
195 +You can also check Windows Event Viewer for entries beginning with `MSSQL server error using the handle`.
196
188 -- Access `ODBC Data Sources`.
189 -- Go to `Drivers` tab.
190 -- Look for `Name` of the `ODBC` or `SQL Server` driver .
src/collectors/windows.plugin/metadata.yaml
+34 -4
@@ -1751,8 +1751,8 @@ modules:
1751 chart_type: line
1752 dimensions:
1753 - name: benefit
1754 - - name: mssql.instance_locks_deadlocks
1755 - description: Lock requests that resulted in deadlock
1754 + - name: mssql.instance_resource_deadlocks
1755 + description: Active lock requests that resulted in deadlock per resource
1756 unit: deadlocks/s
1757 chart_type: line
1758 dimensions:
@@ -1770,8 +1770,8 @@ modules:
1770 - name: rid
1771 - name: row_group
1772 - name: xact
1773 - - name: mssql.instance_locks_lock_wait
1774 - description: Lock requests that required the caller to wait
1773 + - name: mssql.instance_resource_lock_wait
1774 + description: Active lock requests that required the caller to wait
1775 unit: locks/s
1776 chart_type: line
1777 dimensions:
@@ -1821,6 +1821,30 @@ modules:
1821 chart_type: line
1822 dimensions:
1823 - name: write
1824 + - name: mssql.database_lockwait
1825 + description: Lock requests that required the caller to wait.
1826 + unit: locks/s
1827 + chart_type: line
1828 + dimensions:
1829 + - name: lock
1830 + - name: mssql.database_deadlocks
1831 + description: Lock requests that resulted in deadlock.
1832 + unit: deadlocks/s
1833 + chart_type: line
1834 + dimensions:
1835 + - name: deadlocks
1836 + - name: mssql.database_lock_timeouts
1837 + description: Lock that timed out.
1838 + unit: timeouts/s
1839 + chart_type: line
1840 + dimensions:
1841 + - name: timeouts
1842 + - name: mssql.database_lock_requests
1843 + description: Number of new locks and lock conversions requested.
1844 + unit: requests/s
1845 + chart_type: line
1846 + dimensions:
1847 + - name: requests
1848 - name: mssql.database_backup_restore_operations
1849 description: Backup IO per database
1850 unit: operations/s
@@ -1839,6 +1863,12 @@ modules:
1863 chart_type: line
1864 dimensions:
1865 - name: flushed
1866 + - name: mssql.database_data_files_size
1867 + description: Current database size
1868 + unit: bytes
1869 + chart_type: line
1870 + dimensions:
1871 + - name: size
1872 - meta:
1873 plugin_name: windows.plugin
1874 module_name: PerflibNetFramework
src/collectors/windows.plugin/perflib-mssql.c
+580 -163
@@ -9,10 +9,11 @@
9
10 #define MEGA_FACTOR (1048576) // 1024 * 1024
11 // https://learn.microsoft.com/en-us/sql/sql-server/install/instance-configuration?view=sql-server-ver16
12 -#define NETDATA_MAX_INSTANCE_NAME 32
13 -#define NETDATA_MAX_INSTANCE_OBJECT 128
12 +#define NETDATA_MAX_INSTANCE_NAME (32)
13 +#define NETDATA_MAX_INSTANCE_OBJECT (128)
14 // https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms191240(v=sql.105)#sysname
15 -#define SQLSERVER_MAX_NAME_LENGTH (128)
15 +#define SQLSERVER_MAX_NAME_LENGTH NETDATA_MAX_INSTANCE_OBJECT
16 +#define NETDATA_MSSQL_NEXT_TRY (60)
17
18 BOOL is_sqlexpress = FALSE;
19
@@ -29,7 +30,12 @@ struct netdata_mssql_conn {
30
31 SQLHENV netdataSQLEnv;
32 SQLHDBC netdataSQLHDBc;
33 +
34 + SQLHSTMT checkPermSTMT;
35 + SQLHSTMT databaseListSTMT;
36 SQLHSTMT dataFileSizeSTMT;
37 + SQLHSTMT dbTransactionSTMT;
38 + SQLHSTMT dbLocksSTMT;
39
40 BOOL is_connected;
41 };
@@ -133,6 +139,8 @@ struct mssql_instance {
139 };
140
141 struct mssql_lock_instance {
142 + struct mssql_instance *parent;
143 +
144 char *resourceID;
145
146 COUNTER_DATA lockWait;
@@ -142,20 +150,11 @@ struct mssql_lock_instance {
150 RRDDIM *rd_deadLocks;
151 };
152
145 -enum db_instance_idx {
146 - NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS,
147 - NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP,
148 - NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED,
149 - NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES,
150 - NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS,
151 - NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS,
152 -
153 - NETDATA_MSSQL_ENUM_MDI_IDX_END
154 -};
155 -
153 struct mssql_db_instance {
154 struct mssql_instance *parent;
155
156 + bool collecting_data;
157 +
158 RRDSET *st_db_data_file_size;
159 RRDSET *st_db_active_transactions;
160 RRDSET *st_db_backup_restore_operations;
@@ -163,6 +162,10 @@ struct mssql_db_instance {
162 RRDSET *st_db_log_flushes;
163 RRDSET *st_db_transactions;
164 RRDSET *st_db_write_transactions;
165 + RRDSET *st_db_lockwait;
166 + RRDSET *st_db_deadlock;
167 + RRDSET *st_lock_timeouts;
168 + RRDSET *st_lock_requests;
169
170 RRDDIM *rd_db_data_file_size;
171 RRDDIM *rd_db_active_transactions;
@@ -171,15 +174,25 @@ struct mssql_db_instance {
174 RRDDIM *rd_db_log_flushes;
175 RRDDIM *rd_db_transactions;
176 RRDDIM *rd_db_write_transactions;
177 + RRDDIM *rd_db_lockwait;
178 + RRDDIM *rd_db_deadlock;
179 + RRDDIM *rd_lock_timeouts;
180 + RRDDIM *rd_lock_requests;
181 +
182 + COUNTER_DATA MSSQLDatabaseDataFileSize;
183
184 COUNTER_DATA MSSQLDatabaseActiveTransactions;
185 COUNTER_DATA MSSQLDatabaseBackupRestoreOperations;
177 - COUNTER_DATA MSSQLDatabaseDataFileSize;
186 COUNTER_DATA MSSQLDatabaseLogFlushed;
187 COUNTER_DATA MSSQLDatabaseLogFlushes;
188 COUNTER_DATA MSSQLDatabaseTransactions;
189 COUNTER_DATA MSSQLDatabaseWriteTransactions;
190
191 + COUNTER_DATA MSSQLDatabaseLockWaitSec;
192 + COUNTER_DATA MSSQLDatabaseDeadLockSec;
193 + COUNTER_DATA MSSQLDatabaseLockTimeoutsSec;
194 + COUNTER_DATA MSSQLDatabaseLockRequestsSec;
195 +
196 uint32_t updated;
197 };
198
@@ -223,17 +236,19 @@ static char *netdata_MSSQL_type_text(uint32_t type)
236 }
237
238 // Connection and SQL
226 -static void netdata_MSSQL_error(uint32_t type, SQLHANDLE handle, enum netdata_mssql_odbc_errors step)
239 +static void netdata_MSSQL_error(uint32_t type, SQLHANDLE handle, enum netdata_mssql_odbc_errors step, char *instance)
240 {
241 SQLCHAR state[1024];
242 SQLCHAR message[1024];
243 if (SQL_SUCCESS == SQLGetDiagRec((short)type, handle, 1, state, NULL, message, 1024, NULL)) {
244 char *str_step = netdata_MSSQL_error_text(step);
245 char *str_type = netdata_MSSQL_type_text(type);
246 + char *use_instance = (!instance) ? "no instance" : instance;
247 nd_log(
248 NDLS_COLLECTORS,
235 - NDLP_ERR,
236 - "MSSQL server error using the handle %s running %s : %s, %s",
249 + NDLP_INFO,
250 + "MSSQL server error on %s using the handle %s running %s : %s, %s",
251 + use_instance,
252 str_type,
253 str_step,
254 message,
@@ -241,60 +256,336 @@ static void netdata_MSSQL_error(uint32_t type, SQLHANDLE handle, enum netdata_ms
256 }
257 }
258
244 -static ULONGLONG netdata_MSSQL_fill_data_file_size_dict(SQLHSTMT *stmt, SQLCHAR *query)
259 +static inline void netdata_MSSQL_release_results(SQLHSTMT *stmt) {
260 + SQLFreeStmt(stmt, SQL_CLOSE);
261 + SQLFreeStmt(stmt, SQL_UNBIND);
262 + SQLFreeStmt(stmt, SQL_RESET_PARAMS);
263 +}
264 +
265 +static ULONGLONG netdata_MSSQL_fill_long_value(SQLHSTMT *stmt, const char *mask, const char *dbname, char *instance)
266 {
246 - static long db_size = 0;
247 - static SQLLEN col_data_len = 0;
267 + long db_size = 0;
268 + SQLLEN col_data_len = 0;
269 +
270 + SQLCHAR query[512];
271 + snprintfz((char *)query, 511, mask, dbname);
272
273 SQLRETURN ret;
274
275 ret = SQLExecDirect(stmt, query, SQL_NTS);
276 if (ret != SQL_SUCCESS) {
253 - netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_QUERY);
254 - return 0;
277 + netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_QUERY, instance);
278 + return (ULONGLONG)ULONG_LONG_MAX;
279 }
280
281 ret = SQLBindCol(stmt, 1, SQL_C_LONG, &db_size, sizeof(long), &col_data_len);
282
283 if (ret != SQL_SUCCESS) {
260 - netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_PREPARE);
261 - return 0;
284 + netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_PREPARE, instance);
285 + return (ULONGLONG)ULONG_LONG_MAX;
286 }
287
288 ret = SQLFetch(stmt);
289 if (ret != SQL_SUCCESS) {
266 - netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_FETCH);
267 - return 0;
290 + netdata_MSSQL_error(SQL_HANDLE_STMT, stmt, NETDATA_MSSQL_ODBC_FETCH, instance);
291 + return (ULONGLONG)ULONG_LONG_MAX;
292 }
293
294 + netdata_MSSQL_release_results(stmt);
295 return (ULONGLONG)(db_size * MEGA_FACTOR);
296 }
297
273 -ULONGLONG netdata_MSSQL_fill_data_file_size(struct netdata_mssql_conn *nmc, char *dbname)
298 +#define NETDATA_QUERY_LIST_DB "SELECT name FROM sys.databases;"
299 +
300 +// https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-files-transact-sql?view=sql-server-ver16
301 +#define NETDATA_QUERY_DATA_FILE_SIZE_MASK "SELECT size * 8/1024 FROM %s.sys.database_files WHERE type = 0;"
302 +
303 +// https://learn.microsoft.com/en-us/sql/relational-databases/system-compatibility-views/sys-sysprocesses-transact-sql?view=sql-server-ver16
304 +// SQL SERVER BEFORE 2008 DOES NOT HAVE DATA IN THIS TABLE
305 +// https://github.com/influxdata/telegraf/blob/081dfa26e80d8764fb7f9aac5230e81584b62b56/plugins/inputs/sqlserver/sqlqueriesV2.go#L1259
306 +#define NETDATA_QUERY_TRANSACTIONS_MASK \
307 + "select counter_name, cntr_value from %s.sys.dm_os_performance_counters where instance_name = '%s' and counter_name in ('Active Transactions', 'Transactions/sec', 'Write Transactions/sec', 'Backup/Restore Throughput/sec', 'Log Bytes Flushed/sec', 'Log Flushes/sec', 'Number of Deadlocks/sec', 'Lock Waits/sec', 'Lock Timeouts/sec', 'Lock Requests/sec');"
308 +
309 +#define NETDATA_MSSQL_ACTIVE_TRANSACTIONS_METRIC "Active Transactions"
310 +#define NETDATA_MSSQL_TRANSACTION_PER_SEC_METRIC "Transactions/sec"
311 +#define NETDATA_MSSQL_WRITE_TRANSACTIONS_METRIC "Write Transactions/sec"
312 +#define NETDATA_MSSQL_BACKUP_RESTORE_METRIC "Backup/Restore Throughput/sec"
313 +#define NETDATA_MSSQL_LOG_FLUSHED_METRIC "Log Bytes Flushed/sec"
314 +#define NETDATA_MSSQL_LOG_FLUSHES_METRIC "Log Flushes/sec"
315 +#define NETDATA_MSSQL_NUMBER_DEADLOCKS_METRIC "Number of Deadlocks/sec"
316 +#define NETDATA_MSSQL_LOCK_WAITS_METRIC "Lock Waits/sec"
317 +#define NETDATA_MSSQL_LOCK_TIMEOUTS_METRIC "Lock Timeouts/sec"
318 +#define NETDATA_MSSQL_LOCK_REQUESTS_METRIC "Lock Requests/sec"
319 +
320 +void dict_mssql_fill_transactions(struct mssql_db_instance *mdi, const char *dbname)
321 {
275 - ULONGLONG value = 0;
322 + char object_name[NETDATA_MAX_INSTANCE_OBJECT + 1];
323 + long value;
324 + SQLLEN col_object_len = 0, col_value_len = 0;
325
277 - // We cannot access data for these tables without additional changes.
278 - // They should be blacklisted.
279 - if (!strcmp(dbname, "model") || !!strcmp(dbname, "model_msdb") || !!strcmp(dbname, "model_replicatedmaster") || strcmp(dbname, "mssqlsystemresource"))
280 - return ULONG_LONG_MAX;
326 + SQLCHAR query[sizeof(NETDATA_QUERY_TRANSACTIONS_MASK) + 2 * NETDATA_MAX_INSTANCE_OBJECT + 1];
327 + snprintfz(
328 + (char *)query,
329 + sizeof(NETDATA_QUERY_TRANSACTIONS_MASK) + 2 * NETDATA_MAX_INSTANCE_OBJECT,
330 + NETDATA_QUERY_TRANSACTIONS_MASK,
331 + dbname,
332 + dbname);
333
282 - // https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-files-transact-sql?view=sql-server-ver16
283 - SQLCHAR query[512];
284 - snprintfz((char *)query, 511, "SELECT size * 8/1024 FROM %s.sys.database_files WHERE type = 0;", dbname);
334 + SQLRETURN ret = SQLExecDirect(mdi->parent->conn.dbTransactionSTMT, (SQLCHAR *)query, SQL_NTS);
335 + if (ret != SQL_SUCCESS) {
336 + mdi->collecting_data = false;
337 + netdata_MSSQL_error(
338 + SQL_HANDLE_STMT, mdi->parent->conn.dbTransactionSTMT, NETDATA_MSSQL_ODBC_QUERY, mdi->parent->instanceID);
339 + goto endtransactions;
340 + }
341
286 - value = netdata_MSSQL_fill_data_file_size_dict(nmc->dataFileSizeSTMT, query);
342 + ret = SQLBindCol(
343 + mdi->parent->conn.dbTransactionSTMT, 1, SQL_C_CHAR, object_name, sizeof(object_name), &col_object_len);
344 + if (ret != SQL_SUCCESS) {
345 + netdata_MSSQL_error(
346 + SQL_HANDLE_STMT, mdi->parent->conn.dbTransactionSTMT, NETDATA_MSSQL_ODBC_PREPARE, mdi->parent->instanceID);
347 + goto endtransactions;
348 + }
349
288 - SQLFreeStmt(nmc->dataFileSizeSTMT, SQL_CLOSE);
289 - return value;
350 + ret = SQLBindCol(mdi->parent->conn.dbTransactionSTMT, 2, SQL_C_LONG, &value, sizeof(value), &col_value_len);
351 + if (ret != SQL_SUCCESS) {
352 + netdata_MSSQL_error(
353 + SQL_HANDLE_STMT, mdi->parent->conn.dbTransactionSTMT, NETDATA_MSSQL_ODBC_PREPARE, mdi->parent->instanceID);
354 + goto endtransactions;
355 + }
356 +
357 + do {
358 + ret = SQLFetch(mdi->parent->conn.dbTransactionSTMT);
359 + switch (ret) {
360 + case SQL_SUCCESS:
361 + case SQL_SUCCESS_WITH_INFO:
362 + break;
363 + case SQL_NO_DATA:
364 + default:
365 + goto endtransactions;
366 + }
367 +
368 + // We cannot use strcmp, because buffer is filled with spaces instead NULL.
369 + if (!strncmp(
370 + object_name,
371 + NETDATA_MSSQL_ACTIVE_TRANSACTIONS_METRIC,
372 + sizeof(NETDATA_MSSQL_ACTIVE_TRANSACTIONS_METRIC) - 1))
373 + mdi->MSSQLDatabaseActiveTransactions.current.Data = (ULONGLONG)value;
374 + else if (!strncmp(
375 + object_name,
376 + NETDATA_MSSQL_TRANSACTION_PER_SEC_METRIC,
377 + sizeof(NETDATA_MSSQL_TRANSACTION_PER_SEC_METRIC) - 1))
378 + mdi->MSSQLDatabaseTransactions.current.Data = (ULONGLONG)value;
379 + else if (!strncmp(
380 + object_name,
381 + NETDATA_MSSQL_WRITE_TRANSACTIONS_METRIC,
382 + sizeof(NETDATA_MSSQL_WRITE_TRANSACTIONS_METRIC) - 1))
383 + mdi->MSSQLDatabaseWriteTransactions.current.Data = (ULONGLONG)value;
384 + else if (!strncmp(
385 + object_name, NETDATA_MSSQL_BACKUP_RESTORE_METRIC, sizeof(NETDATA_MSSQL_BACKUP_RESTORE_METRIC) - 1))
386 + mdi->MSSQLDatabaseBackupRestoreOperations.current.Data = (ULONGLONG)value;
387 + else if (!strncmp(object_name, NETDATA_MSSQL_LOG_FLUSHED_METRIC, sizeof(NETDATA_MSSQL_LOG_FLUSHED_METRIC) - 1))
388 + mdi->MSSQLDatabaseLogFlushed.current.Data = (ULONGLONG)value;
389 + else if (!strncmp(object_name, NETDATA_MSSQL_LOG_FLUSHES_METRIC, sizeof(NETDATA_MSSQL_LOG_FLUSHES_METRIC) - 1))
390 + mdi->MSSQLDatabaseLogFlushes.current.Data = (ULONGLONG)value;
391 + else if (!strncmp(
392 + object_name,
393 + NETDATA_MSSQL_NUMBER_DEADLOCKS_METRIC,
394 + sizeof(NETDATA_MSSQL_NUMBER_DEADLOCKS_METRIC) - 1))
395 + mdi->MSSQLDatabaseDeadLockSec.current.Data = (ULONGLONG)value;
396 + else if (!strncmp(object_name, NETDATA_MSSQL_LOCK_WAITS_METRIC, sizeof(NETDATA_MSSQL_LOCK_WAITS_METRIC) - 1))
397 + mdi->MSSQLDatabaseLockWaitSec.current.Data = (ULONGLONG)value;
398 + else if (!strncmp(
399 + object_name, NETDATA_MSSQL_LOCK_TIMEOUTS_METRIC, sizeof(NETDATA_MSSQL_LOCK_TIMEOUTS_METRIC) - 1))
400 + mdi->MSSQLDatabaseLockTimeoutsSec.current.Data = (ULONGLONG)value;
401 + else if (!strncmp(
402 + object_name, NETDATA_MSSQL_LOCK_REQUESTS_METRIC, sizeof(NETDATA_MSSQL_LOCK_REQUESTS_METRIC) - 1))
403 + mdi->MSSQLDatabaseLockRequestsSec.current.Data = (ULONGLONG)value;
404 +
405 + } while (true);
406 +
407 +endtransactions:
408 + netdata_MSSQL_release_results(mdi->parent->conn.dbTransactionSTMT);
409 }
410
292 -int dict_mssql_databases_run_query(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
411 +// https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-tran-locks-transact-sql?view=sql-server-ver16
412 +#define NETDATA_QUERY_LOCKS_MASK \
413 + "SELECT resource_type, count(*) FROM %s.sys.dm_tran_locks WHERE DB_NAME(resource_database_id) = '%s' group by resource_type;"
414 +void dict_mssql_fill_locks(struct mssql_db_instance *mdi, const char *dbname)
415 +{
416 +#define NETDATA_MSSQL_MAX_RESOURCE_TYPE (60)
417 + char resource_type[NETDATA_MSSQL_MAX_RESOURCE_TYPE + 1];
418 + long value;
419 + SQLLEN col_object_len = 0, col_value_len = 0;
420 +
421 + SQLCHAR query[sizeof(NETDATA_QUERY_LOCKS_MASK) + 2 * NETDATA_MAX_INSTANCE_OBJECT + 1];
422 + snprintfz(
423 + (char *)query,
424 + sizeof(NETDATA_QUERY_TRANSACTIONS_MASK) + 2 * NETDATA_MAX_INSTANCE_OBJECT,
425 + NETDATA_QUERY_LOCKS_MASK,
426 + dbname,
427 + dbname);
428 +
429 + SQLRETURN ret = SQLExecDirect(mdi->parent->conn.dbLocksSTMT, (SQLCHAR *)query, SQL_NTS);
430 + if (ret != SQL_SUCCESS) {
431 + mdi->collecting_data = false;
432 + netdata_MSSQL_error(
433 + SQL_HANDLE_STMT, mdi->parent->conn.dbLocksSTMT, NETDATA_MSSQL_ODBC_QUERY, mdi->parent->instanceID);
434 + goto endlocks;
435 + }
436 +
437 + ret =
438 + SQLBindCol(mdi->parent->conn.dbLocksSTMT, 1, SQL_C_CHAR, resource_type, sizeof(resource_type), &col_object_len);
439 + if (ret != SQL_SUCCESS) {
440 + netdata_MSSQL_error(
441 + SQL_HANDLE_STMT, mdi->parent->conn.dbLocksSTMT, NETDATA_MSSQL_ODBC_PREPARE, mdi->parent->instanceID);
442 + goto endlocks;
443 + }
444 +
445 + ret = SQLBindCol(mdi->parent->conn.dbLocksSTMT, 2, SQL_C_LONG, &value, sizeof(value), &col_value_len);
446 + if (ret != SQL_SUCCESS) {
447 + netdata_MSSQL_error(
448 + SQL_HANDLE_STMT, mdi->parent->conn.dbLocksSTMT, NETDATA_MSSQL_ODBC_PREPARE, mdi->parent->instanceID);
449 + goto endlocks;
450 + }
451 +
452 + do {
453 + ret = SQLFetch(mdi->parent->conn.dbLocksSTMT);
454 + switch (ret) {
455 + case SQL_SUCCESS:
456 + case SQL_SUCCESS_WITH_INFO:
457 + break;
458 + case SQL_NO_DATA:
459 + default:
460 + goto endlocks;
461 + }
462 +
463 + char *space = strchr(resource_type, ' ');
464 + if (space)
465 + *space = '\0';
466 +
467 + struct mssql_lock_instance *mli =
468 + dictionary_set(mdi->parent->locks_instances, resource_type, NULL, sizeof(*mli));
469 + if (!mli)
470 + continue;
471 + } while (true);
472 +
473 +endlocks:
474 + netdata_MSSQL_release_results(mdi->parent->conn.dbLocksSTMT);
475 +}
476 +
477 +int dict_mssql_databases_run_queries(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
478 {
479 struct mssql_db_instance *mdi = value;
480 const char *dbname = dictionary_acquired_item_name((DICTIONARY_ITEM *)item);
481
297 - mdi->MSSQLDatabaseDataFileSize.current.Data = netdata_MSSQL_fill_data_file_size(&mdi->parent->conn, (char *)dbname);
482 + if (!mdi->collecting_data) {
483 + goto enddrunquery;
484 + }
485 +
486 + // We failed to collect this for the database, so we are not going to try again
487 + if (mdi->MSSQLDatabaseDataFileSize.current.Data != ULONG_LONG_MAX)
488 + mdi->MSSQLDatabaseDataFileSize.current.Data = netdata_MSSQL_fill_long_value(
489 + mdi->parent->conn.dataFileSizeSTMT, NETDATA_QUERY_DATA_FILE_SIZE_MASK, dbname, mdi->parent->instanceID);
490 + else {
491 + mdi->collecting_data = false;
492 + goto enddrunquery;
493 + }
494 +
495 + dict_mssql_fill_transactions(mdi, dbname);
496 + dict_mssql_fill_locks(mdi, dbname);
497 +
498 +enddrunquery:
499 + return 1;
500 +}
501 +
502 +#define NETDATA_QUERY_CHECK_PERM \
503 + "SELECT CASE WHEN IS_SRVROLEMEMBER('sysadmin') = 1 OR HAS_PERMS_BY_NAME(null, null, 'VIEW SERVER STATE') = 1 THEN 1 ELSE 0 END AS has_permission;"
504 +long metdata_mssql_check_permission(struct mssql_instance *mi)
505 +{
506 + static int next_try = NETDATA_MSSQL_NEXT_TRY - 1;
507 + long perm = 0;
508 + SQLLEN col_data_len = 0;
509 +
510 + if (++next_try != NETDATA_MSSQL_NEXT_TRY)
511 + return 1;
512 +
513 + next_try = 0;
514 +
515 + SQLRETURN ret;
516 +
517 + ret = SQLExecDirect(mi->conn.checkPermSTMT, (SQLCHAR *)NETDATA_QUERY_CHECK_PERM, SQL_NTS);
518 + if (ret != SQL_SUCCESS) {
519 + netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn.checkPermSTMT, NETDATA_MSSQL_ODBC_QUERY, mi->instanceID);
520 + perm = LONG_MAX;
521 + goto endperm;
522 + }
523 +
524 + ret = SQLBindCol(mi->conn.checkPermSTMT, 1, SQL_C_LONG, &perm, sizeof(perm), &col_data_len);
525 +
526 + if (ret != SQL_SUCCESS) {
527 + netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn.checkPermSTMT, NETDATA_MSSQL_ODBC_PREPARE, mi->instanceID);
528 + perm = LONG_MAX;
529 + goto endperm;
530 + }
531 +
532 + ret = SQLFetch(mi->conn.checkPermSTMT);
533 + if (ret != SQL_SUCCESS) {
534 + netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn.checkPermSTMT, NETDATA_MSSQL_ODBC_FETCH, mi->instanceID);
535 + perm = LONG_MAX;
536 + goto endperm;
537 + }
538 +
539 +endperm:
540 + netdata_MSSQL_release_results(mi->conn.checkPermSTMT);
541 + return perm;
542 +}
543 +
544 +void metdata_mssql_fill_dictionary_from_db(struct mssql_instance *mi)
545 +{
546 + char dbname[SQLSERVER_MAX_NAME_LENGTH + 1];
547 + SQLLEN col_data_len = 0;
548 +
549 + static int next_try = NETDATA_MSSQL_NEXT_TRY - 1;
550 +
551 + if (++next_try != NETDATA_MSSQL_NEXT_TRY)
552 + return;
553 +
554 + next_try = 0;
555 +
556 + SQLRETURN ret;
557 +
558 + ret = SQLExecDirect(mi->conn.databaseListSTMT, (SQLCHAR *)NETDATA_QUERY_LIST_DB, SQL_NTS);
559 + if (ret != SQL_SUCCESS) {
560 + netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn.databaseListSTMT, NETDATA_MSSQL_ODBC_QUERY, mi->instanceID);
561 + goto enddblist;
562 + }
563 +
564 + ret = SQLBindCol(mi->conn.databaseListSTMT, 1, SQL_C_CHAR, dbname, sizeof(dbname), &col_data_len);
565 +
566 + if (ret != SQL_SUCCESS) {
567 + netdata_MSSQL_error(SQL_HANDLE_STMT, mi->conn.databaseListSTMT, NETDATA_MSSQL_ODBC_PREPARE, mi->instanceID);
568 + goto enddblist;
569 + }
570 +
571 + do {
572 + ret = SQLFetch(mi->conn.databaseListSTMT);
573 + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
574 + goto enddblist;
575 + }
576 +
577 + struct mssql_db_instance *mdi = dictionary_set(mi->databases, dbname, NULL, sizeof(*mdi));
578 + if (!mdi)
579 + continue;
580 +
581 + mdi->updated = 0;
582 + if (!mdi->parent) {
583 + mdi->parent = mi;
584 + }
585 + } while (true);
586 +
587 +enddblist:
588 + netdata_MSSQL_release_results(mi->conn.databaseListSTMT);
589 }
590
591 static bool netdata_MSSQL_initialize_conection(struct netdata_mssql_conn *nmc)
@@ -336,7 +627,7 @@ static bool netdata_MSSQL_initialize_conection(struct netdata_mssql_conn *nmc)
627 case SQL_INVALID_HANDLE:
628 case SQL_ERROR:
629 default:
339 - netdata_MSSQL_error(SQL_HANDLE_DBC, nmc->netdataSQLHDBc, NETDATA_MSSQL_ODBC_CONNECT);
630 + netdata_MSSQL_error(SQL_HANDLE_DBC, nmc->netdataSQLHDBc, NETDATA_MSSQL_ODBC_CONNECT, NULL);
631 retConn = FALSE;
632 break;
633 case SQL_SUCCESS:
@@ -346,9 +637,25 @@ static bool netdata_MSSQL_initialize_conection(struct netdata_mssql_conn *nmc)
637 }
638
639 if (retConn) {
640 + ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->checkPermSTMT);
641 + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
642 + retConn = FALSE;
643 +
644 + ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->databaseListSTMT);
645 + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
646 + retConn = FALSE;
647 +
648 ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->dataFileSizeSTMT);
649 if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
650 retConn = FALSE;
651 +
652 + ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->dbTransactionSTMT);
653 + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
654 + retConn = FALSE;
655 +
656 + ret = SQLAllocHandle(SQL_HANDLE_STMT, nmc->netdataSQLHDBc, &nmc->dbLocksSTMT);
657 + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
658 + retConn = FALSE;
659 }
660
661 return retConn;
@@ -444,15 +751,9 @@ void dict_mssql_insert_locks_cb(const DICTIONARY_ITEM *item __maybe_unused, void
751
752 void dict_mssql_insert_databases_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
753 {
447 - struct mssql_db_instance *ptr = value;
448 -
449 - // https://learn.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-databases-object
450 - ptr->MSSQLDatabaseActiveTransactions.key = "Active Transactions";
451 - ptr->MSSQLDatabaseBackupRestoreOperations.key = "Backup/Restore Throughput/sec";
452 - ptr->MSSQLDatabaseLogFlushed.key = "Log Bytes Flushed/sec";
453 - ptr->MSSQLDatabaseLogFlushes.key = "Log Flushes/sec";
454 - ptr->MSSQLDatabaseTransactions.key = "Transactions/sec";
455 - ptr->MSSQLDatabaseWriteTransactions.key = "Write Transactions/sec";
754 + struct mssql_db_instance *mdi = value;
755 +
756 + mdi->collecting_data = true;
757 }
758
759 // Options
@@ -505,7 +806,11 @@ static void netdata_read_config_options(struct netdata_mssql_conn *dbconn)
806 {
807 dbconn->netdataSQLEnv = NULL;
808 dbconn->netdataSQLHDBc = NULL;
809 + dbconn->checkPermSTMT = NULL;
810 + dbconn->databaseListSTMT = NULL;
811 dbconn->dataFileSizeSTMT = NULL;
812 + dbconn->dbTransactionSTMT = NULL;
813 + dbconn->dbLocksSTMT = NULL;
814
815 dbconn->is_connected = FALSE;
816
@@ -1068,15 +1373,15 @@ void dict_mssql_locks_wait_charts(struct mssql_instance *mi, int update_every)
1373 if (!mi->st_lockWait) {
1374 char id[RRD_ID_LENGTH_MAX + 1];
1375
1071 - snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_lock_wait", mi->instanceID);
1376 + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_resource_lock_wait", mi->instanceID);
1377 netdata_fix_chart_name(id);
1378 mi->st_lockWait = rrdset_create_localhost(
1379 "mssql",
1380 id,
1381 NULL,
1382 "locks",
1078 - "mssql.instance_locks_lock_wait",
1079 - "Lock requests that required the caller to wait.",
1383 + "mssql.instance_resource_lock_wait",
1384 + "Lock requests that required the caller to wait per resource.",
1385 "locks/s",
1386 PLUGIN_WINDOWS_NAME,
1387 "PerflibMSSQL",
@@ -1088,36 +1393,24 @@ void dict_mssql_locks_wait_charts(struct mssql_instance *mi, int update_every)
1393 }
1394 }
1395
1091 -void dict_mssql_locks_wait_dimension(struct mssql_instance *mi, struct mssql_lock_instance *mli)
1092 -{
1093 - if (!mli->rd_lockWait) {
1094 - char id[RRD_ID_LENGTH_MAX + 1];
1095 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s", mli->resourceID);
1096 - netdata_fix_chart_name(id);
1097 -
1098 - mli->rd_lockWait = rrddim_add(mi->st_lockWait, id, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1099 - }
1100 - rrddim_set_by_pointer(mi->st_lockWait, mli->rd_lockWait, (collected_number)(mli->lockWait.current.Data));
1101 -}
1102 -
1396 void dict_mssql_dead_locks_charts(struct mssql_instance *mi, int update_every)
1397 {
1398 if (!mi->st_deadLocks) {
1399 char id[RRD_ID_LENGTH_MAX + 1];
1400
1108 - snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_deadlocks", mi->instanceID);
1401 + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_resource_deadlocks", mi->instanceID);
1402 netdata_fix_chart_name(id);
1403 mi->st_deadLocks = rrdset_create_localhost(
1404 "mssql",
1405 id,
1406 NULL,
1407 "locks",
1115 - "mssql.instance_locks_deadlocks",
1116 - "Lock requests that resulted in deadlock.",
1408 + "mssql.instance_resource_deadlocks",
1409 + "Active lock requests that resulted in deadlock per resource.",
1410 "deadlocks/s",
1411 PLUGIN_WINDOWS_NAME,
1412 "PerflibMSSQL",
1120 - PRIO_MSSQL_LOCKS_DEADLOCK,
1413 + PRIO_MSSQL_LOCKS_DEADLOCK_PER_RESOURCE,
1414 update_every,
1415 RRDSET_TYPE_LINE);
1416
@@ -1125,18 +1418,6 @@ void dict_mssql_dead_locks_charts(struct mssql_instance *mi, int update_every)
1418 }
1419 }
1420
1128 -void dict_mssql_deadlocks_dimension(struct mssql_instance *mi, struct mssql_lock_instance *mli)
1129 -{
1130 - if (!mli->rd_deadLocks) {
1131 - char id[RRD_ID_LENGTH_MAX + 1];
1132 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s", mli->resourceID);
1133 - netdata_fix_chart_name(id);
1134 -
1135 - mli->rd_deadLocks = rrddim_add(mi->st_deadLocks, id, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1136 - }
1137 - rrddim_set_by_pointer(mi->st_deadLocks, mli->rd_deadLocks, (collected_number)mli->deadLocks.current.Data);
1138 -}
1139 -
1421 static void do_mssql_locks(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *mi, int update_every)
1422 {
1423 PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, mi->objectName[NETDATA_MSSQL_LOCKS]);
@@ -1148,29 +1429,6 @@ static void do_mssql_locks(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *m
1429
1430 dict_mssql_locks_wait_charts(mi, update_every);
1431 dict_mssql_dead_locks_charts(mi, update_every);
1151 - PERF_INSTANCE_DEFINITION *pi = NULL;
1152 - for (LONG i = 0; i < pObjectType->NumInstances; i++) {
1153 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
1154 - if (!pi)
1155 - break;
1156 -
1157 - if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
1158 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
1159 -
1160 - if (!strcasecmp(windows_shared_buffer, "_Total"))
1161 - continue;
1162 -
1163 - struct mssql_lock_instance *mli =
1164 - dictionary_set(mi->locks_instances, windows_shared_buffer, NULL, sizeof(*mli));
1165 - if (!mli)
1166 - continue;
1167 -
1168 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->lockWait))
1169 - dict_mssql_locks_wait_dimension(mi, mli);
1170 -
1171 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->deadLocks))
1172 - dict_mssql_deadlocks_dimension(mi, mli);
1173 - }
1432
1433 if (mi->st_lockWait)
1434 rrdset_done(mi->st_lockWait);
@@ -1181,6 +1439,9 @@ static void do_mssql_locks(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *m
1439
1440 static void mssql_database_backup_restore_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1441 {
1442 + if (unlikely(!mli->parent->conn.is_connected))
1443 + return;
1444 +
1445 char id[RRD_ID_LENGTH_MAX + 1];
1446
1447 if (!mli->st_db_backup_restore_operations) {
@@ -1213,18 +1474,19 @@ static void mssql_database_backup_restore_chart(struct mssql_db_instance *mli, c
1474 rrddim_add(mli->st_db_backup_restore_operations, "backup", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1475 }
1476
1216 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP)) {
1217 - rrddim_set_by_pointer(
1218 - mli->st_db_backup_restore_operations,
1219 - mli->rd_db_backup_restore_operations,
1220 - (collected_number)mli->MSSQLDatabaseBackupRestoreOperations.current.Data);
1221 - }
1477 + rrddim_set_by_pointer(
1478 + mli->st_db_backup_restore_operations,
1479 + mli->rd_db_backup_restore_operations,
1480 + (collected_number)mli->MSSQLDatabaseBackupRestoreOperations.current.Data);
1481
1482 rrdset_done(mli->st_db_backup_restore_operations);
1483 }
1484
1485 static void mssql_database_log_flushes_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1486 {
1487 + if (unlikely(!mli->parent->conn.is_connected))
1488 + return;
1489 +
1490 char id[RRD_ID_LENGTH_MAX + 1];
1491
1492 if (!mli->st_db_log_flushes) {
@@ -1252,18 +1514,17 @@ static void mssql_database_log_flushes_chart(struct mssql_db_instance *mli, cons
1514 mli->rd_db_log_flushes = rrddim_add(mli->st_db_log_flushes, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1515 }
1516
1255 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES)) {
1256 - rrddim_set_by_pointer(
1257 - mli->st_db_log_flushes,
1258 - mli->rd_db_log_flushes,
1259 - (collected_number)mli->MSSQLDatabaseLogFlushes.current.Data);
1260 - }
1517 + rrddim_set_by_pointer(
1518 + mli->st_db_log_flushes, mli->rd_db_log_flushes, (collected_number)mli->MSSQLDatabaseLogFlushes.current.Data);
1519
1520 rrdset_done(mli->st_db_log_flushes);
1521 }
1522
1523 static void mssql_database_log_flushed_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1524 {
1525 + if (unlikely(!mli->parent->conn.is_connected))
1526 + return;
1527 +
1528 char id[RRD_ID_LENGTH_MAX + 1];
1529
1530 if (!mli->st_db_log_flushed) {
@@ -1291,18 +1552,17 @@ static void mssql_database_log_flushed_chart(struct mssql_db_instance *mli, cons
1552 mli->rd_db_log_flushed = rrddim_add(mli->st_db_log_flushed, "flushed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1553 }
1554
1294 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED)) {
1295 - rrddim_set_by_pointer(
1296 - mli->st_db_log_flushed,
1297 - mli->rd_db_log_flushed,
1298 - (collected_number)mli->MSSQLDatabaseLogFlushed.current.Data);
1299 - }
1555 + rrddim_set_by_pointer(
1556 + mli->st_db_log_flushed, mli->rd_db_log_flushed, (collected_number)mli->MSSQLDatabaseLogFlushed.current.Data);
1557
1558 rrdset_done(mli->st_db_log_flushed);
1559 }
1560
1561 static void mssql_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1562 {
1563 + if (unlikely(!mli->parent->conn.is_connected))
1564 + return;
1565 +
1566 char id[RRD_ID_LENGTH_MAX + 1];
1567
1568 if (!mli->st_db_transactions) {
@@ -1331,18 +1591,19 @@ static void mssql_transactions_chart(struct mssql_db_instance *mli, const char *
1591 rrddim_add(mli->st_db_transactions, "transactions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1592 }
1593
1334 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS)) {
1335 - rrddim_set_by_pointer(
1336 - mli->st_db_transactions,
1337 - mli->rd_db_transactions,
1338 - (collected_number)mli->MSSQLDatabaseTransactions.current.Data);
1339 - }
1594 + rrddim_set_by_pointer(
1595 + mli->st_db_transactions,
1596 + mli->rd_db_transactions,
1597 + (collected_number)mli->MSSQLDatabaseTransactions.current.Data);
1598
1599 rrdset_done(mli->st_db_transactions);
1600 }
1601
1602 static void mssql_write_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1603 {
1604 + if (unlikely(!mli->parent->conn.is_connected))
1605 + return;
1606 +
1607 char id[RRD_ID_LENGTH_MAX + 1];
1608
1609 if (!mli->st_db_write_transactions) {
@@ -1372,18 +1633,163 @@ static void mssql_write_transactions_chart(struct mssql_db_instance *mli, const
1633 rrddim_add(mli->st_db_write_transactions, "write", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1634 }
1635
1375 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS)) {
1376 - rrddim_set_by_pointer(
1377 - mli->st_db_write_transactions,
1378 - mli->rd_db_write_transactions,
1379 - (collected_number)mli->MSSQLDatabaseWriteTransactions.current.Data);
1380 - }
1636 + rrddim_set_by_pointer(
1637 + mli->st_db_write_transactions,
1638 + mli->rd_db_write_transactions,
1639 + (collected_number)mli->MSSQLDatabaseWriteTransactions.current.Data);
1640
1641 rrdset_done(mli->st_db_write_transactions);
1642 }
1643
1644 +static void mssql_lockwait_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1645 +{
1646 + if (unlikely(!mli->parent->conn.is_connected))
1647 + return;
1648 +
1649 + char id[RRD_ID_LENGTH_MAX + 1];
1650 +
1651 + if (!mli->st_db_lockwait) {
1652 + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_lockwait", db, mli->parent->instanceID);
1653 + netdata_fix_chart_name(id);
1654 + mli->st_db_lockwait = rrdset_create_localhost(
1655 + "mssql",
1656 + id,
1657 + NULL,
1658 + "locks",
1659 + "mssql.database_lockwait",
1660 + "Lock requests that required the caller to wait.",
1661 + "locks/s",
1662 + PLUGIN_WINDOWS_NAME,
1663 + "PerflibMSSQL",
1664 + PRIO_MSSQL_DATABASE_LOCKWAIT_PER_SECOND,
1665 + update_every,
1666 + RRDSET_TYPE_LINE);
1667 +
1668 + rrdlabels_add(mli->st_db_lockwait->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO);
1669 + rrdlabels_add(mli->st_db_lockwait->rrdlabels, "database", db, RRDLABEL_SRC_AUTO);
1670 +
1671 + mli->rd_db_lockwait = rrddim_add(mli->st_db_lockwait, "lock", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1672 + }
1673 +
1674 + rrddim_set_by_pointer(
1675 + mli->st_db_lockwait, mli->rd_db_lockwait, (collected_number)mli->MSSQLDatabaseLockWaitSec.current.Data);
1676 +
1677 + rrdset_done(mli->st_db_lockwait);
1678 +}
1679 +
1680 +static void mssql_deadlock_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1681 +{
1682 + if (unlikely(!mli->parent->conn.is_connected))
1683 + return;
1684 +
1685 + char id[RRD_ID_LENGTH_MAX + 1];
1686 +
1687 + if (!mli->st_db_deadlock) {
1688 + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_deadlocks", db, mli->parent->instanceID);
1689 + netdata_fix_chart_name(id);
1690 + mli->st_db_deadlock = rrdset_create_localhost(
1691 + "mssql",
1692 + id,
1693 + NULL,
1694 + "locks",
1695 + "mssql.database_deadlocks",
1696 + "Lock requests that resulted in deadlock.",
1697 + "deadlocks/s",
1698 + PLUGIN_WINDOWS_NAME,
1699 + "PerflibMSSQL",
1700 + PRIO_MSSQL_DATABASE_DEADLOCKS_PER_SECOND,
1701 + update_every,
1702 + RRDSET_TYPE_LINE);
1703 +
1704 + rrdlabels_add(mli->st_db_deadlock->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO);
1705 + rrdlabels_add(mli->st_db_deadlock->rrdlabels, "database", db, RRDLABEL_SRC_AUTO);
1706 +
1707 + mli->rd_db_deadlock = rrddim_add(mli->st_db_deadlock, "deadlocks", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1708 + }
1709 +
1710 + rrddim_set_by_pointer(
1711 + mli->st_db_deadlock, mli->rd_db_deadlock, (collected_number)mli->MSSQLDatabaseDeadLockSec.current.Data);
1712 +
1713 + rrdset_done(mli->st_db_deadlock);
1714 +}
1715 +
1716 +static void mssql_lock_request_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1717 +{
1718 + if (unlikely(!mli->parent->conn.is_connected))
1719 + return;
1720 +
1721 + char id[RRD_ID_LENGTH_MAX + 1];
1722 +
1723 + if (!mli->st_lock_requests) {
1724 + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_lock_requests", db, mli->parent->instanceID);
1725 + netdata_fix_chart_name(id);
1726 + mli->st_lock_requests = rrdset_create_localhost(
1727 + "mssql",
1728 + id,
1729 + NULL,
1730 + "locks",
1731 + "mssql.database_lock_requests",
1732 + "Number of new locks and lock conversions requested.",
1733 + "requests/s",
1734 + PLUGIN_WINDOWS_NAME,
1735 + "PerflibMSSQL",
1736 + PRIO_MSSQL_DATABASE_LOCK_REQUESTS_SEC,
1737 + update_every,
1738 + RRDSET_TYPE_LINE);
1739 +
1740 + rrdlabels_add(mli->st_lock_requests->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO);
1741 + rrdlabels_add(mli->st_lock_requests->rrdlabels, "database", db, RRDLABEL_SRC_AUTO);
1742 +
1743 + mli->rd_lock_requests = rrddim_add(mli->st_lock_requests, "requests", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1744 + }
1745 +
1746 + rrddim_set_by_pointer(
1747 + mli->st_lock_requests, mli->rd_lock_requests, (collected_number)mli->MSSQLDatabaseLockRequestsSec.current.Data);
1748 +
1749 + rrdset_done(mli->st_lock_requests);
1750 +}
1751 +
1752 +static void mssql_lock_timeout_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1753 +{
1754 + if (unlikely(!mli->parent->conn.is_connected))
1755 + return;
1756 +
1757 + char id[RRD_ID_LENGTH_MAX + 1];
1758 +
1759 + if (!mli->st_lock_timeouts) {
1760 + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_lock_timeouts", db, mli->parent->instanceID);
1761 + netdata_fix_chart_name(id);
1762 + mli->st_lock_timeouts = rrdset_create_localhost(
1763 + "mssql",
1764 + id,
1765 + NULL,
1766 + "locks",
1767 + "mssql.database_lock_timeouts",
1768 + "Lock that timed out.",
1769 + "timeouts/s",
1770 + PLUGIN_WINDOWS_NAME,
1771 + "PerflibMSSQL",
1772 + PRIO_MSSQL_DATABASE_LOCKS_TIMEOUT_PER_SECOND,
1773 + update_every,
1774 + RRDSET_TYPE_LINE);
1775 +
1776 + rrdlabels_add(mli->st_lock_timeouts->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO);
1777 + rrdlabels_add(mli->st_lock_timeouts->rrdlabels, "database", db, RRDLABEL_SRC_AUTO);
1778 +
1779 + mli->rd_lock_timeouts = rrddim_add(mli->st_lock_timeouts, "timeouts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1780 + }
1781 +
1782 + rrddim_set_by_pointer(
1783 + mli->st_lock_timeouts, mli->rd_lock_timeouts, (collected_number)mli->MSSQLDatabaseLockTimeoutsSec.current.Data);
1784 +
1785 + rrdset_done(mli->st_lock_timeouts);
1786 +}
1787 +
1788 static void mssql_active_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1789 {
1790 + if (unlikely(!mli->parent->conn.is_connected))
1791 + return;
1792 +
1793 char id[RRD_ID_LENGTH_MAX + 1];
1794
1795 if (!mli->st_db_active_transactions) {
@@ -1413,19 +1819,17 @@ static void mssql_active_transactions_chart(struct mssql_db_instance *mli, const
1819 rrddim_add(mli->st_db_active_transactions, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1820 }
1821
1416 - if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS)) {
1417 - rrddim_set_by_pointer(
1418 - mli->st_db_active_transactions,
1419 - mli->rd_db_active_transactions,
1420 - (collected_number)mli->MSSQLDatabaseActiveTransactions.current.Data);
1421 - }
1822 + rrddim_set_by_pointer(
1823 + mli->st_db_active_transactions,
1824 + mli->rd_db_active_transactions,
1825 + (collected_number)mli->MSSQLDatabaseActiveTransactions.current.Data);
1826
1827 rrdset_done(mli->st_db_active_transactions);
1828 }
1829
1830 static inline void mssql_data_file_size_chart(struct mssql_db_instance *mli, const char *db, int update_every)
1831 {
1428 - if (unlikely(!mli->parent->conn.is_connected) || mli->MSSQLDatabaseDataFileSize.current.Data == ULONG_LONG_MAX)
1832 + if (unlikely(!mli->parent->conn.is_connected))
1833 return;
1834
1835 char id[RRD_ID_LENGTH_MAX + 1];
@@ -1467,6 +1871,10 @@ int dict_mssql_databases_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, v
1871 struct mssql_db_instance *mli = value;
1872 const char *db = dictionary_acquired_item_name((DICTIONARY_ITEM *)item);
1873
1874 + if (!mli->collecting_data) {
1875 + goto endchartcb;
1876 + }
1877 +
1878 int *update_every = data;
1879
1880 void (*transaction_chart[])(struct mssql_db_instance *, const char *, int) = {
@@ -1477,6 +1885,10 @@ int dict_mssql_databases_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, v
1885 mssql_database_log_flushes_chart,
1886 mssql_active_transactions_chart,
1887 mssql_write_transactions_chart,
1888 + mssql_lockwait_chart,
1889 + mssql_deadlock_chart,
1890 + mssql_lock_timeout_chart,
1891 + mssql_lock_request_chart,
1892
1893 // Last function pointer must be NULL
1894 NULL};
@@ -1486,6 +1898,7 @@ int dict_mssql_databases_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, v
1898 transaction_chart[i](mli, db, *update_every);
1899 }
1900
1901 +endchartcb:
1902 return 1;
1903 }
1904
@@ -1511,28 +1924,9 @@ static void do_mssql_databases(PERF_DATA_BLOCK *pDataBlock, struct mssql_instanc
1924 if (!mdi)
1925 continue;
1926
1514 - mdi->updated = 0;
1927 if (!mdi->parent) {
1928 mdi->parent = mi;
1929 }
1518 -
1519 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseActiveTransactions))
1520 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS);
1521 -
1522 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseBackupRestoreOperations))
1523 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP);
1524 -
1525 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushed))
1526 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED);
1527 -
1528 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushes))
1529 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES);
1530 -
1531 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseTransactions))
1532 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS);
1533 -
1534 - if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseWriteTransactions))
1535 - mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS);
1930 }
1931
1932 dictionary_sorted_walkthrough_read(mi->databases, dict_mssql_databases_charts_cb, &update_every);
@@ -1670,13 +2064,36 @@ static void do_mssql_memory_mgr(PERF_DATA_BLOCK *pDataBlock, struct mssql_instan
2064 }
2065 }
2066
2067 +int netdata_mssql_reset_value(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
2068 +{
2069 + struct mssql_db_instance *mdi = value;
2070 +
2071 + mdi->collecting_data = false;
2072 +
2073 + return 1;
2074 +}
2075 +
2076 int dict_mssql_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
2077 {
2078 struct mssql_instance *mi = value;
2079 + static long have_perm = 1;
2080 int *update_every = data;
2081
1678 - if (mi->conn.is_connected) {
1679 - dictionary_sorted_walkthrough_read(mi->databases, dict_mssql_databases_run_query, NULL);
2082 + if (mi->conn.is_connected && have_perm) {
2083 + have_perm = metdata_mssql_check_permission(mi);
2084 + if (!have_perm) {
2085 + nd_log(
2086 + NDLS_COLLECTORS,
2087 + NDLP_ERR,
2088 + "User %s does not have permission to run queries on %s",
2089 + mi->conn.username,
2090 + mi->instanceID);
2091 + } else {
2092 + metdata_mssql_fill_dictionary_from_db(mi);
2093 + dictionary_sorted_walkthrough_read(mi->databases, dict_mssql_databases_run_queries, NULL);
2094 + }
2095 + } else {
2096 + dictionary_sorted_walkthrough_read(mi->databases, netdata_mssql_reset_value, NULL);
2097 }
2098
2099 static void (*doMSSQL[])(PERF_DATA_BLOCK *, struct mssql_instance *, int) = {
src/collectors/windows.plugin/windows_plugin.h
+6 -1
@@ -69,6 +69,11 @@ enum PERFLIB_PRIO {
69 PRIO_MSSQL_DATABASE_LOG_FLUSHES,
70 PRIO_MSSQL_DATABASE_LOG_FLUSHED,
71
72 + PRIO_MSSQL_DATABASE_LOCKWAIT_PER_SECOND,
73 + PRIO_MSSQL_DATABASE_DEADLOCKS_PER_SECOND,
74 + PRIO_MSSQL_DATABASE_LOCKS_TIMEOUT_PER_SECOND,
75 + PRIO_MSSQL_DATABASE_LOCK_REQUESTS_SEC,
76 +
77 PRIO_MSSQL_DATABASE_DATA_FILE_SIZE,
78
79 PRIO_MSSQL_STATS_BATCH_REQUEST,
@@ -91,7 +96,7 @@ enum PERFLIB_PRIO {
96 PRIO_MSSQL_MEMMGR_PENDING_MEMORY_GRANTS,
97
98 PRIO_MSSQL_LOCKS_WAIT,
94 - PRIO_MSSQL_LOCKS_DEADLOCK,
99 + PRIO_MSSQL_LOCKS_DEADLOCK_PER_RESOURCE,
100
101 PRIO_MSSQL_SQL_ERRORS,
102