Skip to content

New pool scaffolding #3352

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

Merged
merged 19 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@
<Compile Include="$(CommonSourceRoot)\Microsoft\Data\ProviderBase\DbConnectionFactory.cs">
<Link>Microsoft\Data\ProviderBase\DbConnectionFactory.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\ChannelDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\ChannelDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPool.cs</Link>
</Compile>
Expand Down Expand Up @@ -125,9 +128,6 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolState.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolState.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand All @@ -137,6 +137,9 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\ProviderBase\DbMetaDataFactory.cs">
<Link>Microsoft\Data\ProviderBase\DbMetaDataFactory.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\ProviderBase\DbConnectionInternal.cs">
<Link>Microsoft\Data\ProviderBase\DbConnectionInternal.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\ChannelDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\ChannelDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPool.cs</Link>
</Compile>
Expand Down Expand Up @@ -312,9 +315,6 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolState.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolState.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand All @@ -324,6 +324,9 @@
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs">
<Link>Microsoft\Data\SqlClient\ConnectionPool\WaitHandleDbConnectionPool.cs</Link>
</Compile>
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs">
<Link>Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<Reference Include="System.Configuration" Condition="'$(TargetFramework)' == 'net462'"/>
<Reference Include="System.Configuration" Condition="'$(TargetFramework)' == 'net462'" />
<Reference Include="System.Transactions" Condition="'$(TargetFramework)' == 'net462'" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,10 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac
// into the pool.
if (connectionPool is not null)
{
// PutObject calls Deactivate for us...
connectionPool.PutObject(this, owningObject);
// ReturnInternalConnection calls Deactivate for us...
connectionPool.ReturnInternalConnection(this, owningObject);

// NOTE: Before we leave the PutObject call, another thread may have
// NOTE: Before we leave the ReturnInternalConnection call, another thread may have
// already popped the connection from the pool, so don't expect to be
// able to verify it.
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Data.Common;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.Common;
using Microsoft.Data.ProviderBase;

#nullable enable

namespace Microsoft.Data.SqlClient.ConnectionPool
{
/// <summary>
/// A connection pool implementation based on the channel data structure.
/// Provides methods to manage the pool of connections, including acquiring and releasing connections.
/// </summary>
internal sealed class ChannelDbConnectionPool : DbConnectionPool
{
internal override int Count => throw new NotImplementedException();

internal override DbConnectionFactory ConnectionFactory => throw new NotImplementedException();

internal override bool ErrorOccurred => throw new NotImplementedException();

internal override TimeSpan LoadBalanceTimeout => throw new NotImplementedException();

internal override DbConnectionPoolIdentity Identity => throw new NotImplementedException();

internal override bool IsRunning => throw new NotImplementedException();

internal override DbConnectionPoolGroup PoolGroup => throw new NotImplementedException();

internal override DbConnectionPoolGroupOptions PoolGroupOptions => throw new NotImplementedException();

internal override DbConnectionPoolProviderInfo ProviderInfo => throw new NotImplementedException();

internal override ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext> AuthenticationContexts => throw new NotImplementedException();

internal override bool UseLoadBalancing => throw new NotImplementedException();

internal override void Clear()
{
throw new NotImplementedException();
}

internal override void PutObjectFromTransactedPool(DbConnectionInternal obj)
{
throw new NotImplementedException();
}

internal override DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
{
throw new NotImplementedException();
}

internal override void ReturnInternalConnection(DbConnectionInternal obj, object owningObject)
{
throw new NotImplementedException();
}

internal override void Shutdown()
{
throw new NotImplementedException();
}

internal override void Startup()
{
throw new NotImplementedException();
}

internal override void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject)
{
throw new NotImplementedException();
}

internal override bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

namespace Microsoft.Data.SqlClient.ConnectionPool
{
/// <summary>
/// A base class for implementing database connection pools.
/// Responsible for managing the lifecycle of connections and providing access to database connections.
/// </summary>
internal abstract class DbConnectionPool
{
private static int _objectTypeCount;
Expand Down Expand Up @@ -47,15 +51,11 @@ internal abstract class DbConnectionPool
#region Abstract Methods
internal abstract void Clear();

internal abstract void DestroyObject(DbConnectionInternal obj);

internal abstract bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection);

internal abstract DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection);

internal abstract void PutNewObject(DbConnectionInternal obj);

internal abstract void PutObject(DbConnectionInternal obj, object owningObject);
internal abstract void ReturnInternalConnection(DbConnectionInternal obj, object owningObject);

internal abstract void PutObjectFromTransactedPool(DbConnectionInternal obj);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,18 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor
if (!_poolCollection.TryGetValue(currentIdentity, out pool))
{
DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(ConnectionOptions);
DbConnectionPool newPool = new WaitHandleDbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo);

DbConnectionPool newPool;
if (LocalAppContextSwitches.UseConnectionPoolV2)
{
// ChannelDbConnectionPool is the new pool implementation
newPool = new ChannelDbConnectionPool();
}
else
{
// WaitHandleDbConnectionPool is the old pool implementation, and used by default if UseConnectionPoolV2 is off
newPool = new WaitHandleDbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo);
}

if (MarkPoolGroupAsActive())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ private void DeactivateObject(DbConnectionInternal obj)
Debug.Assert(rootTxn == true || returnToGeneralPool == true || destroyObject == true);
}

