Skip to content

Fix sp_BlitzLock parallel deadlock filtering regression#3887

Merged
BrentOzar merged 2 commits intodevfrom
3885_fix_blitzlock_qn
Apr 6, 2026
Merged

Fix sp_BlitzLock parallel deadlock filtering regression#3887
BrentOzar merged 2 commits intodevfrom
3885_fix_blitzlock_qn

Conversation

@BrentOzar
Copy link
Copy Markdown
Member

Summary

  • Restores ROW_NUMBER() - 1 (0-based) for the qn column in sp_BlitzLock, which was accidentally changed to 1-based ROW_NUMBER() in PR 2026-04-06 Release #3885.
  • Without the - 1, downstream code breaks in two ways:
    • Parallel deadlock filtering (d.qn < 2): only keeps 1 row instead of 2, silently dropping the second query involved in the deadlock.
    • Deadlock group labeling (WHEN d.qn = 0): this branch becomes dead code since qn never equals 0.

Test plan

  • Verified with a query that the 0-based qn correctly keeps 2 rows for qn < 2 (parallel deadlock filter), while 1-based only keeps 1.
  • Verified WHEN d.qn = 0 THEN N'1' branch is reachable with 0-based numbering.
  • Deployed to local SQL Server instance successfully.

🤖 Generated with Claude Code

The qn column was changed from ROW_NUMBER() - 1 (0-based) to
ROW_NUMBER() (1-based), but downstream code still expects 0-based
values. This caused parallel deadlocks to silently drop the second
query (d.qn < 2 only keeps 1 row instead of 2) and made the
WHEN d.qn = 0 labeling branch dead code.

Fixes issue identified by Copilot in PR #3885.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 6, 2026 23:43
@BrentOzar BrentOzar added this to the 2026-04 Release milestone Apr 6, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restores the intended 0-based qn numbering in sp_BlitzLock so that parallel deadlock filtering and related downstream logic behave correctly after a prior regression.

Changes:

  • Change qn to be 0-based again by using ROW_NUMBER() ... - 1 in both deadlock-process projections (regular + parallel).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 3618 to 3622
en =
DENSE_RANK() OVER (ORDER BY dp.event_date),
qn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date),
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
dn =
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qn is now 0-based (ROW_NUMBER() - 1), but downstream formatting later builds deadlock_group with CASE WHEN d.qn = 0 THEN '1' ELSE CONVERT(..., d.qn) END, which will label both qn=0 and qn=1 as "Query #1". Update the consumer to display d.qn + 1 (or otherwise adjust) so query numbering remains correct, especially for parallel deadlocks where only qn<2 rows are kept.

Copilot uses AI. Check for mistakes.
Comment on lines +3621 to 3623
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date),
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ROW_NUMBER() uses ORDER BY dp.event_date inside PARTITION BY dp.event_date, which is non-deterministic because all rows in the partition have the same ordering key. Since qn is used to filter parallel deadlocks to the first 2 rows, consider ordering by a stable discriminator (e.g., dp.id and/or dp.spid) to ensure consistent row selection.

Suggested change
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date),
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.id, dp.spid) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.spid, dp.id),

Copilot uses AI. Check for mistakes.
DENSE_RANK() OVER (ORDER BY dp.event_date),
qn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date),
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as earlier: with qn now 0-based, the later deadlock_group label logic (WHEN d.qn = 0 THEN '1' ELSE CONVERT(..., d.qn)) will mislabel qn=1 as "Query #1". Adjust the label computation to use d.qn + 1 (or equivalent) so output query numbering stays correct.

Suggested change
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date),

Copilot uses AI. Check for mistakes.
Comment on lines +3706 to 3708
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date),
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same non-determinism here: ROW_NUMBER() orders by dp.event_date while partitioning by dp.event_date, so qn assignment can change between runs/plans. Because later logic filters qn < 2 for parallel deadlocks, order by a stable column (e.g., dp.id, dp.spid) to make the filter deterministic.

Suggested change
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.event_date) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date),
ROW_NUMBER() OVER (PARTITION BY dp.event_date ORDER BY dp.id, dp.spid) - 1,
dn =
ROW_NUMBER() OVER (PARTITION BY dp.event_date, dp.id ORDER BY dp.spid),

Copilot uses AI. Check for mistakes.
The CASE expression mapped qn=0 to "Query #1" but then used qn
directly for all other values, so qn=1 also displayed as "Query #1".
Replace the CASE with a simple qn + 1 conversion so labels are
always correct (qn=0 → "Query #1", qn=1 → "Query #2", etc).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@BrentOzar BrentOzar merged commit 22b3d4f into dev Apr 6, 2026
@BrentOzar BrentOzar deleted the 3885_fix_blitzlock_qn branch April 6, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants