Skip to content

Commit cdc2e6d

Browse files
Merge pull request #88 from erikdarlingdata/feature/86-collector-chain-triggers
Chain-trigger parsers, optimize XML collection, tune schedule
2 parents d5df71c + cc15b19 commit cdc2e6d

4 files changed

Lines changed: 270 additions & 124 deletions

File tree

install/04_create_schedule_table.sql

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,39 +43,39 @@ SELECT
4343
FROM
4444
(
4545
VALUES
46-
(N'wait_stats_collector', 1, 5, 2, 30, N'Wait statistics - high frequency for trending'),
46+
(N'wait_stats_collector', 1, 1, 2, 30, N'Wait statistics - high frequency for trending'),
4747
(N'query_stats_collector', 1, 2, 5, 30, N'Plan cache queries - recent activity focused'),
48-
(N'memory_stats_collector', 1, 5, 2, 30, N'Memory pressure monitoring'),
49-
(N'memory_pressure_events_collector', 1, 5, 5, 30, N'Ring buffer system events'),
48+
(N'memory_stats_collector', 1, 1, 2, 30, N'Memory pressure monitoring'),
49+
(N'memory_pressure_events_collector', 1, 1, 5, 30, N'Ring buffer system events'),
5050
(N'system_health_collector', 1, 5, 10, 30, N'System health extended events via sp_HealthParser'),
51-
(N'blocked_process_xml_collector', 1, 5, 2, 30, N'Fast blocked process XML collection'),
52-
(N'deadlock_xml_collector', 1, 5, 3, 30, N'Fast deadlock XML collection'),
53-
(N'process_blocked_process_xml', 1, 5, 5, 30, N'Parse blocked process XML via sp_HumanEventsBlockViewer'),
54-
(N'blocking_deadlock_analyzer', 1, 5, 5, 30, N'Analyze blocking/deadlock trends and alert on significant increases'),
55-
(N'process_deadlock_xml', 1, 5, 5, 30, N'Parse deadlock XML via sp_BlitzLock'),
56-
(N'query_store_collector', 1, 5, 10, 30, N'Query Store data collection'),
57-
(N'procedure_stats_collector', 1, 5, 10, 30, N'Procedure/trigger/function statistics'),
51+
(N'blocked_process_xml_collector', 1, 1, 2, 30, N'Fast blocked process XML collection (chain-triggers parser + analyzer)'),
52+
(N'deadlock_xml_collector', 1, 1, 3, 30, N'Fast deadlock XML collection (chain-triggers parser + analyzer)'),
53+
(N'process_blocked_process_xml', 1, 5, 5, 30, N'Parse blocked process XML via sp_HumanEventsBlockViewer (also chain-triggered)'),
54+
(N'blocking_deadlock_analyzer', 1, 5, 5, 30, N'Analyze blocking/deadlock trends (also chain-triggered)'),
55+
(N'process_deadlock_xml', 1, 5, 5, 30, N'Parse deadlock XML via sp_BlitzLock (also chain-triggered)'),
56+
(N'query_store_collector', 1, 2, 10, 30, N'Query Store data collection'),
57+
(N'procedure_stats_collector', 1, 2, 10, 30, N'Procedure/trigger/function statistics'),
5858
(N'query_snapshots_collector', 1, 1, 2, 10, N'Currently executing queries with session wait stats (every minute - high frequency)'),
59-
(N'file_io_stats_collector', 1, 5, 2, 30, N'File I/O statistics from dm_io_virtual_file_stats'),
60-
(N'memory_grant_stats_collector', 1, 5, 2, 30, N'Memory grant semaphore pressure monitoring'),
61-
(N'cpu_scheduler_stats_collector', 1, 5, 2, 30, N'CPU scheduler and workload group statistics'),
59+
(N'file_io_stats_collector', 1, 1, 2, 30, N'File I/O statistics from dm_io_virtual_file_stats'),
60+
(N'memory_grant_stats_collector', 1, 1, 2, 30, N'Memory grant semaphore pressure monitoring'),
61+
(N'cpu_scheduler_stats_collector', 1, 1, 2, 30, N'CPU scheduler and workload group statistics'),
6262
(N'memory_clerks_stats_collector', 1, 5, 3, 30, N'Memory clerk allocation tracking'),
6363
(N'perfmon_stats_collector', 1, 5, 2, 30, N'Performance counter statistics from dm_os_performance_counters'),
64-
(N'cpu_utilization_stats_collector', 1, 5, 2, 30, N'CPU utilization from ring buffer (SQL vs other processes)'),
64+
(N'cpu_utilization_stats_collector', 1, 1, 2, 30, N'CPU utilization from ring buffer (SQL vs other processes)'),
6565
(N'trace_management_collector', 1, 1440, 5, 30, N'SQL Trace management for long-running queries'),
66-
(N'trace_analysis_collector', 1, 5, 5, 30, N'Process trace files into analysis tables'),
66+
(N'trace_analysis_collector', 1, 2, 5, 30, N'Process trace files into analysis tables'),
6767
(N'default_trace_collector', 1, 5, 3, 30, N'System events from default trace (memory, autogrow, config changes)'),
6868
(N'server_configuration_collector', 1, 1440, 5, 30, N'Server-level configuration settings and trace flags (daily collection)'),
6969
(N'database_configuration_collector', 1, 1440, 10, 30, N'Database-level configuration settings including scoped configs (daily collection)'),
7070
(N'configuration_issues_analyzer', 1, 1, 2, 30, N'Analyze configuration for issues: database config (Query Store, auto shrink/close), memory/CPU pressure warnings, server config (MAXDOP, priority boost)'),
71-
(N'latch_stats_collector', 1, 5, 3, 30, N'Latch contention statistics - internal synchronization object waits'),
72-
(N'spinlock_stats_collector', 1, 5, 3, 30, N'Spinlock contention statistics - lightweight synchronization primitive collisions'),
73-
(N'tempdb_stats_collector', 1, 5, 2, 30, N'TempDB space usage - version store, user/internal objects, allocation contention'),
71+
(N'latch_stats_collector', 1, 1, 3, 30, N'Latch contention statistics - internal synchronization object waits'),
72+
(N'spinlock_stats_collector', 1, 1, 3, 30, N'Spinlock contention statistics - lightweight synchronization primitive collisions'),
73+
(N'tempdb_stats_collector', 1, 1, 2, 30, N'TempDB space usage - version store, user/internal objects, allocation contention'),
7474
(N'plan_cache_stats_collector', 1, 5, 5, 30, N'Plan cache composition statistics - single-use plans and plan cache bloat detection'),
75-
(N'session_stats_collector', 1, 5, 2, 30, N'Session and connection statistics - connection leaks and application patterns'),
76-
(N'waiting_tasks_collector', 1, 5, 2, 30, N'Currently waiting tasks - blocking chains and wait analysis'),
75+
(N'session_stats_collector', 1, 1, 2, 30, N'Session and connection statistics - connection leaks and application patterns'),
76+
(N'waiting_tasks_collector', 1, 1, 2, 30, N'Currently waiting tasks - blocking chains and wait analysis'),
7777
(N'session_wait_stats_collector', 1, 1, 2, 30, N'Per-session wait statistics - correlates waits with specific sessions/queries (requires SQL Server 2016 SP1+)'),
78-
(N'running_jobs_collector', 1, 5, 2, 7, N'Currently running SQL Agent jobs with historical duration comparison')
78+
(N'running_jobs_collector', 1, 1, 2, 7, N'Currently running SQL Agent jobs with historical duration comparison')
7979
) AS v (collector_name, enabled, frequency_minutes, max_duration_minutes, retention_days, description)
8080
WHERE NOT EXISTS
8181
(

install/22_collect_blocked_processes.sql

Lines changed: 122 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -162,89 +162,103 @@ BEGIN
162162
IF @is_azure_sql_db = 1
163163
BEGIN
164164
SET @sql = N'
165-
WITH
166-
ring_buffer_xml AS
165+
DECLARE
166+
@ring_buffer TABLE
167167
(
168-
SELECT
169-
target_data = TRY_CAST(xet.target_data AS xml)
170-
FROM sys.dm_xe_database_session_targets AS xet
171-
JOIN sys.dm_xe_database_sessions AS xes
172-
ON xes.address = xet.event_session_address
173-
WHERE xes.name = @session_name
174-
AND xet.target_name = N''ring_buffer''
175-
),
176-
recent_events AS
168+
ring_buffer xml NOT NULL
169+
);
170+
171+
INSERT
172+
@ring_buffer
177173
(
178-
SELECT TOP (1000)
179-
event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)''),
180-
blocked_process_xml = evt.query(''.'')
181-
FROM ring_buffer_xml AS rb
182-
CROSS APPLY rb.target_data.nodes(''RingBufferTarget/event[@name="blocked_process_report"]'') AS q(evt)
183-
WHERE evt.value(''(@timestamp)[1]'', ''datetime2(7)'') >= @cutoff_time
184-
ORDER BY
185-
evt.value(''(@timestamp)[1]'', ''datetime2(7)'') DESC
174+
ring_buffer
186175
)
176+
SELECT
177+
ring_xml = TRY_CAST(xet.target_data AS xml)
178+
FROM sys.dm_xe_database_session_targets AS xet
179+
JOIN sys.dm_xe_database_sessions AS xes
180+
ON xes.address = xet.event_session_address
181+
WHERE xes.name = @session_name
182+
AND xet.target_name = N''ring_buffer''
183+
OPTION(RECOMPILE);
184+
187185
INSERT INTO
188186
collect.blocked_process_xml
189187
(
190188
event_time,
191189
blocked_process_xml
192190
)
193-
SELECT
194-
re.event_time,
195-
re.blocked_process_xml
196-
FROM recent_events AS re
197-
WHERE NOT EXISTS
191+
SELECT TOP (1000)
192+
event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)''),
193+
blocked_process_xml = evt.query(''.'')
194+
FROM
195+
(
196+
SELECT
197+
rb.ring_buffer
198+
FROM @ring_buffer AS rb
199+
) AS rb
200+
CROSS APPLY rb.ring_buffer.nodes(''RingBufferTarget/event[@name="blocked_process_report"]'') AS q(evt)
201+
WHERE evt.value(''(@timestamp)[1]'', ''datetime2(7)'') >= @cutoff_time
202+
AND NOT EXISTS
198203
(
199204
SELECT
200205
1/0
201206
FROM collect.blocked_process_xml AS bx
202-
WHERE bx.event_time = re.event_time
207+
WHERE bx.event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)'')
203208
)
209+
ORDER BY
210+
evt.value(''(@timestamp)[1]'', ''datetime2(7)'') DESC
204211
OPTION(RECOMPILE);';
205212
END;
206213
ELSE
207214
BEGIN
208215
SET @sql = N'
209-
WITH
210-
ring_buffer_xml AS
216+
DECLARE
217+
@ring_buffer TABLE
211218
(
212-
SELECT
213-
target_data = TRY_CAST(xet.target_data AS xml)
214-
FROM sys.dm_xe_session_targets AS xet
215-
JOIN sys.dm_xe_sessions AS xes
216-
ON xes.address = xet.event_session_address
217-
WHERE xes.name = @session_name
218-
AND xet.target_name = N''ring_buffer''
219-
),
220-
recent_events AS
219+
ring_buffer xml NOT NULL
220+
);
221+
222+
INSERT
223+
@ring_buffer
221224
(
222-
SELECT TOP (1000)
223-
event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)''),
224-
blocked_process_xml = evt.query(''.'')
225-
FROM ring_buffer_xml AS rb
226-
CROSS APPLY rb.target_data.nodes(''RingBufferTarget/event[@name="blocked_process_report"]'') AS q(evt)
227-
WHERE evt.value(''(@timestamp)[1]'', ''datetime2(7)'') >= @cutoff_time
228-
ORDER BY
229-
evt.value(''(@timestamp)[1]'', ''datetime2(7)'') DESC
225+
ring_buffer
230226
)
227+
SELECT
228+
ring_xml = TRY_CAST(xet.target_data AS xml)
229+
FROM sys.dm_xe_session_targets AS xet
230+
JOIN sys.dm_xe_sessions AS xes
231+
ON xes.address = xet.event_session_address
232+
WHERE xes.name = @session_name
233+
AND xet.target_name = N''ring_buffer''
234+
OPTION(RECOMPILE);
235+
231236
INSERT INTO
232237
collect.blocked_process_xml
233238
(
234239
event_time,
235240
blocked_process_xml
236241
)
237-
SELECT
238-
re.event_time,
239-
re.blocked_process_xml
240-
FROM recent_events AS re
241-
WHERE NOT EXISTS
242+
SELECT TOP (1000)
243+
event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)''),
244+
blocked_process_xml = evt.query(''.'')
245+
FROM
246+
(
247+
SELECT
248+
rb.ring_buffer
249+
FROM @ring_buffer AS rb
250+
) AS rb
251+
CROSS APPLY rb.ring_buffer.nodes(''RingBufferTarget/event[@name="blocked_process_report"]'') AS q(evt)
252+
WHERE evt.value(''(@timestamp)[1]'', ''datetime2(7)'') >= @cutoff_time
253+
AND NOT EXISTS
242254
(
243255
SELECT
244256
1/0
245257
FROM collect.blocked_process_xml AS bx
246-
WHERE bx.event_time = re.event_time
258+
WHERE bx.event_time = evt.value(''(@timestamp)[1]'', ''datetime2(7)'')
247259
)
260+
ORDER BY
261+
evt.value(''(@timestamp)[1]'', ''datetime2(7)'') DESC
248262
OPTION(RECOMPILE);';
249263
END;
250264

