diff --git a/doc/samples/SqlClientDiagnosticCounter.cs b/doc/samples/SqlClientDiagnosticCounter.cs new file mode 100644 index 0000000000..576563f6fd --- /dev/null +++ b/doc/samples/SqlClientDiagnosticCounter.cs @@ -0,0 +1,62 @@ +// +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; + +// This listener class will listen for events from the SqlClientEventSource class. +// SqlClientEventSource is an implementation of the EventSource class which gives +// it the ability to create events. +public class EventCounterListener : EventListener +{ + protected override void OnEventSourceCreated(EventSource eventSource) + { + // Only enable events from SqlClientEventSource. + if (eventSource.Name.Equals("Microsoft.Data.SqlClient.EventSource")) + { + var options = new Dictionary(); + // define time interval 1 second + // without defining this parameter event counters will not enabled + options.Add("EventCounterIntervalSec", "1"); + // enable for the None keyword + EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None, options); + } + } + + // This callback runs whenever an event is written by SqlClientEventSource. + // Event data is accessed through the EventWrittenEventArgs parameter. + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if (eventData.Payload.FirstOrDefault(p => p is IDictionary x && x.ContainsKey("Name")) is IDictionary counters) + { + if (counters.TryGetValue("DisplayName", out object name) && name is string cntName + && counters.TryGetValue("Mean", out object value) && value is double cntValue) + { + // print event counter's name and mean value + Console.WriteLine($"{cntName}\t\t{cntValue}"); + } + } + } +} + +class Program +{ + static void Main(string[] args) + { + // Create a new event listener + using (var listener = new EventCounterListener()) + { + string connectionString = "Data Source=localhost; Integrated Security=true"; + + for (int i = 0; i < 50; i++) + { + // Open a connection + SqlConnection cnn = new SqlConnection(connectionString); + cnn.Open(); + // wait for sampling interval happens + System.Threading.Thread.Sleep(500); + } + } + } +} +// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index bdecce6fc1..a41eeb8b02 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -25,7 +25,7 @@ internal abstract partial class DbConnectionFactory private static int _objectTypeCount; // EventSource counter internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); - // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to + // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to // a maximum of Environment.ProcessorCount at a time. private static uint s_pendingOpenNonPooledNext = 0; private static Task[] s_pendingOpenNonPooled = new Task[Environment.ProcessorCount]; @@ -124,6 +124,7 @@ internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConne DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions); if (null != newConnection) { + SqlClientEventSource.Log.HardConnectRequest(); newConnection.MakeNonPooledObject(owningConnection); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Non-pooled database connection created.", ObjectID); @@ -138,6 +139,7 @@ internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbCo DbConnectionInternal newConnection = CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningObject, userOptions); if (null != newConnection) { + SqlClientEventSource.Log.HardConnectRequest(); newConnection.MakePooledConnection(pool); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Pooled database connection created.", ObjectID); @@ -281,6 +283,7 @@ internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, D // lock prevents race condition with PruneConnectionPoolGroups newConnectionPoolGroups.Add(key, newConnectionPoolGroup); + SqlClientEventSource.Log.EnterActiveConnectionPoolGroup(); connectionPoolGroup = newConnectionPoolGroup; _connectionPoolGroups = newConnectionPoolGroups; } @@ -304,7 +307,7 @@ private void PruneConnectionPoolGroups(object state) { // when debugging this method, expect multiple threads at the same time SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}", ObjectID); - + // First, walk the pool release list and attempt to clear each // pool, when the pool is finally empty, we dispose of it. If the // pool isn't empty, it's because there are active connections or @@ -324,6 +327,7 @@ private void PruneConnectionPoolGroups(object state) { _poolsToRelease.Remove(pool); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePool={1}", ObjectID, pool.ObjectID); + SqlClientEventSource.Log.ExitInactiveConnectionPool(); } } } @@ -348,6 +352,7 @@ private void PruneConnectionPoolGroups(object state) { _poolGroupsToRelease.Remove(poolGroup); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePoolGroup={1}", ObjectID, poolGroup.ObjectID); + SqlClientEventSource.Log.ExitInactiveConnectionPoolGroup(); } } } @@ -372,7 +377,8 @@ private void PruneConnectionPoolGroups(object state) // move idle entries from last prune pass to a queue for pending release // otherwise process entry which may move it from active to idle if (entry.Value.Prune()) - { // may add entries to _poolsToRelease + { + // may add entries to _poolsToRelease QueuePoolGroupForRelease(entry.Value); } else @@ -405,6 +411,8 @@ internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) } _poolsToRelease.Add(pool); } + SqlClientEventSource.Log.EnterInactiveConnectionPool(); + SqlClientEventSource.Log.ExitActiveConnectionPool(); } internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) @@ -416,6 +424,8 @@ internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { _poolGroupsToRelease.Add(poolGroup); } + SqlClientEventSource.Log.EnterInactiveConnectionPoolGroup(); + SqlClientEventSource.Log.ExitActiveConnectionPoolGroup(); } virtual protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 0e92ed4f00..b24ed6810f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -231,6 +231,7 @@ internal void DeactivateConnection() int activateCount = Interlocked.Decrement(ref _activateCount); #endif // DEBUG + SqlClientEventSource.Log.ExitActiveConnection(); if (!_connectionIsDoomed && Pool.UseLoadBalancing) { 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 5801057092..51109fc388 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 @@ -187,6 +187,7 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor 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"); + SqlClientEventSource.Log.EnterActiveConnectionPool(); pool = newPool; } else @@ -194,8 +195,8 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor // 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 + // don't need to call connectionFactory.QueuePoolForRelease(newPool) because + // pool callbacks were delayed and no risk of connections being created newPool.Shutdown(); } } @@ -262,7 +263,6 @@ internal bool Prune() // pool into a list of pools to be released when they // are completely empty. DbConnectionFactory connectionFactory = pool.ConnectionFactory; - connectionFactory.QueuePoolForRelease(pool, false); } else 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 79d03a8863..3f956374f7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -327,6 +327,10 @@ + + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index d620caa34a..1a2e809147 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -108,6 +108,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour } connection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions); + SqlClientEventSource.Log.EnterNonPooledConnection(); } else { @@ -209,6 +210,10 @@ private static void TryGetConnectionCompletedContinuation(Task {0}, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.", ObjectID); + SqlClientEventSource.Log.EnterStasisConnection(); } private void TerminateStasis(bool returningToPool) @@ -494,6 +499,7 @@ private void TerminateStasis(bool returningToPool) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Delegated Transaction has ended, connection is closed/leaked. Disposing.", ObjectID); } + SqlClientEventSource.Log.ExitStasisConnection(); _isInStasis = false; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index d0921da04c..6b83fdce3b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -231,6 +231,7 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal } SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Added.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } + SqlClientEventSource.Log.EnterFreeConnection(); } internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) @@ -293,6 +294,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // connections, we'll put it back... if (0 <= entry) { + SqlClientEventSource.Log.ExitFreeConnection(); Pool.PutObjectFromTransactedPool(transactedObject); } } @@ -600,6 +602,7 @@ private void CleanupCallback(object state) { Debug.Assert(obj != null, "null connection is not expected"); // If we obtained one from the old stack, destroy it. + SqlClientEventSource.Log.ExitFreeConnection(); // Transaction roots must survive even aging out (TxEnd event will clean them up). bool shouldDestroy = true; @@ -696,11 +699,13 @@ internal void Clear() while (_stackNew.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); + SqlClientEventSource.Log.ExitFreeConnection(); DestroyObject(obj); } while (_stackOld.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); + SqlClientEventSource.Log.ExitFreeConnection(); DestroyObject(obj); } @@ -742,6 +747,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio } _objectList.Add(newObj); _totalObjects = _objectList.Count; + SqlClientEventSource.Log.EnterPooledConnection(); } // If the old connection belonged to another pool, we need to remove it from that @@ -967,9 +973,11 @@ internal void DestroyObject(DbConnectionInternal obj) if (removed) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Removed from pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitPooledConnection(); } obj.Dispose(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Disposed.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.HardDisconnectRequest(); } } @@ -1301,6 +1309,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } connection = obj; + SqlClientEventSource.Log.SoftConnectRequest(); return true; } @@ -1337,6 +1346,7 @@ internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbCon if (newConnection != null) { + SqlClientEventSource.Log.SoftConnectRequest(); PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction); oldConnection.PrepareForReplaceConnection(); oldConnection.DeactivateConnection(); @@ -1374,6 +1384,7 @@ private DbConnectionInternal GetFromGeneralPool() if (null != obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from general pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitFreeConnection(); } return (obj); } @@ -1390,6 +1401,7 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) if (null != obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from transacted pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitFreeConnection(); if (obj.IsTransactionRoot) { @@ -1544,12 +1556,13 @@ internal void PutNewObject(DbConnectionInternal obj) _stackNew.Push(obj); _waitHandles.PoolSemaphore.Release(1); + SqlClientEventSource.Log.EnterFreeConnection(); } internal void PutObject(DbConnectionInternal obj, object owningObject) { Debug.Assert(null != obj, "null obj?"); - + SqlClientEventSource.Log.SoftDisconnectRequest(); // Once a connection is closing (which is the state that we're in at // this point in time) you cannot delegate a transaction to or enlist @@ -1662,6 +1675,8 @@ private bool ReclaimEmancipatedObjects() { DbConnectionInternal obj = reclaimedObjects[i]; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Reclaiming.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ReclaimedConnectionRequest(); + emancipatedObjectFound = true; obj.DetachCurrentTransactionIfEnded(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs new file mode 100644 index 0000000000..1cab291611 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs @@ -0,0 +1,396 @@ +// 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.Diagnostics.Tracing; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// supported frameworks: .Net core 3.1 and .Net standard 2.1 and above + /// + internal partial class SqlClientEventSource : SqlClientEventSourceBase + { + private PollingCounter _activeHardConnections; + private IncrementingPollingCounter _hardConnectsPerSecond; + private IncrementingPollingCounter _hardDisconnectsPerSecond; + + private PollingCounter _activeSoftConnections; + private IncrementingPollingCounter _softConnects; + private IncrementingPollingCounter _softDisconnects; + + private PollingCounter _numberOfNonPooledConnections; + private PollingCounter _numberOfPooledConnections; + + private PollingCounter _numberOfActiveConnectionPoolGroups; + private PollingCounter _numberOfInactiveConnectionPoolGroups; + + private PollingCounter _numberOfActiveConnectionPools; + private PollingCounter _numberOfInactiveConnectionPools; + + private PollingCounter _numberOfActiveConnections; + private PollingCounter _numberOfFreeConnections; + private PollingCounter _numberOfStasisConnections; + private IncrementingPollingCounter _numberOfReclaimedConnections; + + private long _activeHardConnectionsCounter = 0; + private long _hardConnectsCounter = 0; + private long _hardDisconnectsCounter = 0; + + private long _activeSoftConnectionsCounter = 0; + private long _softConnectsCounter = 0; + private long _softDisconnectsCounter = 0; + + private long _nonPooledConnectionsCounter = 0; + private long _pooledConnectionsCounter = 0; + + private long _activeConnectionPoolGroupsCounter = 0; + private long _inactiveConnectionPoolGroupsCounter = 0; + + private long _activeConnectionPoolsCounter = 0; + private long _inactiveConnectionPoolsCounter = 0; + + private long _activeConnectionsCounter = 0; + private long _freeConnectionsCounter = 0; + private long _stasisConnectionsCounter = 0; + private long _reclaimedConnectionsCounter = 0; + + protected override void EventCommandMethodCall(EventCommandEventArgs command) + { + if(command.Command != EventCommand.Enable) + { + return; + } + + _activeHardConnections = _activeHardConnections ?? + new PollingCounter("active-hard-connections", this, () => _activeHardConnectionsCounter) + { + DisplayName = "Actual active connections are made to servers", + DisplayUnits = "count" + }; + + _hardConnectsPerSecond = _hardConnectsPerSecond ?? + new IncrementingPollingCounter("hard-connects", this, () => _hardConnectsCounter) + { + DisplayName = "Actual connections are made to servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ?? + new IncrementingPollingCounter("hard-disconnects", this, () => _hardDisconnectsCounter) + { + DisplayName = "Actual disconnections are made to servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _activeSoftConnections = _activeSoftConnections ?? + new PollingCounter("active-soft-connects", this, () => _activeSoftConnectionsCounter) + { + DisplayName = "Active connections got from connection pool", + DisplayUnits = "count" + }; + + _softConnects = _softConnects ?? + new IncrementingPollingCounter("soft-connects", this, () => _softConnectsCounter) + { + DisplayName = "Connections got from connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _softDisconnects = _softDisconnects ?? + new IncrementingPollingCounter("soft-disconnects", this, () => _softDisconnectsCounter) + { + DisplayName = "Connections returned to the connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _numberOfNonPooledConnections = _numberOfNonPooledConnections ?? + new PollingCounter("number-of-non-pooled-connections", this, () => _nonPooledConnectionsCounter) + { + DisplayName = "Number of connections are not using connection pooling", + DisplayUnits = "count / sec" + }; + + _numberOfPooledConnections = _numberOfPooledConnections ?? + new PollingCounter("number-of-pooled-connections", this, () => _pooledConnectionsCounter) + { + DisplayName = "Number of connections are managed by connection pooler", + DisplayUnits = "count / sec" + }; + + _numberOfActiveConnectionPoolGroups = _numberOfActiveConnectionPoolGroups ?? + new PollingCounter("number-of-active-connection-pool-groups", this, () => _activeConnectionPoolGroupsCounter) + { + DisplayName = "Number of active unique connection strings", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPoolGroups = _numberOfInactiveConnectionPoolGroups ?? + new PollingCounter("number-of-inactive-connection-pool-groups", this, () => _inactiveConnectionPoolGroupsCounter) + { + DisplayName = "Number of unique connection strings waiting for pruning", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionPools = _numberOfActiveConnectionPools ?? + new PollingCounter("number-of-active-connection-pools", this, () => _activeConnectionPoolsCounter) + { + DisplayName = "Number of active connection pools", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPools = _numberOfInactiveConnectionPools ?? + new PollingCounter("number-of-inactive-connection-pools", this, () => _inactiveConnectionPoolsCounter) + { + DisplayName = "Number of inactive connection pools", + DisplayUnits = "count" + }; + + _numberOfActiveConnections = _numberOfActiveConnections ?? + new PollingCounter("number-of-active-connections", this, () => _activeConnectionsCounter) + { + DisplayName = "Number of active connections", + DisplayUnits = "count" + }; + + _numberOfFreeConnections = _numberOfFreeConnections ?? + new PollingCounter("number-of-free-connections", this, () => _freeConnectionsCounter) + { + DisplayName = "Number of free-ready connections", + DisplayUnits = "count" + }; + + _numberOfStasisConnections = _numberOfStasisConnections ?? + new PollingCounter("number-of-stasis-connections", this, () => _stasisConnectionsCounter) + { + DisplayName = "Number of connections currently waiting to be ready", + DisplayUnits = "count" + }; + + _numberOfReclaimedConnections = _numberOfReclaimedConnections ?? + new IncrementingPollingCounter("number-of-reclaimed-connections", this, () => _reclaimedConnectionsCounter) + { + DisplayName = "Number of reclaimed connections from GC", + DisplayUnits = "count", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + } + + /// + /// The number of actual connections that are being made to servers + /// + [NonEvent] + internal override void HardConnectRequest() + { + Interlocked.Increment(ref _activeHardConnectionsCounter); + Interlocked.Increment(ref _hardConnectsCounter); + } + + /// + /// The number of actual disconnects that are being made to servers + /// + [NonEvent] + internal override void HardDisconnectRequest() + { + Interlocked.Decrement(ref _activeHardConnectionsCounter); + Interlocked.Increment(ref _hardDisconnectsCounter); + } + + /// + /// The number of connections we get from the pool + /// + [NonEvent] + internal override void SoftConnectRequest() + { + Interlocked.Increment(ref _activeSoftConnectionsCounter); + Interlocked.Increment(ref _softConnectsCounter); + } + + /// + /// The number of connections we return to the pool + /// + [NonEvent] + internal override void SoftDisconnectRequest() + { + Interlocked.Decrement(ref _activeSoftConnectionsCounter); + Interlocked.Increment(ref _softDisconnectsCounter); + } + + /// + /// The number of connections that are not using connection pooling + /// + [NonEvent] + internal override void EnterNonPooledConnection() + { + Interlocked.Increment(ref _nonPooledConnectionsCounter); + } + + /// + /// The number of connections that are not using connection pooling + /// + [NonEvent] + internal override void ExitNonPooledConnection() + { + Interlocked.Decrement(ref _nonPooledConnectionsCounter); + } + + /// + /// The number of connections that are managed by the connection pooler + /// + [NonEvent] + internal override void EnterPooledConnection() + { + Interlocked.Increment(ref _pooledConnectionsCounter); + } + + /// + /// The number of connections that are managed by the connection pooler + /// + [NonEvent] + internal override void ExitPooledConnection() + { + Interlocked.Decrement(ref _pooledConnectionsCounter); + } + + /// + /// The number of unique connection strings + /// + [NonEvent] + internal override void EnterActiveConnectionPoolGroup() + { + Interlocked.Increment(ref _activeConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings + /// + [NonEvent] + internal override void ExitActiveConnectionPoolGroup() + { + Interlocked.Decrement(ref _activeConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + [NonEvent] + internal override void EnterInactiveConnectionPoolGroup() + { + Interlocked.Increment(ref _inactiveConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + [NonEvent] + internal override void ExitInactiveConnectionPoolGroup() + { + Interlocked.Decrement(ref _inactiveConnectionPoolGroupsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void EnterActiveConnectionPool() + { + Interlocked.Increment(ref _activeConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void ExitActiveConnectionPool() + { + Interlocked.Decrement(ref _activeConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void EnterInactiveConnectionPool() + { + Interlocked.Increment(ref _inactiveConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void ExitInactiveConnectionPool() + { + Interlocked.Decrement(ref _inactiveConnectionPoolsCounter); + } + + /// + /// The number of connections currently in-use + /// + [NonEvent] + internal override void EnterActiveConnection() + { + Interlocked.Increment(ref _activeConnectionsCounter); + } + + /// + /// The number of connections currently in-use + /// + [NonEvent] + internal override void ExitActiveConnection() + { + Interlocked.Decrement(ref _activeConnectionsCounter); + } + + /// + /// The number of connections currently available for use + /// + [NonEvent] + internal override void EnterFreeConnection() + { + Interlocked.Increment(ref _freeConnectionsCounter); + } + + /// + /// The number of connections currently available for use + /// + [NonEvent] + internal override void ExitFreeConnection() + { + Interlocked.Decrement(ref _freeConnectionsCounter); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + [NonEvent] + internal override void EnterStasisConnection() + { + Interlocked.Increment(ref _stasisConnectionsCounter); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + [NonEvent] + internal override void ExitStasisConnection() + { + Interlocked.Decrement(ref _stasisConnectionsCounter); + } + + /// + /// The number of connections we reclaim from GC'd external connections + /// + [NonEvent] + internal override void ReclaimedConnectionRequest() + { + Interlocked.Increment(ref _reclaimedConnectionsCounter); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs index caa30908c6..a4b3f0bded 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.Windows.cs @@ -8,7 +8,7 @@ namespace Microsoft.Data.SqlClient { - internal partial class SqlClientEventSource : EventSource + internal partial class SqlClientEventSource : SqlClientEventSourceBase { private bool _traceLoggingProviderEnabled = false; @@ -29,6 +29,7 @@ internal partial class SqlClientEventSource : EventSource protected override void OnEventCommand(EventCommandEventArgs e) { + base.OnEventCommand(e); // Internally, EventListener.EnableEvents sends an event command, with a reserved value of 0, -2, or -3. // When a command is sent via EnableEvents or SendCommand, check if it is a user-defined value // to enable or disable event tracing in sni.dll. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 045b8c7eba..a32d202bb0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -9,8 +9,67 @@ namespace Microsoft.Data.SqlClient { + internal abstract class SqlClientEventSourceBase : EventSource + { + protected override void OnEventCommand(EventCommandEventArgs command) + { + base.OnEventCommand(command); + EventCommandMethodCall(command); + } + + protected virtual void EventCommandMethodCall(EventCommandEventArgs command) { } + + #region not implemented for .Net core 2.1, .Net standard 2.0 and lower + internal virtual void HardConnectRequest() { /*no-op*/ } + + internal virtual void HardDisconnectRequest() { /*no-op*/ } + + internal virtual void SoftConnectRequest() { /*no-op*/ } + + internal virtual void SoftDisconnectRequest() { /*no-op*/ } + + internal virtual void EnterNonPooledConnection() { /*no-op*/ } + + internal virtual void ExitNonPooledConnection() { /*no-op*/ } + + internal virtual void EnterPooledConnection() { /*no-op*/ } + + internal virtual void ExitPooledConnection() { /*no-op*/ } + + internal virtual void EnterActiveConnectionPoolGroup() { /*no-op*/ } + + internal virtual void ExitActiveConnectionPoolGroup() { /*no-op*/ } + + internal virtual void EnterInactiveConnectionPoolGroup() { /*no-op*/ } + + internal virtual void ExitInactiveConnectionPoolGroup() { /*no-op*/ } + + internal virtual void EnterActiveConnectionPool() { /*no-op*/ } + + internal virtual void ExitActiveConnectionPool() { /*no-op*/ } + + internal virtual void EnterInactiveConnectionPool() { /*no-op*/ } + + internal virtual void ExitInactiveConnectionPool() { /*no-op*/ } + + internal virtual void EnterActiveConnection() { /*no-op*/ } + + internal virtual void ExitActiveConnection() { /*no-op*/ } + + internal virtual void EnterFreeConnection() { /*no-op*/ } + + internal virtual void ExitFreeConnection() { /*no-op*/ } + + internal virtual void EnterStasisConnection() { /*no-op*/ } + + internal virtual void ExitStasisConnection() { /*no-op*/ } + + internal virtual void ReclaimedConnectionRequest() { /*no-op*/ } + #endregion + } + [EventSource(Name = "Microsoft.Data.SqlClient.EventSource")] - internal partial class SqlClientEventSource : EventSource + internal partial class SqlClientEventSource : SqlClientEventSourceBase { // Defines the singleton instance for the Resources ETW provider internal static readonly SqlClientEventSource Log = new SqlClientEventSource(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DDBasics/DDAsyncTest/DDAsyncTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DDBasics/DDAsyncTest/DDAsyncTest.cs index d1f7ed0a24..2bdde5eda1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DDBasics/DDAsyncTest/DDAsyncTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DDBasics/DDAsyncTest/DDAsyncTest.cs @@ -26,7 +26,7 @@ public static void OpenConnection_WithAsyncTrue() { // Passes on NetFx var asyncConnectionString = DataTestUtility.TCPConnectionString + ";async=true"; - SqlConnection connection = new SqlConnection(asyncConnectionString); + using (SqlConnection connection = new SqlConnection(asyncConnectionString)){} } #region <> @@ -60,16 +60,17 @@ private static bool DoesProcessExecutedAsync(IReadOnlyList executedProce private static async Task ExecuteCommandWithNewConnectionAsync(string processName, string cmdText, ICollection executedProcessList) { - var conn = new SqlConnection(DataTestUtility.TCPConnectionString); - - await conn.OpenAsync(); - var cmd = new SqlCommand(cmdText, conn); - - using (SqlDataReader reader = await cmd.ExecuteReaderAsync()) + using (var conn = new SqlConnection(DataTestUtility.TCPConnectionString)) { - while (await reader.ReadAsync()) + await conn.OpenAsync(); + var cmd = new SqlCommand(cmdText, conn); + + using (SqlDataReader reader = await cmd.ExecuteReaderAsync()) { - executedProcessList.Add(processName); + while (await reader.ReadAsync()) + { + executedProcessList.Add(processName); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index e6cb387284..80701905f3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -776,7 +776,7 @@ protected override void OnEventSourceCreated(EventSource eventSource) { if (eventSource.Name.Equals("Microsoft.Data.SqlClient.EventSource")) { - // Collect all traces for better code coverage + //// Collect all traces for better code coverage EnableEvents(eventSource, EventLevel.Informational, EventKeywords.All); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index e441bea00d..9ad8c827e0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -245,6 +245,10 @@ + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index 6249c4fae3..ae52a6efab 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -91,7 +91,8 @@ public void SimpleFillTest() public void PrepUnprepTest() { // share the connection - using (SqlCommand cmd = new SqlCommand("select * from shippers", new SqlConnection(DataTestUtility.TCPConnectionString))) + using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlCommand cmd = new SqlCommand("select * from shippers", connection)) using (SqlDataAdapter sqlAdapter = new SqlDataAdapter()) { cmd.Connection.Open(); @@ -183,7 +184,8 @@ public void SqlVariantTest() ExecuteNonQueryCommand("CREATE TABLE " + tableName + " (c0_bigint bigint, c1_variant sql_variant)"); // good test for null values and unicode strings - using (SqlCommand cmd = new SqlCommand(null, new SqlConnection(DataTestUtility.TCPConnectionString))) + using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlCommand cmd = new SqlCommand(null, conn)) using (SqlDataAdapter sqlAdapter = new SqlDataAdapter()) { cmd.Connection.Open(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs index 2696f354aa..f86f71468f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs @@ -46,31 +46,41 @@ private static void RunDataTestForSingleConnString(string tcpConnectionString) /// private static void BasicConnectionPoolingTest(string connectionString) { - SqlConnection connection = new SqlConnection(connectionString); - connection.Open(); - InternalConnectionWrapper internalConnection = new InternalConnectionWrapper(connection); - ConnectionPoolWrapper connectionPool = new ConnectionPoolWrapper(connection); - connection.Close(); + InternalConnectionWrapper internalConnection; + ConnectionPoolWrapper connectionPool; + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + internalConnection = new InternalConnectionWrapper(connection); + connectionPool = new ConnectionPoolWrapper(connection); + connection.Close(); + } - SqlConnection connection2 = new SqlConnection(connectionString); - connection2.Open(); - Assert.True(internalConnection.IsInternalConnectionOf(connection2), "New connection does not use same internal connection"); - Assert.True(connectionPool.ContainsConnection(connection2), "New connection is in a different pool"); - connection2.Close(); + using (SqlConnection connection2 = new SqlConnection(connectionString)) + { + connection2.Open(); + Assert.True(internalConnection.IsInternalConnectionOf(connection2), "New connection does not use same internal connection"); + Assert.True(connectionPool.ContainsConnection(connection2), "New connection is in a different pool"); + connection2.Close(); + } - SqlConnection connection3 = new SqlConnection(connectionString + ";App=SqlConnectionPoolUnitTest;"); - connection3.Open(); - Assert.False(internalConnection.IsInternalConnectionOf(connection3), "Connection with different connection string uses same internal connection"); - Assert.False(connectionPool.ContainsConnection(connection3), "Connection with different connection string uses same connection pool"); - connection3.Close(); + using (SqlConnection connection3 = new SqlConnection(connectionString + ";App=SqlConnectionPoolUnitTest;")) + { + connection3.Open(); + Assert.False(internalConnection.IsInternalConnectionOf(connection3), "Connection with different connection string uses same internal connection"); + Assert.False(connectionPool.ContainsConnection(connection3), "Connection with different connection string uses same connection pool"); + connection3.Close(); + } connectionPool.Cleanup(); - SqlConnection connection4 = new SqlConnection(connectionString); - connection4.Open(); - Assert.True(internalConnection.IsInternalConnectionOf(connection4), "New connection does not use same internal connection"); - Assert.True(connectionPool.ContainsConnection(connection4), "New connection is in a different pool"); - connection4.Close(); + using (SqlConnection connection4 = new SqlConnection(connectionString)) + { + connection4.Open(); + Assert.True(internalConnection.IsInternalConnectionOf(connection4), "New connection does not use same internal connection"); + Assert.True(connectionPool.ContainsConnection(connection4), "New connection is in a different pool"); + connection4.Close(); + } } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup), nameof(DataTestUtility.IsAADAuthorityURLSetup))] @@ -154,18 +164,20 @@ private static void ClearAllPoolsTest(string connectionString) SqlConnection.ClearAllPools(); Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); - SqlConnection connection = new SqlConnection(connectionString); - connection.Open(); - ConnectionPoolWrapper pool = new ConnectionPoolWrapper(connection); - connection.Close(); - ConnectionPoolWrapper[] allPools = ConnectionPoolWrapper.AllConnectionPools(); - DataTestUtility.AssertEqualsWithDescription(1, allPools.Length, "Incorrect number of pools exist."); - Assert.True(allPools[0].Equals(pool), "Saved pool is not in the list of all pools"); - DataTestUtility.AssertEqualsWithDescription(1, pool.ConnectionCount, "Saved pool has incorrect number of connections"); - - SqlConnection.ClearAllPools(); - Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); - DataTestUtility.AssertEqualsWithDescription(0, pool.ConnectionCount, "Saved pool has incorrect number of connections."); + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + ConnectionPoolWrapper pool = new ConnectionPoolWrapper(connection); + connection.Close(); + ConnectionPoolWrapper[] allPools = ConnectionPoolWrapper.AllConnectionPools(); + DataTestUtility.AssertEqualsWithDescription(1, allPools.Length, "Incorrect number of pools exist."); + Assert.True(allPools[0].Equals(pool), "Saved pool is not in the list of all pools"); + DataTestUtility.AssertEqualsWithDescription(1, pool.ConnectionCount, "Saved pool has incorrect number of connections"); + + SqlConnection.ClearAllPools(); + Assert.True(0 == ConnectionPoolWrapper.AllConnectionPools().Length, "Pools exist after clearing all pools"); + DataTestUtility.AssertEqualsWithDescription(0, pool.ConnectionCount, "Saved pool has incorrect number of connections."); + } } /// diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs index 81cbeae7f2..8f6965d043 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTest.cs @@ -283,33 +283,35 @@ private static void TestSqlDataReaderParameterToTVP_Type(object paramValue, stri xsql(conn, string.Format("create type dbo.{0} as table (f1 {1})", tvpTypeName, expectedBaseTypeName)); // Send TVP using SqlDataReader. - SqlConnection connInput = new SqlConnection(s_connStr); - connInput.Open(); - - using (SqlCommand cmdInput = connInput.CreateCommand()) + using (SqlConnection connInput = new SqlConnection(s_connStr)) { - cmdInput.CommandText = "select @p1 as f1"; - cmdInput.Parameters.Add("@p1", GetSqlDbType(expectedBaseTypeName)); - cmdInput.Parameters["@p1"].Value = paramValue; + connInput.Open(); - using (SqlDataReader drInput = cmdInput.ExecuteReader(CommandBehavior.CloseConnection)) + using (SqlCommand cmdInput = connInput.CreateCommand()) { - using (SqlCommand cmd = conn.CreateCommand()) + cmdInput.CommandText = "select @p1 as f1"; + cmdInput.Parameters.Add("@p1", GetSqlDbType(expectedBaseTypeName)); + cmdInput.Parameters["@p1"].Value = paramValue; + + using (SqlDataReader drInput = cmdInput.ExecuteReader(CommandBehavior.CloseConnection)) { - cmd.CommandText = "select f1 from @tvpParam"; - SqlParameter p = cmd.Parameters.AddWithValue("@tvpParam", drInput); - p.SqlDbType = SqlDbType.Structured; - p.TypeName = string.Format("dbo.{0}", tvpTypeName); - using (SqlDataReader dr = cmd.ExecuteReader()) + using (SqlCommand cmd = conn.CreateCommand()) { - dr.Read(); - VerifyReaderTypeAndValue("Test SqlDataReader Parameter To TVP [Data Type]", expectedBaseTypeName, expectedTypeName, dr[0], expectedTypeName, paramValue); - dr.Dispose(); + cmd.CommandText = "select f1 from @tvpParam"; + SqlParameter p = cmd.Parameters.AddWithValue("@tvpParam", drInput); + p.SqlDbType = SqlDbType.Structured; + p.TypeName = string.Format("dbo.{0}", tvpTypeName); + using (SqlDataReader dr = cmd.ExecuteReader()) + { + dr.Read(); + VerifyReaderTypeAndValue("Test SqlDataReader Parameter To TVP [Data Type]", expectedBaseTypeName, expectedTypeName, dr[0], expectedTypeName, paramValue); + dr.Dispose(); + } } } } + connInput.Close(); } - connInput.Close(); } } catch (Exception e) @@ -345,31 +347,33 @@ private static void TestSqlDataReaderParameterToTVP_Variant(object paramValue, s xsql(conn, string.Format("create type dbo.{0} as table (f1 sql_variant)", tvpTypeName)); // Send TVP using SqlDataReader. - SqlConnection connInput = new SqlConnection(s_connStr); - connInput.Open(); - using (SqlCommand cmdInput = connInput.CreateCommand()) + using (SqlConnection connInput = new SqlConnection(s_connStr)) { - cmdInput.CommandText = "select @p1 as f1"; - cmdInput.Parameters.Add("@p1", SqlDbType.Variant); - cmdInput.Parameters["@p1"].Value = paramValue; - using (SqlDataReader drInput = cmdInput.ExecuteReader(CommandBehavior.CloseConnection)) + connInput.Open(); + using (SqlCommand cmdInput = connInput.CreateCommand()) { - using (SqlCommand cmd = conn.CreateCommand()) + cmdInput.CommandText = "select @p1 as f1"; + cmdInput.Parameters.Add("@p1", SqlDbType.Variant); + cmdInput.Parameters["@p1"].Value = paramValue; + using (SqlDataReader drInput = cmdInput.ExecuteReader(CommandBehavior.CloseConnection)) { - cmd.CommandText = "select f1, sql_variant_property(f1,'BaseType') as BaseType from @tvpParam"; - SqlParameter p = cmd.Parameters.AddWithValue("@tvpParam", drInput); - p.SqlDbType = SqlDbType.Structured; - p.TypeName = string.Format("dbo.{0}", tvpTypeName); - using (SqlDataReader dr = cmd.ExecuteReader()) + using (SqlCommand cmd = conn.CreateCommand()) { - dr.Read(); - VerifyReaderTypeAndValue("Test SqlDataReader Parameter To TVP [Variant Type]", "SqlDbType.Variant", dr, expectedTypeName, expectedBaseTypeName, paramValue); - dr.Dispose(); + cmd.CommandText = "select f1, sql_variant_property(f1,'BaseType') as BaseType from @tvpParam"; + SqlParameter p = cmd.Parameters.AddWithValue("@tvpParam", drInput); + p.SqlDbType = SqlDbType.Structured; + p.TypeName = string.Format("dbo.{0}", tvpTypeName); + using (SqlDataReader dr = cmd.ExecuteReader()) + { + dr.Read(); + VerifyReaderTypeAndValue("Test SqlDataReader Parameter To TVP [Variant Type]", "SqlDbType.Variant", dr, expectedTypeName, expectedBaseTypeName, paramValue); + dr.Dispose(); + } } } } + connInput.Close(); } - connInput.Close(); } } catch (Exception e) @@ -727,36 +731,38 @@ private static void SqlBulkCopySqlDataReader_Type(object paramValue, string expe } xsql(conn, string.Format("insert into {0}(f1) values(CAST('{1}' AS {2}));", bulkCopySrcTableName, value, expectedBaseTypeName)); - SqlConnection connInput = new SqlConnection(s_connStr); - connInput.Open(); - using (SqlCommand cmdInput = connInput.CreateCommand()) + using (SqlConnection connInput = new SqlConnection(s_connStr)) { - cmdInput.CommandText = string.Format("select * from {0}", bulkCopySrcTableName); - using (SqlDataReader drInput = cmdInput.ExecuteReader()) + connInput.Open(); + using (SqlCommand cmdInput = connInput.CreateCommand()) { - // Perform bulk copy to target. - using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn)) + cmdInput.CommandText = string.Format("select * from {0}", bulkCopySrcTableName); + using (SqlDataReader drInput = cmdInput.ExecuteReader()) { - bulkCopy.BulkCopyTimeout = 60; - bulkCopy.BatchSize = 1; - bulkCopy.DestinationTableName = bulkCopyTableName; - bulkCopy.WriteToServer(drInput); - } + // Perform bulk copy to target. + using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn)) + { + bulkCopy.BulkCopyTimeout = 60; + bulkCopy.BatchSize = 1; + bulkCopy.DestinationTableName = bulkCopyTableName; + bulkCopy.WriteToServer(drInput); + } - // Verify target. - using (SqlCommand cmd = conn.CreateCommand()) - { - cmd.CommandText = string.Format("select f1 from {0}", bulkCopyTableName); - using (SqlDataReader drVerify = cmd.ExecuteReader()) + // Verify target. + using (SqlCommand cmd = conn.CreateCommand()) { - drVerify.Read(); - VerifyReaderTypeAndValue("SqlBulkCopy From SqlDataReader [Data Type]", expectedBaseTypeName, expectedTypeName, drVerify[0], expectedTypeName, paramValue); - drVerify.Dispose(); + cmd.CommandText = string.Format("select f1 from {0}", bulkCopyTableName); + using (SqlDataReader drVerify = cmd.ExecuteReader()) + { + drVerify.Read(); + VerifyReaderTypeAndValue("SqlBulkCopy From SqlDataReader [Data Type]", expectedBaseTypeName, expectedTypeName, drVerify[0], expectedTypeName, paramValue); + drVerify.Dispose(); + } } } } + connInput.Close(); } - connInput.Close(); } } catch (Exception e) @@ -810,38 +816,40 @@ private static void SqlBulkCopySqlDataReader_Variant(object paramValue, string e } xsql(conn, string.Format("insert into {0}(f1) values(CAST('{1}' AS {2}));", bulkCopySrcTableName, value, expectedBaseTypeName)); - SqlConnection connInput = new SqlConnection(s_connStr); - connInput.Open(); - using (SqlCommand cmdInput = connInput.CreateCommand()) + using (SqlConnection connInput = new SqlConnection(s_connStr)) { - cmdInput.CommandText = string.Format("select * from {0}", bulkCopySrcTableName); - using (SqlDataReader drInput = cmdInput.ExecuteReader()) + connInput.Open(); + using (SqlCommand cmdInput = connInput.CreateCommand()) { + cmdInput.CommandText = string.Format("select * from {0}", bulkCopySrcTableName); + using (SqlDataReader drInput = cmdInput.ExecuteReader()) { - // Perform bulk copy to target. - using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn)) { - bulkCopy.BulkCopyTimeout = 60; - bulkCopy.BatchSize = 1; - bulkCopy.DestinationTableName = bulkCopyTableName; - bulkCopy.WriteToServer(drInput); - } + // Perform bulk copy to target. + using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn)) + { + bulkCopy.BulkCopyTimeout = 60; + bulkCopy.BatchSize = 1; + bulkCopy.DestinationTableName = bulkCopyTableName; + bulkCopy.WriteToServer(drInput); + } - // Verify target. - using (SqlCommand cmd = conn.CreateCommand()) - { - cmd.CommandText = string.Format("select f1, sql_variant_property(f1,'BaseType') as BaseType from {0}", bulkCopyTableName); - using (SqlDataReader drVerify = cmd.ExecuteReader()) + // Verify target. + using (SqlCommand cmd = conn.CreateCommand()) { - drVerify.Read(); - VerifyReaderTypeAndValue("SqlBulkCopy From SqlDataReader [Variant Type]", "SqlDbType.Variant", drVerify, expectedTypeName, expectedBaseTypeName, paramValue); - drVerify.Dispose(); + cmd.CommandText = string.Format("select f1, sql_variant_property(f1,'BaseType') as BaseType from {0}", bulkCopyTableName); + using (SqlDataReader drVerify = cmd.ExecuteReader()) + { + drVerify.Read(); + VerifyReaderTypeAndValue("SqlBulkCopy From SqlDataReader [Variant Type]", "SqlDbType.Variant", drVerify, expectedTypeName, expectedBaseTypeName, paramValue); + drVerify.Dispose(); + } } } } } + connInput.Close(); } - connInput.Close(); conn.Close(); } @@ -1288,7 +1296,7 @@ private static bool IsExpectedException(Exception e, object paramValue, string e return false; } } - + private static bool IsExpectedInvalidOperationException(Exception e, string expectedBaseTypeName) { return ((e.GetType() == typeof(InvalidOperationException)) && diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs index 3bed4b5e12..8eb8ec7684 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RandomStressTest/RandomStressTest.cs @@ -64,7 +64,6 @@ public void TestMain() if (_randPool.ReproMode) { - _runningThreads = 1; TestThread(); } else @@ -75,6 +74,8 @@ public void TestMain() t.Start(); } } + + _endEvent.WaitOne(); } private void NextConnection(ref SqlConnection con, Randomizer rand) @@ -82,6 +83,7 @@ private void NextConnection(ref SqlConnection con, Randomizer rand) if (con != null) { con.Close(); + con.Dispose(); } string connString = _connectionStrings[rand.Next(_connectionStrings.Length)]; @@ -92,6 +94,7 @@ private void NextConnection(ref SqlConnection con, Randomizer rand) private void TestThread() { + Interlocked.Increment(ref _runningThreads); try { using (var rootScope = _randPool.RootScope()) @@ -132,6 +135,7 @@ private void TestThread() if (con != null) { con.Close(); + con.Dispose(); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs index b223a709e9..b32dd75f46 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -246,12 +246,14 @@ private void ExceptionTest() DataTestUtility.AssertThrowsWrapper(() => { - SqlConnection con1 = new SqlConnection(_connectionString); - con1.Open(); + using (SqlConnection con1 = new SqlConnection(_connectionString)) + { + con1.Open(); - SqlCommand command = new SqlCommand("sql", con1); - command.Transaction = tx; - command.ExecuteNonQuery(); + SqlCommand command = new SqlCommand("sql", con1); + command.Transaction = tx; + command.ExecuteNonQuery(); + } }, transactionConflictErrorMessage); DataTestUtility.AssertThrowsWrapper(() => diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs new file mode 100644 index 0000000000..3635bacf7d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs @@ -0,0 +1,368 @@ +// 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.Diagnostics; +using System.Reflection; +using System.Transactions; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + /// + /// This unit test is just valid for .NetCore 3.0 and above + /// + public class EventCounterTest + { + public EventCounterTest() + { + ClearConnectionPools(); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void EventCounter_HardConnectionsCounters_Functional() + { + //create a non-pooled connection + var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) {Pooling = false}; + + var ahc = SqlClientEventSourceProps.ActiveHardConnections; + var npc = SqlClientEventSourceProps.NonPooledConnections; + + using (var conn = new SqlConnection(stringBuilder.ToString())) + { + //initially we have no open physical connections + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + + conn.Open(); + + //when the connection gets opened, the real physical connection appears + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(npc + 1, SqlClientEventSourceProps.NonPooledConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + + conn.Close(); + + //when the connection gets closed, the real physical connection is also closed + Assert.Equal(ahc, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void EventCounter_SoftConnectionsCounters_Functional() + { + //create a pooled connection + var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) {Pooling = true}; + + var ahc = SqlClientEventSourceProps.ActiveHardConnections; + var asc = SqlClientEventSourceProps.ActiveSoftConnections; + var pc = SqlClientEventSourceProps.PooledConnections; + var npc = SqlClientEventSourceProps.NonPooledConnections; + var acp = SqlClientEventSourceProps.ActiveConnectionPools; + var ac = SqlClientEventSourceProps.ActiveConnections; + var fc = SqlClientEventSourceProps.FreeConnections; + + using (var conn = new SqlConnection(stringBuilder.ToString())) + { + //initially we have no open physical connections + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + + conn.Open(); + + //when the connection gets opened, the real physical connection appears + //and the appropriate pooling infrastructure gets deployed + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections); + Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections); + Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools); + Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + + conn.Close(); + + //when the connection gets closed, the real physical connection gets returned to the pool + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections); + Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections); + Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools); + Assert.Equal(ac, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc + 1, SqlClientEventSourceProps.FreeConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + } + + using (var conn2 = new SqlConnection(stringBuilder.ToString())) + { + conn2.Open(); + + //the next open connection will reuse the underlying physical connection + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections); + Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections); + Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools); + Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void EventCounter_StasisCounters_Functional() + { + var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) {Pooling = false}; + + using (var conn = new SqlConnection(stringBuilder.ToString())) + using (new TransactionScope()) + { + conn.Open(); + conn.EnlistTransaction(System.Transactions.Transaction.Current); + conn.Close(); + + //when the connection gets closed, but the ambient transaction is still in prigress + //the physical connection gets in stasis, until the transaction ends + Assert.Equal(1, SqlClientEventSourceProps.StasisConnections); + } + + //when the transaction finally ends, the physical connection is returned from stasis + Assert.Equal(0, SqlClientEventSourceProps.StasisConnections); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void EventCounter_ReclaimedConnectionsCounter_Functional() + { + SqlConnection.ClearAllPools(); + var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true, MaxPoolSize = 1}; + + long rc = SqlClientEventSourceProps.ReclaimedConnections; + + InternalConnectionWrapper internalConnection = CreateEmancipatedConnection(stringBuilder.ToString()); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + using (SqlConnection conn = new SqlConnection(stringBuilder.ToString())) + { + conn.Open(); + + // when calling open, the connection is reclaimed. + Assert.Equal(rc + 1, SqlClientEventSourceProps.ReclaimedConnections); + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void EventCounter_ConnectionPoolGroupsCounter_Functional() + { + SqlConnection.ClearAllPools(); + + var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true}; + + long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups; + long iacpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups; + + using (SqlConnection conn = new SqlConnection(stringBuilder.ToString())) { + conn.Open(); + + // when calling open, we have 1 more active connection pool group + Assert.Equal(acpg + 1, SqlClientEventSourceProps.ActiveConnectionPoolGroups); + + conn.Close(); + } + + SqlConnection.ClearAllPools(); + + // poolGroup state is changed from Active to Idle + PruneConnectionPoolGroups(); + + // poolGroup state is changed from Idle to Disabled + PruneConnectionPoolGroups(); + Assert.Equal(acpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups); + Assert.Equal(iacpg + 1, SqlClientEventSourceProps.InactiveConnectionPoolGroups); + + // Remove poolGroup from poolGroupsToRelease list + PruneConnectionPoolGroups(); + Assert.Equal(iacpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups); + } + + private static InternalConnectionWrapper CreateEmancipatedConnection(string connectionString) + { + SqlConnection connection = new SqlConnection(connectionString); + connection.Open(); + return new InternalConnectionWrapper(connection); + } + + private void ClearConnectionPools() + { + //ClearAllPoos kills all the existing pooled connection thus deactivating all the active pools + var liveConnectionPools = SqlClientEventSourceProps.ActiveConnectionPools + + SqlClientEventSourceProps.InactiveConnectionPools; + SqlConnection.ClearAllPools(); + Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, liveConnectionPools); + Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPools); + + //the 1st PruneConnectionPoolGroups call cleans the dangling inactive connection pools + PruneConnectionPoolGroups(); + Assert.Equal(0, SqlClientEventSourceProps.InactiveConnectionPools); + + //the 2nd call deactivates the dangling connection pool groups + var liveConnectionPoolGroups = SqlClientEventSourceProps.ActiveConnectionPoolGroups + + SqlClientEventSourceProps.InactiveConnectionPoolGroups; + PruneConnectionPoolGroups(); + Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, liveConnectionPoolGroups); + Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPoolGroups); + + //the 3rd call cleans the dangling connection pool groups + PruneConnectionPoolGroups(); + Assert.Equal(0, SqlClientEventSourceProps.InactiveConnectionPoolGroups); + } + + private static void PruneConnectionPoolGroups() + { + FieldInfo connectionFactoryField = GetConnectionFactoryField(); + MethodInfo pruneConnectionPoolGroupsMethod = + connectionFactoryField.FieldType.GetMethod("PruneConnectionPoolGroups", + BindingFlags.NonPublic | BindingFlags.Instance); + Debug.Assert(pruneConnectionPoolGroupsMethod != null); + pruneConnectionPoolGroupsMethod.Invoke(connectionFactoryField.GetValue(null), new[] {(object)null}); + } + + private static FieldInfo GetConnectionFactoryField() + { + FieldInfo connectionFactoryField = + typeof(SqlConnection).GetField("s_connectionFactory", BindingFlags.Static | BindingFlags.NonPublic); + Debug.Assert(connectionFactoryField != null); + return connectionFactoryField; + } + } + + internal static class SqlClientEventSourceProps + { + private static readonly object _log; + private static readonly FieldInfo _activeHardConnectionsCounter; + private static readonly FieldInfo _hardConnectsCounter; + private static readonly FieldInfo _hardDisconnectsCounter; + private static readonly FieldInfo _activeSoftConnectionsCounter; + private static readonly FieldInfo _softConnectsCounter; + private static readonly FieldInfo _softDisconnectsCounter; + private static readonly FieldInfo _nonPooledConnectionsCounter; + private static readonly FieldInfo _pooledConnectionsCounter; + private static readonly FieldInfo _activeConnectionPoolGroupsCounter; + private static readonly FieldInfo _inactiveConnectionPoolGroupsCounter; + private static readonly FieldInfo _activeConnectionPoolsCounter; + private static readonly FieldInfo _inactiveConnectionPoolsCounter; + private static readonly FieldInfo _activeConnectionsCounter; + private static readonly FieldInfo _freeConnectionsCounter; + private static readonly FieldInfo _stasisConnectionsCounter; + private static readonly FieldInfo _reclaimedConnectionsCounter; + + static SqlClientEventSourceProps() + { + var sqlClientEventSourceType = + Assembly.GetAssembly(typeof(SqlConnection))!.GetType("Microsoft.Data.SqlClient.SqlClientEventSource"); + Debug.Assert(sqlClientEventSourceType != null); + var logField = sqlClientEventSourceType.GetField("Log", BindingFlags.Static | BindingFlags.NonPublic); + Debug.Assert(logField != null); + _log = logField.GetValue(null); + + var _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; + _activeHardConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_activeHardConnectionsCounter), _bindingFlags); + Debug.Assert(_activeHardConnectionsCounter != null); + _hardConnectsCounter = + sqlClientEventSourceType.GetField(nameof(_hardConnectsCounter), _bindingFlags); + Debug.Assert(_hardConnectsCounter != null); + _hardDisconnectsCounter = + sqlClientEventSourceType.GetField(nameof(_hardDisconnectsCounter), _bindingFlags); + Debug.Assert(_hardDisconnectsCounter != null); + _activeSoftConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_activeSoftConnectionsCounter), _bindingFlags); + Debug.Assert(_activeSoftConnectionsCounter != null); + _softConnectsCounter = + sqlClientEventSourceType.GetField(nameof(_softConnectsCounter), _bindingFlags); + Debug.Assert(_softConnectsCounter != null); + _softDisconnectsCounter = + sqlClientEventSourceType.GetField(nameof(_softDisconnectsCounter), _bindingFlags); + Debug.Assert(_softDisconnectsCounter != null); + _nonPooledConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_nonPooledConnectionsCounter), _bindingFlags); + Debug.Assert(_nonPooledConnectionsCounter != null); + _pooledConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_pooledConnectionsCounter), _bindingFlags); + Debug.Assert(_pooledConnectionsCounter != null); + _activeConnectionPoolGroupsCounter = + sqlClientEventSourceType.GetField(nameof(_activeConnectionPoolGroupsCounter), _bindingFlags); + Debug.Assert(_activeConnectionPoolGroupsCounter != null); + _inactiveConnectionPoolGroupsCounter = + sqlClientEventSourceType.GetField(nameof(_inactiveConnectionPoolGroupsCounter), _bindingFlags); + Debug.Assert(_inactiveConnectionPoolGroupsCounter != null); + _activeConnectionPoolsCounter = + sqlClientEventSourceType.GetField(nameof(_activeConnectionPoolsCounter), _bindingFlags); + Debug.Assert(_activeConnectionPoolsCounter != null); + _inactiveConnectionPoolsCounter = + sqlClientEventSourceType.GetField(nameof(_inactiveConnectionPoolsCounter), _bindingFlags); + Debug.Assert(_inactiveConnectionPoolsCounter != null); + _activeConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_activeConnectionsCounter), _bindingFlags); + Debug.Assert(_activeConnectionsCounter != null); + _freeConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_freeConnectionsCounter), _bindingFlags); + Debug.Assert(_freeConnectionsCounter != null); + _stasisConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_stasisConnectionsCounter), _bindingFlags); + Debug.Assert(_stasisConnectionsCounter != null); + _reclaimedConnectionsCounter = + sqlClientEventSourceType.GetField(nameof(_reclaimedConnectionsCounter), _bindingFlags); + Debug.Assert(_reclaimedConnectionsCounter != null); + } + + public static long ActiveHardConnections => (long)_activeHardConnectionsCounter.GetValue(_log)!; + + public static long HardConnects => (long)_hardConnectsCounter.GetValue(_log)!; + + public static long HardDisconnects => (long)_hardDisconnectsCounter.GetValue(_log)!; + + public static long ActiveSoftConnections => (long)_activeSoftConnectionsCounter.GetValue(_log)!; + + public static long SoftConnects => (long)_softConnectsCounter.GetValue(_log)!; + + public static long SoftDisconnects => (long)_softDisconnectsCounter.GetValue(_log)!; + + public static long NonPooledConnections => (long)_nonPooledConnectionsCounter.GetValue(_log)!; + + public static long PooledConnections => (long)_pooledConnectionsCounter.GetValue(_log)!; + + public static long ActiveConnectionPoolGroups => (long)_activeConnectionPoolGroupsCounter.GetValue(_log)!; + + public static long InactiveConnectionPoolGroups => (long)_inactiveConnectionPoolGroupsCounter.GetValue(_log)!; + + public static long ActiveConnectionPools => (long)_activeConnectionPoolsCounter.GetValue(_log)!; + + public static long InactiveConnectionPools => (long)_inactiveConnectionPoolsCounter.GetValue(_log)!; + + public static long ActiveConnections => (long)_activeConnectionsCounter.GetValue(_log)!; + + public static long FreeConnections => (long)_freeConnectionsCounter.GetValue(_log)!; + + public static long StasisConnections => (long)_stasisConnectionsCounter.GetValue(_log)!; + + public static long ReclaimedConnections => (long)_reclaimedConnectionsCounter.GetValue(_log)!; + } +}