Skip to content

Commit 190bd4d

Browse files
authored
Parameterize SQL Server agent history query (#24203)
* Parameterize SQL Server agent history query * Add SQL Server changelog entry
1 parent ca6e876 commit 190bd4d

3 files changed

Lines changed: 56 additions & 27 deletions

File tree

sqlserver/changelog.d/24203.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Parameterize SQL Server Agent job history collection to avoid plan cache churn.

sqlserver/datadog_checks/sqlserver/agent_history.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
message
9595
FROM HISTORY_ENTRIES
9696
WHERE
97-
completion_epoch_time > {last_collection_time_filter};
97+
completion_epoch_time > ?;
9898
"""
9999

100100

@@ -136,14 +136,12 @@ def run_job(self):
136136

137137
@tracked_method(agent_check_getter=agent_check_getter)
138138
def _get_new_agent_job_history(self, cursor):
139-
last_collection_time_filter = "{last_collection_time}".format(last_collection_time=self._last_collection_time)
140139
history_row_limit_filter = "TOP {history_row_limit}".format(history_row_limit=self.history_row_limit)
141-
query = AGENT_HISTORY_QUERY.format(
142-
history_row_limit_filter=history_row_limit_filter, last_collection_time_filter=last_collection_time_filter
143-
)
140+
query = AGENT_HISTORY_QUERY.format(history_row_limit_filter=history_row_limit_filter)
141+
params = (self._last_collection_time,)
144142
self.log.debug("collecting sql server agent jobs history")
145-
self.log.debug("Running query [%s]", query)
146-
cursor.execute(query)
143+
self.log.debug("Running query [%s] %s", query, params)
144+
cursor.execute(query, params)
147145
columns = [i[0] for i in cursor.description]
148146
# construct row dicts manually as there's no DictCursor for pyodbc
149147
rows = [dict(zip(columns, row)) for row in cursor.fetchall()]

sqlserver/tests/test_agent_jobs.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import logging
66
import time
77
from copy import copy
8+
from unittest.mock import Mock
89

910
import pytest
1011

1112
from datadog_checks.sqlserver import SQLServer
13+
from datadog_checks.sqlserver.agent_history import SqlserverAgentHistory
1214

1315
from .common import (
1416
CHECK_NAME,
@@ -96,7 +98,7 @@
9698
message
9799
FROM HISTORY_ENTRIES
98100
WHERE
99-
completion_epoch_time > {last_collection_time_filter};
101+
completion_epoch_time > ?;
100102
"""
101103

102104

@@ -176,7 +178,7 @@
176178
message
177179
FROM HISTORY_ENTRIES
178180
WHERE
179-
completion_epoch_time > 10000;
181+
completion_epoch_time > ?;
180182
"""
181183

182184
AGENT_ACTIVITY_DURATION_QUERY = """\
@@ -404,16 +406,52 @@ def test_connection_with_agent_history(instance_docker):
404406

405407
with check.connection.open_managed_default_connection(KEY_PREFIX):
406408
with check.connection.get_managed_cursor(KEY_PREFIX) as cursor:
407-
last_collection_time_filter = "{last_collection_time}".format(last_collection_time=10000)
408409
history_row_limit_filter = "TOP {history_row_limit}".format(history_row_limit=10000)
409-
query = AGENT_HISTORY_QUERY.format(
410-
history_row_limit_filter=history_row_limit_filter,
411-
last_collection_time_filter=last_collection_time_filter,
412-
)
413-
cursor.execute(query)
410+
query = AGENT_HISTORY_QUERY.format(history_row_limit_filter=history_row_limit_filter)
411+
cursor.execute(query, (10000,))
414412
assert query == FORMATTED_HISTORY_QUERY
415413

416414

415+
class AgentHistoryCursor:
416+
description = [('run_epoch_time',), ('run_duration_seconds',)]
417+
418+
def __init__(self) -> None:
419+
self.executions: list[tuple[str, tuple[int, ...]]] = []
420+
421+
def execute(self, query: str, params: tuple[int, ...]) -> None:
422+
self.executions.append((query, params))
423+
424+
def fetchall(self) -> list[tuple[int, int]]:
425+
return []
426+
427+
428+
class AgentHistoryCheck:
429+
name = CHECK_NAME
430+
431+
def __init__(self) -> None:
432+
self.log = Mock()
433+
self.count = Mock()
434+
self.gauge = Mock()
435+
self.histogram = Mock()
436+
437+
438+
def test_agent_history_query_parameterizes_last_collection_time() -> None:
439+
check = AgentHistoryCheck()
440+
agent_history = object.__new__(SqlserverAgentHistory)
441+
agent_history._check = check
442+
agent_history.log = check.log
443+
agent_history.history_row_limit = 10000
444+
agent_history._last_collection_time = 10000
445+
cursor = AgentHistoryCursor()
446+
447+
agent_history._get_new_agent_job_history(cursor)
448+
449+
query, params = cursor.executions[0]
450+
assert "completion_epoch_time > ?;" in query
451+
assert "completion_epoch_time > 10000;" not in query
452+
assert params == (10000,)
453+
454+
417455
@pytest.mark.usefixtures('dd_environment')
418456
def test_connection_with_agent_activity_duration(instance_docker):
419457
check = SQLServer(CHECK_NAME, {}, [instance_docker])
@@ -466,23 +504,15 @@ def test_history_output(instance_docker, sa_conn):
466504
check.initialize_connection()
467505
with check.connection.open_managed_default_connection(KEY_PREFIX):
468506
with check.connection.get_managed_cursor(KEY_PREFIX) as cursor:
469-
last_collection_time_filter = "{last_collection_time}".format(last_collection_time=now - 1)
470507
history_row_limit_filter = "TOP {history_row_limit}".format(history_row_limit=10000)
471-
query = AGENT_HISTORY_QUERY.format(
472-
history_row_limit_filter=history_row_limit_filter,
473-
last_collection_time_filter=last_collection_time_filter,
474-
)
475-
cursor.execute(query)
508+
query = AGENT_HISTORY_QUERY.format(history_row_limit_filter=history_row_limit_filter)
509+
cursor.execute(query, (now - 1,))
476510
results = cursor.fetchall()
477511
assert len(results) == 7, "should have 7 steps associated with completed jobs"
478512
assert len(results[0]) == 10, "should have 10 columns per step"
479-
last_collection_time_filter = "{last_collection_time}".format(last_collection_time=now + 1)
480513
history_row_limit_filter = "TOP {history_row_limit}".format(history_row_limit=10000)
481-
query = AGENT_HISTORY_QUERY.format(
482-
history_row_limit_filter=history_row_limit_filter,
483-
last_collection_time_filter=last_collection_time_filter,
484-
)
485-
cursor.execute(query)
514+
query = AGENT_HISTORY_QUERY.format(history_row_limit_filter=history_row_limit_filter)
515+
cursor.execute(query, (now + 1,))
486516
results = cursor.fetchall()
487517
assert len(results) == 4, (
488518
"should only have 4 steps associated with completed jobs when filtering with last collection time"

0 commit comments

Comments
 (0)