@@ -303,6 +317,64 @@ BEGIN
303317

304318
COMMIT TRANSACTION;
305319

320+
/*
321+
Chain-trigger: when new blocked process XML is found, immediately
322+
parse it and run the analyzer instead of waiting for their next
323+
scheduled runs. This eliminates up to 10 minutes of pipeline latency.
324+
Parser/analyzer errors are logged but do not fail this collector.
325+
*/
326+
IF @rows_collected > 0
327+
BEGIN
328+
IF @debug = 1
329+
BEGIN
330+
RAISERROR(N'Chain-triggering blocked process parser and analyzer', 0, 1) WITH NOWAIT;
331+
END;
332+
333+
BEGIN TRY
334+
EXECUTE collect.process_blocked_process_xml
335+
@debug = @debug;
336+
END TRY
337+
BEGIN CATCH
338+
INSERT INTO
339+
config.collection_log
340+
(
341+
collector_name,
342+
collection_status,
343+
duration_ms,
344+
error_message
345+
)
346+
VALUES
347+
(
348+
N'blocked_process_xml_collector',
349+
N'CHAIN_ERROR',
350+
DATEDIFF(MILLISECOND, @start_time, SYSDATETIME()),
351+
N'Chain-triggered parser failed: ' + ERROR_MESSAGE()
352+
);
353+
END CATCH;
354+
355+
BEGIN TRY
356+
EXECUTE collect.blocking_deadlock_analyzer
357+
@debug = @debug;
358+
END TRY
359+
BEGIN CATCH
360+
INSERT INTO
361+
config.collection_log
362+
(
363+
collector_name,
364+
collection_status,
365+
duration_ms,
366+
error_message
367+
)
368+
VALUES
369+
(
370+
N'blocked_process_xml_collector',
371+
N'CHAIN_ERROR',
372+
DATEDIFF(MILLISECOND, @start_time, SYSDATETIME()),
373+
N'Chain-triggered analyzer failed: ' + ERROR_MESSAGE()
374+
);
375+
END CATCH;
376+
END;
377+
306378
END TRY
307379
BEGIN CATCH
308380
IF @@TRANCOUNT > 0

0 commit comments

Comments
 (0)