Skip to content

0001 — SqlClient's rewritten connection pool is switched off

Status: accepted · 2026-07-15

Context

Microsoft.Data.SqlClient 6.1 shipped a rewritten connection pool (WaitHandleDbConnectionPool). Under it, physical connection opens serialise through the synchronous native SNI path, each taking roughly 4.5 seconds. While the pool is growing — which is exactly what happens after an idle period, a restart, or a burst — requests queue behind those opens.

The symptom is nothing like the cause. The app appears to hang for several seconds on the first page load after a quiet period, then behaves normally. No query is slow; the slow-query interceptor sees nothing, because the time is spent before a command is ever sent. Nothing is logged, because nothing failed.

Diagnosed in TaricHive with dotnet-stack on 2026-07-15, which is what made it visible at all — the stacks showed threads parked inside the pool's open path rather than in any query.

Decision

Two AppContext switches, set at the very top of Program.cs before the host builder — they must be set before the first SqlConnection is constructed or they have no effect:

AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2", false);
AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true);

The first opts back into the previous, proven pool. The second uses the managed networking transport rather than native SNI.

Consequences

  • These lines look like dead configuration. They are not, and deleting them reintroduces a multi-second stall that no logging in this application will explain.
  • We are pinned to a code path Microsoft intends to replace. Re-test on every SqlClient version bump — if the rewritten pool is fixed, the right move is to delete both switches, and we will only find that out by trying.
  • To verify a bump: restart with the switches removed, leave the app idle past the pool's idle timeout, then load a page that opens several connections and watch the first-request latency. A regression shows up as seconds, not milliseconds, so it does not need careful measurement.