Harden SqlConnection state transitions with CAS guards#4267
Harden SqlConnection state transitions with CAS guards#4267paulmedynski wants to merge 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request hardens SqlConnection’s internal state machine by guarding critical inner-connection transitions with Interlocked.CompareExchange (CAS), making contention/race paths deterministic and ensuring side effects (close-count, StateChange) occur only for committed transitions.
Changes:
- Made
TryOpenInnersnapshot/type-check the active inner connection before accessing parser state (and exposed it asinternalfor unit testing). - Updated
SetInnerConnectionEvent/SetInnerConnectionToto validate expected source states, commit with CAS, and apply side effects only after a successful CAS. - Added new unit tests covering race-path determinism and transition side-effect invariants.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs | Adds CAS-based guards + expected-state validation for key inner-connection transitions; snapshots/type-checks inner connection in TryOpenInner. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStateTransitionTests.cs | Adds regression tests for race-sensitive state transitions and side-effect invariants. |
bb83c58 to
cdfd27a
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4267 +/- ##
==========================================
- Coverage 65.96% 64.38% -1.58%
==========================================
Files 275 270 -5
Lines 42993 65794 +22801
==========================================
+ Hits 28361 42363 +14002
- Misses 14632 23431 +8799
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
SqlConnectioninner-state transitions with targetedInterlocked.CompareExchangeguards.Key Changes
TryOpenInnernow snapshots and type-checks the inner connection before parser access.SetInnerConnectionEventnow:SetInnerConnectionTonow:ConnectionString_Setnow releases Busy -> Closed via guarded helper instead of direct write.Tests
SqlConnectionStateTransitionTestsdotnet test src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj --filter "FullyQualifiedName~SqlConnectionStateTransitionTests" -p:Configuration=Debug -p:ReferenceType=ProjectApp-visible Behavior Analysis
Potential app-visible behavior changes are limited to race paths; normal single-threaded use should be unchanged.
InvalidOperationExceptioninstead ofInvalidCastExceptionSqlConnection.TryOpenInner, the method now snapshots and type-checks the inner connection after open/replace.InvalidCastException.InvalidOperationExceptionpath.InvalidCastExceptionfrom this race would now seeInvalidOperationException.SetInnerConnectionEventandSetInnerConnectionTonow enforce expected source states and use CAS.InvalidOperationExceptionerrors in contention cases.StateChangepatterns may see fewer spurious side effects from losing writers.ConnectionString_Setnow releases Busy -> Closed through the guarded helper rather than direct assignment.TryOpenInneraccess changed from private to internal for testability.Checklist
Observability Notes
ADP.ConnectionErrorvalues for guarded transition failures:InvalidSourceStateForSetInnerConnectionToFailedToCommitSetInnerConnectionToInvalidSourceStateForSetInnerConnectionEventFailedToCommitSetInnerConnectionEventSetInnerConnectionToandSetInnerConnectionEvent.InvalidOperationException; however, the internal connection error code embedded in the message (Internal DbConnection Error: {0}) is now more specific for these paths.