Skip to content

Joe feedback b2, b3, b6, b7 from #215#235

Merged
erikdarlingdata merged 2 commits into
devfrom
fix/joe-feedback-b
Apr 17, 2026
Merged

Joe feedback b2, b3, b6, b7 from #215#235
erikdarlingdata merged 2 commits into
devfrom
fix/joe-feedback-b

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Summary

Four items from Joe's latest feedback on issue #215:

  • b3: Serial Plan message now says "MAXDOP is set to 1 using a query hint" when the user explicitly wrote MAXDOP 1 in the query
  • b6: Rule 6 (Scalar UDF) is now suppressed when Serial Plan fires for a UDF-related reason — the Serial Plan warning already explains the issue
  • b2: Rework Serial Plan benefit formula per Joe: (cpu * (DOP-1) / DOP) / elapsed * 100, DOP=4 assumed, no benefit below cost 1. Removed the unnecessary CPU-bound branch and the 50% cap
  • b7: Excessive Memory Grant warning now notes when an adaptive join executed as Nested Loops at runtime — the grant was sized for the hash alternative

Test plan

  • 72 tests pass (Rule 6 test updated for new suppression behavior)
  • Load Joe's plan (ALa237EaOO) and verify MAXDOP 1 wording + benefit math
  • Load plan (cUPI3Dkit3) and verify Scalar UDF warning is suppressed when Serial Plan covers it

Remaining items from Joe's comment (not in this PR): b1 (bare scan benefit), b4/b5 (surface significant waits as actionable items — overlaps with a1 in the per-operator consolidation design).

🤖 Generated with Claude Code

erikdarlingdata and others added 2 commits April 17, 2026 08:00
- b3: Change Serial Plan message to "MAXDOP is set to 1 using a query
  hint" when user explicitly wrote MAXDOP 1 in the query
- b6: Suppress Rule 6 (Scalar UDF) when Serial Plan fires for a
  UDF-related reason (TSQLUserDefinedFunctionsNotParallelizable,
  CLRUserDefinedFunctionRequiresDataAccess, or CouldNotGenerateValidParallelPlan) —
  the Serial Plan warning already explains the issue. Updated test to
  assert the new suppression behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- b2: Rework Serial Plan benefit formula per Joe's proposal:
  (cpu * (DOP - 1) / DOP) / elapsed * 100, assuming DOP 4 when unknown.
  No benefit when StatementSubTreeCost < 1. Drops the unnecessary
  CPU-bound branch and the 50% cap for non-CPU-bound cases.
- b7: Excessive Memory Grant warning now notes when an adaptive join
  executed as Nested Loops at runtime — the grant was sized for the
  hash alternative that wasn't used.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner Author

@erikdarlingdata erikdarlingdata left a comment

Choose a reason for hiding this comment

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

Review — Joe feedback b2/b3/b6/b7

What it does

  • Rule 3 Serial Plan: hardcodes "MAXDOP is set to 1 using a query hint" for the MaxDOPSetToOne + explicit-hint path (PlanAnalyzer.cs:215).
  • Rule 6 Scalar UDF: suppressed when NonParallelPlanReason is UDF-related (PlanAnalyzer.cs:634–640).
  • Rule 9 Excessive Memory Grant: appends a note when an adaptive join ran as Nested Loops (PlanAnalyzer.cs:257–260 + new HasAdaptiveJoinChoseNestedLoop at 1451).
  • BenefitScorer Serial Plan: new formula (cpu * (DOP-1) / DOP) / elapsed * 100, DOP=4, cost≥1 gate (BenefitScorer.cs:53–66).

Good

  • Targets dev, not main.
  • Rule 6 test was updated to match the new suppression behavior (PlanAnalyzerTests.cs:97).
  • The adaptive-join traversal is contained and side-effect free.

Needs attention

  • Dashboard/Lite sync: Rules 3, 6, 9 and the Serial Plan benefit formula all changed. PerformanceMonitor Dashboard and Lite keep their own copy of this rule set — confirm the equivalent changes are landing there, otherwise the three surfaces drift.
  • Duplicate <summary> doc block at PlanAnalyzer.cs:1444–1450. See inline.
  • Missing test coverage for the new BenefitScorer formula and the adaptive-join memory-grant branch. See inline.
  • Minor: unbraced foreach under a compound if at PlanAnalyzer.cs:642. See inline.

No Avalonia/XAML changes in this diff, so the usual AvaloniaEdit/ComboBox/contrast checks don't apply.


Generated by Claude Code

/// <summary>
/// Returns true if the plan contains an adaptive join that executed as a Nested Loop.
/// Indicates a memory grant was sized for the hash alternative but never needed.
/// </summary>
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Duplicate <summary> doc block. The pre-existing "Finds Sort and Hash Match operators..." summary at 1444–1446 belonged to FindMemoryConsumers below. Inserting HasAdaptiveJoinChoseNestedLoop here orphaned it — now this method has two summaries and FindMemoryConsumers has none. Move the old summary back down to line 1464 (immediately above FindMemoryConsumers).


Generated by Claude Code

// Note adaptive joins that chose Nested Loops at runtime — the grant
// was sized for a hash join that never happened.
if (stmt.RootNode != null && HasAdaptiveJoinChoseNestedLoop(stmt.RootNode))
message += " An adaptive join in this plan executed as a Nested Loop at runtime — the memory grant was sized for the hash join alternative that wasn't used.";
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

No test covers the adaptive-join-chose-nested-loop branch. Rule09a_ExcessiveMemoryGrant_SmallGrantSuppressed (tests/PlanViewer.Core.Tests/PlanAnalyzerTests.cs:182) only asserts the suppression path. Add a fixture plan with IsAdaptive=true + ActualJoinType="NestedLoops" and an over-granted statement so the new message suffix is exercised.


Generated by Claude Code

warning.MaxBenefitPercent = Math.Round(Math.Min(50, benefit), 1);
var benefit = ((double)cpu * (potentialDop - 1) / potentialDop) / elapsedMs * 100;
warning.MaxBenefitPercent = Math.Round(Math.Min(100, benefit), 1);
}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

No test asserts the new formula. Existing Serial Plan test Rule03_SerialPlan_MaxDOPSetToOne (PlanAnalyzerTests.cs:53) only checks the message text, not MaxBenefitPercent. Recommend at least one test pinning the numeric output for a known cpu/elapsed pair so regressions in the formula surface — e.g. cpu≈elapsed → ~75%, cpu=0 → null/unset, subTreeCost<1 → unset.

Also worth confirming: the StatementSubTreeCost >= 1.0 gate silently drops benefit for sub-cost-1 serial plans but the warning still fires from PlanAnalyzer (which has no matching cost gate). Intentional? If so, a comment on the asymmetry would help the next reader.


Generated by Claude Code

if (!cfg.IsRuleDisabled(6) && !serialPlanCoversUdf)
foreach (var udf in node.ScalarUdfs)
{
var type = udf.IsClrFunction ? "CLR" : "T-SQL";
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Style nit: the if (!cfg.IsRuleDisabled(6) && !serialPlanCoversUdf) guards a foreach with no braces. Works, but mixing a compound condition with an unbraced loop is easy to misread — wrap the foreach in braces to match the rest of the file.


Generated by Claude Code

@erikdarlingdata erikdarlingdata merged commit 429333e into dev Apr 17, 2026
2 checks passed
@erikdarlingdata erikdarlingdata deleted the fix/joe-feedback-b branch April 19, 2026 00:25
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.

1 participant