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 279c861b45..42ca312302 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -227,6 +227,9 @@ Microsoft\Data\SqlClient\Diagnostics\SqlClientConnectionOpenError.netcore.cs + + Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs + Microsoft\Data\SqlClient\Diagnostics\SqlClientTransactionCommitAfter.netcore.cs @@ -745,7 +748,6 @@ - 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 deleted file mode 100644 index c0312ca219..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs +++ /dev/null @@ -1,396 +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. - -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 currently made to servers", - DisplayUnits = "count" - }; - - _hardConnectsPerSecond = _hardConnectsPerSecond ?? - new IncrementingPollingCounter("hard-connects", this, () => _hardConnectsCounter) - { - DisplayName = "Actual connection rate to servers", - DisplayUnits = "count / sec", - DisplayRateTimeScale = TimeSpan.FromSeconds(1) - }; - - _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ?? - new IncrementingPollingCounter("hard-disconnects", this, () => _hardDisconnectsCounter) - { - DisplayName = "Actual disconnection rate from servers", - DisplayUnits = "count / sec", - DisplayRateTimeScale = TimeSpan.FromSeconds(1) - }; - - _activeSoftConnections = _activeSoftConnections ?? - new PollingCounter("active-soft-connects", this, () => _activeSoftConnectionsCounter) - { - DisplayName = "Active connections retrieved from the connection pool", - DisplayUnits = "count" - }; - - _softConnects = _softConnects ?? - new IncrementingPollingCounter("soft-connects", this, () => _softConnectsCounter) - { - DisplayName = "Rate of connections retrieved from the connection pool", - DisplayUnits = "count / sec", - DisplayRateTimeScale = TimeSpan.FromSeconds(1) - }; - - _softDisconnects = _softDisconnects ?? - new IncrementingPollingCounter("soft-disconnects", this, () => _softDisconnectsCounter) - { - DisplayName = "Rate of 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 not using connection pooling", - DisplayUnits = "count" - }; - - _numberOfPooledConnections = _numberOfPooledConnections ?? - new PollingCounter("number-of-pooled-connections", this, () => _pooledConnectionsCounter) - { - DisplayName = "Number of connections managed by the connection pool", - DisplayUnits = "count" - }; - - _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 ready connections in the connection pool", - 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 pool - /// - [NonEvent] - internal override void EnterPooledConnection() - { - Interlocked.Increment(ref _pooledConnectionsCounter); - } - - /// - /// The number of connections that are managed by the connection pool - /// - [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/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 7408ce42ac..8b8bda848e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -288,9 +288,6 @@ Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolAuthenticationContextKey.cs - - Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolCounters.netfx.cs - Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolGroup.cs @@ -327,6 +324,9 @@ Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs + + Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs + Microsoft\Data\ProviderBase\DbMetaDataFactory.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index 34b496ab02..d3402a48bd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -18,7 +18,7 @@ namespace Microsoft.Data.SqlClient { sealed internal class SqlConnectionFactory : DbConnectionFactory { - private SqlConnectionFactory() : base(SqlPerformanceCounters.SingletonInstance) + private SqlConnectionFactory() : base() { } @@ -343,19 +343,5 @@ override internal void SetInnerConnectionTo(DbConnection owningObject, DbConnect } } - - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")] - sealed internal class SqlPerformanceCounters : DbConnectionPoolCounters - { - private const string CategoryName = ".NET Data Provider for SqlServer"; - private const string CategoryHelp = "Counters for Microsoft.Data.SqlClient"; - - public static readonly SqlPerformanceCounters SingletonInstance = new SqlPerformanceCounters(); - - [System.Diagnostics.PerformanceCounterPermissionAttribute(System.Security.Permissions.SecurityAction.Assert, PermissionAccess = PerformanceCounterPermissionAccess.Write, MachineName = ".", CategoryName = CategoryName)] - private SqlPerformanceCounters() : base(CategoryName, CategoryHelp) - { - } - } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index f357e6109b..61c4e18579 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -34,25 +34,6 @@ internal abstract class DbConnectionFactory private static Task[] s_pendingOpenNonPooled = new Task[Environment.ProcessorCount]; private static Task s_completedTask; -#if NETFRAMEWORK - private readonly DbConnectionPoolCounters _performanceCounters; - - protected DbConnectionFactory() : this(DbConnectionPoolCountersNoCounters.SingletonInstance) { } - - protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) - { - _performanceCounters = performanceCounters; - _connectionPoolGroups = new Dictionary(); - _poolsToRelease = new List(); - _poolGroupsToRelease = new List(); - _pruningTimer = CreatePruningTimer(); - } - - internal DbConnectionPoolCounters PerformanceCounters - { - get { return _performanceCounters; } - } -#else protected DbConnectionFactory() { _connectionPoolGroups = new Dictionary(); @@ -60,7 +41,6 @@ protected DbConnectionFactory() _poolGroupsToRelease = new List(); _pruningTimer = CreatePruningTimer(); } -#endif public abstract DbProviderFactory ProviderFactory { @@ -135,13 +115,8 @@ internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConne DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions); if (newConnection != null) { -#if NETFRAMEWORK - PerformanceCounters.HardConnectsPerSecond.Increment(); - newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters); -#else - SqlClientEventSource.Log.HardConnectRequest(); + SqlClientEventSource.Metrics.HardConnectRequest(); newConnection.MakeNonPooledObject(owningConnection); -#endif } SqlClientEventSource.Log.TryTraceEvent(" {0}, Non-pooled database connection created.", ObjectID); return newConnection; @@ -155,11 +130,8 @@ internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbCo if (newConnection != null) { -#if NETFRAMEWORK - PerformanceCounters.HardConnectsPerSecond.Increment(); -#else - SqlClientEventSource.Log.HardConnectRequest(); -#endif + SqlClientEventSource.Metrics.HardConnectRequest(); + newConnection.MakePooledConnection(pool); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Pooled database connection created.", ObjectID); @@ -289,11 +261,8 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour } connection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions); -#if NETFRAMEWORK - PerformanceCounters.NumberOfNonPooledConnections.Increment(); -#else - SqlClientEventSource.Log.EnterNonPooledConnection(); -#endif + + SqlClientEventSource.Metrics.EnterNonPooledConnection(); } else { @@ -397,11 +366,7 @@ private void TryGetConnectionCompletedContinuation(Task ta } else { -#if NETFRAMEWORK - PerformanceCounters.NumberOfNonPooledConnections.Increment(); -#else - SqlClientEventSource.Log.EnterNonPooledConnection(); -#endif + SqlClientEventSource.Metrics.EnterNonPooledConnection(); } } } @@ -510,11 +475,8 @@ internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, D // lock prevents race condition with PruneConnectionPoolGroups newConnectionPoolGroups.Add(key, newConnectionPoolGroup); -#if NETFRAMEWORK - PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment(); -#else - SqlClientEventSource.Log.EnterActiveConnectionPoolGroup(); -#endif + + SqlClientEventSource.Metrics.EnterActiveConnectionPoolGroup(); connectionPoolGroup = newConnectionPoolGroup; _connectionPoolGroups = newConnectionPoolGroups; } @@ -579,11 +541,8 @@ private void PruneConnectionPoolGroups(object state) { _poolsToRelease.Remove(pool); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePool={1}", ObjectID, pool.ObjectId); -#if NETFRAMEWORK - PerformanceCounters.NumberOfInactiveConnectionPools.Decrement(); -#else - SqlClientEventSource.Log.ExitInactiveConnectionPool(); -#endif + + SqlClientEventSource.Metrics.ExitInactiveConnectionPool(); } } } @@ -608,11 +567,8 @@ private void PruneConnectionPoolGroups(object state) { _poolGroupsToRelease.Remove(poolGroup); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePoolGroup={1}", ObjectID, poolGroup.ObjectID); -#if NETFRAMEWORK - PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement(); -#else - SqlClientEventSource.Log.ExitInactiveConnectionPoolGroup(); -#endif + + SqlClientEventSource.Metrics.ExitInactiveConnectionPoolGroup(); } } } @@ -639,9 +595,6 @@ private void PruneConnectionPoolGroups(object state) if (entry.Value.Prune()) { // may add entries to _poolsToRelease -#if NETFRAMEWORK - PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement(); -#endif QueuePoolGroupForRelease(entry.Value); } else @@ -674,12 +627,8 @@ internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) } _poolsToRelease.Add(pool); } -#if NETFRAMEWORK - PerformanceCounters.NumberOfInactiveConnectionPools.Increment(); -#else - SqlClientEventSource.Log.EnterInactiveConnectionPool(); - SqlClientEventSource.Log.ExitActiveConnectionPool(); -#endif + SqlClientEventSource.Metrics.EnterInactiveConnectionPool(); + SqlClientEventSource.Metrics.ExitActiveConnectionPool(); } internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) @@ -691,12 +640,9 @@ internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { _poolGroupsToRelease.Add(poolGroup); } -#if NETFRAMEWORK - PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment(); -#else - SqlClientEventSource.Log.EnterInactiveConnectionPoolGroup(); - SqlClientEventSource.Log.ExitActiveConnectionPoolGroup(); -#endif + + SqlClientEventSource.Metrics.EnterInactiveConnectionPoolGroup(); + SqlClientEventSource.Metrics.ExitActiveConnectionPoolGroup(); } virtual protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 7268f00eea..b2cf6c25af 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -324,10 +324,6 @@ protected internal DbConnection Owner get => _owningObject.TryGetTarget(out DbConnection connection) ? connection : null; } - #if NETFRAMEWORK - protected DbConnectionPoolCounters PerformanceCounters { get; private set; } - #endif - protected virtual bool ReadyToPrepareTransaction { get => true; @@ -368,11 +364,7 @@ internal void ActivateConnection(Transaction transaction) Activate(transaction); - #if NETFRAMEWORK - PerformanceCounters.NumberOfActiveConnections.Increment(); - #else - SqlClientEventSource.Log.EnterActiveConnection(); - #endif + SqlClientEventSource.Metrics.EnterActiveConnection(); } internal void AddWeakReference(object value, int tag) @@ -485,11 +477,7 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac // and transactions may not get cleaned up... Deactivate(); - #if NETFRAMEWORK - PerformanceCounters.HardDisconnectsPerSecond.Increment(); - #else - SqlClientEventSource.Log.HardDisconnectRequest(); - #endif + SqlClientEventSource.Metrics.HardDisconnectRequest(); // To prevent an endless recursion, we need to clear the owning object // before we call dispose so that we can't get here a second time... @@ -504,16 +492,8 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac } else { - #if NETFRAMEWORK - PerformanceCounters.NumberOfNonPooledConnections.Decrement(); - if (this is not SqlInternalConnectionSmi) - { - Dispose(); - } - #else - SqlClientEventSource.Log.ExitNonPooledConnection(); + SqlClientEventSource.Metrics.ExitNonPooledConnection(); Dispose(); - #endif } } } @@ -543,15 +523,7 @@ internal void DeactivateConnection() Debug.Assert(activateCount == 0, "activated multiple times?"); #endif - #if NETFRAMEWORK - if (PerformanceCounters is not null) - { - // Pool.Clear will DestroyObject that will clean performanceCounters before going here - PerformanceCounters.NumberOfActiveConnections.Decrement(); - } - #else - SqlClientEventSource.Log.ExitActiveConnection(); - #endif + SqlClientEventSource.Metrics.ExitActiveConnection(); if (!IsConnectionDoomed && Pool.UseLoadBalancing) { @@ -611,11 +583,7 @@ internal virtual void DelegatedTransactionEnded() // once and for all, or the server will have fits about us // leaving connections open until the client-side GC kicks // in. - #if NETFRAMEWORK - PerformanceCounters.NumberOfNonPooledConnections.Decrement(); - #else - SqlClientEventSource.Log.ExitNonPooledConnection(); - #endif + SqlClientEventSource.Metrics.ExitNonPooledConnection(); Dispose(); } @@ -690,10 +658,6 @@ public virtual void Dispose() IsConnectionDoomed = true; _enlistedTransactionOriginal = null; // should not be disposed - #if NETFRAMEWORK - PerformanceCounters = null; - #endif - // Dispose of the _enlistedTransaction since it is a clone of the original reference. // VSDD 780271 - _enlistedTransaction can be changed by another thread (TX end event) Transaction enlistedTransaction = Interlocked.Exchange(ref _enlistedTransaction, null); @@ -723,16 +687,8 @@ public virtual void Dispose() /// /// Used by DbConnectionFactory to indicate that this object IS NOT part of a connection pool. /// - #if NETFRAMEWORK - internal void MakeNonPooledObject(DbConnection owningObject, DbConnectionPoolCounters performanceCounters) - #else internal void MakeNonPooledObject(DbConnection owningObject) - #endif { - #if NETFRAMEWORK - PerformanceCounters = performanceCounters; - #endif - Pool = null; _owningObject.SetTarget(owningObject); _pooledCount = -1; @@ -746,10 +702,6 @@ internal void MakePooledConnection(DbConnectionPool connectionPool) { _createTime = DateTime.UtcNow; Pool = connectionPool; - - #if NETFRAMEWORK - PerformanceCounters = connectionPool.PerformanceCounters; - #endif } internal void NotifyWeakReference(int message) => @@ -849,11 +801,7 @@ internal void SetInStasis() IsTxRootWaitingForTxEnd = true; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.", ObjectID); - #if NETFRAMEWORK - PerformanceCounters.NumberOfStasisConnections.Increment(); - #else - SqlClientEventSource.Log.EnterStasisConnection(); - #endif + SqlClientEventSource.Metrics.EnterStasisConnection(); } /// @@ -1005,11 +953,7 @@ private void TerminateStasis(bool returningToPool) : "Delegated Transaction has ended, connection is closed/leaked. Disposing."; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, {1}", ObjectID, message); - #if NETFRAMEWORK - PerformanceCounters.NumberOfStasisConnections.Decrement(); - #else - SqlClientEventSource.Log.ExitStasisConnection(); - #endif + SqlClientEventSource.Metrics.ExitStasisConnection(); IsTxRootWaitingForTxEnd = false; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPool.cs index a4efd25d2c..0d00227469 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPool.cs @@ -33,9 +33,6 @@ internal abstract class DbConnectionPool internal abstract bool IsRunning { get; } -#if NETFRAMEWORK - internal abstract DbConnectionPoolCounters PerformanceCounters { get; } -#endif internal abstract DbConnectionPoolGroup PoolGroup { get; } internal abstract DbConnectionPoolGroupOptions PoolGroupOptions { get; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolCounters.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolCounters.netfx.cs deleted file mode 100644 index 1ca4fe3aa6..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolCounters.netfx.cs +++ /dev/null @@ -1,361 +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. - -using Interop.Windows.Kernel32; - -#if NETFRAMEWORK - -namespace Microsoft.Data.SqlClient.ConnectionPool -{ - - using System; - using System.Diagnostics; - using System.Reflection; - using System.Runtime.ConstrainedExecution; - using System.Runtime.Versioning; - using System.Security.Permissions; - using Microsoft.Data.Common; - - internal abstract class DbConnectionPoolCounters - { - private static class CreationData - { - - static internal readonly CounterCreationData HardConnectsPerSecond = new CounterCreationData( - "HardConnectsPerSecond", - "The number of actual connections per second that are being made to servers", - PerformanceCounterType.RateOfCountsPerSecond32); - - static internal readonly CounterCreationData HardDisconnectsPerSecond = new CounterCreationData( - "HardDisconnectsPerSecond", - "The number of actual disconnects per second that are being made to servers", - PerformanceCounterType.RateOfCountsPerSecond32); - - static internal readonly CounterCreationData SoftConnectsPerSecond = new CounterCreationData( - "SoftConnectsPerSecond", - "The number of connections we get from the pool per second", - PerformanceCounterType.RateOfCountsPerSecond32); - - static internal readonly CounterCreationData SoftDisconnectsPerSecond = new CounterCreationData( - "SoftDisconnectsPerSecond", - "The number of connections we return to the pool per second", - PerformanceCounterType.RateOfCountsPerSecond32); - - static internal readonly CounterCreationData NumberOfNonPooledConnections = new CounterCreationData( - "NumberOfNonPooledConnections", - "The number of connections that are not using connection pooling", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfPooledConnections = new CounterCreationData( - "NumberOfPooledConnections", - "The number of connections that are managed by the connection pooler", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfActiveConnectionPoolGroups = new CounterCreationData( - "NumberOfActiveConnectionPoolGroups", - "The number of unique connection strings", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfInactiveConnectionPoolGroups = new CounterCreationData( - "NumberOfInactiveConnectionPoolGroups", - "The number of unique connection strings waiting for pruning", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfActiveConnectionPools = new CounterCreationData( - "NumberOfActiveConnectionPools", - "The number of connection pools", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfInactiveConnectionPools = new CounterCreationData( - "NumberOfInactiveConnectionPools", - "The number of connection pools", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfActiveConnections = new CounterCreationData( - "NumberOfActiveConnections", - "The number of connections currently in-use", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfFreeConnections = new CounterCreationData( - "NumberOfFreeConnections", - "The number of connections currently available for use", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfStasisConnections = new CounterCreationData( - "NumberOfStasisConnections", - "The number of connections currently waiting to be made ready for use", - PerformanceCounterType.NumberOfItems32); - - static internal readonly CounterCreationData NumberOfReclaimedConnections = new CounterCreationData( - "NumberOfReclaimedConnections", - "The number of connections we reclaim from GC'd external connections", - PerformanceCounterType.NumberOfItems32); - }; - - sealed internal class Counter - { - private PerformanceCounter _instance; - - internal Counter(string categoryName, string instanceName, string counterName, PerformanceCounterType counterType) - { - if (ADP.s_isPlatformNT5) - { - try - { - if (!string.IsNullOrEmpty(categoryName) && !string.IsNullOrEmpty(instanceName)) - { - PerformanceCounter instance = new PerformanceCounter(); - instance.CategoryName = categoryName; - instance.CounterName = counterName; - instance.InstanceName = instanceName; - instance.InstanceLifetime = PerformanceCounterInstanceLifetime.Process; - instance.ReadOnly = false; - instance.RawValue = 0; // make sure we start out at zero - _instance = instance; - } - } - catch (InvalidOperationException e) - { - ADP.TraceExceptionWithoutRethrow(e); - // TODO: generate Application EventLog entry about inability to find perf counter - } - } - } - - - internal void Decrement() - { - PerformanceCounter instance = _instance; - if (instance != null) - { - instance.Decrement(); - } - } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - internal void Dispose() - { // TODO: race condition, Dispose at the same time as Increment/Decrement - PerformanceCounter instance = _instance; - _instance = null; - if (instance != null) - { - instance.RemoveInstance(); - // should we be calling instance.Close? - // if we do will it exacerbate the Dispose vs. Decrement race condition - //instance.Close(); - } - } - - internal void Increment() - { - PerformanceCounter instance = _instance; - if (instance != null) - { - instance.Increment(); - } - } - }; - - const int CounterInstanceNameMaxLength = 127; - - internal readonly Counter HardConnectsPerSecond; - internal readonly Counter HardDisconnectsPerSecond; - internal readonly Counter SoftConnectsPerSecond; - internal readonly Counter SoftDisconnectsPerSecond; - internal readonly Counter NumberOfNonPooledConnections; - internal readonly Counter NumberOfPooledConnections; - internal readonly Counter NumberOfActiveConnectionPoolGroups; - internal readonly Counter NumberOfInactiveConnectionPoolGroups; - internal readonly Counter NumberOfActiveConnectionPools; - internal readonly Counter NumberOfInactiveConnectionPools; - internal readonly Counter NumberOfActiveConnections; - internal readonly Counter NumberOfFreeConnections; - internal readonly Counter NumberOfStasisConnections; - internal readonly Counter NumberOfReclaimedConnections; - - protected DbConnectionPoolCounters() : this(null, null) - { - } - - protected DbConnectionPoolCounters(string categoryName, string categoryHelp) - { - AppDomain.CurrentDomain.DomainUnload += new EventHandler(this.UnloadEventHandler); - AppDomain.CurrentDomain.ProcessExit += new EventHandler(this.ExitEventHandler); - AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(this.ExceptionEventHandler); - - string instanceName = null; - - if (!string.IsNullOrEmpty(categoryName)) - { - if (ADP.s_isPlatformNT5) - { - instanceName = GetInstanceName(); - } - } - - // level 0-3: hard connects/disconnects, plus basic pool/pool entry statistics - string basicCategoryName = categoryName; - HardConnectsPerSecond = new Counter(basicCategoryName, instanceName, CreationData.HardConnectsPerSecond.CounterName, CreationData.HardConnectsPerSecond.CounterType); - HardDisconnectsPerSecond = new Counter(basicCategoryName, instanceName, CreationData.HardDisconnectsPerSecond.CounterName, CreationData.HardDisconnectsPerSecond.CounterType); - NumberOfNonPooledConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfNonPooledConnections.CounterName, CreationData.NumberOfNonPooledConnections.CounterType); - NumberOfPooledConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfPooledConnections.CounterName, CreationData.NumberOfPooledConnections.CounterType); - NumberOfActiveConnectionPoolGroups = new Counter(basicCategoryName, instanceName, CreationData.NumberOfActiveConnectionPoolGroups.CounterName, CreationData.NumberOfActiveConnectionPoolGroups.CounterType); - NumberOfInactiveConnectionPoolGroups = new Counter(basicCategoryName, instanceName, CreationData.NumberOfInactiveConnectionPoolGroups.CounterName, CreationData.NumberOfInactiveConnectionPoolGroups.CounterType); - NumberOfActiveConnectionPools = new Counter(basicCategoryName, instanceName, CreationData.NumberOfActiveConnectionPools.CounterName, CreationData.NumberOfActiveConnectionPools.CounterType); - NumberOfInactiveConnectionPools = new Counter(basicCategoryName, instanceName, CreationData.NumberOfInactiveConnectionPools.CounterName, CreationData.NumberOfInactiveConnectionPools.CounterType); - NumberOfStasisConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfStasisConnections.CounterName, CreationData.NumberOfStasisConnections.CounterType); - NumberOfReclaimedConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfReclaimedConnections.CounterName, CreationData.NumberOfReclaimedConnections.CounterType); - - // level 4: expensive stuff - string verboseCategoryName = null; - if (!string.IsNullOrEmpty(categoryName)) - { - // don't load TraceSwitch if no categoryName so that Odbc/OleDb have a chance of not loading TraceSwitch - // which are also used by System.Diagnostics.PerformanceCounter.ctor & System.Transactions.get_Current - TraceSwitch perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters"); - if (TraceLevel.Verbose == perfCtrSwitch.Level) - { - verboseCategoryName = categoryName; - } - } - SoftConnectsPerSecond = new Counter(verboseCategoryName, instanceName, CreationData.SoftConnectsPerSecond.CounterName, CreationData.SoftConnectsPerSecond.CounterType); - SoftDisconnectsPerSecond = new Counter(verboseCategoryName, instanceName, CreationData.SoftDisconnectsPerSecond.CounterName, CreationData.SoftDisconnectsPerSecond.CounterType); - NumberOfActiveConnections = new Counter(verboseCategoryName, instanceName, CreationData.NumberOfActiveConnections.CounterName, CreationData.NumberOfActiveConnections.CounterType); - NumberOfFreeConnections = new Counter(verboseCategoryName, instanceName, CreationData.NumberOfFreeConnections.CounterName, CreationData.NumberOfFreeConnections.CounterType); - } - - [FileIOPermission(SecurityAction.Assert, Unrestricted = true)] - private string GetAssemblyName() - { - string result = null; - - // First try GetEntryAssembly name, then AppDomain.FriendlyName. - Assembly assembly = Assembly.GetEntryAssembly(); - - if (assembly != null) - { - AssemblyName name = assembly.GetName(); - if (name != null) - { - result = name.Name; // MDAC 73469 - } - } - return result; - } - - // SxS: this method uses GetCurrentProcessId to construct the instance name. - // TODO: VSDD 534795 - remove the Resource* attributes if you do not use GetCurrentProcessId after the fix - [ResourceExposure(ResourceScope.None)] - [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] - private string GetInstanceName() - { - string result = null; - - string instanceName = GetAssemblyName(); // instance perfcounter name - - if (string.IsNullOrEmpty(instanceName)) - { - AppDomain appDomain = AppDomain.CurrentDomain; - if (appDomain != null) - { - instanceName = appDomain.FriendlyName; - } - } - - // TODO: If you do not use GetCurrentProcessId after fixing VSDD 534795, please remove Resource* attributes from this method - int pid = Kernel32Safe.GetCurrentProcessId(); - - - // SQLBUDT #366157 -there are several characters which have special meaning - // to PERFMON. They recommend that we translate them as shown below, to - // prevent problems. - - result = String.Format((IFormatProvider)null, "{0}[{1}]", instanceName, pid); - result = result.Replace('(', '[').Replace(')', ']').Replace('#', '_').Replace('/', '_').Replace('\\', '_'); - - // SQLBUVSTS #94625 - counter instance name cannot be greater than 127 - if (result.Length > CounterInstanceNameMaxLength) - { - // Replacing the middle part with "[...]" - // For example: if path is c:\long_path\very_(Ax200)_long__path\perftest.exe and process ID is 1234 than the resulted instance name will be: - // c:\long_path\very_(AxM)[...](AxN)_long__path\perftest.exe[1234] - // while M and N are adjusted to make each part before and after the [...] = 61 (making the total = 61 + 5 + 61 = 127) - const string insertString = "[...]"; - int firstPartLength = (CounterInstanceNameMaxLength - insertString.Length) / 2; - int lastPartLength = CounterInstanceNameMaxLength - firstPartLength - insertString.Length; - result = string.Format((IFormatProvider)null, "{0}{1}{2}", - result.Substring(0, firstPartLength), - insertString, - result.Substring(result.Length - lastPartLength, lastPartLength)); - - Debug.Assert(result.Length == CounterInstanceNameMaxLength, - string.Format((IFormatProvider)null, "wrong calculation of the instance name: expected {0}, actual: {1}", CounterInstanceNameMaxLength, result.Length)); - } - - return result; - } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - public void Dispose() - { - // ExceptionEventHandler with IsTerminating may be called before - // the Connection Close is called or the variables are initialized - SafeDispose(HardConnectsPerSecond); - SafeDispose(HardDisconnectsPerSecond); - SafeDispose(SoftConnectsPerSecond); - SafeDispose(SoftDisconnectsPerSecond); - SafeDispose(NumberOfNonPooledConnections); - SafeDispose(NumberOfPooledConnections); - SafeDispose(NumberOfActiveConnectionPoolGroups); - SafeDispose(NumberOfInactiveConnectionPoolGroups); - SafeDispose(NumberOfActiveConnectionPools); - SafeDispose(NumberOfActiveConnections); - SafeDispose(NumberOfFreeConnections); - SafeDispose(NumberOfStasisConnections); - SafeDispose(NumberOfReclaimedConnections); - } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - private void SafeDispose(Counter counter) - { // WebData 103603 - if (counter != null) - { - counter.Dispose(); - } - } - - [PrePrepareMethod] - void ExceptionEventHandler(object sender, UnhandledExceptionEventArgs e) - { - if (e != null && e.IsTerminating) - { - Dispose(); - } - } - - [PrePrepareMethod] - void ExitEventHandler(object sender, EventArgs e) - { - Dispose(); - } - - [PrePrepareMethod] - void UnloadEventHandler(object sender, EventArgs e) - { - Dispose(); - } - } - - sealed internal class DbConnectionPoolCountersNoCounters : DbConnectionPoolCounters - { - - public static readonly DbConnectionPoolCountersNoCounters SingletonInstance = new DbConnectionPoolCountersNoCounters(); - - private DbConnectionPoolCountersNoCounters() : base() - { - } - } -} - -#endif diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs index 5aaa4fdcd7..af3b2d6bdf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs @@ -132,9 +132,7 @@ internal int Clear() if (pool != null) { DbConnectionFactory connectionFactory = pool.ConnectionFactory; -#if NETFRAMEWORK - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); -#endif + connectionFactory.QueuePoolForRelease(pool, true); } } @@ -197,10 +195,8 @@ 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(); -#if NETFRAMEWORK - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment(); -#endif + SqlClientEventSource.Metrics.EnterActiveConnectionPool(); + pool = newPool; } else @@ -276,9 +272,7 @@ 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 diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs index 8dd1a7d745..9db1c7a5ec 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs @@ -228,11 +228,8 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal } SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Added.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } -#if NET - SqlClientEventSource.Log.EnterFreeConnection(); -#else - Pool.PerformanceCounters.NumberOfFreeConnections.Increment(); -#endif + + SqlClientEventSource.Metrics.EnterFreeConnection(); } internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) @@ -295,11 +292,8 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // connections, we'll put it back... if (0 <= entry) { -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - Pool.PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); Pool.PutObjectFromTransactedPool(transactedObject); } } @@ -505,10 +499,6 @@ internal override bool IsRunning private int MinPoolSize => PoolGroupOptions.MinPoolSize; -#if NETFRAMEWORK - internal override DbConnectionPoolCounters PerformanceCounters => _connectionFactory.PerformanceCounters; -#endif - internal override DbConnectionPoolGroup PoolGroup => _connectionPoolGroup; internal override DbConnectionPoolGroupOptions PoolGroupOptions => _connectionPoolGroupOptions; @@ -559,11 +549,8 @@ private void CleanupCallback(object state) { Debug.Assert(obj != null, "null connection is not expected"); // If we obtained one from the old stack, destroy it. -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); // Transaction roots must survive even aging out (TxEnd event will clean them up). bool shouldDestroy = true; @@ -660,21 +647,15 @@ internal override void Clear() while (_stackNew.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); DestroyObject(obj); } while (_stackOld.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); DestroyObject(obj); } @@ -749,11 +730,8 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio } _objectList.Add(newObj); _totalObjects = _objectList.Count; -#if NET - SqlClientEventSource.Log.EnterPooledConnection(); -#else - PerformanceCounters.NumberOfPooledConnections.Increment(); // TODO: Performance: Consider moving outside of lock? -#endif + + SqlClientEventSource.Metrics.EnterPooledConnection(); } SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Added to pool.", ObjectId, newObj?.ObjectID); @@ -981,19 +959,13 @@ internal override void DestroyObject(DbConnectionInternal obj) if (removed) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Removed from pool.", ObjectId, obj.ObjectID); -#if NET - SqlClientEventSource.Log.ExitPooledConnection(); -#else - PerformanceCounters.NumberOfPooledConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitPooledConnection(); } obj.Dispose(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Disposed.", ObjectId, obj.ObjectID); -#if NET - SqlClientEventSource.Log.HardDisconnectRequest(); -#else - PerformanceCounters.HardDisconnectsPerSecond.Increment(); -#endif + + SqlClientEventSource.Metrics.HardDisconnectRequest(); } } @@ -1199,9 +1171,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { DbConnectionInternal obj = null; Transaction transaction = null; -#if NETFRAMEWORK - PerformanceCounters.SoftConnectsPerSecond.Increment(); -#endif + SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Getting connection.", ObjectId); // If automatic transaction enlistment is required, then we try to @@ -1386,9 +1356,9 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } connection = obj; -#if NET - SqlClientEventSource.Log.SoftConnectRequest(); -#endif + + SqlClientEventSource.Metrics.SoftConnectRequest(); + return true; } @@ -1420,17 +1390,12 @@ private void PrepareConnection(DbConnection owningObject, DbConnectionInternal o /// A new inner connection that is attached to the internal override DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) { -#if NETFRAMEWORK - PerformanceCounters.SoftConnectsPerSecond.Increment(); -#endif SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, replacing connection.", ObjectId); DbConnectionInternal newConnection = UserCreateRequest(owningObject, userOptions, oldConnection); if (newConnection != null) { -#if NET - SqlClientEventSource.Log.SoftConnectRequest(); -#endif + SqlClientEventSource.Metrics.SoftConnectRequest(); PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction); oldConnection.PrepareForReplaceConnection(); oldConnection.DeactivateConnection(); @@ -1468,11 +1433,8 @@ private DbConnectionInternal GetFromGeneralPool() if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from general pool.", ObjectId, obj.ObjectID); -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); } return obj; } @@ -1489,11 +1451,8 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from transacted pool.", ObjectId, obj.ObjectID); -#if NET - SqlClientEventSource.Log.ExitFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Decrement(); -#endif + + SqlClientEventSource.Metrics.ExitFreeConnection(); if (obj.IsTransactionRoot) { @@ -1662,11 +1621,8 @@ internal override void PutNewObject(DbConnectionInternal obj) _stackNew.Push(obj); _waitHandles.PoolSemaphore.Release(1); -#if NET - SqlClientEventSource.Log.EnterFreeConnection(); -#else - PerformanceCounters.NumberOfFreeConnections.Increment(); -#endif + + SqlClientEventSource.Metrics.EnterFreeConnection(); } @@ -1674,11 +1630,7 @@ internal override void PutObject(DbConnectionInternal obj, object owningObject) { Debug.Assert(obj != null, "null obj?"); -#if NET - SqlClientEventSource.Log.SoftDisconnectRequest(); -#else - PerformanceCounters.SoftDisconnectsPerSecond.Increment(); -#endif + SqlClientEventSource.Metrics.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 @@ -1795,11 +1747,8 @@ private bool ReclaimEmancipatedObjects() { DbConnectionInternal obj = reclaimedObjects[i]; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Reclaiming.", ObjectId, obj.ObjectID); -#if NET - SqlClientEventSource.Log.ReclaimedConnectionRequest(); -#else - PerformanceCounters.NumberOfReclaimedConnections.Increment(); -#endif + + SqlClientEventSource.Metrics.ReclaimedConnectionRequest(); emancipatedObjectFound = true; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs new file mode 100644 index 0000000000..f62c65b122 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs @@ -0,0 +1,570 @@ +// 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; + +#if NET +using System.Threading; +#else +using Interop.Windows.Kernel32; +using Microsoft.Data.Common; + +using System.Diagnostics; +using System.Reflection; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; +using System.Security.Permissions; +#endif + +#nullable enable + +namespace Microsoft.Data.SqlClient.Diagnostics +{ +#if NETFRAMEWORK + [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] +#endif + internal sealed partial class SqlClientMetrics + { +#if NETFRAMEWORK + private const string PerformanceCounterCategoryName = ".NET Data Provider for SqlServer"; + private const string PerformanceCounterCategoryHelp = "Counters for Microsoft.Data.SqlClient"; + private const int CounterInstanceNameMaxLength = 127; +#endif + + private readonly SqlClientEventSource _eventSource; + // The names of the below variables must match between .NET Framework and .NET Core. +#if NET + private PollingCounter? _activeHardConnectionsCounter; + private long _activeHardConnections = 0; + private IncrementingPollingCounter? _hardConnectsPerSecondCounter; + private long _hardConnectsRate = 0; + private IncrementingPollingCounter? _hardDisconnectsPerSecondCounter; + private long _hardDisconnectsRate = 0; + + private PollingCounter? _activeSoftConnectionsCounter; + private long _activeSoftConnections = 0; + private IncrementingPollingCounter? _softConnectsPerSecondCounter; + private long _softConnectsRate = 0; + private IncrementingPollingCounter? _softDisconnectsPerSecondCounter; + private long _softDisconnectsRate = 0; + + private PollingCounter? _numberOfNonPooledConnectionsCounter; + private long _nonPooledConnections = 0; + private PollingCounter? _numberOfPooledConnectionsCounter; + private long _pooledConnections = 0; + + private PollingCounter? _numberOfActiveConnectionPoolGroupsCounter; + private long _activeConnectionPoolGroups = 0; + private PollingCounter? _numberOfInactiveConnectionPoolGroupsCounter; + private long _inactiveConnectionPoolGroups = 0; + + private PollingCounter? _numberOfActiveConnectionPoolsCounter; + private long _activeConnectionPools = 0; + private PollingCounter? _numberOfInactiveConnectionPoolsCounter; + private long _inactiveConnectionPools = 0; + + private PollingCounter? _numberOfActiveConnectionsCounter; + private long _activeConnections = 0; + private PollingCounter? _numberOfFreeConnectionsCounter; + private long _freeConnections = 0; + private PollingCounter? _numberOfStasisConnectionsCounter; + private long _stasisConnections = 0; + private IncrementingPollingCounter? _numberOfReclaimedConnectionsCounter; + private long _reclaimedConnections = 0; +#else + private PerformanceCounter? _hardConnectsRate; + private PerformanceCounter? _hardDisconnectsRate; + private PerformanceCounter? _softConnectsRate; + private PerformanceCounter? _softDisconnectsRate; + private PerformanceCounter? _nonPooledConnections; + private PerformanceCounter? _pooledConnections; + private PerformanceCounter? _activeConnectionPoolGroups; + private PerformanceCounter? _inactiveConnectionPoolGroups; + private PerformanceCounter? _activeConnectionPools; + private PerformanceCounter? _inactiveConnectionPools; + private PerformanceCounter? _activeConnections; + private PerformanceCounter? _freeConnections; + private PerformanceCounter? _stasisConnections; + private PerformanceCounter? _reclaimedConnections; + + private string? _instanceName; +#endif + + public SqlClientMetrics(SqlClientEventSource eventSource) + { + _eventSource = eventSource; + +#if NETFRAMEWORK + // On .NET Framework, metrics are exposed as performance counters and are always enabled. + // On .NET Core, metrics are exposed as EventCounters, and require explicit enablement. + EnablePerformanceCounters(); +#endif + } + +#if NET + private static void IncrementPlatformSpecificCounter(ref long counter) + => Interlocked.Increment(ref counter); + + private static void DecrementPlatformSpecificCounter(ref long counter) + => Interlocked.Decrement(ref counter); +#else + // .NET Framework doesn't strictly require the PerformanceCounter parameter to be passed as a ref, but doing + // so means that IncrementPlatformSpecificCounter and DecrementPlatformSpecificCounter can be called in identical + // ways between .NET Framework and .NET Core. + private static void IncrementPlatformSpecificCounter(ref PerformanceCounter? counter) + => counter?.Increment(); + + private static void DecrementPlatformSpecificCounter(ref PerformanceCounter? counter) + => counter?.Decrement(); +#endif + + /// + /// The number of actual connections that are being made to servers + /// + internal void HardConnectRequest() + { +#if NET + IncrementPlatformSpecificCounter(ref _activeHardConnections); +#endif + IncrementPlatformSpecificCounter(ref _hardConnectsRate); + } + + /// + /// The number of actual disconnects that are being made to servers + /// + internal void HardDisconnectRequest() + { +#if NET + DecrementPlatformSpecificCounter(ref _activeHardConnections); +#endif + IncrementPlatformSpecificCounter(ref _hardDisconnectsRate); + } + + /// + /// The number of connections we get from the pool + /// + internal void SoftConnectRequest() + { +#if NET + IncrementPlatformSpecificCounter(ref _activeSoftConnections); +#endif + IncrementPlatformSpecificCounter(ref _softConnectsRate); + } + + /// + /// The number of connections we return to the pool + /// + internal void SoftDisconnectRequest() + { +#if NET + DecrementPlatformSpecificCounter(ref _activeSoftConnections); +#endif + IncrementPlatformSpecificCounter(ref _softDisconnectsRate); + } + + /// + /// The number of connections that are not using connection pooling + /// + internal void EnterNonPooledConnection() + { + IncrementPlatformSpecificCounter(ref _nonPooledConnections); + } + + /// + /// The number of connections that are not using connection pooling + /// + internal void ExitNonPooledConnection() + { + DecrementPlatformSpecificCounter(ref _nonPooledConnections); + } + + /// + /// The number of connections that are managed by the connection pool + /// + internal void EnterPooledConnection() + { + IncrementPlatformSpecificCounter(ref _pooledConnections); + } + + /// + /// The number of connections that are managed by the connection pool + /// + internal void ExitPooledConnection() + { + DecrementPlatformSpecificCounter(ref _pooledConnections); + } + + /// + /// The number of unique connection strings + /// + internal void EnterActiveConnectionPoolGroup() + { + IncrementPlatformSpecificCounter(ref _activeConnectionPoolGroups); + } + + /// + /// The number of unique connection strings + /// + internal void ExitActiveConnectionPoolGroup() + { + DecrementPlatformSpecificCounter(ref _activeConnectionPoolGroups); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + internal void EnterInactiveConnectionPoolGroup() + { + IncrementPlatformSpecificCounter(ref _inactiveConnectionPoolGroups); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + internal void ExitInactiveConnectionPoolGroup() + { + DecrementPlatformSpecificCounter(ref _inactiveConnectionPoolGroups); + } + + /// + /// The number of connection pools + /// + internal void EnterActiveConnectionPool() + { + IncrementPlatformSpecificCounter(ref _activeConnectionPools); + } + + /// + /// The number of connection pools + /// + internal void ExitActiveConnectionPool() + { + DecrementPlatformSpecificCounter(ref _activeConnectionPools); + } + + /// + /// The number of connection pools + /// + internal void EnterInactiveConnectionPool() + { + IncrementPlatformSpecificCounter(ref _inactiveConnectionPools); + } + + /// + /// The number of connection pools + /// + internal void ExitInactiveConnectionPool() + { + DecrementPlatformSpecificCounter(ref _inactiveConnectionPools); + } + + /// + /// The number of connections currently in-use + /// + internal void EnterActiveConnection() + { + IncrementPlatformSpecificCounter(ref _activeConnections); + } + + /// + /// The number of connections currently in-use + /// + internal void ExitActiveConnection() + { + DecrementPlatformSpecificCounter(ref _activeConnections); + } + + /// + /// The number of connections currently available for use + /// + internal void EnterFreeConnection() + { + IncrementPlatformSpecificCounter(ref _freeConnections); + } + + /// + /// The number of connections currently available for use + /// + internal void ExitFreeConnection() + { + DecrementPlatformSpecificCounter(ref _freeConnections); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + internal void EnterStasisConnection() + { + IncrementPlatformSpecificCounter(ref _stasisConnections); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + internal void ExitStasisConnection() + { + DecrementPlatformSpecificCounter(ref _stasisConnections); + } + + /// + /// The number of connections we reclaim from GC'd external connections + /// + internal void ReclaimedConnectionRequest() + { + IncrementPlatformSpecificCounter(ref _reclaimedConnections); + } + +#if NET + public void EnableEventCounters() + { + _activeHardConnectionsCounter ??= new PollingCounter("active-hard-connections", _eventSource, () => _activeHardConnections) + { + DisplayName = "Actual active connections currently made to servers", + DisplayUnits = "count" + }; + + _hardConnectsPerSecondCounter ??= new IncrementingPollingCounter("hard-connects", _eventSource, () => _hardConnectsRate) + { + DisplayName = "Actual connection rate to servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _hardDisconnectsPerSecondCounter ??= new IncrementingPollingCounter("hard-disconnects", _eventSource, () => _hardDisconnectsRate) + { + DisplayName = "Actual disconnection rate from servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _activeSoftConnectionsCounter ??= new PollingCounter("active-soft-connects", _eventSource, () => _activeSoftConnections) + { + DisplayName = "Active connections retrieved from the connection pool", + DisplayUnits = "count" + }; + + _softConnectsPerSecondCounter ??= new IncrementingPollingCounter("soft-connects", _eventSource, () => _softConnectsRate) + { + DisplayName = "Rate of connections retrieved from the connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _softDisconnectsPerSecondCounter ??= new IncrementingPollingCounter("soft-disconnects", _eventSource, () => _softDisconnectsRate) + { + DisplayName = "Rate of connections returned to the connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _numberOfNonPooledConnectionsCounter ??= new PollingCounter("number-of-non-pooled-connections", _eventSource, () => _nonPooledConnections) + { + DisplayName = "Number of connections not using connection pooling", + DisplayUnits = "count" + }; + + _numberOfPooledConnectionsCounter ??= new PollingCounter("number-of-pooled-connections", _eventSource, () => _pooledConnections) + { + DisplayName = "Number of connections managed by the connection pool", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionPoolGroupsCounter ??= new PollingCounter("number-of-active-connection-pool-groups", _eventSource, () => _activeConnectionPoolGroups) + { + DisplayName = "Number of active unique connection strings", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPoolGroupsCounter ??= new PollingCounter("number-of-inactive-connection-pool-groups", _eventSource, () => _inactiveConnectionPoolGroups) + { + DisplayName = "Number of unique connection strings waiting for pruning", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionPoolsCounter ??= new PollingCounter("number-of-active-connection-pools", _eventSource, () => _activeConnectionPools) + { + DisplayName = "Number of active connection pools", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPoolsCounter ??= new PollingCounter("number-of-inactive-connection-pools", _eventSource, () => _inactiveConnectionPools) + { + DisplayName = "Number of inactive connection pools", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionsCounter ??= new PollingCounter("number-of-active-connections", _eventSource, () => _activeConnections) + { + DisplayName = "Number of active connections", + DisplayUnits = "count" + }; + + _numberOfFreeConnectionsCounter ??= new PollingCounter("number-of-free-connections", _eventSource, () => _freeConnections) + { + DisplayName = "Number of ready connections in the connection pool", + DisplayUnits = "count" + }; + + _numberOfStasisConnectionsCounter ??= new PollingCounter("number-of-stasis-connections", _eventSource, () => _stasisConnections) + { + DisplayName = "Number of connections currently waiting to be ready", + DisplayUnits = "count" + }; + + _numberOfReclaimedConnectionsCounter ??= new IncrementingPollingCounter("number-of-reclaimed-connections", _eventSource, () => _reclaimedConnections) + { + DisplayName = "Number of reclaimed connections from GC", + DisplayUnits = "count", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + } +#else + [PerformanceCounterPermission(SecurityAction.Assert, PermissionAccess = PerformanceCounterPermissionAccess.Write, + MachineName = ".", CategoryName = PerformanceCounterCategoryName)] + private void EnablePerformanceCounters() + { + AppDomain.CurrentDomain.DomainUnload += ExitOrUnloadEventHandler; + AppDomain.CurrentDomain.ProcessExit += ExitOrUnloadEventHandler; + AppDomain.CurrentDomain.UnhandledException += ExceptionEventHandler; + + // level 0-3: hard connects/disconnects, plus basic pool/pool entry statistics + _hardConnectsRate = CreatePerformanceCounter("HardConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32); + _hardDisconnectsRate = CreatePerformanceCounter("HardDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32); + _nonPooledConnections = CreatePerformanceCounter("NumberOfNonPooledConnections", PerformanceCounterType.NumberOfItems32); + _pooledConnections = CreatePerformanceCounter("NumberOfPooledConnections", PerformanceCounterType.NumberOfItems32); + _activeConnectionPoolGroups = CreatePerformanceCounter("NumberOfActiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32); + _inactiveConnectionPoolGroups = CreatePerformanceCounter("NumberOfInactiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32); + _activeConnectionPools = CreatePerformanceCounter("NumberOfActiveConnectionPools", PerformanceCounterType.NumberOfItems32); + _inactiveConnectionPools = CreatePerformanceCounter("NumberOfInactiveConnectionPools", PerformanceCounterType.NumberOfItems32); + _stasisConnections = CreatePerformanceCounter("NumberOfStasisConnections", PerformanceCounterType.NumberOfItems32); + _reclaimedConnections = CreatePerformanceCounter("NumberOfReclaimedConnections", PerformanceCounterType.NumberOfItems32); + + TraceSwitch perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters"); + if (TraceLevel.Verbose == perfCtrSwitch.Level) + { + _softConnectsRate = CreatePerformanceCounter("SoftConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32); + _softDisconnectsRate = CreatePerformanceCounter("SoftDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32); + _activeConnections = CreatePerformanceCounter("NumberOfActiveConnections", PerformanceCounterType.NumberOfItems32); + _freeConnections = CreatePerformanceCounter("NumberOfFreeConnections", PerformanceCounterType.NumberOfItems32); + } + } + + [PrePrepareMethod] + private void ExitOrUnloadEventHandler(object sender, EventArgs e) + { + RemovePerformanceCounters(); + } + + [PrePrepareMethod] + private void ExceptionEventHandler(object sender, UnhandledExceptionEventArgs e) + { + if (e != null && e.IsTerminating) + { + RemovePerformanceCounters(); + } + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + private void RemovePerformanceCounters() + { + // ExceptionEventHandler with IsTerminating may be called before + // the Connection Close is called or the variables are initialized + _hardConnectsRate?.RemoveInstance(); + _hardDisconnectsRate?.RemoveInstance(); + _softConnectsRate?.RemoveInstance(); + _softDisconnectsRate?.RemoveInstance(); + _nonPooledConnections?.RemoveInstance(); + _pooledConnections?.RemoveInstance(); + _activeConnectionPoolGroups?.RemoveInstance(); + _inactiveConnectionPoolGroups?.RemoveInstance(); + _activeConnectionPools?.RemoveInstance(); + _inactiveConnectionPools?.RemoveInstance(); + _activeConnections?.RemoveInstance(); + _freeConnections?.RemoveInstance(); + _stasisConnections?.RemoveInstance(); + _reclaimedConnections?.RemoveInstance(); + } + + private PerformanceCounter? CreatePerformanceCounter(string counterName, PerformanceCounterType counterType) + { + PerformanceCounter? instance = null; + + _instanceName ??= GetInstanceName(); + try + { + instance = new PerformanceCounter(); + instance.CategoryName = PerformanceCounterCategoryName; + instance.CounterName = counterName; + instance.InstanceName = _instanceName; + instance.InstanceLifetime = PerformanceCounterInstanceLifetime.Process; + instance.ReadOnly = false; + instance.RawValue = 0; // make sure we start out at zero + } + catch (InvalidOperationException e) + { + ADP.TraceExceptionWithoutRethrow(e); + } + + return instance; + } + + // SxS: this method uses GetCurrentProcessId to construct the instance name. + // TODO: VSDD 534795 - remove the Resource* attributes if you do not use GetCurrentProcessId after the fix + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] + private static string GetInstanceName() + { + string result; + string? instanceName = GetAssemblyName(); // instance perfcounter name + + if (string.IsNullOrEmpty(instanceName)) + { + instanceName = AppDomain.CurrentDomain?.FriendlyName; + } + + // TODO: If you do not use GetCurrentProcessId after fixing VSDD 534795, please remove Resource* attributes from this method + int pid = Kernel32Safe.GetCurrentProcessId(); + + // SQLBUDT #366157 -there are several characters which have special meaning + // to PERFMON. They recommend that we translate them as shown below, to + // prevent problems. + + result = string.Format(null, "{0}[{1}]", instanceName, pid); + result = result.Replace('(', '[').Replace(')', ']').Replace('#', '_').Replace('/', '_').Replace('\\', '_'); + + // SQLBUVSTS #94625 - counter instance name cannot be greater than 127 + if (result.Length > CounterInstanceNameMaxLength) + { + // Replacing the middle part with "[...]" + // For example: if path is c:\long_path\very_(Ax200)_long__path\perftest.exe and process ID is 1234 than the resulted instance name will be: + // c:\long_path\very_(AxM)[...](AxN)_long__path\perftest.exe[1234] + // while M and N are adjusted to make each part before and after the [...] = 61 (making the total = 61 + 5 + 61 = 127) + const string insertString = "[...]"; + int firstPartLength = (CounterInstanceNameMaxLength - insertString.Length) / 2; + int lastPartLength = CounterInstanceNameMaxLength - firstPartLength - insertString.Length; + result = string.Format(null, "{0}{1}{2}", + result.Substring(0, firstPartLength), + insertString, + result.Substring(result.Length - lastPartLength, lastPartLength)); + + Debug.Assert(result.Length == CounterInstanceNameMaxLength, + string.Format(null, "wrong calculation of the instance name: expected {0}, actual: {1}", CounterInstanceNameMaxLength, result.Length)); + } + + return result; + } + + [FileIOPermission(SecurityAction.Assert, Unrestricted = true)] + private static string? GetAssemblyName() + { + // First try GetEntryAssembly name, then AppDomain.FriendlyName. + Assembly? assembly = Assembly.GetEntryAssembly(); + AssemblyName? name = assembly?.GetName(); + + return name?.Name; + } +#endif + } +} 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 c794cb0d2a..90a69b5670 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data.SqlClient.Diagnostics; using System; using System.Diagnostics.Tracing; using System.Text; @@ -9,78 +10,34 @@ 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 - } - // Any changes to event writers might be considered as a breaking change. // Other libraries such as OpenTelemetry and ApplicationInsight have based part of their code on BeginExecute and EndExecute arguments number. [EventSource(Name = "Microsoft.Data.SqlClient.EventSource")] - internal partial class SqlClientEventSource : SqlClientEventSourceBase + internal partial class SqlClientEventSource : EventSource { // Defines the singleton instance for the Resources ETW provider - internal static readonly SqlClientEventSource Log = new(); + public static readonly SqlClientEventSource Log = new(); + + // Provides access to metrics. + public static readonly SqlClientMetrics Metrics = new SqlClientMetrics(Log); private SqlClientEventSource() { } private const string NullStr = "null"; private const string SqlCommand_ClassName = nameof(SqlCommand); +#if NET + protected override void OnEventCommand(EventCommandEventArgs command) + { + base.OnEventCommand(command); + + if (command.Command == EventCommand.Enable) + { + Metrics.EnableEventCounters(); + } + } +#endif + #region Event IDs // Initialized static Scope IDs private static long s_nextScopeId = 0; 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 bd5b6750f5..e3414ea7ee 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 @@ -249,8 +249,10 @@ + + + - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs similarity index 51% rename from src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs rename to src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs index 7a8e4bfe1a..4e90bbf6c7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs @@ -9,18 +9,19 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - /// - /// This unit test is just valid for .NetCore 3.0 and above - /// - public class EventCounterTest + public class MetricsTest { - public EventCounterTest() +#if NETFRAMEWORK + private readonly static TraceSwitch s_perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters"); +#endif + + public MetricsTest() { ClearConnectionPools(); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void EventCounter_HardConnectionsCounters_Functional() + public void NonPooledConnectionsCounters_Functional() { //create a non-pooled connection var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false }; @@ -30,30 +31,39 @@ public void EventCounter_HardConnectionsCounters_Functional() using (var conn = new SqlConnection(stringBuilder.ToString())) { - //initially we have no open physical connections - Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, - SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + if (SupportsActiveConnectionCounters) + { + //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); + if (SupportsActiveConnectionCounters) + { + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + } 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); + if (SupportsActiveConnectionCounters) + { + Assert.Equal(ahc, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + } Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections); - Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, - SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); } } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void EventCounter_SoftConnectionsCounters_Functional() + public void PooledConnectionsCounters_Functional() { //create a pooled connection var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true }; @@ -68,42 +78,57 @@ public void EventCounter_SoftConnectionsCounters_Functional() 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); + if (SupportsActiveConnectionCounters) + { + //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); + if (SupportsActiveConnectionCounters) + { + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + } 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); + if (VerboseActiveConnectionCountersEnabled) + { + Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections); + } 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); + if (SupportsActiveConnectionCounters) + { + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + } 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); + if (VerboseActiveConnectionCountersEnabled) + { + Assert.Equal(ac, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc + 1, SqlClientEventSourceProps.FreeConnections); + } } using (var conn2 = new SqlConnection(stringBuilder.ToString())) @@ -111,22 +136,28 @@ public void EventCounter_SoftConnectionsCounters_Functional() conn2.Open(); //the next open connection will reuse the underlying physical connection - Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); - Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections); + if (SupportsActiveConnectionCounters) + { + Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections); + Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections); + Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections, + SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects); + Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections, + SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects); + } 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); + if (VerboseActiveConnectionCountersEnabled) + { + Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections); + Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections); + } } } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public void EventCounter_StasisCounters_Functional() + public void StasisCounters_Functional() { var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false, Enlist = false }; @@ -148,7 +179,7 @@ public void EventCounter_StasisCounters_Functional() [ActiveIssue("https://github.com/dotnet/SqlClient/issues/3031")] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void EventCounter_ReclaimedConnectionsCounter_Functional() + public void ReclaimedConnectionsCounter_Functional() { // clean pools and pool groups ClearConnectionPools(); @@ -180,7 +211,7 @@ public void EventCounter_ReclaimedConnectionsCounter_Functional() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void EventCounter_ConnectionPoolGroupsCounter_Functional() + public void ConnectionPoolGroupsCounter_Functional() { SqlConnection.ClearAllPools(); @@ -264,123 +295,139 @@ private static void PruneConnectionPoolGroups() private static FieldInfo GetConnectionFactoryField() { +#if NET FieldInfo connectionFactoryField = typeof(SqlConnection).GetField("s_connectionFactory", BindingFlags.Static | BindingFlags.NonPublic); +#else + FieldInfo connectionFactoryField = + typeof(SqlConnection).GetField("_connectionFactory", BindingFlags.Static | BindingFlags.NonPublic); +#endif Debug.Assert(connectionFactoryField != null); return connectionFactoryField; } + + // Only the .NET Core build supports the active-hard-connections and active-soft-connects counters. The .NET Framework + // build doesn't have comparable performance counters. + private static bool SupportsActiveConnectionCounters => +#if NET + true; +#else + false; +#endif + + private static bool VerboseActiveConnectionCountersEnabled => + SupportsActiveConnectionCounters || +#if NET + true; +#else + s_perfCtrSwitch.Level == TraceLevel.Verbose; +#endif } internal static class SqlClientEventSourceProps { private static readonly object s_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; + private static readonly Func s_getActiveHardConnections; + private static readonly Func s_getHardConnects; + private static readonly Func s_getHardDisconnects; + private static readonly Func s_getActiveSoftConnections; + private static readonly Func s_getSoftConnects; + private static readonly Func s_getSoftDisconnects; + private static readonly Func s_getNonPooledConnections; + private static readonly Func s_getPooledConnections; + private static readonly Func s_getActiveConnectionPoolGroups; + private static readonly Func s_getInactiveConnectionPoolGroups; + private static readonly Func s_getActiveConnectionPools; + private static readonly Func s_getInactiveConnectionPools; + private static readonly Func s_getActiveConnections; + private static readonly Func s_getFreeConnections; + private static readonly Func s_getStasisConnections; + private static readonly Func s_getReclaimedConnections; static SqlClientEventSourceProps() { Type sqlClientEventSourceType = Assembly.GetAssembly(typeof(SqlConnection))!.GetType("Microsoft.Data.SqlClient.SqlClientEventSource"); Debug.Assert(sqlClientEventSourceType != null); - FieldInfo logField = sqlClientEventSourceType.GetField("Log", BindingFlags.Static | BindingFlags.NonPublic); - Debug.Assert(logField != null); - s_log = logField.GetValue(null); - - BindingFlags _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); + FieldInfo metricsField = sqlClientEventSourceType.GetField("Metrics", BindingFlags.Static | BindingFlags.Public); + Debug.Assert(metricsField != null); + Type sqlClientMetricsType = metricsField.FieldType; + s_log = metricsField.GetValue(null); + +#if NETFRAMEWORK + Func notApplicableFunction = static () => -1; + + // .NET Framework doesn't have performance counters for the number of hard and soft connections. + s_getActiveHardConnections = notApplicableFunction; + s_getActiveSoftConnections = notApplicableFunction; +#endif + s_getActiveHardConnections = GenerateFieldGetter("_activeHardConnections"); + s_getHardConnects = GenerateFieldGetter("_hardConnectsRate"); + s_getHardDisconnects = GenerateFieldGetter("_hardDisconnectsRate"); + s_getActiveSoftConnections = GenerateFieldGetter("_activeSoftConnections"); + s_getSoftConnects = GenerateFieldGetter("_softConnectsRate"); + s_getSoftDisconnects = GenerateFieldGetter("_softDisconnectsRate"); + s_getNonPooledConnections = GenerateFieldGetter("_nonPooledConnections"); + s_getPooledConnections = GenerateFieldGetter("_pooledConnections"); + s_getActiveConnectionPoolGroups = GenerateFieldGetter("_activeConnectionPoolGroups"); + s_getInactiveConnectionPoolGroups = GenerateFieldGetter("_inactiveConnectionPoolGroups"); + s_getActiveConnectionPools = GenerateFieldGetter("_activeConnectionPools"); + s_getInactiveConnectionPools = GenerateFieldGetter("_inactiveConnectionPools"); + s_getActiveConnections = GenerateFieldGetter("_activeConnections"); + s_getFreeConnections = GenerateFieldGetter("_freeConnections"); + s_getStasisConnections = GenerateFieldGetter("_stasisConnections"); + s_getReclaimedConnections = GenerateFieldGetter("_reclaimedConnections"); + +#if NET + static Func GenerateFieldGetter(string fieldName) + { + FieldInfo counterField = s_log.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + + Debug.Assert(counterField != null); + return () => (long)counterField.GetValue(s_log)!; + } +#else + static Func GenerateFieldGetter(string fieldName) + { + FieldInfo counterField = s_log.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + Debug.Assert(counterField != null); + + PerformanceCounter counter = counterField?.GetValue(s_log) as PerformanceCounter; + return () => counter is null ? -1 : counter.RawValue; + } +#endif } - public static long ActiveHardConnections => (long)_activeHardConnectionsCounter.GetValue(s_log)!; + public static long ActiveHardConnections => s_getActiveHardConnections(); - public static long HardConnects => (long)_hardConnectsCounter.GetValue(s_log)!; + public static long HardConnects => s_getHardConnects(); - public static long HardDisconnects => (long)_hardDisconnectsCounter.GetValue(s_log)!; + public static long HardDisconnects => s_getHardDisconnects(); - public static long ActiveSoftConnections => (long)_activeSoftConnectionsCounter.GetValue(s_log)!; + public static long ActiveSoftConnections => s_getActiveSoftConnections(); - public static long SoftConnects => (long)_softConnectsCounter.GetValue(s_log)!; + public static long SoftConnects => s_getSoftConnects(); - public static long SoftDisconnects => (long)_softDisconnectsCounter.GetValue(s_log)!; + public static long SoftDisconnects => s_getSoftDisconnects(); - public static long NonPooledConnections => (long)_nonPooledConnectionsCounter.GetValue(s_log)!; + public static long NonPooledConnections => s_getNonPooledConnections(); - public static long PooledConnections => (long)_pooledConnectionsCounter.GetValue(s_log)!; + public static long PooledConnections => s_getPooledConnections(); - public static long ActiveConnectionPoolGroups => (long)_activeConnectionPoolGroupsCounter.GetValue(s_log)!; + public static long ActiveConnectionPoolGroups => s_getActiveConnectionPoolGroups(); - public static long InactiveConnectionPoolGroups => (long)_inactiveConnectionPoolGroupsCounter.GetValue(s_log)!; + public static long InactiveConnectionPoolGroups => s_getInactiveConnectionPoolGroups(); - public static long ActiveConnectionPools => (long)_activeConnectionPoolsCounter.GetValue(s_log)!; + public static long ActiveConnectionPools => s_getActiveConnectionPools(); - public static long InactiveConnectionPools => (long)_inactiveConnectionPoolsCounter.GetValue(s_log)!; + public static long InactiveConnectionPools => s_getInactiveConnectionPools(); - public static long ActiveConnections => (long)_activeConnectionsCounter.GetValue(s_log)!; + public static long ActiveConnections => s_getActiveConnections(); - public static long FreeConnections => (long)_freeConnectionsCounter.GetValue(s_log)!; + public static long FreeConnections => s_getFreeConnections(); - public static long StasisConnections => (long)_stasisConnectionsCounter.GetValue(s_log)!; + public static long StasisConnections => s_getStasisConnections(); - public static long ReclaimedConnections => (long)_reclaimedConnectionsCounter.GetValue(s_log)!; + public static long ReclaimedConnections => s_getReclaimedConnections(); } }