Skip to content

Commit b9ea479

Browse files
Expand Rule 3 NonParallelPlanReason coverage (#200)
* Add "Powered by Performance Studio" line on landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add Darling Data favicon to web app Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add Open Graph and Twitter Card meta tags for social sharing Uses the Darling Data barbell logo as hero image when shared on social media. Also adds meta description for SEO. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Clarify OG description: in-browser, nothing to install Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix Rule 3 severity: CouldNotGenerateValidParallelPlan is actionable This reason means something in the query blocks parallelism (scalar UDFs, table variable inserts, etc.) — that's worth a Warning, not Info. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Expand Rule 3 to cover all NonParallelPlanReason values Adds human-readable messages for all 25 known reasons. Severity: - Warning: actionable reasons (UDFs, cursors, table variables, remote queries, trace flags, hints, DML OUTPUT, writeback variables) - Info: passive/environmental (cost below threshold, edition limits, memory-optimized tables, upgrade mode, index build edge cases) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c907018 commit b9ea479

5 files changed

Lines changed: 82 additions & 4 deletions

File tree

src/PlanViewer.Core/Services/PlanAnalyzer.cs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,72 @@ private static void AnalyzeStatement(PlanStatement stmt, AnalyzerConfig cfg)
133133
{
134134
var reason = stmt.NonParallelPlanReason switch
135135
{
136+
// User/config forced serial
136137
"MaxDOPSetToOne" => "MAXDOP is set to 1",
138+
"QueryHintNoParallelSet" => "OPTION (MAXDOP 1) hint forces serial execution",
139+
"ParallelismDisabledByTraceFlag" => "Parallelism disabled by trace flag",
140+
141+
// Passive — optimizer chose serial, nothing wrong
137142
"EstimatedDOPIsOne" => "Estimated DOP is 1 (the plan's estimated cost was below the cost threshold for parallelism)",
143+
144+
// Edition/environment limitations
138145
"NoParallelPlansInDesktopOrExpressEdition" => "Express/Desktop edition does not support parallelism",
146+
"NoParallelCreateIndexInNonEnterpriseEdition" => "Parallel index creation requires Enterprise edition",
147+
"NoParallelPlansDuringUpgrade" => "Parallel plans disabled during upgrade",
148+
"NoParallelForPDWCompilation" => "Parallel plans not supported for PDW compilation",
149+
"NoParallelForCloudDBReplication" => "Parallel plans not supported during cloud DB replication",
150+
151+
// Query constructs that block parallelism (actionable)
139152
"CouldNotGenerateValidParallelPlan" => "Optimizer could not generate a valid parallel plan. Common causes: scalar UDFs, inserts into table variables, certain system functions, or OPTION (MAXDOP 1) hints",
140-
"QueryHintNoParallelSet" => "OPTION (MAXDOP 1) hint forces serial execution",
153+
"TSQLUserDefinedFunctionsNotParallelizable" => "T-SQL scalar UDF prevents parallelism. Rewrite as an inline table-valued function, or on SQL Server 2019+ check if the UDF is eligible for automatic inlining",
154+
"CLRUserDefinedFunctionRequiresDataAccess" => "CLR UDF with data access prevents parallelism",
155+
"NonParallelizableIntrinsicFunction" => "Non-parallelizable intrinsic function in the query",
156+
"TableVariableTransactionsDoNotSupportParallelNestedTransaction" => "Table variable transaction prevents parallelism. Consider using a #temp table instead",
157+
"UpdatingWritebackVariable" => "Updating a writeback variable prevents parallelism",
158+
"DMLQueryReturnsOutputToClient" => "DML with OUTPUT clause returning results to client prevents parallelism",
159+
"MixedSerialAndParallelOnlineIndexBuildNotSupported" => "Mixed serial/parallel online index build not supported",
160+
"NoRangesResumableCreate" => "Resumable index create cannot use parallelism for this operation",
161+
162+
// Cursor limitations
163+
"NoParallelCursorFetchByBookmark" => "Cursor fetch by bookmark cannot use parallelism",
164+
"NoParallelDynamicCursor" => "Dynamic cursors cannot use parallelism",
165+
"NoParallelFastForwardCursor" => "Fast-forward cursors cannot use parallelism",
166+
167+
// Memory-optimized / natively compiled
168+
"NoParallelForMemoryOptimizedTables" => "Memory-optimized tables do not support parallel plans",
169+
"NoParallelForDmlOnMemoryOptimizedTable" => "DML on memory-optimized tables cannot use parallelism",
170+
"NoParallelForNativelyCompiledModule" => "Natively compiled modules do not support parallelism",
171+
172+
// Remote queries
173+
"NoParallelWithRemoteQuery" => "Remote queries cannot use parallelism",
174+
"NoRemoteParallelismForMatrix" => "Remote parallelism not available for this query shape",
175+
141176
_ => stmt.NonParallelPlanReason
142177
};
143178

144-
// Only warn (not info) when the user explicitly forced serial execution
145-
var isExplicit = stmt.NonParallelPlanReason is "MaxDOPSetToOne" or "QueryHintNoParallelSet";
179+
// Actionable: user forced serial, or something in the query blocks parallelism
180+
// that could potentially be rewritten. Info: passive (cost too low) or
181+
// environmental (edition, upgrade, cursor type, memory-optimized).
182+
var isActionable = stmt.NonParallelPlanReason is
183+
"MaxDOPSetToOne" or "QueryHintNoParallelSet" or "ParallelismDisabledByTraceFlag"
184+
or "CouldNotGenerateValidParallelPlan"
185+
or "TSQLUserDefinedFunctionsNotParallelizable"
186+
or "CLRUserDefinedFunctionRequiresDataAccess"
187+
or "NonParallelizableIntrinsicFunction"
188+
or "TableVariableTransactionsDoNotSupportParallelNestedTransaction"
189+
or "UpdatingWritebackVariable"
190+
or "DMLQueryReturnsOutputToClient"
191+
or "NoParallelCursorFetchByBookmark"
192+
or "NoParallelDynamicCursor"
193+
or "NoParallelFastForwardCursor"
194+
or "NoParallelWithRemoteQuery"
195+
or "NoRemoteParallelismForMatrix";
146196

147197
stmt.PlanWarnings.Add(new PlanWarning
148198
{
149199
WarningType = "Serial Plan",
150200
Message = $"Query running serially: {reason}.",
151-
Severity = isExplicit ? PlanWarningSeverity.Warning : PlanWarningSeverity.Info
201+
Severity = isActionable ? PlanWarningSeverity.Warning : PlanWarningSeverity.Info
152202
});
153203
}
154204

src/PlanViewer.Web/Pages/Index.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<div class="landing-content">
88
<h2>Free SQL Server Query Plan Analysis</h2>
99
<p class="privacy">Paste or upload a .sqlplan file. Your plan XML never leaves your browser.</p>
10+
<p class="powered-by">Powered by the same analysis engine in the <a href="https://github.com/erikdarlingdata/PerformanceStudio" target="_blank" rel="noopener">Performance Studio</a> app.</p>
1011

1112
@if (errorMessage != null)
1213
{

src/PlanViewer.Web/wwwroot/css/app.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,25 @@ main {
168168
font-family: 'Montserrat', sans-serif;
169169
font-size: 1.05rem;
170170
font-weight: 600;
171+
margin-bottom: 0.4rem;
172+
}
173+
174+
.powered-by {
175+
color: var(--text-secondary);
176+
font-size: 0.9rem;
171177
margin-bottom: 1.5rem;
172178
}
173179

180+
.powered-by a {
181+
color: var(--accent);
182+
text-decoration: none;
183+
font-weight: 600;
184+
}
185+
186+
.powered-by a:hover {
187+
text-decoration: underline;
188+
}
189+
174190
/* === Input Area === */
175191
.input-area {
176192
text-align: left;
38.8 KB
Loading

src/PlanViewer.Web/wwwroot/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44
<meta charset="utf-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Free SQL Server Query Plan Analysis — Darling Data</title>
7+
<meta name="description" content="Free browser-based SQL Server execution plan analysis. Paste or upload a .sqlplan file — your plan XML never leaves your browser." />
8+
<meta property="og:title" content="Free SQL Server Query Plan Analysis" />
9+
<meta property="og:description" content="Analyze SQL Server execution plans instantly in your browser. Paste or upload a .sqlplan file — nothing to install, no data sent to a server." />
10+
<meta property="og:image" content="https://plans.erikdarling.com/darling-data-logo.jpg" />
11+
<meta property="og:url" content="https://plans.erikdarling.com" />
12+
<meta property="og:type" content="website" />
13+
<meta name="twitter:card" content="summary_large_image" />
14+
<meta name="twitter:title" content="Free SQL Server Query Plan Analysis" />
15+
<meta name="twitter:description" content="Analyze SQL Server execution plans instantly in your browser. Paste or upload a .sqlplan file — nothing to install, no data sent to a server." />
16+
<meta name="twitter:image" content="https://plans.erikdarling.com/darling-data-logo.jpg" />
17+
<link rel="icon" type="image/png" href="favicon.png" />
718
<base href="/" />
819
<link rel="preconnect" href="https://fonts.googleapis.com" />
920
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

0 commit comments

Comments
 (0)