Skip to content

Add automatic pool size reduction (pruning) to ChannelDbConnectionPool#4304

Merged
apoorvdeshmukh merged 21 commits into
mainfrom
dev/ad/conn-prune
Jun 5, 2026
Merged

Add automatic pool size reduction (pruning) to ChannelDbConnectionPool#4304
apoorvdeshmukh merged 21 commits into
mainfrom
dev/ad/conn-prune

Conversation

@apoorvdeshmukh

@apoorvdeshmukh apoorvdeshmukh commented May 21, 2026

Copy link
Copy Markdown
Contributor

Description

Summary

Implements automatic pool size reduction for ChannelDbConnectionPool (connection pool V2). When demand drops, excess idle connections are gradually closed based on observed usage patterns rather than reacting to instantaneous idle counts.

Design

  • Sampling-based pruning: A one-shot timer fires every 10 seconds, recording the current idle connection count into a circular buffer.
  • Median-based decision: Once the buffer fills, samples are sorted and the median is used as the prune target. This smooths out brief traffic lulls and avoids over-pruning.
  • Sample window: LoadBalanceTimeout / PruningInterval samples (defaults to 300s / 10s = 30 samples when Connection Lifetime is 0), clamped to MaxSampleSize (300).
  • MinPoolSize floor: Pruning never reduces the pool below MinPoolSize.
  • Fixed-size pools skip pruning: When MinPoolSize >= MaxPoolSize, no PoolPruner is created.

Architecture

Pruning logic is encapsulated in a dedicated PoolPruner class (IDisposable):

  • PoolPruner owns the timer, sample buffer, and sampling/pruning logic.
  • ChannelDbConnectionPool holds a nullable Pruner auto-property — null for fixed-size pools.
  • The pool calls Pruner?.UpdateTimer() after connections are opened or closed.
  • The pool exposes PruneConnections(int count) for the pruner to call back into.
  • ChannelDbConnectionPool implements IDisposable (delegates to Shutdown()).

Changes

  • PoolPruner.cs (new): Encapsulates all pruning state and logic — timer, sample buffer, median calculation, enable/disable, and disposal.
  • ChannelDbConnectionPool.cs: Added PoolPruner? Pruner auto-property, PruneConnections() callback method, IDisposable implementation, and pruner creation in constructor.
  • ChannelDbConnectionPoolPruningTest.cs (new): Unit tests for pruning behavior.

Thread Safety

  • Timer callback acquires lock(_timer) to read/write shared sample state.
  • Dispose() acquires the same lock with a _disposed guard to prevent double-disposal.
  • UpdateTimer() checks _pool.IsRunning both before and inside the lock to avoid post-shutdown races.
  • Prune loop runs outside the lock and checks IsRunning and MinPoolSize floor on each iteration.
  • Timer uses ADP.UnsafeCreateTimer to avoid capturing ExecutionContext onto the long-lived timer.

Issues

AB#44848

Testing

Tests

  • ChannelDbConnectionPoolPruningTest.cs: 19 test methods (28 runtime cases including Theory data) covering:
    • Timer creation and configuration (fixed-size pool skips, sample size calculation, clamp)
    • UpdateTimer enable/disable logic
    • Pruning callback: sampling, buffer reset, MinPoolSize floor, in-use connection safety
    • Sample values verified after each callback
    • DivideRoundingUp correctness (7 cases)
    • Shutdown/Dispose behavior
    • Integration: full prune to zero, median prevents aggressive pruning during brief lulls
  • All tests use using var pool for deterministic cleanup via IDisposable
  • GetPruner() helper eliminates null-forgiving operator noise

Guidelines

Please review the contribution guidelines before submitting a pull request:

@apoorvdeshmukh
apoorvdeshmukh requested a review from a team as a code owner May 21, 2026 16:17
Copilot AI review requested due to automatic review settings May 21, 2026 16:17
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board May 21, 2026
@apoorvdeshmukh
apoorvdeshmukh marked this pull request as draft May 21, 2026 16:17
@apoorvdeshmukh apoorvdeshmukh added this to the 7.1.0-preview2 milestone May 21, 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

Adds an automatic “pruning” mechanism to ChannelDbConnectionPool (pool V2) to gradually reduce excess idle connections during low demand by sampling idle counts over time and pruning based on the median sample.

Changes:

  • Introduces pruning state, sampling logic, and a one-shot timer callback to periodically evaluate and close excess idle connections.
  • Wires pruning timer enable/disable behavior into connection add/remove paths and disposes the timer during pool shutdown.
  • Adds a dedicated unit test suite validating pruning configuration, sampling/median behavior, floors (MinPoolSize), and shutdown behavior.

Reviewed changes

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

File Description
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs Implements the pruning timer, sampling window/median computation, and integrates timer lifecycle with pool growth/shrink/shutdown.
src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/ChannelDbConnectionPoolPruningTest.cs Adds unit tests covering pruning timer creation/configuration, sampling/median pruning behavior, and shutdown semantics.

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

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

