WaitHandle replacement activation failure loses the original pooled connection
- Dominant language
- C#
- Stars
- 989
- Forks
- 340
- Avg merge
- 4d 19h
- Merged PRs (30d)
- 72
Description
### Describe the bug
When `WaitHandleDbConnectionPool.ReplaceConnection` creates a replacement successfully but the replacement throws during activation, `CreateObject` has already removed the original connection from `_objectList` and added the replacement. `PrepareConnection` then returns the failed replacement to the idle pool.
The original connection remains alive and checked out but is no longer tracked by `_objectList`, while the failed replacement remains pooled instead of being disposed. Pool ownership and metrics no longer describe the physical connections accurately, and the original connection cannot be reclaimed through `ReclaimEmancipatedObjects`.
If you are seeing an exception, include the full exceptions details (message and stack trace).
```
Exception message: The replacement's activation exception is propagated.
Stack trace: N/A
```
### To reproduce
Use a test connection factory whose first connection activates normally and whose second connection opens successfully but throws from `Activate`. Acquire the first connection, then call `WaitHandleDbConnectionPool.ReplaceConnection`.
```c#
DbConnectionInternal original = GetConnection(pool, owner);
factory.FailReplacementActivation = true;
Assert.Throws(() =>
pool.ReplaceConnection(owner, original, timeout));
```
After the exception, the failed replacement is not disposed and is counted as free, while the live original is absent from the pool's tracking list.
### Expected behavior
Dispose the failed replacement, restore the original connection to the pool's tracking list, and leave the original checked out for the caller's reconnect retry. Hard, soft, pooled, free, and active connection counters should reflect exactly that state.
### Further technical details
Microsoft.Data.SqlClient version: current `main`
.NET target: .NET 9.0 reproduction; shared pool code also targets .NET 8.0 and .NET Framework 4.6.2
SQL Server version: Not applicable; deterministic unit-test reproduction uses a fake connection factory
Operating system: macOS; issue is platform-independent
**Additional context**
`ChannelDbConnectionPool.ReplaceConnection` already preserves the original until the replacement is fully activated. The WaitHandle implementation should provide the same rollback behavior.
Contributor guide
Assessment
This issue has not been assessed yet.