Skip to content

Commit cd26fe5

Browse files
Fall back to system_health/event_file when querying deadlocks (#19189)
* query with target * Fall back to system_health event_file if datadog XE doesn't exist * parameter * Fall back to system_health/event_file when querying deadlocks * relnote * linter
1 parent f4d5ccc commit cd26fe5

File tree

5 files changed

+59
-14
lines changed

5 files changed

+59
-14
lines changed

sqlserver/changelog.d/19189.changed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fall back to ``system_health/event_file`` when querying deadlocks if `datadog` XE session wasn't created.

sqlserver/datadog_checks/sqlserver/database_metrics/xe_session_metrics.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
from .base import SqlserverDatabaseMetricsBase
88

9+
XE_RING_BUFFER = "ring_buffer"
10+
XE_EVENT_FILE = "event_file"
11+
912
XE_SESSION_STATUS_QUERY = {
1013
"name": "sys.dm_xe_sessions",
1114
"query": """SELECT

sqlserver/datadog_checks/sqlserver/deadlocks.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from datadog_checks.base.utils.tracking import tracked_method
1212
from datadog_checks.sqlserver.config import SQLServerConfig
1313
from datadog_checks.sqlserver.const import STATIC_INFO_ENGINE_EDITION, STATIC_INFO_VERSION
14+
from datadog_checks.sqlserver.database_metrics.xe_session_metrics import XE_EVENT_FILE, XE_RING_BUFFER
1415
from datadog_checks.sqlserver.queries import (
1516
DEADLOCK_TIMESTAMP_ALIAS,
1617
DEADLOCK_XML_ALIAS,
@@ -53,6 +54,7 @@ def __init__(self, check, config: SQLServerConfig):
5354
self.collection_interval = config.deadlocks_config.get("collection_interval", DEFAULT_COLLECTION_INTERVAL)
5455
self._force_convert_xml_to_str = False
5556
self._xe_session_name = None
57+
self._xe_session_target = None
5658
super(Deadlocks, self).__init__(
5759
check,
5860
run_sync=True,
@@ -134,14 +136,24 @@ def _set_xe_session_name(self):
134136
if not rows:
135137
raise NoXESessionError(NO_XE_SESSION_ERROR)
136138
xe_system_found = False
139+
xe_system_xe_file_found = False
137140
for row in rows:
138-
if (session := row[0]) in (XE_SESSION_DATADOG):
141+
(session, target) = row
142+
if session in (XE_SESSION_DATADOG):
139143
self._xe_session_name = session
144+
self._xe_session_target = target
140145
return
141146
if session == XE_SESSION_SYSTEM:
142147
xe_system_found = True
148+
if target == XE_EVENT_FILE:
149+
xe_system_xe_file_found = True
150+
143151
if xe_system_found:
144152
self._xe_session_name = XE_SESSION_SYSTEM
153+
if xe_system_xe_file_found:
154+
self._xe_session_target = XE_EVENT_FILE
155+
else:
156+
self._xe_session_target = XE_RING_BUFFER
145157
return
146158
raise NoXESessionError(NO_XE_SESSION_ERROR)
147159

@@ -152,15 +164,19 @@ def _query_deadlocks(self):
152164
except NoXESessionError as e:
153165
self._log.error(str(e))
154166
return
155-
self._log.info(f'Using XE session {self._xe_session_name} to collect deadlocks')
167+
self._log.info(
168+
f'Using XE session [{self._xe_session_name}], target [{self._xe_session_target}] to collect deadlocks'
169+
)
156170

157171
with self._check.connection.open_managed_default_connection(key_prefix=self._conn_key_prefix):
158172
with self._check.connection.get_managed_cursor(key_prefix=self._conn_key_prefix) as cursor:
159173
convert_xml_to_str = False
160174
if self._force_convert_xml_to_str or self._get_connector() == "adodbapi":
161175
convert_xml_to_str = True
162176
query = get_deadlocks_query(
163-
convert_xml_to_str=convert_xml_to_str, xe_session_name=self._xe_session_name
177+
convert_xml_to_str=convert_xml_to_str,
178+
xe_session_name=self._xe_session_name,
179+
xe_target_name=self._xe_session_target,
164180
)
165181
lookback = self._get_lookback_seconds()
166182
self._log.debug(

sqlserver/datadog_checks/sqlserver/queries.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# All rights reserved
33
# Licensed under a 3-clause BSD style license (see LICENSE)
44

5+
from datadog_checks.sqlserver.database_metrics.xe_session_metrics import XE_RING_BUFFER
56

67
DB_QUERY = """
78
SELECT
@@ -135,22 +136,21 @@
135136
XE_SESSION_SYSTEM = "system_health"
136137
XE_SESSIONS_QUERY = f"""
137138
SELECT
138-
s.name AS session_name
139+
s.name AS session_name, t.target_name AS target_name
139140
FROM
140141
sys.dm_xe_sessions s
141142
JOIN
142143
sys.dm_xe_session_targets t
143144
ON s.address = t.event_session_address
144145
WHERE
145-
t.target_name = 'ring_buffer'
146-
AND s.name IN ('{XE_SESSION_DATADOG}', '{XE_SESSION_SYSTEM}');
146+
s.name IN ('{XE_SESSION_DATADOG}', '{XE_SESSION_SYSTEM}');
147147
"""
148148

149149
DEADLOCK_TIMESTAMP_ALIAS = "timestamp"
150150
DEADLOCK_XML_ALIAS = "event_xml"
151151

152152

153-
def get_deadlocks_query(convert_xml_to_str=False, xe_session_name="datadog"):
153+
def get_deadlocks_query(convert_xml_to_str=False, xe_session_name=XE_SESSION_DATADOG, xe_target_name=XE_RING_BUFFER):
154154
"""
155155
Construct the query to fetch deadlocks from the system_health extended event session
156156
:param convert_xml_to_str: Whether to convert the XML to a string. This option is for MSOLEDB drivers
@@ -161,16 +161,27 @@ def get_deadlocks_query(convert_xml_to_str=False, xe_session_name="datadog"):
161161
if convert_xml_to_str:
162162
xml_expression = "CAST(xdr.query('.') AS NVARCHAR(MAX))"
163163

164-
return f"""
165-
SELECT TOP(?) xdr.value('@timestamp', 'datetime') AS [{DEADLOCK_TIMESTAMP_ALIAS}],
166-
{xml_expression} AS [{DEADLOCK_XML_ALIAS}]
164+
if xe_target_name == XE_RING_BUFFER:
165+
return f"""SELECT TOP(?) xdr.value('@timestamp', 'datetime') AS [{DEADLOCK_TIMESTAMP_ALIAS}],
166+
{xml_expression} AS [{DEADLOCK_XML_ALIAS}]
167167
FROM (SELECT CAST([target_data] AS XML) AS Target_Data
168168
FROM sys.dm_xe_session_targets AS xt
169169
INNER JOIN sys.dm_xe_sessions AS xs ON xs.address = xt.event_session_address
170170
WHERE xs.name = N'{xe_session_name}'
171-
AND xt.target_name = N'ring_buffer'
171+
AND xt.target_name = N'{XE_RING_BUFFER}'
172172
) AS XML_Data
173173
CROSS APPLY Target_Data.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS XEventData(xdr)
174174
WHERE xdr.value('@timestamp', 'datetime')
175175
>= DATEADD(SECOND, ?, TODATETIMEOFFSET(GETDATE(), DATEPART(TZOFFSET, SYSDATETIMEOFFSET())) AT TIME ZONE 'UTC')
176176
;"""
177+
178+
return f"""SELECT TOP(?)
179+
event_data AS [{DEADLOCK_XML_ALIAS}],
180+
CONVERT(xml, event_data).value('(event[@name="xml_deadlock_report"]/@timestamp)[1]','datetime')
181+
AS [{DEADLOCK_TIMESTAMP_ALIAS}]
182+
FROM
183+
sys.fn_xe_file_target_read_file
184+
('system_health*.xel', null, null, null)
185+
WHERE object_name like 'xml_deadlock_report'
186+
and CONVERT(xml, event_data).value('(event[@name="xml_deadlock_report"]/@timestamp)[1]','datetime')
187+
>= DATEADD(SECOND, ?, TODATETIMEOFFSET(GETDATE(), DATEPART(TZOFFSET, SYSDATETIMEOFFSET())) AT TIME ZONE 'UTC');"""

sqlserver/tests/test_deadlocks.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
from mock import patch
1717

1818
from datadog_checks.sqlserver import SQLServer
19+
from datadog_checks.sqlserver.database_metrics.xe_session_metrics import XE_EVENT_FILE, XE_RING_BUFFER
1920
from datadog_checks.sqlserver.deadlocks import (
2021
PAYLOAD_QUERY_SIGNATURE,
2122
PAYLOAD_TIMESTAMP,
22-
XE_SESSION_DATADOG,
2323
Deadlocks,
2424
)
25-
from datadog_checks.sqlserver.queries import DEADLOCK_TIMESTAMP_ALIAS, DEADLOCK_XML_ALIAS
25+
from datadog_checks.sqlserver.queries import (
26+
DEADLOCK_TIMESTAMP_ALIAS,
27+
DEADLOCK_XML_ALIAS,
28+
XE_SESSION_DATADOG,
29+
XE_SESSION_SYSTEM,
30+
)
2631

2732
from .common import CHECK_NAME
2833

@@ -137,9 +142,18 @@ def _create_deadlock(dd_environment, dbm_instance):
137142
@pytest.mark.usefixtures('dd_environment')
138143
@pytest.mark.usefixtures('_create_deadlock')
139144
@pytest.mark.parametrize("convert_xml_to_str", [False, True])
140-
def test_deadlocks(aggregator, dd_run_check, dbm_instance, convert_xml_to_str):
145+
@pytest.mark.parametrize(
146+
"xe_session_name, xe_session_target",
147+
[
148+
[XE_SESSION_DATADOG, XE_RING_BUFFER],
149+
[XE_SESSION_SYSTEM, XE_EVENT_FILE],
150+
],
151+
)
152+
def test_deadlocks(aggregator, dd_run_check, dbm_instance, convert_xml_to_str, xe_session_name, xe_session_target):
141153
check = SQLServer(CHECK_NAME, {}, [dbm_instance])
142154
check.deadlocks._force_convert_xml_to_str = convert_xml_to_str
155+
check.deadlocks._xe_session_name = xe_session_name
156+
check.deadlocks._xe_session_target = xe_session_target
143157

144158
dbm_instance['dbm_enabled'] = True
145159
deadlock_payloads = _run_check_and_get_deadlock_payloads(dd_run_check, check, aggregator)

0 commit comments

Comments
 (0)