@apoorvdeshmukh
apoorvdeshmukh marked this pull request as ready for review May 27, 2026 13:44
Copilot AI review requested due to automatic review settings May 27, 2026 13:44

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

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

@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.27586% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.61%. Comparing base (bfbdd30) to head (73fe4ae).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...rosoft/Data/SqlClient/ConnectionPool/PoolPruner.cs 97.26% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4304      +/-   ##
==========================================
- Coverage   66.69%   64.61%   -2.08%     
==========================================
  Files         284      280       -4     
  Lines       43238    66162   +22924     
==========================================
+ Hits        28836    42750   +13914     
- Misses      14402    23412    +9010     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 64.61% <98.27%> (?)

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.

@mdaigle mdaigle 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.

Would like to see some improvements to the tests, otherwise looks good!

@github-project-automation github-project-automation Bot moved this from To triage to Waiting for customer in SqlClient Board May 27, 2026
@apoorvdeshmukh
apoorvdeshmukh requested a review from mdaigle May 29, 2026 17:08
Copilot AI review requested due to automatic review settings May 30, 2026 10:33
@paulmedynski paulmedynski added the Author attention needed PRs that require author to respond or make updates to PR. label Jun 4, 2026
Copilot stopped reviewing on behalf of apoorvdeshmukh due to an error June 4, 2026 19:13
@apoorvdeshmukh apoorvdeshmukh removed the Author attention needed PRs that require author to respond or make updates to PR. label Jun 4, 2026
mdaigle
mdaigle previously approved these changes Jun 5, 2026
paulmedynski
paulmedynski previously approved these changes Jun 5, 2026
Copilot AI review requested due to automatic review settings June 5, 2026 12:38
@apoorvdeshmukh
apoorvdeshmukh dismissed stale reviews from paulmedynski and mdaigle via 73fe4ae June 5, 2026 12:38

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

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

@paulmedynski paulmedynski 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.

Beautiful!

@paulmedynski paulmedynski moved this from Waiting for customer to In review in SqlClient Board Jun 5, 2026
@apoorvdeshmukh
apoorvdeshmukh merged commit 4afd45e into main Jun 5, 2026
304 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in SqlClient Board Jun 5, 2026
@apoorvdeshmukh
apoorvdeshmukh deleted the dev/ad/conn-prune branch June 5, 2026 15:39
priyankatiwari08 added a commit to priyankatiwari08/SqlClient that referenced this pull request Jun 11, 2026
Resolve conflict in ChannelDbConnectionPool.Shutdown(): integrate PoolPruner.Dispose() from upstream dotnet#4304 with the branch's idempotent shutdown sequence (CAS guard, State transition, channel completion, Clear() reuse, final drain). Pruner is disposed before the channel is completed and drained so a pruning tick cannot race with the final drain. PoolPruner.Dispose is idempotent and non-throwing.
priyankatiwari08 added a commit that referenced this pull request Jul 7, 2026
…notes

Per Malcolm's follow-up review, channel pool (pool v2) changes will be announced in preview 3. Removes:

- The 'Automatic Pool Pruning and Shutdown for the Channel-Based Pool' section (What Changed / Who Benefits / Impact) from release-notes/7.1/7.1.0-preview2.md.

- The matching 'Implemented pool shutdown for ChannelDbConnectionPool and added automatic pool size reduction (pruning) for the channel-based pool' bullet from CHANGELOG.md's Changed section.

PRs #4302 and #4304 are no longer referenced in the preview2 notes.
priyankatiwari08 added a commit that referenced this pull request Jul 7, 2026
* [v7.1.0-preview2] Release Notes and vBump

* vBump in Versions.props, OneBranch pipelines, and CI/PR pipelines to v7.1.0-preview2

* Release notes for v7.1.0-preview2

* Docs: clarify FileVersion vs AssemblyVersion in per-package alignment note

Address Paul's PR feedback: the phrase 'unchanged from 7.0.2' attached to both FileVersion and AssemblyVersion, making it sound like FileVersion was also unchanged. Split into two sentences so only AssemblyVersion is described as unchanged from 7.0.2. Applied across all 4 per-package 7.1.0-preview2 release-notes files (AzureKeyVaultProvider, Extensions.Azure, Extensions.Abstractions, Internal.Logging).

* Docs: correct WAM broker attribution in 7.1.0-preview2 release notes

Address review feedback on PR #4422:

