master
go 840 lines 36.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package mssql
4
5 // queryVersion retrieves SQL Server version info
6 const queryVersion = `
7 SELECT SERVERPROPERTY('ProductVersion') AS version;
8 `
9
10 // queryUserConnections counts user vs system connections
11 const queryUserConnections = `
12 SELECT
13 SUM(CASE WHEN is_user_process = 1 THEN 1 ELSE 0 END) AS user_connections,
14 SUM(CASE WHEN is_user_process = 0 THEN 1 ELSE 0 END) AS system_connections
15 FROM sys.dm_exec_sessions;
16 `
17
18 // queryBlockedProcesses counts blocked sessions
19 const queryBlockedProcesses = `
20 SELECT COUNT(DISTINCT session_id) AS blocked_sessions
21 FROM sys.dm_exec_requests
22 WHERE blocking_session_id <> 0;
23 `
24
25 // queryBatchRequests gets batch request rate from performance counters
26 const queryBatchRequests = `
27 SELECT cntr_value
28 FROM sys.dm_os_performance_counters
29 WHERE counter_name = 'Batch Requests/sec'
30 AND object_name LIKE '%SQL Statistics%';
31 `
32
33 // queryCompilations gets SQL compilation metrics
34 const queryCompilations = `
35 SELECT counter_name, cntr_value
36 FROM sys.dm_os_performance_counters
37 WHERE object_name LIKE '%SQL Statistics%'
38 AND counter_name IN (
39 'SQL Compilations/sec',
40 'SQL Re-Compilations/sec',
41 'Auto-Param Attempts/sec',
42 'Safe Auto-Params/sec',
43 'Failed Auto-Params/sec'
44 );
45 `
46
47 // queryBufferManager gets buffer manager metrics
48 const queryBufferManager = `
49 SELECT counter_name, cntr_value
50 FROM sys.dm_os_performance_counters
51 WHERE object_name LIKE '%Buffer Manager%'
52 AND counter_name IN (
53 'Page reads/sec',
54 'Page writes/sec',
55 'Buffer cache hit ratio',
56 'Buffer cache hit ratio base',
57 'Checkpoint pages/sec',
58 'Page life expectancy',
59 'Lazy writes/sec',
60 'Page lookups/sec'
61 );
62 `
63
64 // queryMemoryManager gets memory manager metrics
65 const queryMemoryManager = `
66 SELECT counter_name, cntr_value
67 FROM sys.dm_os_performance_counters
68 WHERE object_name LIKE '%Memory Manager%'
69 AND counter_name IN (
70 'Total Server Memory (KB)',
71 'Connection Memory (KB)',
72 'Memory Grants Pending',
73 'External benefit of memory'
74 );
75 `
76
77 // queryAccessMethods gets access method metrics
78 const queryAccessMethods = `
79 SELECT cntr_value
80 FROM sys.dm_os_performance_counters
81 WHERE object_name LIKE '%Access Methods%'
82 AND counter_name = 'Page Splits/sec';
83 `
84
85 // queryDatabaseCounters gets per-database performance counters
86 const queryDatabaseCounters = `
87 SELECT
88 RTRIM(instance_name) AS database_name,
89 counter_name,
90 cntr_value
91 FROM sys.dm_os_performance_counters
92 WHERE object_name LIKE '%Databases%'
93 AND instance_name NOT IN ('_Total', 'mssqlsystemresource')
94 AND counter_name IN (
95 'Active Transactions',
96 'Transactions/sec',
97 'Write Transactions/sec',
98 'Backup/Restore Throughput/sec',
99 'Log Bytes Flushed/sec',
100 'Log Flushes/sec'
101 );
102 `
103
104 // queryDatabaseLogCounters gets per-database transaction log size and activity counters
105 const queryDatabaseLogCounters = `
106 SELECT
107 RTRIM(pc.instance_name) AS database_name,
108 RTRIM(pc.counter_name) AS counter_name,
109 pc.cntr_value
110 FROM sys.dm_os_performance_counters AS pc
111 INNER JOIN sys.databases AS d
112 ON d.database_id = DB_ID(RTRIM(pc.instance_name))
113 WHERE pc.object_name LIKE '%Databases%'
114 AND pc.instance_name NOT IN ('_Total', 'mssqlsystemresource')
115 AND d.database_id > 4
116 AND pc.counter_name IN (
117 'Log File(s) Size (KB)',
118 'Log File(s) Used Size (KB)',
119 'Log Truncations',
120 'Log Shrinks'
121 );
122 `
123
124 // queryDatabaseLocks gets per-database lock metrics
125 const queryDatabaseLocks = `
126 SELECT
127 RTRIM(instance_name) AS database_name,
128 counter_name,
129 cntr_value
130 FROM sys.dm_os_performance_counters
131 WHERE object_name LIKE '%Locks%'
132 AND instance_name NOT IN ('_Total')
133 AND counter_name IN (
134 'Number of Deadlocks/sec',
135 'Lock Waits/sec',
136 'Lock Timeouts/sec',
137 'Lock Requests/sec'
138 );
139 `
140
141 // queryDatabaseSize gets the size of data files for each database
142 const queryDatabaseSize = `
143 SELECT
144 DB_NAME(database_id) AS database_name,
145 SUM(CAST(size AS BIGINT)) * 8 * 1024 AS size_bytes
146 FROM sys.master_files
147 WHERE type = 0 -- data files only
148 AND database_id > 4 -- exclude system databases
149 GROUP BY database_id;
150 `
151
152 // queryLocksByResource gets lock counts grouped by resource type
153 const queryLocksByResource = `
154 SELECT
155 resource_type,
156 COUNT(*) AS lock_count
157 FROM sys.dm_tran_locks
158 WHERE resource_database_id > 4 -- exclude system databases
159 GROUP BY resource_type;
160 `
161
162 // queryWaitStats gets wait statistics with category mapping
163 const queryWaitStats = `
164 SELECT
165 ws.[wait_type],
166 ws.[wait_time_ms] AS total_wait_ms,
167 ws.[wait_time_ms] - ws.[signal_wait_time_ms] AS resource_wait_ms,
168 ws.[signal_wait_time_ms] AS signal_wait_ms,
169 ws.[max_wait_time_ms] AS max_wait_ms,
170 ws.[waiting_tasks_count] AS waiting_tasks
171 FROM sys.dm_os_wait_stats AS ws WITH(NOLOCK)
172 WHERE ws.[waiting_tasks_count] > 0
173 AND ws.[wait_time_ms] > 100
174 AND ws.[wait_type] NOT IN (
175 'BROKER_EVENTHANDLER', 'BROKER_RECEIVE_WAITFOR', 'BROKER_TASK_STOP',
176 'BROKER_TO_FLUSH', 'BROKER_TRANSMITTER', 'CHECKPOINT_QUEUE',
177 'CHKPT', 'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'CLR_SEMAPHORE',
178 'DBMIRROR_DBM_EVENT', 'DBMIRROR_EVENTS_QUEUE', 'DBMIRROR_WORKER_QUEUE',
179 'DBMIRRORING_CMD', 'DIRTY_PAGE_POLL', 'DISPATCHER_QUEUE_SEMAPHORE',
180 'EXECSYNC', 'FSAGENT', 'FT_IFTS_SCHEDULER_IDLE_WAIT',
181 'FT_IFTSHC_MUTEX', 'HADR_CLUSAPI_CALL', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
182 'HADR_LOGCAPTURE_WAIT', 'HADR_NOTIFICATION_DEQUEUE', 'HADR_TIMER_TASK',
183 'HADR_WORK_QUEUE', 'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP',
184 'LOGMGR_QUEUE', 'MEMORY_ALLOCATION_EXT', 'ONDEMAND_TASK_QUEUE',
185 'PREEMPTIVE_OS_AUTHENTICATIONOPS', 'PREEMPTIVE_OS_GETPROCADDRESS',
186 'PREEMPTIVE_XE_CALLBACKEXECUTE', 'PREEMPTIVE_XE_DISPATCHER',
187 'PREEMPTIVE_XE_GETTARGETSTATE', 'PREEMPTIVE_XE_SESSIONCOMMIT',
188 'PREEMPTIVE_XE_TARGETFINALIZE', 'PREEMPTIVE_XE_TARGETINIT',
189 'PWAIT_ALL_COMPONENTS_INITIALIZED', 'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
190 'QDS_ASYNC_QUEUE', 'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
191 'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', 'QDS_SHUTDOWN_QUEUE',
192 'REDO_THREAD_PENDING_WORK', 'REQUEST_FOR_DEADLOCK_SEARCH',
193 'RESOURCE_QUEUE', 'SERVER_IDLE_CHECK', 'SLEEP_BPOOL_FLUSH',
194 'SLEEP_DBSTARTUP', 'SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY',
195 'SLEEP_MASTERMDREADY', 'SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP',
196 'SLEEP_SYSTEMTASK', 'SLEEP_TASK', 'SLEEP_TEMPDBSTARTUP',
197 'SNI_HTTP_ACCEPT', 'SP_SERVER_DIAGNOSTICS_SLEEP',
198 'SQLTRACE_BUFFER_FLUSH', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
199 'SQLTRACE_WAIT_ENTRIES', 'UCS_SESSION_REGISTRATION',
200 'WAIT_FOR_RESULTS', 'WAIT_XTP_CKPT_CLOSE', 'WAIT_XTP_HOST_WAIT',
201 'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'WAIT_XTP_RECOVERY',
202 'WAITFOR', 'WAITFOR_TASKSHUTDOWN', 'XE_BUFFERMGR_ALLPROCESSED_EVENT',
203 'XE_DISPATCHER_JOIN', 'XE_DISPATCHER_WAIT', 'XE_LIVE_TARGET_TVF',
204 'XE_TIMER_EVENT'
205 )
206 ORDER BY ws.[wait_time_ms] DESC;
207 `
208
209 // queryJobs gets SQL Agent job status
210 const queryJobs = `
211 SELECT name, enabled
212 FROM msdb.dbo.sysjobs;
213 `
214
215 // querySQLErrors gets SQL error counts from performance counters
216 const querySQLErrors = `
217 SELECT cntr_value
218 FROM sys.dm_os_performance_counters
219 WHERE object_name LIKE '%SQL Errors%'
220 AND instance_name = '_Total'
221 AND counter_name = 'Errors/sec';
222 `
223
224 // querySystemHealthLatestDeadlockEventFile retrieves the latest xml_deadlock_report event
225 // from the system_health Extended Events file target.
226 const querySystemHealthLatestDeadlockEventFile = `
227 SELECT TOP (1)
228 timestamp_utc AS deadlock_time,
229 CONVERT(nvarchar(max), CAST(event_data AS XML).query('(event/data[@name="xml_report"]/value/deadlock)[1]')) AS deadlock_xml
230 FROM sys.fn_xe_file_target_read_file('system_health*.xel', NULL, NULL, NULL)
231 WHERE object_name = 'xml_deadlock_report'
232 ORDER BY timestamp_utc DESC;
233 `
234
235 // querySystemHealthLatestDeadlockRingBuffer retrieves the latest xml_deadlock_report event
236 // from the system_health Extended Events ring buffer.
237 // Note: This can be slow with large buffers due to XML parsing overhead.
238 const querySystemHealthLatestDeadlockRingBuffer = `
239 WITH xevents AS (
240 SELECT CAST(xet.target_data AS XML) AS target_data
241 FROM sys.dm_xe_session_targets AS xet
242 JOIN sys.dm_xe_sessions AS xs ON xs.address = xet.event_session_address
243 WHERE xs.name = 'system_health'
244 AND xet.target_name = 'ring_buffer'
245 )
246 SELECT TOP (1)
247 xevent.value('@timestamp', 'datetime2(7)') AS deadlock_time,
248 CONVERT(nvarchar(max), xevent.query('(data/value/deadlock)[1]')) AS deadlock_xml
249 FROM xevents
250 CROSS APPLY target_data.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS T(xevent)
251 ORDER BY deadlock_time DESC;
252 `
253
254 // queryDatabaseStatus gets database state and read-only status
255 const queryDatabaseStatus = `
256 SELECT name, state, is_read_only
257 FROM sys.databases;
258 `
259
260 // queryDatabaseNamesByID retrieves database_id to name mappings.
261 const queryDatabaseNamesByID = `
262 SELECT database_id, name
263 FROM sys.databases;
264 `
265
266 // queryMSSQLErrorSessionExists checks for the configured Extended Events session.
267 const queryMSSQLErrorSessionExists = `
268 SELECT COUNT(*)
269 FROM sys.dm_xe_sessions
270 WHERE name = @sessionName;
271 `
272
273 // queryMSSQLErrorSessionHasEventFile verifies that the session has an event_file target.
274 const queryMSSQLErrorSessionHasEventFile = `
275 SELECT COUNT(*)
276 FROM sys.dm_xe_session_targets AS xet
277 JOIN sys.dm_xe_sessions AS xs ON xs.address = xet.event_session_address
278 WHERE xs.name = @sessionName
279 AND xet.target_name = 'event_file';
280 `
281
282 // queryMSSQLErrorSessionHasRingBuffer verifies that the session has a ring_buffer target.
283 const queryMSSQLErrorSessionHasRingBuffer = `
284 SELECT COUNT(*)
285 FROM sys.dm_xe_session_targets AS xet
286 JOIN sys.dm_xe_sessions AS xs ON xs.address = xet.event_session_address
287 WHERE xs.name = @sessionName
288 AND xet.target_name = 'ring_buffer';
289 `
290
291 // queryMSSQLErrorInfoEventFile reads recent error_reported events from the event_file target.
292 const queryMSSQLErrorInfoEventFile = `
293 SELECT TOP (@limit)
294 timestamp_utc AS event_time,
295 CAST(event_data AS XML).value('(event/data[@name="error_number"]/value)[1]', 'int') AS error_number,
296 CAST(event_data AS XML).value('(event/data[@name="state"]/value)[1]', 'int') AS error_state,
297 CAST(event_data AS XML).value('(event/data[@name="message"]/value)[1]', 'nvarchar(max)') AS message,
298 CAST(event_data AS XML).value('(event/action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS sql_text,
299 CONVERT(VARCHAR(64), CAST(event_data AS XML).value('(event/action[@name="query_hash"]/value)[1]', 'varbinary(8)'), 1) AS query_hash
300 FROM sys.fn_xe_file_target_read_file(@sessionName + N'*.xel', NULL, NULL, NULL)
301 WHERE object_name = 'error_reported'
302 ORDER BY event_time DESC;
303 `
304
305 // queryMSSQLErrorInfoRingBuffer reads recent error_reported events from the ring_buffer target.
306 // Note: This can be slow with large buffers due to XML parsing overhead.
307 const queryMSSQLErrorInfoRingBuffer = `
308 WITH xevents AS (
309 SELECT CAST(xet.target_data AS XML) AS target_data
310 FROM sys.dm_xe_session_targets AS xet
311 JOIN sys.dm_xe_sessions AS xs ON xs.address = xet.event_session_address
312 WHERE xs.name = @sessionName
313 AND xet.target_name = 'ring_buffer'
314 )
315 SELECT TOP (@limit)
316 xevent.value('@timestamp', 'datetime2(7)') AS event_time,
317 xevent.value('(data[@name="error_number"]/value)[1]', 'int') AS error_number,
318 xevent.value('(data[@name="state"]/value)[1]', 'int') AS error_state,
319 xevent.value('(data[@name="message"]/value)[1]', 'nvarchar(max)') AS message,
320 xevent.value('(action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS sql_text,
321 CONVERT(VARCHAR(64), xevent.value('(action[@name="query_hash"]/value)[1]', 'varbinary(8)'), 1) AS query_hash
322 FROM xevents
323 CROSS APPLY target_data.nodes('RingBufferTarget/event[@name="error_reported"]') AS T(xevent)
324 ORDER BY event_time DESC;
325 `
326
327 // queryReplicationStatus gets replication publication status (if configured)
328 // Groups by publication to aggregate across agent types and excludes 'ALL' placeholder
329 const queryReplicationStatus = `
330 SELECT
331 publisher_db,
332 publication,
333 MAX(status) AS status,
334 MAX(warning) AS warning,
335 MAX(ISNULL(worst_latency, 0)) AS worst_latency,
336 MIN(CASE WHEN best_latency IS NULL OR best_latency = 0 THEN 999999 ELSE best_latency END) AS best_latency,
337 AVG(ISNULL(avg_latency, 0)) AS avg_latency,
338 SUM(CASE WHEN isagentrunningnow = 1 THEN 1 ELSE 0 END) AS running_agents
339 FROM distribution.dbo.MSreplication_monitordata
340 WHERE publication != 'ALL'
341 GROUP BY publisher_db, publication;
342 `
343
344 // querySubscriptionCount gets subscription counts per publication
345 const querySubscriptionCount = `
346 SELECT
347 p.publisher_db,
348 p.publication,
349 COUNT(s.subscription_type) AS subscription_count
350 FROM distribution.dbo.MSpublications p
351 LEFT JOIN distribution.dbo.MSsubscriptions s
352 ON p.publication_id = s.publication_id
353 GROUP BY p.publisher_db, p.publication;
354 `
355
356 // queryIOStall gets I/O stall (latency) metrics per database from sys.dm_io_virtual_file_stats
357 const queryIOStall = `
358 SELECT
359 DB_NAME(a.database_id) AS database_name,
360 SUM(io_stall_read_ms) AS io_stall_read_ms,
361 SUM(io_stall_write_ms) AS io_stall_write_ms,
362 SUM(io_stall) AS io_stall_total_ms
363 FROM sys.dm_io_virtual_file_stats(NULL, NULL) a
364 INNER JOIN sys.master_files b ON a.database_id = b.database_id AND a.file_id = b.file_id
365 WHERE a.database_id > 4
366 GROUP BY a.database_id;
367 `
368
369 // queryProcessMemory gets SQL Server process memory metrics from sys.dm_os_process_memory
370 const queryProcessMemory = `
371 SELECT
372 physical_memory_in_use_kb * 1024 AS resident_memory_bytes,
373 virtual_address_space_committed_kb * 1024 AS virtual_memory_bytes,
374 memory_utilization_percentage,
375 page_fault_count
376 FROM sys.dm_os_process_memory;
377 `
378
379 // queryOSMemory gets OS memory metrics from sys.dm_os_sys_memory
380 const queryOSMemory = `
381 SELECT
382 (total_physical_memory_kb - available_physical_memory_kb) * 1024 AS os_memory_used_bytes,
383 available_physical_memory_kb * 1024 AS os_memory_available_bytes,
384 (total_page_file_kb - available_page_file_kb) * 1024 AS os_pagefile_used_bytes,
385 available_page_file_kb * 1024 AS os_pagefile_available_bytes
386 FROM sys.dm_os_sys_memory;
387 `
388
389 // queryLogGrowths gets log growth events per database from performance counters
390 const queryLogGrowths = `
391 SELECT
392 RTRIM(instance_name) AS database_name,
393 cntr_value
394 FROM sys.dm_os_performance_counters
395 WHERE object_name LIKE '%Databases%'
396 AND counter_name = 'Log Growths'
397 AND instance_name NOT IN ('_Total', 'mssqlsystemresource');
398 `
399
400 // queryHadrEnabled checks if Always On Availability Groups is enabled
401 const queryHadrEnabled = `
402 SELECT CAST(SERVERPROPERTY('IsHadrEnabled') AS int);
403 `
404
405 // queryAGHealth gets AG-level health rollup
406 const queryAGHealth = `
407 SELECT
408 ag.name AS ag_name,
409 ISNULL(ags.synchronization_health, -1) AS synchronization_health,
410 ISNULL(ags.primary_recovery_health, -1) AS primary_recovery_health,
411 ISNULL(ags.secondary_recovery_health, -1) AS secondary_recovery_health
412 FROM sys.availability_groups AS ag
413 INNER JOIN sys.dm_hadr_availability_group_states AS ags
414 ON ag.group_id = ags.group_id;
415 `
416
417 // queryAGReplicaStates gets per-replica role, connectivity, and sync health
418 const queryAGReplicaStates = `
419 SELECT
420 ag.name AS ag_name,
421 ar.replica_server_name,
422 LOWER(ar.availability_mode_desc) AS availability_mode,
423 LOWER(ar.failover_mode_desc) AS failover_mode,
424 ISNULL(ars.role, -1) AS role,
425 ISNULL(ars.connected_state, -1) AS connected_state,
426 ISNULL(ars.synchronization_health, -1) AS synchronization_health
427 FROM sys.availability_replicas AS ar
428 INNER JOIN sys.availability_groups AS ag
429 ON ar.group_id = ag.group_id
430 INNER JOIN sys.dm_hadr_availability_replica_states AS ars
431 ON ar.replica_id = ars.replica_id;
432 `
433
434 // queryAGDatabaseReplicasPre16 gets per-database replica metrics (SQL Server 2012-2014)
435 // No secondary_lag_seconds
436 const queryAGDatabaseReplicasPre16 = `
437 SELECT
438 ag.name AS ag_name,
439 ar.replica_server_name,
440 DB_NAME(drs.database_id) AS database_name,
441 ISNULL(drs.synchronization_state, -1) AS synchronization_state,
442 CAST(ISNULL(drs.is_suspended, 0) AS int) AS is_suspended,
443 ISNULL(drs.log_send_queue_size, 0) * 1024 AS log_send_queue_size,
444 ISNULL(drs.log_send_rate, 0) * 1024 AS log_send_rate,
445 ISNULL(drs.redo_queue_size, 0) * 1024 AS redo_queue_size,
446 ISNULL(drs.redo_rate, 0) * 1024 AS redo_rate,
447 ISNULL(drs.filestream_send_rate, 0) * 1024 AS filestream_send_rate,
448 -1 AS secondary_lag_seconds
449 FROM sys.dm_hadr_database_replica_states AS drs
450 INNER JOIN sys.availability_replicas AS ar
451 ON drs.replica_id = ar.replica_id
452 INNER JOIN sys.availability_groups AS ag
453 ON drs.group_id = ag.group_id;
454 `
455
456 // queryAGDatabaseReplicas16 gets per-database replica metrics (SQL Server 2016+)
457 // Adds secondary_lag_seconds
458 const queryAGDatabaseReplicas16 = `
459 SELECT
460 ag.name AS ag_name,
461 ar.replica_server_name,
462 DB_NAME(drs.database_id) AS database_name,
463 ISNULL(drs.synchronization_state, -1) AS synchronization_state,
464 CAST(ISNULL(drs.is_suspended, 0) AS int) AS is_suspended,
465 ISNULL(drs.log_send_queue_size, 0) * 1024 AS log_send_queue_size,
466 ISNULL(drs.log_send_rate, 0) * 1024 AS log_send_rate,
467 ISNULL(drs.redo_queue_size, 0) * 1024 AS redo_queue_size,
468 ISNULL(drs.redo_rate, 0) * 1024 AS redo_rate,
469 ISNULL(drs.filestream_send_rate, 0) * 1024 AS filestream_send_rate,
470 ISNULL(drs.secondary_lag_seconds, -1) AS secondary_lag_seconds
471 FROM sys.dm_hadr_database_replica_states AS drs
472 INNER JOIN sys.availability_replicas AS ar
473 ON drs.replica_id = ar.replica_id
474 INNER JOIN sys.availability_groups AS ag
475 ON drs.group_id = ag.group_id;
476 `
477
478 // queryAGCluster gets WSFC cluster quorum state
479 const queryAGCluster = `
480 SELECT
481 quorum_state
482 FROM sys.dm_hadr_cluster;
483 `
484
485 // queryAGClusterMembers gets WSFC cluster member states
486 const queryAGClusterMembers = `
487 SELECT
488 member_name,
489 member_state,
490 number_of_quorum_votes
491 FROM sys.dm_hadr_cluster_members;
492 `
493
494 // queryAGFailoverReadiness gets per-database failover readiness
495 const queryAGFailoverReadiness = `
496 SELECT
497 ag.name AS ag_name,
498 ar.replica_server_name,
499 dcs.database_name,
500 CAST(ISNULL(dcs.is_failover_ready, 0) AS int) AS is_failover_ready,
501 CAST(ISNULL(dcs.is_database_joined, 0) AS int) AS is_database_joined
502 FROM sys.dm_hadr_database_replica_cluster_states AS dcs
503 INNER JOIN sys.availability_replicas AS ar
504 ON dcs.replica_id = ar.replica_id
505 INNER JOIN sys.availability_groups AS ag
506 ON ar.group_id = ag.group_id;
507 `
508
509 // queryAGAutoPageRepair gets page repair event counts per database
510 const queryAGAutoPageRepair = `
511 SELECT
512 DB_NAME(database_id) AS database_name,
513 SUM(CASE WHEN page_status = 4 THEN 1 ELSE 0 END) AS successful_repairs,
514 SUM(CASE WHEN page_status = 5 THEN 1 ELSE 0 END) AS failed_repairs
515 FROM sys.dm_hadr_auto_page_repair
516 GROUP BY database_id;
517 `
518
519 // queryAGThreads gets AG thread usage (SQL Server 2019+ only)
520 const queryAGThreads = `
521 SELECT
522 name AS ag_name,
523 num_capture_threads,
524 num_redo_threads,
525 num_parallel_redo_threads
526 FROM sys.dm_hadr_ag_threads;
527 `
528
529 // waitTypeCategories maps wait types to their categories
530 var waitTypeCategories = map[string]string{
531 "ASYNC_IO_COMPLETION": "Other Disk IO",
532 "ASYNC_NETWORK_IO": "Network IO",
533 "BACKUPIO": "Other Disk IO",
534 "BACKUPBUFFER": "Other Disk IO",
535 "BROKER_DISPATCHER": "Service Broker",
536 "BROKER_FORWARDER": "Service Broker",
537 "BROKER_INIT": "Service Broker",
538 "BROKER_MASTERSTART": "Service Broker",
539 "BROKER_REGISTERALLENDPOINTS": "Service Broker",
540 "BROKER_SERVICE": "Service Broker",
541 "BROKER_SHUTDOWN": "Service Broker",
542 "CXPACKET": "Parallelism",
543 "CXCONSUMER": "Parallelism",
544 "DBMIRROR_DBM_MUTEX": "Mirroring",
545 "DBMIRROR_SEND": "Mirroring",
546 "DTC": "Transaction",
547 "DTC_ABORT_REQUEST": "Transaction",
548 "DTC_RESOLVE": "Transaction",
549 "DTC_STATE": "Transaction",
550 "DTC_TMDOWN_REQUEST": "Transaction",
551 "DTC_WAITFOR_OUTCOME": "Transaction",
552 "FT_COMPROWSET_RWLOCK": "Full Text Search",
553 "FT_IFTS_RWLOCK": "Full Text Search",
554 "FT_IFTS_SCHEDULER_IDLE_WAIT": "Full Text Search",
555 "FT_IFTSHC_MUTEX": "Full Text Search",
556 "FT_MASTER_MERGE": "Full Text Search",
557 "IO_COMPLETION": "Other Disk IO",
558 "IO_QUEUE_LIMIT": "Other Disk IO",
559 "IO_RETRY": "Other Disk IO",
560 "LATCH_DT": "Latch",
561 "LATCH_EX": "Latch",
562 "LATCH_KP": "Latch",
563 "LATCH_NL": "Latch",
564 "LATCH_SH": "Latch",
565 "LATCH_UP": "Latch",
566 "LCK_M_BU": "Lock",
567 "LCK_M_IS": "Lock",
568 "LCK_M_IU": "Lock",
569 "LCK_M_IX": "Lock",
570 "LCK_M_RIn_NL": "Lock",
571 "LCK_M_RIn_S": "Lock",
572 "LCK_M_RIn_U": "Lock",
573 "LCK_M_RIn_X": "Lock",
574 "LCK_M_RS_S": "Lock",
575 "LCK_M_RS_U": "Lock",
576 "LCK_M_RX_S": "Lock",
577 "LCK_M_RX_U": "Lock",
578 "LCK_M_RX_X": "Lock",
579 "LCK_M_S": "Lock",
580 "LCK_M_SCH_M": "Lock",
581 "LCK_M_SCH_S": "Lock",
582 "LCK_M_SIU": "Lock",
583 "LCK_M_SIX": "Lock",
584 "LCK_M_U": "Lock",
585 "LCK_M_UIX": "Lock",
586 "LCK_M_X": "Lock",
587 "LOGBUFFER": "Tran Log IO",
588 "LOGMGR": "Tran Log IO",
589 "LOGMGR_FLUSH": "Tran Log IO",
590 "LOGMGR_PMM_LOG": "Tran Log IO",
591 "LOGMGR_RESERVE_APPEND": "Tran Log IO",
592 "MSQL_DQ": "Network IO",
593 "MSQL_XP": "Network IO",
594 "NET_WAITFOR_PACKET": "Network IO",
595 "OLEDB": "Network IO",
596 "PAGELATCH_DT": "Buffer Latch",
597 "PAGELATCH_EX": "Buffer Latch",
598 "PAGELATCH_KP": "Buffer Latch",
599 "PAGELATCH_NL": "Buffer Latch",
600 "PAGELATCH_SH": "Buffer Latch",
601 "PAGELATCH_UP": "Buffer Latch",
602 "PAGEIOLATCH_DT": "Buffer IO",
603 "PAGEIOLATCH_EX": "Buffer IO",
604 "PAGEIOLATCH_KP": "Buffer IO",
605 "PAGEIOLATCH_NL": "Buffer IO",
606 "PAGEIOLATCH_SH": "Buffer IO",
607 "PAGEIOLATCH_UP": "Buffer IO",
608 "PARALLEL_BACKUP_QUEUE": "Backup",
609 "PARALLEL_REDO_DRAIN_WORKER": "Backup",
610 "PARALLEL_REDO_LOG_CACHE": "Backup",
611 "PARALLEL_REDO_TRAN_LIST": "Backup",
612 "PARALLEL_REDO_WORKER_SYNC": "Backup",
613 "PARALLEL_REDO_WORKER_WAIT_WORK": "Backup",
614 "PREEMPTIVE_ABR": "Preemptive",
615 "PREEMPTIVE_AUDIT_ACCESS_EVENTLOG": "Preemptive",
616 "PREEMPTIVE_AUDIT_ACCESS_SECLOG": "Preemptive",
617 "PREEMPTIVE_CLOSEBACKUPMEDIA": "Preemptive",
618 "PREEMPTIVE_CLOSEBACKUPTAPE": "Preemptive",
619 "PREEMPTIVE_CLOSEBACKUPVDIDEVICE": "Preemptive",
620 "PREEMPTIVE_CLUSAPI_CLUSTERRESOURCECONTROL": "Preemptive",
621 "PREEMPTIVE_COM_COCREATEINSTANCE": "Preemptive",
622 "PREEMPTIVE_COM_COGETCLASSOBJECT": "Preemptive",
623 "PREEMPTIVE_COM_CREATEACCESSOR": "Preemptive",
624 "PREEMPTIVE_COM_DELETEROWS": "Preemptive",
625 "PREEMPTIVE_COM_GETCOMMANDTEXT": "Preemptive",
626 "PREEMPTIVE_COM_GETDATA": "Preemptive",
627 "PREEMPTIVE_COM_GETNEXTROWS": "Preemptive",
628 "PREEMPTIVE_COM_GETRESULT": "Preemptive",
629 "PREEMPTIVE_COM_GETROWSBYBOOKMARK": "Preemptive",
630 "PREEMPTIVE_COM_LBFLUSH": "Preemptive",
631 "PREEMPTIVE_COM_LBLOCKREGION": "Preemptive",
632 "PREEMPTIVE_COM_LBREADAT": "Preemptive",
633 "PREEMPTIVE_COM_LBSETSIZE": "Preemptive",
634 "PREEMPTIVE_COM_LBSTAT": "Preemptive",
635 "PREEMPTIVE_COM_LBUNLOCKREGION": "Preemptive",
636 "PREEMPTIVE_COM_LBWRITEAT": "Preemptive",
637 "PREEMPTIVE_COM_QUERYINTERFACE": "Preemptive",
638 "PREEMPTIVE_COM_RELEASE": "Preemptive",
639 "PREEMPTIVE_COM_RELEASEACCESSOR": "Preemptive",
640 "PREEMPTIVE_COM_RELEASEROWS": "Preemptive",
641 "PREEMPTIVE_COM_RELEASESESSION": "Preemptive",
642 "PREEMPTIVE_COM_RESTARTPOSITION": "Preemptive",
643 "PREEMPTIVE_COM_SEQITHROW": "Preemptive",
644 "PREEMPTIVE_COM_SETDATAFAILURE": "Preemptive",
645 "PREEMPTIVE_COM_SETPARAMETERINFO": "Preemptive",
646 "PREEMPTIVE_COM_SETPARAMETERPROPERTIES": "Preemptive",
647 "PREEMPTIVE_CONSOLEWRITE": "Preemptive",
648 "PREEMPTIVE_CREATEPARAM": "Preemptive",
649 "PREEMPTIVE_DEBUG": "Preemptive",
650 "PREEMPTIVE_DFSADDLINK": "Preemptive",
651 "PREEMPTIVE_DFSLINKEXISTCHECK": "Preemptive",
652 "PREEMPTIVE_DFSLINKHEALTHCHECK": "Preemptive",
653 "PREEMPTIVE_DFSREMOVELINK": "Preemptive",
654 "PREEMPTIVE_DFSREMOVEROOT": "Preemptive",
655 "PREEMPTIVE_DFSROOTFOLDERCHECK": "Preemptive",
656 "PREEMPTIVE_DFSROOTINIT": "Preemptive",
657 "PREEMPTIVE_DFSROOTSHARECHECK": "Preemptive",
658 "PREEMPTIVE_DTC_ABORT": "Preemptive",
659 "PREEMPTIVE_DTC_ABORTREQUESTDONE": "Preemptive",
660 "PREEMPTIVE_DTC_BEGINTRANSACTION": "Preemptive",
661 "PREEMPTIVE_DTC_COMMITREQUESTDONE": "Preemptive",
662 "PREEMPTIVE_DTC_ENLIST": "Preemptive",
663 "PREEMPTIVE_DTC_PREPAREREQUESTDONE": "Preemptive",
664 "PREEMPTIVE_FILESIZEGET": "Preemptive",
665 "PREEMPTIVE_FSAREATEFILLBACKUP": "Preemptive",
666 "PREEMPTIVE_FSACREATERESTORE": "Preemptive",
667 "PREEMPTIVE_FSAFREEHEAP": "Preemptive",
668 "PREEMPTIVE_FSGETTARGETCOMPLETE": "Preemptive",
669 "PREEMPTIVE_FSGETTARGETPREPARE": "Preemptive",
670 "PREEMPTIVE_FSQUERYALLOCATEDRANGES": "Preemptive",
671 "PREEMPTIVE_GETRMIDENTITY": "Preemptive",
672 "PREEMPTIVE_LOCKMONITOR": "Preemptive",
673 "PREEMPTIVE_MSS_RELEASE": "Preemptive",
674 "PREEMPTIVE_ODBCOPS": "Preemptive",
675 "PREEMPTIVE_OLE_UNINIT": "Preemptive",
676 "PREEMPTIVE_OTHER_ABORT": "Preemptive",
677 "PREEMPTIVE_OTHER_ALERTREGISTER": "Preemptive",
678 "PREEMPTIVE_OTHER_ALERTSIGNAL": "Preemptive",
679 "PREEMPTIVE_OTHER_ALERTWAIT": "Preemptive",
680 "PREEMPTIVE_OTHER_ABORTTRANSACTION": "Preemptive",
681 "PREEMPTIVE_OTHER_CREATETHREAD": "Preemptive",
682 "PREEMPTIVE_OTHER_PREPARETOENLIST": "Preemptive",
683 "PREEMPTIVE_OTHER_RECOVER": "Preemptive",
684 "PREEMPTIVE_OTHER_SCHEMAUNLOCK": "Preemptive",
685 "PREEMPTIVE_OTHER_TRANSIMPORT": "Preemptive",
686 "PREEMPTIVE_OS_ACCEPTSECURITYCONTEXT": "Preemptive",
687 "PREEMPTIVE_OS_ACQUIRECREDENTIALSHANDLE": "Preemptive",
688 "PREEMPTIVE_OS_AUTHZGETINFORMATIONFROMCONTEXT": "Preemptive",
689 "PREEMPTIVE_OS_AUTHZINITIALIZERESOURCEMANAGER": "Preemptive",
690 "PREEMPTIVE_OS_BACKUPREAD": "Preemptive",
691 "PREEMPTIVE_OS_CLOSEHANDLE": "Preemptive",
692 "PREEMPTIVE_OS_CLUSTEROPS": "Preemptive",
693 "PREEMPTIVE_OS_COMOPS": "Preemptive",
694 "PREEMPTIVE_OS_COMPLETEAUTHTOKEN": "Preemptive",
695 "PREEMPTIVE_OS_COPYFILE": "Preemptive",
696 "PREEMPTIVE_OS_CREATEDIRECTORY": "Preemptive",
697 "PREEMPTIVE_OS_CREATEFILE": "Preemptive",
698 "PREEMPTIVE_OS_CRYPTOPS": "Preemptive",
699 "PREEMPTIVE_OS_DECRYPTMESSAGE": "Preemptive",
700 "PREEMPTIVE_OS_DELETEFILE": "Preemptive",
701 "PREEMPTIVE_OS_DELETESECURITYCONTEXT": "Preemptive",
702 "PREEMPTIVE_OS_DEVICEIOCONTROL": "Preemptive",
703 "PREEMPTIVE_OS_DEVICEOPS": "Preemptive",
704 "PREEMPTIVE_OS_DIABORPC": "Preemptive",
705 "PREEMPTIVE_OS_DOMAINSERVICEOPS": "Preemptive",
706 "PREEMPTIVE_OS_DSGETDCNAME": "Preemptive",
707 "PREEMPTIVE_OS_DTCOPS": "Preemptive",
708 "PREEMPTIVE_OS_ENCRYPTMESSAGE": "Preemptive",
709 "PREEMPTIVE_OS_FILEOPS": "Preemptive",
710 "PREEMPTIVE_OS_FINDFILE": "Preemptive",
711 "PREEMPTIVE_OS_FLUSHFILEBUFFERS": "Preemptive",
712 "PREEMPTIVE_OS_FORMATMESSAGE": "Preemptive",
713 "PREEMPTIVE_OS_FREECREDENTIALSHANDLE": "Preemptive",
714 "PREEMPTIVE_OS_FREELIBRARY": "Preemptive",
715 "PREEMPTIVE_OS_GENERICOPS": "Preemptive",
716 "PREEMPTIVE_OS_GETADDRINFO": "Preemptive",
717 "PREEMPTIVE_OS_GETCOMPRESSEDFILESIZE": "Preemptive",
718 "PREEMPTIVE_OS_GETDISKFREESPACE": "Preemptive",
719 "PREEMPTIVE_OS_GETFILEATTRIBUTES": "Preemptive",
720 "PREEMPTIVE_OS_GETFILESIZE": "Preemptive",
721 "PREEMPTIVE_OS_GETFINALFILEPATHBYHANDLE": "Preemptive",
722 "PREEMPTIVE_OS_GETLONGPATHNAME": "Preemptive",
723 "PREEMPTIVE_OS_GETTEMPPATHNAME": "Preemptive",
724 "PREEMPTIVE_OS_GETVOLUMEPATHNAMEOFNAME": "Preemptive",
725 "PREEMPTIVE_OS_INITIALIZESECURITYCONTEXT": "Preemptive",
726 "PREEMPTIVE_OS_LIBRARYOPS": "Preemptive",
727 "PREEMPTIVE_OS_LOADLIBRARY": "Preemptive",
728 "PREEMPTIVE_OS_LOGONUSER": "Preemptive",
729 "PREEMPTIVE_OS_LOOKUPACCOUNTSID": "Preemptive",
730 "PREEMPTIVE_OS_MESSAGEQUEUEOPS": "Preemptive",
731 "PREEMPTIVE_OS_MOVEFILE": "Preemptive",
732 "PREEMPTIVE_OS_NETGROUPGETUSERS": "Preemptive",
733 "PREEMPTIVE_OS_NETLOCALGROUPGETMEMBERS": "Preemptive",
734 "PREEMPTIVE_OS_NETUSERGETGROUPS": "Preemptive",
735 "PREEMPTIVE_OS_NETUSERGETLOCALGROUPS": "Preemptive",
736 "PREEMPTIVE_OS_NETUSERMODALSGET": "Preemptive",
737 "PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICY": "Preemptive",
738 "PREEMPTIVE_OS_NETVALIDATEPASSWORDPOLICYFREE": "Preemptive",
739 "PREEMPTIVE_OS_OPENDIRECTORY": "Preemptive",
740 "PREEMPTIVE_OS_PABORPC": "Preemptive",
741 "PREEMPTIVE_OS_PIPEOPS": "Preemptive",
742 "PREEMPTIVE_OS_PROCESSOPS": "Preemptive",
743 "PREEMPTIVE_OS_QUERYREGISTRY": "Preemptive",
744 "PREEMPTIVE_OS_QUERYSECURITYCONTEXTTOKEN": "Preemptive",
745 "PREEMPTIVE_OS_READFILE": "Preemptive",
746 "PREEMPTIVE_OS_REMOVEDIRECTORY": "Preemptive",
747 "PREEMPTIVE_OS_REPORTEVENT": "Preemptive",
748 "PREEMPTIVE_OS_REVERTTOSELF": "Preemptive",
749 "PREEMPTIVE_OS_RABORPC": "Preemptive",
750 "PREEMPTIVE_OS_SABORPC": "Preemptive",
751 "PREEMPTIVE_OS_SECURITYOPS": "Preemptive",
752 "PREEMPTIVE_OS_SERVICEOPS": "Preemptive",
753 "PREEMPTIVE_OS_SETENDOFFILE": "Preemptive",
754 "PREEMPTIVE_OS_SETFILEPOINTER": "Preemptive",
755 "PREEMPTIVE_OS_SETFILEVALIDDATA": "Preemptive",
756 "PREEMPTIVE_OS_SQLTHREADOPS": "Preemptive",
757 "PREEMPTIVE_OS_VERIFYSIGNATURE": "Preemptive",
758 "PREEMPTIVE_OS_WAITFORSINGLEOBJECT": "Preemptive",
759 "PREEMPTIVE_OS_WINSOCKOPS": "Preemptive",
760 "PREEMPTIVE_OS_WRITEFILE": "Preemptive",
761 "PREEMPTIVE_OS_WRITEFILEGATHER": "Preemptive",
762 "PREEMPTIVE_REENLIST": "Preemptive",
763 "PREEMPTIVE_RESIZELOG": "Preemptive",
764 "PREEMPTIVE_ROLLFORWARDREDO": "Preemptive",
765 "PREEMPTIVE_ROLLFORWARDUNDO": "Preemptive",
766 "PREEMPTIVE_SB_STOPENDPOINT": "Preemptive",
767 "PREEMPTIVE_SERVER_STARTUP": "Preemptive",
768 "PREEMPTIVE_SETRMIDENTITY": "Preemptive",
769 "PREEMPTIVE_SHAREDMEM_GETDATA": "Preemptive",
770 "PREEMPTIVE_SNIOPEN": "Preemptive",
771 "PREEMPTIVE_SOSHOST": "Preemptive",
772 "PREEMPTIVE_SOSTESTING": "Preemptive",
773 "PREEMPTIVE_SP_SERVER_DIAGNOSTICS": "Preemptive",
774 "PREEMPTIVE_STARTRM": "Preemptive",
775 "PREEMPTIVE_STREAMFCB_CHECKPOINT": "Preemptive",
776 "PREEMPTIVE_STREAMFCB_RECOVER": "Preemptive",
777 "PREEMPTIVE_STRESSDRIVER": "Preemptive",
778 "PREEMPTIVE_TESTING": "Preemptive",
779 "PREEMPTIVE_TRANSIMPORT": "Preemptive",
780 "PREEMPTIVE_UNMARSHALPROPAGATIONTOKEN": "Preemptive",
781 "PREEMPTIVE_VSS_CREATESNAPSHOT": "Preemptive",
782 "PREEMPTIVE_VSS_CREATEVOLUMESNAPSHOT": "Preemptive",
783 "PREEMPTIVE_XE_CALLBACKEXECUTE": "Preemptive",
784 "PREEMPTIVE_XE_CX_FILE_OPEN": "Preemptive",
785 "PREEMPTIVE_XE_CX_HTTP_CALL": "Preemptive",
786 "PREEMPTIVE_XE_DISPATCHER": "Preemptive",
787 "PREEMPTIVE_XE_ENGINEINIT": "Preemptive",
788 "PREEMPTIVE_XE_GETTARGETSTATE": "Preemptive",
789 "PREEMPTIVE_XE_SESSIONCOMMIT": "Preemptive",
790 "PREEMPTIVE_XE_TARGETFINALIZE": "Preemptive",
791 "PREEMPTIVE_XE_TARGETINIT": "Preemptive",
792 "PREEMPTIVE_XE_TIMERRUN": "Preemptive",
793 "PREEMPTIVE_XETESTING": "Preemptive",
794 "PWAIT_HADR_ACTION_COMPLETED": "Replication",
795 "PWAIT_HADR_CHANGE_NOTIFIER_TERMINATION_SYNC": "Replication",
796 "PWAIT_HADR_CLUSTER_INTEGRATION": "Replication",
797 "PWAIT_HADR_FAILOVER_COMPLETED": "Replication",
798 "PWAIT_HADR_JOIN": "Replication",
799 "PWAIT_HADR_OFFLINE_COMPLETED": "Replication",
800 "PWAIT_HADR_ONLINE_COMPLETED": "Replication",
801 "PWAIT_HADR_POST_ONLINE_COMPLETED": "Replication",
802 "PWAIT_HADR_SERVER_READY_CONNECTIONS": "Replication",
803 "PWAIT_HADR_WORKITEM_COMPLETED": "Replication",
804 "REPLICA_WRITES": "Replication",
805 "RESOURCE_SEMAPHORE": "Memory",
806 "RESOURCE_SEMAPHORE_MUTEX": "Memory",
807 "RESOURCE_SEMAPHORE_QUERY_COMPILE": "Compilation",
808 "RESOURCE_SEMAPHORE_SMALL_QUERY": "Memory",
809 "SOS_PHYS_PAGE_CACHE": "Memory",
810 "SOS_RESERVEDMEMBLOCKLIST": "Memory",
811 "SOS_SCHEDULER_YIELD": "CPU",
812 "SOS_VIRTUALMEMORY_LOW": "Memory",
813 "SOS_WORK_DISPATCHER": "Worker Thread",
814 "SQLCLR_APPDOMAIN": "SQL CLR",
815 "SQLCLR_ASSEMBLY": "SQL CLR",
816 "SQLCLR_DEADLOCK_DETECTION": "SQL CLR",
817 "SQLCLR_QUANTUM_PUNISHMENT": "SQL CLR",
818 "THREADPOOL": "Worker Thread",
819 "TRACEWRITE": "Tracing",
820 "TRAN_MARKLATCH_DT": "Transaction",
821 "TRAN_MARKLATCH_EX": "Transaction",
822 "TRAN_MARKLATCH_KP": "Transaction",
823 "TRAN_MARKLATCH_NL": "Transaction",
824 "TRAN_MARKLATCH_SH": "Transaction",
825 "TRAN_MARKLATCH_UP": "Transaction",
826 "TRANSACTION_MUTEX": "Transaction",
827 "WRITELOG": "Tran Log IO",
828 "XACTLOCKINFO": "Transaction",
829 "XACT_OWN_TRANSACTION": "Transaction",
830 "XACT_RECLAIM_SESSION": "Transaction",
831 "XACT_SNAPSHOT": "Transaction",
832 }
833
834 // getWaitCategory returns the category for a wait type, or "OTHER" if unknown
835 func getWaitCategory(waitType string) string {
836 if cat, ok := waitTypeCategories[waitType]; ok {
837 return cat
838 }
839 return "OTHER"
840 }