internal override void DestroyObject(DbConnectionInternal obj)
private void DestroyObject(DbConnectionInternal obj)
{
// A connection with a delegated transaction cannot be disposed of
// until the delegated transaction has actually completed. Instead,
Expand Down Expand Up @@ -1093,7 +1093,7 @@ private void WaitForPendingOpen()
if (!next.Completion.TrySetResult(connection))
{
// if the completion was cancelled, lets try and get this connection back for the next try
PutObject(connection, next.Owner);
ReturnInternalConnection(connection, next.Owner);
}
}
}
Expand All @@ -1108,12 +1108,12 @@ private void WaitForPendingOpen()
} while (_pendingOpens.TryPeek(out next));
}

internal override bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection)
internal override bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> taskCompletionSource, DbConnectionOptions userOptions, out DbConnectionInternal connection)
{
uint waitForMultipleObjectsTimeout = 0;
bool allowCreate = false;

if (retry == null)
if (taskCompletionSource == null)
{
waitForMultipleObjectsTimeout = (uint)CreationTimeout;

Expand All @@ -1136,7 +1136,7 @@ internal override bool TryGetConnection(DbConnection owningObject, TaskCompletio
{
return true;
}
else if (retry == null)
else if (taskCompletionSource == null)
{
// timed out on a sync call
return true;
Expand All @@ -1146,7 +1146,7 @@ internal override bool TryGetConnection(DbConnection owningObject, TaskCompletio
new PendingGetConnection(
CreationTimeout == 0 ? Timeout.Infinite : ADP.TimerCurrent() + ADP.TimerFromSeconds(CreationTimeout / 1000),
owningObject,
retry,
taskCompletionSource,
userOptions);
_pendingOpens.Enqueue(pendingGetConnection);

Expand Down Expand Up @@ -1376,7 +1376,7 @@ private void PrepareConnection(DbConnection owningObject, DbConnectionInternal o
{
// if Activate throws an exception
// put it back in the pool or have it properly disposed of
this.PutObject(obj, owningObject);
this.ReturnInternalConnection(obj, owningObject);
throw;
}
}
Expand Down Expand Up @@ -1613,7 +1613,7 @@ private void PoolCreateRequest(object state)
}
}

internal override void PutNewObject(DbConnectionInternal obj)
private void PutNewObject(DbConnectionInternal obj)
{
Debug.Assert(obj != null, "why are we adding a null object to the pool?");

Expand All @@ -1626,7 +1626,7 @@ internal override void PutNewObject(DbConnectionInternal obj)

}

internal override void PutObject(DbConnectionInternal obj, object owningObject)
internal override void ReturnInternalConnection(DbConnectionInternal obj, object owningObject)
{
Debug.Assert(obj != null, "null obj?");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private enum Tristate : byte
internal const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
internal const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
internal const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
internal const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";

// this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
private static Tristate s_legacyRowVersionNullBehavior;
Expand All @@ -32,6 +33,7 @@ private enum Tristate : byte
private static Tristate s_legacyVarTimeZeroScaleBehaviour;
private static Tristate s_useCompatProcessSni;
private static Tristate s_useCompatAsyncBehaviour;
private static Tristate s_useConnectionPoolV2;

#if NET
static LocalAppContextSwitches()
Expand Down Expand Up @@ -270,5 +272,29 @@ public static bool LegacyVarTimeZeroScaleBehaviour
return s_legacyVarTimeZeroScaleBehaviour == Tristate.True;
}
}

/// <summary>
/// When set to true, the connection pool will use the new V2 connection pool implementation.
/// When set to false, the connection pool will use the legacy V1 implementation.
/// This app context switch defaults to 'false'.
/// </summary>
public static bool UseConnectionPoolV2
{
get
{
if (s_useConnectionPoolV2 == Tristate.NotInitialized)
{
if (AppContext.TryGetSwitch(UseConnectionPoolV2String, out bool returnedValue) && returnedValue)
{
s_useConnectionPoolV2 = Tristate.True;
}
else
{
s_useConnectionPoolV2 = Tristate.False;
}
}
return s_useConnectionPoolV2 == Tristate.True;
}
}
}
}
Loading