You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix active connection pools being retired during creation - #4754
Make empty-pool retirement atomic with admission in both WaitHandle and Channel pools. Count == 0 does not mean idle while physical creation, queued acquisition, or background warmup is in flight.
Keep admission through async worker completion (including caller cancellation), protect replacement/background creation, and reject stale references after retirement. Preserve blocking-period throttling, explicit Clear/Shutdown semantics, and demand-driven timer cleanup.
Independent follow-up to the CI investigation in Re-issue session isolation level on TransactionScope re-enlistment #4335; no isolation-level changes or unrelated issue closures. The race is reproduced on main; its involvement in the earlier CI failures remains inferred, not directly observed.
Validation
Unchanged main d189eeb7: all four deterministic physical-create/pruning cases fail (both pools, sync/async). With this fix they pass.
24 targeted pruning cases, plus existing pool/factory lifecycle coverage: 435 passed each on net462, net8.0, net9.0 and net10.0.
SQL Server 2022: 4 passed each on net8.0 and net10.0. Inject pruning at physical creation, then assert three sequential TransactionScope opens reuse the same session without DTC promotion.
Unix-targeted net8.0 driver build: zero warnings/errors. Local SQL used a process-only development-certificate override; no service/configuration changes.
Released 6.1 V1 comparison
Confirmed on unmodified NuGet 6.1.0 and 6.1.7, using V1 (WaitHandleDbConnectionPool), .NET 8.0.31 on Windows, SQL Server 2022.
Both sync/async controls complete three opens on the same SPID without promotion. Both injected cases, on each version, retire the running zero-count pool during first physical creation; the first open succeeds and the second reaches EnlistNonNull -> GetExportCookie and fails because implicit DTC is disabled.
Verified against the reproduced pruning interleaving
No breaking public API or switch changes introduced
Suggested release note: Prevent pruning from retiring connection pools with in-flight acquisition or creation, avoiding lost transaction-affine reuse and unintended promotion.
Publication note: the app prepended priyankatiwari08- to the requested dev/automation/... branch name. Upstream direct push was rejected by repository rules, so this PR is published from the existing contributor fork.
Coordinate idle retirement with admission in both pool implementations, retaining async workers through cancellation and guarding background creation and replacement. Add deterministic and SQL transaction-affinity regressions.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0a3f98bb-aab0-4cdf-a54b-33578f16e15a
❌ Patch coverage is 80.54054% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.90%. Comparing base (671d010) to head (7ecf69b). ⚠️ Report is 34 commits behind head on main.
This EventListener helper override lacks the required XML summary and parameter documentation for test helpers (.github/instructions/testing.instructions.md:181-194). Describe why SqlClient events are enabled for this regression test.
This issue also appears on line 95 of the same file.
This test helper override is missing the XML documentation required for helper methods, including parameter and return documentation (.github/instructions/testing.instructions.md:181-194). Document the pruning hook and mock-connection behavior so the deterministic setup remains clear.
Addressed the latest review summary in 7da3941: added XML summaries and parameter/return documentation for both EventListener overrides and CreateConnection. Documentation only; behavior unchanged.
- Replace TryEnter/Exit with a disposable PoolPruningGuard.Lease with explicit ownership hand-off.
- Release ignores underflow; each lease releases at most once.
- Run pool Shutdown outside the lock and contain failures.
- Dispose lease when Thread.Start or queuing fails.
- Raise CA2000 to warning for ConnectionPool sources.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The admission starts after RemoveConnection has already decremented _connectionSlots and only then called RequestWarmup. If that was the last connection with MinPoolSize > 0, group pruning can retire the zero-count pool in between; this TryEnter then returns null (or the earlier state check exits), so the pool never refills. Acquire the pruning admission before removing the slot and transfer it through warmup scheduling.
Acquire pruning admission before zero-count replenishment
The pruning admission is still acquired too late for minimum-size replenishment. DeactivateObject removes the last connection with DestroyObject before calling QueuePoolCreateRequest; a concurrent group prune can therefore observe Count == 0, mark the guard pruned, and make this TryEnter return null. With MinPoolSize > 0, the required replenishment is then lost. Hold an admission across the decrement-to-zero and transfer it to the queued worker.
- PoolPruningGuard.Lease is now a struct; admission uses an Interlocked state instead of a monitor.
- Pending requests release their lease through an idempotent field-backed method.
- Document ReplaceConnection's null return in WaitHandleDbConnectionPool.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Publishing Pruned before Shutdown() creates a stale-reference race. A caller that already fetched this pool can observe TryEnter() returning inactive while pool.IsRunning is still true; SqlConnectionFactory.TryGetConnection then treats the (null, running) result as PooledOpenTimeout instead of retrying against the replacement pool. Make the retirement transition expose a non-running pool before admission starts returning inactive (without leaving callers spinning through lengthy shutdown cleanup), and add an interleaving test for this boundary.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4755.
Summary
Count == 0does not mean idle while physical creation, queued acquisition, or background warmup is in flight.Validation
d189eeb7: all four deterministic physical-create/pruning cases fail (both pools, sync/async). With this fix they pass.Released 6.1 V1 comparison
WaitHandleDbConnectionPool), .NET 8.0.31 on Windows, SQL Server 2022.EnlistNonNull -> GetExportCookieand fails because implicit DTC is disabled.Checklist
Suggested release note: Prevent pruning from retiring connection pools with in-flight acquisition or creation, avoiding lost transaction-affine reuse and unintended promotion.
Publication note: the app prepended
priyankatiwari08-to the requesteddev/automation/...branch name. Upstream direct push was rejected by repository rules, so this PR is published from the existing contributor fork.