MaxPoolSize vs. Connection Lifetime: Which Better Prevents Intermittent Pool Exhaustion?
0 reputation · 12 Jun 2024, 00:43 UTC
0 reputation · 12 Jun 2024, 00:43 UTC
Identify the most effective mitigation for intermittent SqlClient pool exhaustion in a .NET application.
Both MaxPoolSize and Connection Lifetime are configurable, but their trade‑offs under real‑world load are unclear. Increasing the pool size postpones exhaustion but may mask leaks; a short lifetime forces recycling, potentially increasing churn.
MaxPoolSize or setting a non‑zero Connection Lifetime?Connection Lifetime impact connection churn and overall latency during peak periods?MaxPoolSize with a short Connection Lifetime versus a low pool size with a long lifetime?31775 reputation · 12 Jun 2024, 09:47 UTC
Neither setting prevents intermittent exhaustion, and raising MaxPoolSize is the worse first move when a leak is suspected. The first line of defense is confirming that connections are actually being returned to the pool. If you must pick a tuning knob, a non-zero Connection Lifetime is the safer experiment, but understand that it recycles idle connections — it does not free connections that are still checked out, so it cannot fix a leak either.
Confirmed behavior (Microsoft.Data.SqlClient / System.Data.SqlClient, .NET 6+):
Max Pool Size caps physical connections per pool (per unique connection string, per process). Default is 100. Raising it does not return leaked connections; it only delays the timeout and increases SQL Server memory and scheduler load.Connection Lifetime retires a pooled connection on check-in if its age exceeds the value. It never touches in-use connections.Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool error almost always means connections are checked out longer than expected — missing Dispose/using, fire-and-forget async, or a long-running query holding a connection.Likely explanation for "intermittent": burst traffic coinciding with a slow code path that holds connections slightly too long, so the pool drains only under load. That pattern looks like a capacity problem but is a residency-time problem.
await using var conn = ... on every path, including exceptions and early returns). If you need an immediate mitigation while investigating, a moderate Connection Lifetime (e.g., 300–600s) is lower-risk than raising MaxPoolSize, because it won't increase peak server load.MaxPoolSize + short lifetime is the worst combination — maximum server-side connection overhead plus maximum reconnect churn. Low pool size + long lifetime minimizes churn but leaves you exposed to leaks and stale connections. Neither combination compensates for unreturned connections.Capture the SqlClient performance counters (or dotnet-counters metrics for Microsoft.Data.SqlClient.EventSource):
dotnet-counters monitor --process-id <pid> \
--counters Microsoft.Data.SqlClient.EventSourceWatch active and pooled connections versus your MaxPoolSize during a burst:
On the server, correlate with sys.dm_exec_connections to see whether physical sessions grow in step with the client pool.
During an exhaustion event, does NumberOfActiveConnections keep growing while pooled connections sit at MaxPoolSize? If yes, this is a leak — both settings are irrelevant until it's fixed. If instead connections are returned promptly but stale (e.g., after a failover or firewall idle-timeout reset), then a Connection Lifetime in the 300–900s range is the right tool, and pool size can stay at default.
MaxPoolSize at 100 unless measurements show genuine, leak-free demand above it.Connection Lifetime only to handle stale connections, and keep it in the minutes range, not seconds.Version note: behavior described applies to current SqlClient providers on .NET 6+; counter names differ slightly between System.Data.SqlClient and Microsoft.Data.SqlClient. Verify against your provider version before relying on specific metric names.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.