Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/PlanViewer.App/PlanViewer.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>EDD.ico</ApplicationIcon>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<Version>1.7.7</Version>
<Version>1.7.8</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>Performance Studio</Product>
Expand Down
32 changes: 32 additions & 0 deletions src/PlanViewer.Core/Services/PlanAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@

if (unsnifffedParams.Count > 0)
{
var hasRecompile = stmt.StatementText.Contains("RECOMPILE", StringComparison.OrdinalIgnoreCase);

Check warning on line 406 in src/PlanViewer.Core/Services/PlanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.

Check warning on line 406 in src/PlanViewer.Core/Services/PlanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.
if (!hasRecompile)
{
var names = string.Join(", ", unsnifffedParams.Select(p => p.Name));
Expand Down Expand Up @@ -1269,7 +1269,7 @@
// Rule 28: Row Count Spool — NOT IN with nullable column
// Pattern: Row Count Spool with high rewinds, child scan has IS NULL predicate,
// and statement text contains NOT IN
if (!cfg.IsRuleDisabled(28) && node.PhysicalOp.Contains("Row Count Spool"))

Check warning on line 1272 in src/PlanViewer.Core/Services/PlanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.

Check warning on line 1272 in src/PlanViewer.Core/Services/PlanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.
{
var rewinds = node.HasActualStats ? (double)node.ActualRewinds : node.EstimateRewinds;
if (rewinds > 10000 && HasNotInPattern(node, stmt))
Expand Down Expand Up @@ -1731,6 +1731,14 @@
if (child.PhysicalOp == "Parallelism" && child.Children.Count > 0)
return child.Children.Max(GetEffectiveChildElapsedMs);

// Batch mode pipelines — each operator's elapsed stands alone rather than
// rolling up its descendants the way row-mode does. For a parent computing
// self-time above a batch-mode subtree, subtract the whole pipeline's time
// (Joe #215 D1: Parallelism gather-streams above three batch operators).
var mode = child.ActualExecutionMode ?? child.ExecutionMode;
if (mode == "Batch" && child.HasActualStats)
return SumBatchSubtreeElapsedMs(child);

// Child has its own stats: use them
if (child.ActualElapsedMs > 0)
return child.ActualElapsedMs;
Expand All @@ -1745,6 +1753,30 @@
return sum;
}

/// <summary>
/// Sums ActualElapsedMs across a contiguous batch-mode subtree (stops at
/// Parallelism exchange zone boundaries). Batch operators pipeline — elapsed
/// times are standalone, not cumulative — so summing gives the total work the
/// zone did, which is what a row-mode parent above the zone should subtract
/// to get its own self-time.
/// </summary>
private static long SumBatchSubtreeElapsedMs(PlanNode node)
{
long sum = node.ActualElapsedMs;
foreach (var child in node.Children)
{
// Zone boundary — stop summing
if (child.PhysicalOp == "Parallelism") continue;

var childMode = child.ActualExecutionMode ?? child.ExecutionMode;
if (childMode == "Batch" && child.HasActualStats)
sum += SumBatchSubtreeElapsedMs(child);
else
sum += GetEffectiveChildElapsedMs(child);
}
return sum;
}

/// <summary>
/// Calculates a Parallelism (exchange) operator's own elapsed time.
/// Exchange times are unreliable — they accumulate wait time caused by
Expand Down
Loading