From 83fd3adb39502f6f2bb08594b713a5c80e773fb5 Mon Sep 17 00:00:00 2001 From: Lawrence LCI Date: Thu, 14 Oct 2021 15:02:19 -0700 Subject: [PATCH 1/3] Merge netfx to netcore for DbConnectionPoolGroup.cs --- .../Data/ProviderBase/DbConnectionPoolGroup.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 51109fc388..ef3ed320ee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -52,6 +52,9 @@ sealed internal class DbConnectionPoolGroup internal DbConnectionPoolGroup(DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) { Debug.Assert(null != connectionOptions, "null connection options"); +#if NETFRAMEWORK + Debug.Assert(null == poolGroupOptions || ADP.s_isWindowsNT, "should not have pooling options on Win9x"); +#endif _connectionOptions = connectionOptions; _poolKey = key; @@ -129,6 +132,9 @@ internal int Clear() if (pool != null) { DbConnectionFactory connectionFactory = pool.ConnectionFactory; +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); +#endif connectionFactory.QueuePoolForRelease(pool, true); } } @@ -149,6 +155,10 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor DbConnectionPool pool = null; if (null != _poolGroupOptions) { +#if NETFRAMEWORK + Debug.Assert(ADP.s_isWindowsNT, "should not be pooling on Win9x"); +#endif + DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; if (_poolGroupOptions.PoolByIdentity) @@ -188,6 +198,9 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor bool addResult = _poolCollection.TryAdd(currentIdentity, newPool); Debug.Assert(addResult, "No other pool with current identity should exist at this point"); SqlClientEventSource.Log.EnterActiveConnectionPool(); +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment(); +#endif pool = newPool; } else @@ -263,6 +276,9 @@ internal bool Prune() // pool into a list of pools to be released when they // are completely empty. DbConnectionFactory connectionFactory = pool.ConnectionFactory; +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); +#endif connectionFactory.QueuePoolForRelease(pool, false); } else From ab9998fa07a209cff340d17c9075621a618371d7 Mon Sep 17 00:00:00 2001 From: Lawrence LCI Date: Thu, 14 Oct 2021 15:15:07 -0700 Subject: [PATCH 2/3] Move the netcore version DbConnectionPoolGroup.cs to shared src and update references in the csproj and reorder the DbConnectionPool entries to be alphabetical --- .../src/Microsoft.Data.SqlClient.csproj | 22 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 20 +- .../ProviderBase/DbConnectionPoolGroup.cs | 351 ------------------ .../ProviderBase/DbConnectionPoolGroup.cs | 0 4 files changed, 22 insertions(+), 371 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs rename src/Microsoft.Data.SqlClient/{netcore/src/Common => }/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs (100%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 6cee49f62c..e148e6e562 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -64,20 +64,23 @@ Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs Common\Microsoft\Data\ProviderBase\DbMetaDataFactory.cs @@ -496,9 +499,6 @@ Common\Microsoft\Data\ProviderBase\DbConnectionFactory.cs - - Common\Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs - Common\Microsoft\Data\ProviderBase\DbReferenceCollection.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index dccab48852..efe86c27a5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -122,20 +122,23 @@ Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs Microsoft\Data\ProviderBase\DbMetaDataFactory.cs @@ -575,7 +578,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs deleted file mode 100644 index cd7c387202..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ /dev/null @@ -1,351 +0,0 @@ -// 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. - -namespace Microsoft.Data.ProviderBase -{ - using System.Collections.Concurrent; - using System.Diagnostics; - using Microsoft.Data.Common; - using Microsoft.Data.SqlClient; - - - // set_ConnectionString calls DbConnectionFactory.GetConnectionPoolGroup - // when not found a new pool entry is created and potentially added - // DbConnectionPoolGroup starts in the Active state - - // Open calls DbConnectionFactory.GetConnectionPool - // if the existing pool entry is Disabled, GetConnectionPoolGroup is called for a new entry - // DbConnectionFactory.GetConnectionPool calls DbConnectionPoolGroup.GetConnectionPool - - // DbConnectionPoolGroup.GetConnectionPool will return pool for the current identity - // or null if identity is restricted or pooling is disabled or state is disabled at time of add - // state changes are Active->Active, Idle->Active - - // DbConnectionFactory.PruneConnectionPoolGroups calls Prune - // which will QueuePoolForRelease on all empty pools - // and once no pools remain, change state from Active->Idle->Disabled - // Once Disabled, factory can remove its reference to the pool entry - - sealed internal class DbConnectionPoolGroup - { - private readonly DbConnectionOptions _connectionOptions; - private readonly DbConnectionPoolKey _poolKey; - private readonly DbConnectionPoolGroupOptions _poolGroupOptions; - private ConcurrentDictionary _poolCollection; - - private int _state; // see PoolGroupState* below - - private DbConnectionPoolGroupProviderInfo _providerInfo; - private DbMetaDataFactory _metaDataFactory; - - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - - // always lock this before changing _state, we don't want to move out of the 'Disabled' state - // PoolGroupStateUninitialized = 0; - private const int PoolGroupStateActive = 1; // initial state, GetPoolGroup from cache, connection Open - private const int PoolGroupStateIdle = 2; // all pools are pruned via Clear - private const int PoolGroupStateDisabled = 4; // factory pool entry pruning method - - internal DbConnectionPoolGroup(DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) - { - Debug.Assert(null != connectionOptions, "null connection options"); - Debug.Assert(null == poolGroupOptions || ADP.s_isWindowsNT, "should not have pooling options on Win9x"); - - _connectionOptions = connectionOptions; - _poolKey = key; - _poolGroupOptions = poolGroupOptions; - - // always lock this object before changing state - // HybridDictionary does not create any sub-objects until add - // so it is safe to use for non-pooled connection as long as - // we check _poolGroupOptions first - _poolCollection = new ConcurrentDictionary(); - _state = PoolGroupStateActive; // VSWhidbey 112102 - } - - internal DbConnectionOptions ConnectionOptions - { - get - { - return _connectionOptions; - } - } - - internal DbConnectionPoolKey PoolKey - { - get - { - return _poolKey; - } - } - - internal DbConnectionPoolGroupProviderInfo ProviderInfo - { - get - { - return _providerInfo; - } - set - { - _providerInfo = value; - if (null != value) - { - _providerInfo.PoolGroup = this; - } - } - } - - internal bool IsDisabled - { - get - { - return (PoolGroupStateDisabled == _state); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal DbConnectionPoolGroupOptions PoolGroupOptions - { - get - { - return _poolGroupOptions; - } - } - - internal DbMetaDataFactory MetaDataFactory - { - get - { - return _metaDataFactory; - } - - set - { - _metaDataFactory = value; - } - } - - internal int Clear() - { - // must be multi-thread safe with competing calls by Clear and Prune via background thread - // will return the number of connections in the group after clearing has finished - - // First, note the old collection and create a new collection to be used - ConcurrentDictionary oldPoolCollection = null; - lock (this) - { - if (_poolCollection.Count > 0) - { - oldPoolCollection = _poolCollection; - _poolCollection = new ConcurrentDictionary(); - } - } - - // Then, if a new collection was created, release the pools from the old collection - if (oldPoolCollection != null) - { - foreach (var entry in oldPoolCollection) - { - DbConnectionPool pool = entry.Value; - if (pool != null) - { - // TODO: SQLBU 422890 - // Pruning a pool while a connection is currently attempting to connect - // will cause the pool to be prematurely abandoned. The only known effect so - // far is that the errorWait throttling will be reset when this occurs. - // We should be able to avoid this situation by not pruning the pool if - // it's _waitCount is non-zero (i.e. no connections *in* the pool, but also - // no connections attempting to be created for the pool). - - DbConnectionFactory connectionFactory = pool.ConnectionFactory; - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); - connectionFactory.QueuePoolForRelease(pool, true); - } - } - } - - // Finally, return the pool collection count - this may be non-zero if something was added while we were clearing - return _poolCollection.Count; - } - - internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactory) - { - // When this method returns null it indicates that the connection - // factory should not use pooling. - - // We don't support connection pooling on Win9x; it lacks too - // many of the APIs we require. - // PoolGroupOptions will only be null when we're not supposed to pool - // connections. - DbConnectionPool pool = null; - if (null != _poolGroupOptions) - { - Debug.Assert(ADP.s_isWindowsNT, "should not be pooling on Win9x"); - - DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; - if (_poolGroupOptions.PoolByIdentity) - { - // if we're pooling by identity (because integrated security is - // being used for these connections) then we need to go out and - // search for the connectionPool that matches the current identity. - - currentIdentity = DbConnectionPoolIdentity.GetCurrent(); - - // If the current token is restricted in some way, then we must - // not attempt to pool these connections. - if (currentIdentity.IsRestricted) - { - currentIdentity = null; - } - } - - if (null != currentIdentity) - { - if (!_poolCollection.TryGetValue(currentIdentity, out pool)) - { // find the pool - - - lock (this) - { - // Did someone already add it to the list? - if (!_poolCollection.TryGetValue(currentIdentity, out pool)) - { - DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions); - DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); - - if (MarkPoolGroupAsActive()) - { - // If we get here, we know for certain that we there isn't - // a pool that matches the current identity, so we have to - // add the optimistically created one - newPool.Startup(); // must start pool before usage - bool addResult = _poolCollection.TryAdd(currentIdentity, newPool); - Debug.Assert(addResult, "No other pool with current identity should exist at this point"); - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment(); - pool = newPool; - } - else - { - // else pool entry has been disabled so don't create new pools - Debug.Assert(PoolGroupStateDisabled == _state, "state should be disabled"); - - // don't need to call connectionFactory.QueuePoolForRelease(newPool) because - // pool callbacks were delayed and no risk of connections being created - newPool.Shutdown(); - } - } - else - { - // else found an existing pool to use instead - Debug.Assert(PoolGroupStateActive == _state, "state should be active since a pool exists and lock holds"); - } - } - } - // the found pool could be in any state - } - } - - if (null == pool) - { - lock (this) - { - // keep the pool entry state active when not pooling - MarkPoolGroupAsActive(); - } - } - return pool; - } - - private bool MarkPoolGroupAsActive() - { - // when getting a connection, make the entry active if it was idle (but not disabled) - // must always lock this before calling - - if (PoolGroupStateIdle == _state) - { - _state = PoolGroupStateActive; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Active", ObjectID); - } - return (PoolGroupStateActive == _state); - } - - internal bool Prune() - { - // must only call from DbConnectionFactory.PruneConnectionPoolGroups on background timer thread - // must lock(DbConnectionFactory._connectionPoolGroups.SyncRoot) before calling ReadyToRemove - // to avoid conflict with DbConnectionFactory.CreateConnectionPoolGroup replacing pool entry - lock (this) - { - if (_poolCollection.Count > 0) - { - var newPoolCollection = new ConcurrentDictionary(); - - foreach (var entry in _poolCollection) - { - DbConnectionPool pool = entry.Value; - if (pool != null) - { - // TODO: SQLBU 422890 - // Pruning a pool while a connection is currently attempting to connect - // will cause the pool to be prematurely abandoned. The only known effect so - // far is that the errorWait throttling will be reset when this occurs. - // We should be able to avoid this situation by not pruning the pool if - // it's _waitCount is non-zero (i.e. no connections *in* the pool, but also - // no connections attempting to be created for the pool). - - // Actually prune the pool if there are no connections in the pool and no errors occurred. - // Empty pool during pruning indicates zero or low activity, but - // an error state indicates the pool needs to stay around to - // throttle new connection attempts. - if ((!pool.ErrorOccurred) && (0 == pool.Count)) - { - - // Order is important here. First we remove the pool - // from the collection of pools so no one will try - // to use it while we're processing and finally we put the - // pool into a list of pools to be released when they - // are completely empty. - DbConnectionFactory connectionFactory = pool.ConnectionFactory; - - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); - connectionFactory.QueuePoolForRelease(pool, false); - } - else - { - newPoolCollection.TryAdd(entry.Key, entry.Value); - } - } - } - _poolCollection = newPoolCollection; - } - - // must be pruning thread to change state and no connections - // otherwise pruning thread risks making entry disabled soon after user calls ClearPool - if (0 == _poolCollection.Count) - { - if (PoolGroupStateActive == _state) - { - _state = PoolGroupStateIdle; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Idle", ObjectID); - } - else if (PoolGroupStateIdle == _state) - { - _state = PoolGroupStateDisabled; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Disabled", ObjectID); - } - } - - return (PoolGroupStateDisabled == _state); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs From 6db1e8c9ea76b94348812b298d52993930f8a1b5 Mon Sep 17 00:00:00 2001 From: Lawrence LCI Date: Thu, 14 Oct 2021 15:24:14 -0700 Subject: [PATCH 3/3] Update file to conform with coding style cleaning up IDE0008, IDE0003 and IDE0090 --- .../Data/ProviderBase/DbConnectionPoolGroup.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index ef3ed320ee..7568340594 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -6,7 +6,7 @@ using Microsoft.Data.Common; using Microsoft.Data.SqlClient; using System.Collections.Concurrent; -using System.Data.Common; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -126,7 +126,7 @@ internal int Clear() // Then, if a new collection was created, release the pools from the old collection if (oldPoolCollection != null) { - foreach (var entry in oldPoolCollection) + foreach (KeyValuePair entry in oldPoolCollection) { DbConnectionPool pool = entry.Value; if (pool != null) @@ -186,8 +186,8 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor // Did someone already add it to the list? if (!_poolCollection.TryGetValue(currentIdentity, out pool)) { - DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions); - DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); + DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(ConnectionOptions); + DbConnectionPool newPool = new(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); if (MarkPoolGroupAsActive()) { @@ -259,7 +259,7 @@ internal bool Prune() { var newPoolCollection = new ConcurrentDictionary(); - foreach (var entry in _poolCollection) + foreach (KeyValuePair entry in _poolCollection) { DbConnectionPool pool = entry.Value; if (pool != null)