- Rewrite Extensions.Azure preview2 notes to document WAM broker support properly (removes false "no functional changes" claim and wrong "added in 7.0.2" pointer). WAM was added in 7.1.0-preview2 per PR #4288's milestone, not 7.0.2.
- Remove WAM Broker Support section from core preview2 notes; WAM lives only in the Extensions.Azure package.
- Drop Microsoft.Identity.Client / Microsoft.Identity.Client.Broker dependency-bump bullet from core preview2 notes and CHANGELOG; MSAL packages are referenced only by Extensions.Azure, not by core Microsoft.Data.SqlClient (which uses the Wilson IdentityModel stack). Keep the .NET 10 SDK bump (PR #4287) in the core notes.
- Split CHANGELOG "Re-shipped" bullet so Extensions.Azure gets its own callout for the WAM addition; AKV, Extensions.Abstractions, and Internal.Logging remain grouped as alignment-only.

* Docs: address Malcolm's review comments on pool timeout release notes

- Drop 'pool v2' framing from Configurable Idle Connection Timeout section; frame the new keyword/property as applying to the existing connection pool as well (channel pool changes will be announced in preview 3).

- Add Switch.Microsoft.Data.SqlClient.UseOverallConnectTimeoutForPoolWait AppContext switch details (default false, opt-in to true) to the Connection Timeout Now Propagates Through the Pool section, and reframe Who Benefits/Impact to lead with unchanged default behavior.

* Docs: drop channel-based pool pruning/shutdown section from preview2 notes

Per Malcolm's follow-up review, channel pool (pool v2) changes will be announced in preview 3. Removes:

- The 'Automatic Pool Pruning and Shutdown for the Channel-Based Pool' section (What Changed / Who Benefits / Impact) from release-notes/7.1/7.1.0-preview2.md.

- The matching 'Implemented pool shutdown for ChannelDbConnectionPool and added automatic pool size reduction (pruning) for the channel-based pool' bullet from CHANGELOG.md's Changed section.

PRs #4302 and #4304 are no longer referenced in the preview2 notes.
cheenamalhotra pushed a commit that referenced this pull request Jul 14, 2026
Introduce a PoolPruner that periodically samples the pool's idle
connection count and, once a sampling window completes, prunes the
median number of idle connections. This lets the pool shrink back
toward MinPoolSize after load subsides while avoiding over-pruning
during brief lulls.
cheenamalhotra pushed a commit that referenced this pull request Jul 14, 2026
* [v7.1.0-preview2] Release Notes and vBump

* vBump in Versions.props, OneBranch pipelines, and CI/PR pipelines to v7.1.0-preview2

* Release notes for v7.1.0-preview2

* Docs: clarify FileVersion vs AssemblyVersion in per-package alignment note

Address Paul's PR feedback: the phrase 'unchanged from 7.0.2' attached to both FileVersion and AssemblyVersion, making it sound like FileVersion was also unchanged. Split into two sentences so only AssemblyVersion is described as unchanged from 7.0.2. Applied across all 4 per-package 7.1.0-preview2 release-notes files (AzureKeyVaultProvider, Extensions.Azure, Extensions.Abstractions, Internal.Logging).

* Docs: correct WAM broker attribution in 7.1.0-preview2 release notes

Address review feedback on PR #4422:

- Rewrite Extensions.Azure preview2 notes to document WAM broker support properly (removes false "no functional changes" claim and wrong "added in 7.0.2" pointer). WAM was added in 7.1.0-preview2 per PR #4288's milestone, not 7.0.2.
- Remove WAM Broker Support section from core preview2 notes; WAM lives only in the Extensions.Azure package.
- Drop Microsoft.Identity.Client / Microsoft.Identity.Client.Broker dependency-bump bullet from core preview2 notes and CHANGELOG; MSAL packages are referenced only by Extensions.Azure, not by core Microsoft.Data.SqlClient (which uses the Wilson IdentityModel stack). Keep the .NET 10 SDK bump (PR #4287) in the core notes.
- Split CHANGELOG "Re-shipped" bullet so Extensions.Azure gets its own callout for the WAM addition; AKV, Extensions.Abstractions, and Internal.Logging remain grouped as alignment-only.

* Docs: address Malcolm's review comments on pool timeout release notes

- Drop 'pool v2' framing from Configurable Idle Connection Timeout section; frame the new keyword/property as applying to the existing connection pool as well (channel pool changes will be announced in preview 3).

- Add Switch.Microsoft.Data.SqlClient.UseOverallConnectTimeoutForPoolWait AppContext switch details (default false, opt-in to true) to the Connection Timeout Now Propagates Through the Pool section, and reframe Who Benefits/Impact to lead with unchanged default behavior.

* Docs: drop channel-based pool pruning/shutdown section from preview2 notes

Per Malcolm's follow-up review, channel pool (pool v2) changes will be announced in preview 3. Removes:

- The 'Automatic Pool Pruning and Shutdown for the Channel-Based Pool' section (What Changed / Who Benefits / Impact) from release-notes/7.1/7.1.0-preview2.md.

- The matching 'Implemented pool shutdown for ChannelDbConnectionPool and added automatic pool size reduction (pruning) for the channel-based pool' bullet from CHANGELOG.md's Changed section.

PRs #4302 and #4304 are no longer referenced in the preview2 notes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area\Connection Pooling Use this label to tag issues that apply to problems with connection pool.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants