Skip to content

Expand connection pool benchmark coverage for ChannelDbConnectionPool#4451

Draft
mdaigle wants to merge 3 commits into
dotnet:dev/cheena/perf-projectfrom
mdaigle:dev/automation/expand-pool-benchmarks
Draft

Expand connection pool benchmark coverage for ChannelDbConnectionPool#4451
mdaigle wants to merge 3 commits into
dotnet:dev/cheena/perf-projectfrom
mdaigle:dev/automation/expand-pool-benchmarks

Conversation

@mdaigle

@mdaigle mdaigle commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Expands the connection pool benchmark coverage introduced in #4447 to more thoroughly evaluate the new ChannelDbConnectionPool (UseConnectionPoolV2) against the legacy WaitHandleDbConnectionPool, aligned with the design goals in #3356: parallel connection opening, async best practices / low threadpool pressure, and minimal thread/lock contention.

Stacks on #4447 (dev/cheena/perf-project). Review/merge that first.

Motivation (from #3356 and linked issues)

The new pool targets the slow, serialized connection opening described in #601 and #979. Good performance coverage needs to exercise the dimensions where the two implementations actually diverge: cold vs warm pools, sync vs async callers, and behavior under pool exhaustion (async waiter wake path).

Correctness fix

The pool-implementation switch (UseConnectionPoolV2) and the SNI switch are read once and cached in LocalAppContextSwitches. Because BenchmarkConfig runs every case in a single process via InProcessEmitToolchain, toggling those AppContext switches per benchmark iteration has no effect — every "Legacy/New" row would silently use whichever value was read first.

Fix: the pool implementation is now a process-level config flag (UseConnectionPoolV2 in runnerconfig.jsonc, applied once in Program.SetupConfigurations), matching how UseManagedSniOnWindows and UseOptimizedAsyncBehaviour are already handled. Comparing the two pools is done with two runs (false then true) — which is how the #3356 charts were produced. Each runner logs the active implementation so results are self-describing.

Benchmark coverage

Runner Scenario Primary signal
ConnectionPoolStressRunner (reworked) Burst-open N connections in parallel; cold/warm × sync/async × pooling on/off Parallel-open / startup latency (#601, #979)
ConnectionPoolContentionRunner (new) Steady-state open→SELECT 1→close across 50 workers; MaxPoolSize ≥ Parallelism (no wait) and ≪ Parallelism (back-pressure); sync/async Async waiter-wake path + lock contention under exhaustion
ConnectionPoolChurnRunner (new) Single-threaded rapid open/close on a warm pool; sync/async Raw per-checkout overhead + per-op allocations, no contention noise

Rate limiting (#4396), pruning (#4304), and shutdown (#4302) are time/behavior-based and are intentionally left to reliability testing (#3667) rather than microbenchmarks.

Metrics come from the existing MemoryDiagnoser + ThreadingDiagnoser (Mean, Completed Work Items, Lock Contentions, Allocated/Gen0).

Sample results

Validated locally against SQL Server (both pools). Burst-open of 100 pooled connections (ConnectionPoolStressRunner):

Scenario Legacy V2 Speedup
Cold, Sync 632 ms 179 ms ~3.5×
Cold, Async 625 ms 173 ms ~3.6×
Warm, Sync 612 ms 141 ms ~4.3×
Warm, Async 576 ms 144 ms ~4.0×

Async Completed Work Items also dropped ~1180 → ~324, confirming reduced threadpool pressure.

Absolute numbers above are from a local containerized SQL instance and are illustrative, not production-representative; the harness and relative/threading metrics are the point.

Checklist

  • Tests added or updated (performance benchmark runners)
  • Public API changes documented (none — test-only project)
  • Verified against a live SQL Server (both pool implementations)
  • No breaking changes introduced

mdaigle and others added 3 commits July 15, 2026 17:46
… pooling matrix

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The connection pool implementation switch (UseConnectionPoolV2) and the SNI
switch are read and cached on first use in LocalAppContextSwitches. Because
BenchmarkConfig runs every case in one process via InProcessEmitToolchain,
toggling these switches per iteration had no effect and would have produced
misleading Legacy/New/Native/Managed rows.

Select the pool implementation once per process via a new UseConnectionPoolV2
config flag (set in Program.SetupConfigurations), matching how SNI and async
behavior are already configured. Comparing pools is done with two runs.

Keep only the genuinely per-iteration dimensions as benchmark parameters:
warm/cold pool, sync/async, pooling on/off, and connection count. This aligns
the benchmark with issue dotnet#3356's evaluation of the ChannelDbConnectionPool.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Round out connection pool benchmark coverage for evaluating ChannelDbConnectionPool
(UseConnectionPoolV2) vs the legacy WaitHandleDbConnectionPool per issue dotnet#3356.

- ConnectionPoolContentionRunner: steady-state open->SELECT 1->close across 50
  concurrent workers on a warm pool, with MaxPoolSize >= Parallelism (no wait) and
  MaxPoolSize << Parallelism (back-pressure). Exercises the async waiter wake path
  and lock contention that most differentiate the two pools under exhaustion
  (dotnet#601, dotnet#979).
- ConnectionPoolChurnRunner: single-threaded rapid open/close on a warm pool. Low-
  noise measure of raw per-checkout overhead and per-op allocations.

Both honor the process-level UseConnectionPoolV2 config flag (compare via two runs)
and are wired into Config.Benchmarks, Program.cs, and runnerconfig.jsonc following
the existing runner pattern. The existing ConnectionPoolStressRunner covers burst
parallel open (cold/warm x sync/async x pooling on/off).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 16, 2026 19:27
@mdaigle
mdaigle requested a review from a team as a code owner July 16, 2026 19:27
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Jul 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Expands the Microsoft.Data.SqlClient.PerformanceTests benchmark harness to better compare the legacy WaitHandleDbConnectionPool with the new ChannelDbConnectionPool by introducing additional pool-focused runners and making pool selection a process-level config choice (so it aligns with how LocalAppContextSwitches caches the switch).

Changes:

  • Adds a process-level UseConnectionPoolV2 flag to runnerconfig.jsonc and applies it once in Program.SetupConfigurations.
  • Reworks ConnectionPoolStressRunner to focus on parallel open throughput dimensions (cold/warm, sync/async, pooling on/off).
  • Introduces two new benchmark runners: ConnectionPoolContentionRunner (steady-state contention/exhaustion) and ConnectionPoolChurnRunner (single-thread checkout overhead).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Microsoft.Data.SqlClient/tests/PerformanceTests/runnerconfig.jsonc Adds UseConnectionPoolV2 and enables configs for new pool runners.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/Program.cs Applies UseConnectionPoolV2 AppContext switch once per process and wires up new runners.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/Config/Config.cs Adds UseConnectionPoolV2 to the deserialized config surface and new runner config fields.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/BenchmarkRunners/ConnectionPoolStressRunner.cs Refactors runner to measure parallel open throughput across key dimensions.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/BenchmarkRunners/ConnectionPoolContentionRunner.cs New runner for steady-state pool contention/exhaustion behavior.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/BenchmarkRunners/ConnectionPoolChurnRunner.cs New runner for low-noise per-checkout overhead measurement.

Comment on lines +169 to +173
_connections.TryAdd(conn);

await Task.Delay(rng.Next(10, 101));
if (returnToPool)
{
conn.Close();
Comment on lines +123 to +127
if (_connections.TryTake(out var first))
{
int seed = i;
tasks[i] = Task.Run(async () =>
{
var rng = new Random(seed);
for (int j = 0; j < 10; j++)
{
using var conn = new SqlConnection(_connectionString);
await conn.OpenAsync();
first.Close();

// ~50% of the time, execute a lightweight query while holding the connection
if (rng.Next(2) == 0)
{
using var cmd = new SqlCommand($"SELECT TOP 1 Val FROM {_tableName}", conn);
_ = await cmd.ExecuteScalarAsync();
}
while (_connections.TryTake(out var conn))
Comment on lines +146 to +150
// Return them all to the pool so subsequent checkouts are served from idle.
for (int i = 0; i < count; i++)
{
conns[i].Close();
}
@mdaigle
mdaigle marked this pull request as draft July 16, 2026 19:36
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.01%. Comparing base (d67bf7a) to head (0468beb).
⚠️ Report is 2 commits behind head on dev/cheena/perf-project.

Additional details and impacted files
@@                     Coverage Diff                     @@
##           dev/cheena/perf-project    #4451      +/-   ##
===========================================================
+ Coverage                    63.95%   65.01%   +1.05%     
===========================================================
  Files                          284      284              
  Lines                        66786    66786              
===========================================================
+ Hits                         42712    43419     +707     
+ Misses                       24074    23367     -707     
Flag Coverage Δ
PR-SqlClient-Project 65.01% <ø> (+1.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To triage

Development

Successfully merging this pull request may close these issues.

2 participants