-
Notifications
You must be signed in to change notification settings - Fork 311
Get/Return pooled connections #3404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Get/Return pooled connections #3404
Conversation
9d95355
to
73a379b
Compare
src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
@roji tagging you here if you have any time to review. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, it looks good. A bunch of things I'd like addressed, but you know I'll accept valid arguments against fixing them :)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...lient/tests/UnitTests/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPoolTest.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have reviewed the ChannelDbConnectionPool changes. I will look at the tests once we have converged on an implementation. It might be a good idea to host another meeting to walk through the commentary here.
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
|
||
return Task.FromResult<DbConnectionInternal?>(newConnection); | ||
} | ||
catch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catch only what is documented to be thrown.
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Show resolved
Hide resolved
/// </summary> | ||
private static int _instanceCount; | ||
|
||
private readonly int _instanceID = Interlocked.Increment(ref _instanceCount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it starts at zero and only ever goes up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Counters are naturally unsigned, so can we use uint or ulong instead? I'd be proud if those ever wrapped :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should this be _instanceId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no Interlocked.Increment() overload for uint. It doesn't matter if it goes negative. It's only used for identification of unique pools in trace messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is for .NET:
Framework is stuck with signed only.
Signed Increment() doesn't throw on overflow, so there's no programming problem here, just perhaps an unexpected logging issue with negative IDs. Not worth worrying about IMO.
src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
@@ -20,52 +25,136 @@ namespace Microsoft.Data.SqlClient.ConnectionPool | |||
/// </summary> | |||
internal sealed class ChannelDbConnectionPool : IDbConnectionPool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
async: true, | ||
timeout | ||
).ConfigureAwait(false); | ||
taskCompletionSource.SetResult(connection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason to not use TrySetResult
and TrySetException
to avoid unnecessary exceptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea, done!
// We're potentially on a new thread, so we need to properly set the ambient transaction. | ||
// We rely on the caller to capture the ambient transaction in the TaskCompletionSource's AsyncState | ||
// so that we can access it here. Read: area for improvement. | ||
ADP.SetCurrentTransaction(taskCompletionSource.Task.AsyncState as Transaction); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the current source code of WaitHandleDbConnectionPool
, it seems like everything between ADP.SetCurrentTransaction
and taskCompletionSource.SetResult
is synchronous, unlike here, where GetInternalConnection
can go async. I wonder whether it's to make the default TransactionScope
work (you have to pass a parameter to support async) with OpenAsync
, although it's kinda weird to do so anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is an important point. I haven't included transaction support in the scope of work for this PR and included this line prematurely. This line is incorrect for the reason you stated. When we go down the async path, we may end up creating/retrieving a connection in a continuation and that continuation would have no reference to the ambient transaction.
This does make me wonder whether we need any additional hygiene measures to make sure we reset ambient transaction state on threads. We wouldn't want a managed thread to hold onto the ambient transaction state and incorrectly enlist other connections.
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
… connection disposal when opening new connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're getting closer!
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
Debug.Assert(finalToken.IsCancellationRequested); | ||
// We didn't find an idle connection, try to open a new one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These ??=
comments are a bit misleading. At line 466 we don't know whether or not we got an idle connection. We only know that if we hit the await
part. Maybe this would be clearer as If we didn't find ..., then try to ...
.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3404 +/- ##
==========================================
- Coverage 68.86% 63.10% -5.76%
==========================================
Files 280 272 -8
Lines 62417 62093 -324
==========================================
- Hits 42982 39184 -3798
- Misses 19435 22909 +3474
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:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still looking for some changes from previous reviews. Good changes in these last 4 commits though.
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ConnectionPoolSlots.cs
Outdated
Show resolved
Hide resolved
...rosoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good. Will look at tests shortly. Going to loop through older comments first.
cleanupCallback(state); | ||
} | ||
|
||
_disposed = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this go on line 42 in case cleanupCallback() throws? We don't want to try disposal again.
/// This can occur if a reservation is not taken before adding a connection. | ||
/// <param name="createCallback">Callback that provides the connection to add to the collection. This callback | ||
/// *must not* call any other ConnectionPoolSlots methods.</param> | ||
/// <param name="cleanupCallback">Callback to clean up resources if an exception occurs. This callback *must |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must also not throw, since you call it during disposal.
Description
This PR adds
Get
andReturn
functionality to the new ChannelDbConnectionPool class.A channel and corresponding channel reader and writer are added to underpin these operations.
An array is added to hold references to all connections managed by the pool.
Logic is included to respect max pool size in all cases.
Not included: pool warm up (including respecting min pool size), pool pruning, transactions, tracing/logs. These will come in follow-up PRs.
Also includes extensive unit test coverage for the implemented functionality. Integration testing is reserved for a later state when more of the pool functionality is available.
Issues
#3356
Testing
Tests focus on the Get and Return flows with special focus on the following: