Skip to content

Commit 6e83e26

Browse files
Rework CDC exclusion: program_name->job_id via msdb, text fallback (#1096)
Replace the text-only CDC filter with a precise, two-tier signal: 1. Primary: resolve capture job_id(s) from msdb.dbo.cdc_jobs (job_type = 'capture') and match the running session by decoding its SQL Agent program_name ('SQLAgent - TSQL JobStep (Job 0x<job_id> : Step N)'). The decode -- CONVERT(uniqueidentifier, CONVERT(binary(16), SUBSTRING(program_name, 32, 32), 2)) -- was validated by round-tripping real msdb.dbo.sysjobs rows. This is CDC-specific and never hides other Agent jobs. 2. Fallback: when the account can't read msdb.dbo.cdc_jobs (or the table doesn't exist -- it is created lazily on first CDC config), match the whole batch/object text (sp_MScdc_capture_job / sp_cdc_scan). The msdb reference is deferred through sp_executesql inside TRY/CATCH so an inaccessible msdb raises a *catchable* error (not an uncatchable cross-db 916) and the query degrades to the text path instead of failing. Verified against SQL 2016: with cdc_jobs absent, the TRY/CATCH sets @cdc_readable=0, the batch completes, and the text fallback engages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent dc0a945 commit 6e83e26

1 file changed

Lines changed: 36 additions & 6 deletions

File tree

Dashboard/Services/DatabaseService.NocHealth.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -766,14 +766,44 @@ private async Task<List<LongRunningQueryInfo>> GetLongRunningQueriesAsync(
766766
string miscWaitsFilter = excludeMiscWaits
767767
? "AND r.wait_type NOT IN (N'XE_LIVE_TARGET_TVF')" : "";
768768
// CDC capture runs continuously as a SQL Agent job (EXEC sys.sp_MScdc_capture_job -> sys.sp_cdc_scan),
769-
// so it permanently exceeds the duration threshold. Filter on request text, not program_name, to stay
770-
// CDC-specific and avoid hiding unrelated Agent jobs (ETL, index maintenance). Keep NULL text (encrypted
771-
// modules) visible -- only drop rows we can positively identify as CDC.
772-
string cdcFilter = excludeCdc
773-
? "AND (t.text IS NULL OR (t.text NOT LIKE N'%sp_MScdc_capture_job%' AND t.text NOT LIKE N'%sp_cdc_scan%'))" : "";
769+
// so it permanently exceeds the duration threshold and none of the wait_type filters above catch it.
770+
//
771+
// Primary signal: resolve the capture job_id(s) from msdb.dbo.cdc_jobs and match the running session via
772+
// its SQL Agent program_name ('SQLAgent - TSQL JobStep (Job 0x<job_id> : Step N)'). This is CDC-specific
773+
// and never hides unrelated Agent jobs. The msdb reference is deferred through sp_executesql inside
774+
// TRY/CATCH so a login without msdb access gets a *catchable* error (not an uncatchable cross-db 916) and
775+
// cleanly falls back to a text match on the whole batch/object text.
776+
string cdcSetup = excludeCdc ? @"
777+
DECLARE @cdc_capture_jobs TABLE (job_id uniqueidentifier PRIMARY KEY);
778+
DECLARE @cdc_readable bit = 0;
779+
BEGIN TRY
780+
INSERT @cdc_capture_jobs (job_id)
781+
EXEC sys.sp_executesql N'SELECT cj.job_id FROM msdb.dbo.cdc_jobs AS cj WHERE cj.job_type = N''capture'';';
782+
SET @cdc_readable = 1;
783+
END TRY
784+
BEGIN CATCH
785+
SET @cdc_readable = 0;
786+
END CATCH;
787+
" : "";
788+
string cdcFilter = excludeCdc ? @"
789+
AND NOT
790+
(
791+
(
792+
@cdc_readable = 1
793+
AND s.program_name LIKE N'SQLAgent - TSQL JobStep (Job 0x%'
794+
AND TRY_CONVERT(uniqueidentifier, TRY_CONVERT(binary(16), SUBSTRING(s.program_name, 32, 32), 2))
795+
IN (SELECT j.job_id FROM @cdc_capture_jobs AS j)
796+
)
797+
OR
798+
(
799+
@cdc_readable = 0
800+
AND t.text IS NOT NULL
801+
AND (t.text LIKE N'%sp_MScdc_capture_job%' OR t.text LIKE N'%sp_cdc_scan%')
802+
)
803+
)" : "";
774804

775805
string query = @$"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
776-
806+
{cdcSetup}
777807
SELECT TOP(@maxResults)
778808
r.session_id,
779809
DB_NAME(r.database_id) AS database_name,

0 commit comments

Comments
 (0)