From c7d9ca23f1495e1d8830a98af2079a56b17c8e6f Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Tue, 25 Mar 2025 23:53:06 +0000
Subject: [PATCH 1/5] Merge event counters and performance counters
Move functionality into a new Microsoft.Data.SqlClient.Diagnostics.SqlClientMetrics type. This common interface encapsulates both event counters and performance counters.
---
.../src/Microsoft.Data.SqlClient.csproj | 7 +-
.../SqlClientEventSource.NetCoreApp.cs | 396 ------------------
.../netfx/src/Microsoft.Data.SqlClient.csproj | 9 +-
.../Data/SqlClient/SqlConnectionFactory.cs | 16 +-
.../Data/ProviderBase/DbConnectionFactory.cs | 88 +---
.../Data/ProviderBase/DbConnectionInternal.cs | 70 +---
.../ConnectionPool/DbConnectionPool.cs | 3 -
.../DbConnectionPoolCounters.netfx.cs | 361 ----------------
.../ConnectionPool/DbConnectionPoolGroup.cs | 14 +-
.../WaitHandleDbConnectionPool.cs | 111 ++---
.../SqlClient/Diagnostics/SqlClientMetrics.cs | 346 +++++++++++++++
.../Diagnostics/SqlClientMetrics.netcore.cs | 178 ++++++++
.../Diagnostics/SqlClientMetrics.netfx.cs | 193 +++++++++
.../Data/SqlClient/SqlClientEventSource.cs | 77 +---
.../TracingTests/EventCounterTest.cs | 39 +-
15 files changed, 824 insertions(+), 1084 deletions(-)
delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs
delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolCounters.netfx.cs
create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
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 bb77ffba3e..226d103078 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -209,6 +209,12 @@
Microsoft\Data\SqlClient\Diagnostics\SqlClientConnectionOpenError.netcore.cs
+
+ Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs
+
+
+ Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.netcore.cs
+
Microsoft\Data\SqlClient\Diagnostics\SqlClientTransactionCommitAfter.netcore.cs
@@ -716,7 +722,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 e5840b17a3..bd45398408 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -271,9 +271,6 @@
Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolAuthenticationContextKey.cs
-
- Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolCounters.netfx.cs
-
Microsoft\Data\SqlClient\ConnectionPool\DbConnectionPoolGroup.cs
@@ -310,6 +307,12 @@
Microsoft\Data\SqlClient\ConnectionPool\SqlConnectionPoolProviderInfo.cs
+
+ Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs
+
+
+ Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.netfx.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 d9bf71b663..d304161b58 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 0eadb7f42c..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 (!ADP.IsEmpty(categoryName) && !ADP.IsEmpty(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 (!ADP.IsEmpty(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 (!ADP.IsEmpty(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 (ADP.IsEmpty(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..504d7507a2
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
@@ -0,0 +1,346 @@
+// 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.Diagnostics.Tracing;
+#if NET
+using System.Threading;
+#endif
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient.Diagnostics
+{
+ // This encapsulates three types of metrics:
+ // * Default: These metrics are always enabled.
+ // * Verbose: These metrics aren't enabled by default, but can be enabled on application startup if a trace switch is set to verbose.
+ // * Trace: These metrics are enabled with the SqlClientEventSource.
+ // Verbose metrics are only present for backwards compatibility.
+ internal sealed partial class SqlClientMetrics
+ {
+ private readonly SqlClientEventSource _eventSource;
+
+ public SqlClientMetrics(SqlClientEventSource eventSource)
+ {
+ _eventSource = eventSource;
+ EnableDefaultMetrics();
+ }
+
+ private void EnableDefaultMetrics()
+ {
+#if NETFRAMEWORK
+ EnablePerformanceCounters();
+#endif
+ }
+
+ public void EnableTraceMetrics()
+ {
+#if NET
+ EnableEventCounters();
+#endif
+ }
+
+ ///
+ /// The number of actual connections that are being made to servers
+ ///
+ internal void HardConnectRequest()
+ {
+#if NET
+ Interlocked.Increment(ref _activeHardConnectionsCounter);
+ Interlocked.Increment(ref _hardConnectsCounter);
+#endif
+#if NETFRAMEWORK
+ _hardConnectsPerSecond?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of actual disconnects that are being made to servers
+ ///
+ internal void HardDisconnectRequest()
+ {
+#if NET
+ Interlocked.Decrement(ref _activeHardConnectionsCounter);
+ Interlocked.Increment(ref _hardDisconnectsCounter);
+#endif
+#if NETFRAMEWORK
+ _hardDisconnectsPerSecond?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections we get from the pool
+ ///
+ internal void SoftConnectRequest()
+ {
+#if NET
+ Interlocked.Increment(ref _activeSoftConnectionsCounter);
+ Interlocked.Increment(ref _softConnectsCounter);
+#endif
+#if NETFRAMEWORK
+ _softConnectsPerSecond?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections we return to the pool
+ ///
+ internal void SoftDisconnectRequest()
+ {
+#if NET
+ Interlocked.Decrement(ref _activeSoftConnectionsCounter);
+ Interlocked.Increment(ref _softDisconnectsCounter);
+#endif
+#if NETFRAMEWORK
+ _softDisconnectsPerSecond?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections that are not using connection pooling
+ ///
+ internal void EnterNonPooledConnection()
+ {
+#if NET
+ Interlocked.Increment(ref _nonPooledConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfNonPooledConnections?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections that are not using connection pooling
+ ///
+ internal void ExitNonPooledConnection()
+ {
+#if NET
+ Interlocked.Decrement(ref _nonPooledConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfNonPooledConnections?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connections that are managed by the connection pool
+ ///
+ internal void EnterPooledConnection()
+ {
+#if NET
+ Interlocked.Increment(ref _pooledConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfPooledConnections?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections that are managed by the connection pool
+ ///
+ internal void ExitPooledConnection()
+ {
+#if NET
+ Interlocked.Decrement(ref _pooledConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfPooledConnections?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of unique connection strings
+ ///
+ internal void EnterActiveConnectionPoolGroup()
+ {
+#if NET
+ Interlocked.Increment(ref _activeConnectionPoolGroupsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnectionPoolGroups?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of unique connection strings
+ ///
+ internal void ExitActiveConnectionPoolGroup()
+ {
+#if NET
+ Interlocked.Decrement(ref _activeConnectionPoolGroupsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnectionPoolGroups?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of unique connection strings waiting for pruning
+ ///
+ internal void EnterInactiveConnectionPoolGroup()
+ {
+#if NET
+ Interlocked.Increment(ref _inactiveConnectionPoolGroupsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfInactiveConnectionPoolGroups?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of unique connection strings waiting for pruning
+ ///
+ internal void ExitInactiveConnectionPoolGroup()
+ {
+#if NET
+ Interlocked.Decrement(ref _inactiveConnectionPoolGroupsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfInactiveConnectionPoolGroups?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connection pools
+ ///
+ internal void EnterActiveConnectionPool()
+ {
+#if NET
+ Interlocked.Increment(ref _activeConnectionPoolsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnectionPools?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connection pools
+ ///
+ internal void ExitActiveConnectionPool()
+ {
+#if NET
+ Interlocked.Decrement(ref _activeConnectionPoolsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnectionPools?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connection pools
+ ///
+ internal void EnterInactiveConnectionPool()
+ {
+#if NET
+ Interlocked.Increment(ref _inactiveConnectionPoolsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfInactiveConnectionPools?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connection pools
+ ///
+ internal void ExitInactiveConnectionPool()
+ {
+#if NET
+ Interlocked.Decrement(ref _inactiveConnectionPoolsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfInactiveConnectionPools?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connections currently in-use
+ ///
+ internal void EnterActiveConnection()
+ {
+#if NET
+ Interlocked.Increment(ref _activeConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnections?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections currently in-use
+ ///
+ internal void ExitActiveConnection()
+ {
+#if NET
+ Interlocked.Decrement(ref _activeConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfActiveConnections?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connections currently available for use
+ ///
+ internal void EnterFreeConnection()
+ {
+#if NET
+ Interlocked.Increment(ref _freeConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfFreeConnections?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections currently available for use
+ ///
+ internal void ExitFreeConnection()
+ {
+#if NET
+ Interlocked.Decrement(ref _freeConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfFreeConnections?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connections currently waiting to be made ready for use
+ ///
+ internal void EnterStasisConnection()
+ {
+#if NET
+ Interlocked.Increment(ref _stasisConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfStasisConnections?.Increment();
+#endif
+ }
+
+ ///
+ /// The number of connections currently waiting to be made ready for use
+ ///
+ internal void ExitStasisConnection()
+ {
+#if NET
+ Interlocked.Decrement(ref _stasisConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfStasisConnections?.Decrement();
+#endif
+ }
+
+ ///
+ /// The number of connections we reclaim from GC'd external connections
+ ///
+ internal void ReclaimedConnectionRequest()
+ {
+#if NET
+ Interlocked.Increment(ref _reclaimedConnectionsCounter);
+#endif
+#if NETFRAMEWORK
+ _numberOfReclaimedConnections?.Increment();
+#endif
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
new file mode 100644
index 0000000000..78ad032b38
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
@@ -0,0 +1,178 @@
+// 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;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient.Diagnostics
+{
+ internal sealed partial class SqlClientMetrics
+ {
+ 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;
+
+ private void EnableEventCounters()
+ {
+ _activeHardConnections = _activeHardConnections ??
+ new PollingCounter("active-hard-connections", _eventSource, () => _activeHardConnectionsCounter)
+ {
+ DisplayName = "Actual active connections currently made to servers",
+ DisplayUnits = "count"
+ };
+
+ _hardConnectsPerSecond = _hardConnectsPerSecond ??
+ new IncrementingPollingCounter("hard-connects", _eventSource, () => _hardConnectsCounter)
+ {
+ DisplayName = "Actual connection rate to servers",
+ DisplayUnits = "count / sec",
+ DisplayRateTimeScale = TimeSpan.FromSeconds(1)
+ };
+
+ _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ??
+ new IncrementingPollingCounter("hard-disconnects", _eventSource, () => _hardDisconnectsCounter)
+ {
+ DisplayName = "Actual disconnection rate from servers",
+ DisplayUnits = "count / sec",
+ DisplayRateTimeScale = TimeSpan.FromSeconds(1)
+ };
+
+ _activeSoftConnections = _activeSoftConnections ??
+ new PollingCounter("active-soft-connects", _eventSource, () => _activeSoftConnectionsCounter)
+ {
+ DisplayName = "Active connections retrieved from the connection pool",
+ DisplayUnits = "count"
+ };
+
+ _softConnects = _softConnects ??
+ new IncrementingPollingCounter("soft-connects", _eventSource, () => _softConnectsCounter)
+ {
+ DisplayName = "Rate of connections retrieved from the connection pool",
+ DisplayUnits = "count / sec",
+ DisplayRateTimeScale = TimeSpan.FromSeconds(1)
+ };
+
+ _softDisconnects = _softDisconnects ??
+ new IncrementingPollingCounter("soft-disconnects", _eventSource, () => _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", _eventSource, () => _nonPooledConnectionsCounter)
+ {
+ DisplayName = "Number of connections not using connection pooling",
+ DisplayUnits = "count"
+ };
+
+ _numberOfPooledConnections = _numberOfPooledConnections ??
+ new PollingCounter("number-of-pooled-connections", _eventSource, () => _pooledConnectionsCounter)
+ {
+ DisplayName = "Number of connections managed by the connection pool",
+ DisplayUnits = "count"
+ };
+
+ _numberOfActiveConnectionPoolGroups = _numberOfActiveConnectionPoolGroups ??
+ new PollingCounter("number-of-active-connection-pool-groups", _eventSource, () => _activeConnectionPoolGroupsCounter)
+ {
+ DisplayName = "Number of active unique connection strings",
+ DisplayUnits = "count"
+ };
+
+ _numberOfInactiveConnectionPoolGroups = _numberOfInactiveConnectionPoolGroups ??
+ new PollingCounter("number-of-inactive-connection-pool-groups", _eventSource, () => _inactiveConnectionPoolGroupsCounter)
+ {
+ DisplayName = "Number of unique connection strings waiting for pruning",
+ DisplayUnits = "count"
+ };
+
+ _numberOfActiveConnectionPools = _numberOfActiveConnectionPools ??
+ new PollingCounter("number-of-active-connection-pools", _eventSource, () => _activeConnectionPoolsCounter)
+ {
+ DisplayName = "Number of active connection pools",
+ DisplayUnits = "count"
+ };
+
+ _numberOfInactiveConnectionPools = _numberOfInactiveConnectionPools ??
+ new PollingCounter("number-of-inactive-connection-pools", _eventSource, () => _inactiveConnectionPoolsCounter)
+ {
+ DisplayName = "Number of inactive connection pools",
+ DisplayUnits = "count"
+ };
+
+ _numberOfActiveConnections = _numberOfActiveConnections ??
+ new PollingCounter("number-of-active-connections", _eventSource, () => _activeConnectionsCounter)
+ {
+ DisplayName = "Number of active connections",
+ DisplayUnits = "count"
+ };
+
+ _numberOfFreeConnections = _numberOfFreeConnections ??
+ new PollingCounter("number-of-free-connections", _eventSource, () => _freeConnectionsCounter)
+ {
+ DisplayName = "Number of ready connections in the connection pool",
+ DisplayUnits = "count"
+ };
+
+ _numberOfStasisConnections = _numberOfStasisConnections ??
+ new PollingCounter("number-of-stasis-connections", _eventSource, () => _stasisConnectionsCounter)
+ {
+ DisplayName = "Number of connections currently waiting to be ready",
+ DisplayUnits = "count"
+ };
+
+ _numberOfReclaimedConnections = _numberOfReclaimedConnections ??
+ new IncrementingPollingCounter("number-of-reclaimed-connections", _eventSource, () => _reclaimedConnectionsCounter)
+ {
+ DisplayName = "Number of reclaimed connections from GC",
+ DisplayUnits = "count",
+ DisplayRateTimeScale = TimeSpan.FromSeconds(1)
+ };
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
new file mode 100644
index 0000000000..f6b822a5ba
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
@@ -0,0 +1,193 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.Versioning;
+using System.Security.Permissions;
+using Interop.Windows.Kernel32;
+using Microsoft.Data.Common;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient.Diagnostics
+{
+ [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
+ internal sealed partial class SqlClientMetrics
+ {
+ private const string PerformanceCounterCategoryName = ".NET Data Provider for SqlServer";
+ private const string PerformanceCounterCategoryHelp = "Counters for Microsoft.Data.SqlClient";
+ private const int CounterInstanceNameMaxLength = 127;
+
+ private PerformanceCounter? _hardConnectsPerSecond;
+ private PerformanceCounter? _hardDisconnectsPerSecond;
+ private PerformanceCounter? _softConnectsPerSecond;
+ private PerformanceCounter? _softDisconnectsPerSecond;
+ private PerformanceCounter? _numberOfNonPooledConnections;
+ private PerformanceCounter? _numberOfPooledConnections;
+ private PerformanceCounter? _numberOfActiveConnectionPoolGroups;
+ private PerformanceCounter? _numberOfInactiveConnectionPoolGroups;
+ private PerformanceCounter? _numberOfActiveConnectionPools;
+ private PerformanceCounter? _numberOfInactiveConnectionPools;
+ private PerformanceCounter? _numberOfActiveConnections;
+ private PerformanceCounter? _numberOfFreeConnections;
+ private PerformanceCounter? _numberOfStasisConnections;
+ private PerformanceCounter? _numberOfReclaimedConnections;
+
+ private static PerformanceCounter? CreatePerformanceCounter(string categoryName, string instanceName, string counterName, PerformanceCounterType counterType)
+ {
+ PerformanceCounter? instance = null;
+
+ try
+ {
+ 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
+ }
+ catch (InvalidOperationException e)
+ {
+ ADP.TraceExceptionWithoutRethrow(e);
+ }
+
+ return instance;
+ }
+
+ [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;
+ }
+
+ // 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;
+ }
+
+ [PerformanceCounterPermission(SecurityAction.Assert, PermissionAccess = PerformanceCounterPermissionAccess.Write,
+ MachineName = ".", CategoryName = PerformanceCounterCategoryName)]
+ private void EnablePerformanceCounters()
+ {
+ AppDomain.CurrentDomain.DomainUnload += UnloadEventHandler;
+ AppDomain.CurrentDomain.ProcessExit += ExitEventHandler;
+ AppDomain.CurrentDomain.UnhandledException += ExceptionEventHandler;
+
+ string instanceName = GetInstanceName();
+
+ // level 0-3: hard connects/disconnects, plus basic pool/pool entry statistics
+
+ _hardConnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "HardConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
+ _hardDisconnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "HardDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
+ _numberOfNonPooledConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfNonPooledConnections", PerformanceCounterType.NumberOfItems32);
+ _numberOfPooledConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfPooledConnections", PerformanceCounterType.NumberOfItems32);
+ _numberOfActiveConnectionPoolGroups = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32);
+ _numberOfInactiveConnectionPoolGroups = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfInactiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32);
+ _numberOfActiveConnectionPools = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnectionPools", PerformanceCounterType.NumberOfItems32);
+ _numberOfInactiveConnectionPools = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfInactiveConnectionPools", PerformanceCounterType.NumberOfItems32);
+ _numberOfStasisConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfStasisConnections", PerformanceCounterType.NumberOfItems32);
+ _numberOfReclaimedConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfReclaimedConnections", PerformanceCounterType.NumberOfItems32);
+
+ TraceSwitch perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters");
+ if (TraceLevel.Verbose == perfCtrSwitch.Level)
+ {
+ _softConnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "SoftConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
+ _softDisconnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "SoftDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
+ _numberOfActiveConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnections", PerformanceCounterType.NumberOfItems32);
+ _numberOfFreeConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfFreeConnections", PerformanceCounterType.NumberOfItems32);
+ }
+ }
+
+ [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
+ _hardConnectsPerSecond?.RemoveInstance();
+ _hardDisconnectsPerSecond?.RemoveInstance();
+ _softConnectsPerSecond?.RemoveInstance();
+ _softDisconnectsPerSecond?.RemoveInstance();
+ _numberOfNonPooledConnections?.RemoveInstance();
+ _numberOfPooledConnections?.RemoveInstance();
+ _numberOfActiveConnectionPoolGroups?.RemoveInstance();
+ _numberOfInactiveConnectionPoolGroups?.RemoveInstance();
+ _numberOfActiveConnectionPools?.RemoveInstance();
+ _numberOfInactiveConnectionPools?.RemoveInstance();
+ _numberOfActiveConnections?.RemoveInstance();
+ _numberOfFreeConnections?.RemoveInstance();
+ _numberOfStasisConnections?.RemoveInstance();
+ _numberOfReclaimedConnections?.RemoveInstance();
+ }
+
+ [PrePrepareMethod]
+ private void ExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
+ {
+ if (e != null && e.IsTerminating)
+ {
+ RemovePerformanceCounters();
+ }
+ }
+
+ [PrePrepareMethod]
+ private void ExitEventHandler(object sender, EventArgs e)
+ {
+ RemovePerformanceCounters();
+ }
+
+ [PrePrepareMethod]
+ private void UnloadEventHandler(object sender, EventArgs e)
+ {
+ RemovePerformanceCounters();
+ }
+ }
+}
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..6a8d503e53 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,32 @@
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);
+ protected override void OnEventCommand(EventCommandEventArgs command)
+ {
+ base.OnEventCommand(command);
+
+ if (command.Command == EventCommand.Enable)
+ {
+ Metrics.EnableTraceMetrics();
+ }
+ }
+
#region Event IDs
// Initialized static Scope IDs
private static long s_nextScopeId = 0;
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs
index 7a8e4bfe1a..63c0cc0b85 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs
@@ -296,58 +296,59 @@ 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);
+ FieldInfo metricsField = sqlClientEventSourceType.GetField("Metrics", BindingFlags.Static | BindingFlags.Public);
+ Debug.Assert(metricsField != null);
+ Type sqlClientMetricsType = metricsField.FieldType;
+ s_log = metricsField.GetValue(null);
BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
_activeHardConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_activeHardConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_activeHardConnectionsCounter), _bindingFlags);
Debug.Assert(_activeHardConnectionsCounter != null);
_hardConnectsCounter =
- sqlClientEventSourceType.GetField(nameof(_hardConnectsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_hardConnectsCounter), _bindingFlags);
Debug.Assert(_hardConnectsCounter != null);
_hardDisconnectsCounter =
- sqlClientEventSourceType.GetField(nameof(_hardDisconnectsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_hardDisconnectsCounter), _bindingFlags);
Debug.Assert(_hardDisconnectsCounter != null);
_activeSoftConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_activeSoftConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_activeSoftConnectionsCounter), _bindingFlags);
Debug.Assert(_activeSoftConnectionsCounter != null);
_softConnectsCounter =
- sqlClientEventSourceType.GetField(nameof(_softConnectsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_softConnectsCounter), _bindingFlags);
Debug.Assert(_softConnectsCounter != null);
_softDisconnectsCounter =
- sqlClientEventSourceType.GetField(nameof(_softDisconnectsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_softDisconnectsCounter), _bindingFlags);
Debug.Assert(_softDisconnectsCounter != null);
_nonPooledConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_nonPooledConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_nonPooledConnectionsCounter), _bindingFlags);
Debug.Assert(_nonPooledConnectionsCounter != null);
_pooledConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_pooledConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_pooledConnectionsCounter), _bindingFlags);
Debug.Assert(_pooledConnectionsCounter != null);
_activeConnectionPoolGroupsCounter =
- sqlClientEventSourceType.GetField(nameof(_activeConnectionPoolGroupsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_activeConnectionPoolGroupsCounter), _bindingFlags);
Debug.Assert(_activeConnectionPoolGroupsCounter != null);
_inactiveConnectionPoolGroupsCounter =
- sqlClientEventSourceType.GetField(nameof(_inactiveConnectionPoolGroupsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_inactiveConnectionPoolGroupsCounter), _bindingFlags);
Debug.Assert(_inactiveConnectionPoolGroupsCounter != null);
_activeConnectionPoolsCounter =
- sqlClientEventSourceType.GetField(nameof(_activeConnectionPoolsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_activeConnectionPoolsCounter), _bindingFlags);
Debug.Assert(_activeConnectionPoolsCounter != null);
_inactiveConnectionPoolsCounter =
- sqlClientEventSourceType.GetField(nameof(_inactiveConnectionPoolsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_inactiveConnectionPoolsCounter), _bindingFlags);
Debug.Assert(_inactiveConnectionPoolsCounter != null);
_activeConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_activeConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_activeConnectionsCounter), _bindingFlags);
Debug.Assert(_activeConnectionsCounter != null);
_freeConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_freeConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_freeConnectionsCounter), _bindingFlags);
Debug.Assert(_freeConnectionsCounter != null);
_stasisConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_stasisConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_stasisConnectionsCounter), _bindingFlags);
Debug.Assert(_stasisConnectionsCounter != null);
_reclaimedConnectionsCounter =
- sqlClientEventSourceType.GetField(nameof(_reclaimedConnectionsCounter), _bindingFlags);
+ sqlClientMetricsType.GetField(nameof(_reclaimedConnectionsCounter), _bindingFlags);
Debug.Assert(_reclaimedConnectionsCounter != null);
}
From 06b837be43ddcbdf7e6dd04d79113e390c780a01 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Tue, 1 Apr 2025 22:09:58 +0100
Subject: [PATCH 2/5] Correct null coalescing behaviour
---
.../Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
index f6b822a5ba..cd2e42731b 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
@@ -64,7 +64,7 @@ internal sealed partial class SqlClientMetrics
{
// First try GetEntryAssembly name, then AppDomain.FriendlyName.
Assembly? assembly = Assembly.GetEntryAssembly();
- AssemblyName? name = assembly.GetName();
+ AssemblyName? name = assembly?.GetName();
return name?.Name;
}
From 9613c3a3577a4bce49268e85cab591ee3d2560ca Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Sat, 5 Apr 2025 11:10:50 +0100
Subject: [PATCH 3/5] Widened existing EventCounter tests to include
PerformanceCounters
---
....Data.SqlClient.ManualTesting.Tests.csproj | 4 +-
.../TracingTests/EventCounterTest.cs | 387 ---------------
.../ManualTests/TracingTests/MetricsTest.cs | 441 ++++++++++++++++++
3 files changed, 444 insertions(+), 388 deletions(-)
delete mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs
create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
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/EventCounterTest.cs
deleted file mode 100644
index 63c0cc0b85..0000000000
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs
+++ /dev/null
@@ -1,387 +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;
-using System.Reflection;
-using System.Transactions;
-using Xunit;
-
-namespace Microsoft.Data.SqlClient.ManualTesting.Tests
-{
- ///
- /// This unit test is just valid for .NetCore 3.0 and above
- ///
- public class EventCounterTest
- {
- public EventCounterTest()
- {
- ClearConnectionPools();
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
- public void EventCounter_HardConnectionsCounters_Functional()
- {
- //create a non-pooled connection
- var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false };
-
- var ahc = SqlClientEventSourceProps.ActiveHardConnections;
- var npc = SqlClientEventSourceProps.NonPooledConnections;
-
- using (var conn = new SqlConnection(stringBuilder.ToString()))
- {
- //initially we have no open physical connections
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
-
- conn.Open();
-
- //when the connection gets opened, the real physical connection appears
- Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections);
- Assert.Equal(npc + 1, SqlClientEventSourceProps.NonPooledConnections);
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
-
- conn.Close();
-
- //when the connection gets closed, the real physical connection is also closed
- Assert.Equal(ahc, SqlClientEventSourceProps.ActiveHardConnections);
- Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections);
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
- }
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
- public void EventCounter_SoftConnectionsCounters_Functional()
- {
- //create a pooled connection
- var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true };
-
- var ahc = SqlClientEventSourceProps.ActiveHardConnections;
- var asc = SqlClientEventSourceProps.ActiveSoftConnections;
- var pc = SqlClientEventSourceProps.PooledConnections;
- var npc = SqlClientEventSourceProps.NonPooledConnections;
- var acp = SqlClientEventSourceProps.ActiveConnectionPools;
- var ac = SqlClientEventSourceProps.ActiveConnections;
- var fc = SqlClientEventSourceProps.FreeConnections;
-
- using (var conn = new SqlConnection(stringBuilder.ToString()))
- {
- //initially we have no open physical connections
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
- Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections,
- SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects);
-
- conn.Open();
-
- //when the connection gets opened, the real physical connection appears
- //and the appropriate pooling infrastructure gets deployed
- Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections);
- Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections);
- Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections);
- Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections);
- Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools);
- Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections);
- Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections);
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
- Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections,
- SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects);
-
- conn.Close();
-
- //when the connection gets closed, the real physical connection gets returned to the pool
- Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections);
- Assert.Equal(asc, SqlClientEventSourceProps.ActiveSoftConnections);
- Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections);
- Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections);
- Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools);
- Assert.Equal(ac, SqlClientEventSourceProps.ActiveConnections);
- Assert.Equal(fc + 1, SqlClientEventSourceProps.FreeConnections);
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
- Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections,
- SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects);
- }
-
- using (var conn2 = new SqlConnection(stringBuilder.ToString()))
- {
- conn2.Open();
-
- //the next open connection will reuse the underlying physical connection
- Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections);
- Assert.Equal(asc + 1, SqlClientEventSourceProps.ActiveSoftConnections);
- Assert.Equal(pc + 1, SqlClientEventSourceProps.PooledConnections);
- Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections);
- Assert.Equal(acp + 1, SqlClientEventSourceProps.ActiveConnectionPools);
- Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections);
- Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections);
- Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
- SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
- Assert.Equal(SqlClientEventSourceProps.ActiveSoftConnections,
- SqlClientEventSourceProps.SoftConnects - SqlClientEventSourceProps.SoftDisconnects);
- }
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
- public void EventCounter_StasisCounters_Functional()
- {
- var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false, Enlist = false };
-
- using (var conn = new SqlConnection(stringBuilder.ToString()))
- using (new TransactionScope())
- {
- conn.Open();
- conn.EnlistTransaction(System.Transactions.Transaction.Current);
- conn.Close();
-
- //when the connection gets closed, but the ambient transaction is still in prigress
- //the physical connection gets in stasis, until the transaction ends
- Assert.Equal(1, SqlClientEventSourceProps.StasisConnections);
- }
-
- //when the transaction finally ends, the physical connection is returned from stasis
- Assert.Equal(0, SqlClientEventSourceProps.StasisConnections);
- }
-
- [ActiveIssue("https://github.com/dotnet/SqlClient/issues/3031")]
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
- public void EventCounter_ReclaimedConnectionsCounter_Functional()
- {
- // clean pools and pool groups
- ClearConnectionPools();
- var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true, MaxPoolSize = 1 };
- stringBuilder.ConnectTimeout = Math.Max(stringBuilder.ConnectTimeout, 30);
-
- long rc = SqlClientEventSourceProps.ReclaimedConnections;
-
- int gcNumber = GC.GetGeneration(CreateEmancipatedConnection(stringBuilder.ToString()));
- // Specifying the generation number makes it to run faster by avoiding a full GC process
- GC.Collect(gcNumber);
- GC.WaitForPendingFinalizers();
- System.Threading.Thread.Sleep(200); // give the pooler some time to reclaim the connection and avoid the conflict.
-
- using (SqlConnection conn = new SqlConnection(stringBuilder.ToString()))
- {
- conn.Open();
-
- // when calling open, the connection could be reclaimed.
- if (GC.GetGeneration(conn) == gcNumber)
- {
- Assert.Equal(rc + 1, SqlClientEventSourceProps.ReclaimedConnections);
- }
- else
- {
- Assert.Equal(rc, SqlClientEventSourceProps.ReclaimedConnections);
- }
- }
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
- public void EventCounter_ConnectionPoolGroupsCounter_Functional()
- {
- SqlConnection.ClearAllPools();
-
- var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true };
-
- long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups;
- long iacpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups;
-
- using (SqlConnection conn = new SqlConnection(stringBuilder.ToString()))
- {
- conn.Open();
-
- // when calling open, we have 1 more active connection pool group
- Assert.Equal(acpg + 1, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
-
- conn.Close();
- }
-
- SqlConnection.ClearAllPools();
-
- // poolGroup state is changed from Active to Idle
- PruneConnectionPoolGroups();
-
- // poolGroup state is changed from Idle to Disabled
- PruneConnectionPoolGroups();
- Assert.Equal(acpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
- Assert.Equal(iacpg + 1, SqlClientEventSourceProps.InactiveConnectionPoolGroups);
-
- // Remove poolGroup from poolGroupsToRelease list
- PruneConnectionPoolGroups();
- Assert.Equal(iacpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
- }
-
- private static InternalConnectionWrapper CreateEmancipatedConnection(string connectionString)
- {
- SqlConnection connection = new SqlConnection(connectionString);
- connection.Open();
- return new InternalConnectionWrapper(connection);
- }
-
- private void ClearConnectionPools()
- {
- //ClearAllPoos kills all the existing pooled connection thus deactivating all the active pools
- long liveConnectionPools = SqlClientEventSourceProps.ActiveConnectionPools +
- SqlClientEventSourceProps.InactiveConnectionPools;
- SqlConnection.ClearAllPools();
- Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, liveConnectionPools);
- Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPools);
-
- long icp = SqlClientEventSourceProps.InactiveConnectionPools;
-
- // The 1st PruneConnectionPoolGroups call cleans the dangling inactive connection pools.
- PruneConnectionPoolGroups();
- // If the pool isn't empty, it's because there are active connections or distributed transactions that need it.
- Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, icp);
-
- //the 2nd call deactivates the dangling connection pool groups
- long liveConnectionPoolGroups = SqlClientEventSourceProps.ActiveConnectionPoolGroups +
- SqlClientEventSourceProps.InactiveConnectionPoolGroups;
- long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups;
- PruneConnectionPoolGroups();
- Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, liveConnectionPoolGroups);
- // If the pool entry isn't empty, it's because there are active pools that need it.
- Assert.InRange(SqlClientEventSourceProps.ActiveConnectionPoolGroups, 0, acpg);
-
- long icpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups;
- //the 3rd call cleans the dangling connection pool groups
- PruneConnectionPoolGroups();
- Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, icpg);
- }
-
- private static void PruneConnectionPoolGroups()
- {
- FieldInfo connectionFactoryField = GetConnectionFactoryField();
- MethodInfo pruneConnectionPoolGroupsMethod =
- connectionFactoryField.FieldType.GetMethod("PruneConnectionPoolGroups",
- BindingFlags.NonPublic | BindingFlags.Instance);
- Debug.Assert(pruneConnectionPoolGroupsMethod != null);
- pruneConnectionPoolGroupsMethod.Invoke(connectionFactoryField.GetValue(null), new[] { (object)null });
- }
-
- private static FieldInfo GetConnectionFactoryField()
- {
- FieldInfo connectionFactoryField =
- typeof(SqlConnection).GetField("s_connectionFactory", BindingFlags.Static | BindingFlags.NonPublic);
- Debug.Assert(connectionFactoryField != null);
- return connectionFactoryField;
- }
- }
-
- internal static class SqlClientEventSourceProps
- {
- private static readonly object 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;
-
- static SqlClientEventSourceProps()
- {
- Type sqlClientEventSourceType =
- Assembly.GetAssembly(typeof(SqlConnection))!.GetType("Microsoft.Data.SqlClient.SqlClientEventSource");
- Debug.Assert(sqlClientEventSourceType != null);
- FieldInfo metricsField = sqlClientEventSourceType.GetField("Metrics", BindingFlags.Static | BindingFlags.Public);
- Debug.Assert(metricsField != null);
- Type sqlClientMetricsType = metricsField.FieldType;
- s_log = metricsField.GetValue(null);
-
- BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
- _activeHardConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_activeHardConnectionsCounter), _bindingFlags);
- Debug.Assert(_activeHardConnectionsCounter != null);
- _hardConnectsCounter =
- sqlClientMetricsType.GetField(nameof(_hardConnectsCounter), _bindingFlags);
- Debug.Assert(_hardConnectsCounter != null);
- _hardDisconnectsCounter =
- sqlClientMetricsType.GetField(nameof(_hardDisconnectsCounter), _bindingFlags);
- Debug.Assert(_hardDisconnectsCounter != null);
- _activeSoftConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_activeSoftConnectionsCounter), _bindingFlags);
- Debug.Assert(_activeSoftConnectionsCounter != null);
- _softConnectsCounter =
- sqlClientMetricsType.GetField(nameof(_softConnectsCounter), _bindingFlags);
- Debug.Assert(_softConnectsCounter != null);
- _softDisconnectsCounter =
- sqlClientMetricsType.GetField(nameof(_softDisconnectsCounter), _bindingFlags);
- Debug.Assert(_softDisconnectsCounter != null);
- _nonPooledConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_nonPooledConnectionsCounter), _bindingFlags);
- Debug.Assert(_nonPooledConnectionsCounter != null);
- _pooledConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_pooledConnectionsCounter), _bindingFlags);
- Debug.Assert(_pooledConnectionsCounter != null);
- _activeConnectionPoolGroupsCounter =
- sqlClientMetricsType.GetField(nameof(_activeConnectionPoolGroupsCounter), _bindingFlags);
- Debug.Assert(_activeConnectionPoolGroupsCounter != null);
- _inactiveConnectionPoolGroupsCounter =
- sqlClientMetricsType.GetField(nameof(_inactiveConnectionPoolGroupsCounter), _bindingFlags);
- Debug.Assert(_inactiveConnectionPoolGroupsCounter != null);
- _activeConnectionPoolsCounter =
- sqlClientMetricsType.GetField(nameof(_activeConnectionPoolsCounter), _bindingFlags);
- Debug.Assert(_activeConnectionPoolsCounter != null);
- _inactiveConnectionPoolsCounter =
- sqlClientMetricsType.GetField(nameof(_inactiveConnectionPoolsCounter), _bindingFlags);
- Debug.Assert(_inactiveConnectionPoolsCounter != null);
- _activeConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_activeConnectionsCounter), _bindingFlags);
- Debug.Assert(_activeConnectionsCounter != null);
- _freeConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_freeConnectionsCounter), _bindingFlags);
- Debug.Assert(_freeConnectionsCounter != null);
- _stasisConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_stasisConnectionsCounter), _bindingFlags);
- Debug.Assert(_stasisConnectionsCounter != null);
- _reclaimedConnectionsCounter =
- sqlClientMetricsType.GetField(nameof(_reclaimedConnectionsCounter), _bindingFlags);
- Debug.Assert(_reclaimedConnectionsCounter != null);
- }
-
- public static long ActiveHardConnections => (long)_activeHardConnectionsCounter.GetValue(s_log)!;
-
- public static long HardConnects => (long)_hardConnectsCounter.GetValue(s_log)!;
-
- public static long HardDisconnects => (long)_hardDisconnectsCounter.GetValue(s_log)!;
-
- public static long ActiveSoftConnections => (long)_activeSoftConnectionsCounter.GetValue(s_log)!;
-
- public static long SoftConnects => (long)_softConnectsCounter.GetValue(s_log)!;
-
- public static long SoftDisconnects => (long)_softDisconnectsCounter.GetValue(s_log)!;
-
- public static long NonPooledConnections => (long)_nonPooledConnectionsCounter.GetValue(s_log)!;
-
- public static long PooledConnections => (long)_pooledConnectionsCounter.GetValue(s_log)!;
-
- public static long ActiveConnectionPoolGroups => (long)_activeConnectionPoolGroupsCounter.GetValue(s_log)!;
-
- public static long InactiveConnectionPoolGroups => (long)_inactiveConnectionPoolGroupsCounter.GetValue(s_log)!;
-
- public static long ActiveConnectionPools => (long)_activeConnectionPoolsCounter.GetValue(s_log)!;
-
- public static long InactiveConnectionPools => (long)_inactiveConnectionPoolsCounter.GetValue(s_log)!;
-
- public static long ActiveConnections => (long)_activeConnectionsCounter.GetValue(s_log)!;
-
- public static long FreeConnections => (long)_freeConnectionsCounter.GetValue(s_log)!;
-
- public static long StasisConnections => (long)_stasisConnectionsCounter.GetValue(s_log)!;
-
- public static long ReclaimedConnections => (long)_reclaimedConnectionsCounter.GetValue(s_log)!;
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
new file mode 100644
index 0000000000..723be21bd5
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
@@ -0,0 +1,441 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Transactions;
+using Xunit;
+
+namespace Microsoft.Data.SqlClient.ManualTesting.Tests
+{
+ public class MetricsTest
+ {
+#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 NonPooledConnectionsCounters_Functional()
+ {
+ //create a non-pooled connection
+ var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false };
+
+ var ahc = SqlClientEventSourceProps.ActiveHardConnections;
+ var npc = SqlClientEventSourceProps.NonPooledConnections;
+
+ using (var conn = new SqlConnection(stringBuilder.ToString()))
+ {
+ 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
+ if (SupportsActiveConnectionCounters)
+ {
+ Assert.Equal(ahc + 1, SqlClientEventSourceProps.ActiveHardConnections);
+ Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
+ SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
+ }
+ Assert.Equal(npc + 1, SqlClientEventSourceProps.NonPooledConnections);
+
+ conn.Close();
+
+ //when the connection gets closed, the real physical connection is also closed
+ if (SupportsActiveConnectionCounters)
+ {
+ Assert.Equal(ahc, SqlClientEventSourceProps.ActiveHardConnections);
+ Assert.Equal(SqlClientEventSourceProps.ActiveHardConnections,
+ SqlClientEventSourceProps.HardConnects - SqlClientEventSourceProps.HardDisconnects);
+ }
+ Assert.Equal(npc, SqlClientEventSourceProps.NonPooledConnections);
+ }
+ }
+
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
+ public void PooledConnectionsCounters_Functional()
+ {
+ //create a pooled connection
+ var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true };
+
+ var ahc = SqlClientEventSourceProps.ActiveHardConnections;
+ var asc = SqlClientEventSourceProps.ActiveSoftConnections;
+ var pc = SqlClientEventSourceProps.PooledConnections;
+ var npc = SqlClientEventSourceProps.NonPooledConnections;
+ var acp = SqlClientEventSourceProps.ActiveConnectionPools;
+ var ac = SqlClientEventSourceProps.ActiveConnections;
+ var fc = SqlClientEventSourceProps.FreeConnections;
+
+ using (var conn = new SqlConnection(stringBuilder.ToString()))
+ {
+ 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
+ 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);
+ 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
+ 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);
+ if (VerboseActiveConnectionCountersEnabled)
+ {
+ Assert.Equal(ac, SqlClientEventSourceProps.ActiveConnections);
+ Assert.Equal(fc + 1, SqlClientEventSourceProps.FreeConnections);
+ }
+ }
+
+ using (var conn2 = new SqlConnection(stringBuilder.ToString()))
+ {
+ conn2.Open();
+
+ //the next open connection will reuse the underlying physical connection
+ 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);
+ if (VerboseActiveConnectionCountersEnabled)
+ {
+ Assert.Equal(ac + 1, SqlClientEventSourceProps.ActiveConnections);
+ Assert.Equal(fc, SqlClientEventSourceProps.FreeConnections);
+ }
+ }
+ }
+
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
+ public void StasisCounters_Functional()
+ {
+ var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = false, Enlist = false };
+
+ using (var conn = new SqlConnection(stringBuilder.ToString()))
+ using (new TransactionScope())
+ {
+ conn.Open();
+ conn.EnlistTransaction(System.Transactions.Transaction.Current);
+ conn.Close();
+
+ //when the connection gets closed, but the ambient transaction is still in prigress
+ //the physical connection gets in stasis, until the transaction ends
+ Assert.Equal(1, SqlClientEventSourceProps.StasisConnections);
+ }
+
+ //when the transaction finally ends, the physical connection is returned from stasis
+ Assert.Equal(0, SqlClientEventSourceProps.StasisConnections);
+ }
+
+ [ActiveIssue("https://github.com/dotnet/SqlClient/issues/3031")]
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
+ public void ReclaimedConnectionsCounter_Functional()
+ {
+ // clean pools and pool groups
+ ClearConnectionPools();
+ var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true, MaxPoolSize = 1 };
+ stringBuilder.ConnectTimeout = Math.Max(stringBuilder.ConnectTimeout, 30);
+
+ long rc = SqlClientEventSourceProps.ReclaimedConnections;
+
+ int gcNumber = GC.GetGeneration(CreateEmancipatedConnection(stringBuilder.ToString()));
+ // Specifying the generation number makes it to run faster by avoiding a full GC process
+ GC.Collect(gcNumber);
+ GC.WaitForPendingFinalizers();
+ System.Threading.Thread.Sleep(200); // give the pooler some time to reclaim the connection and avoid the conflict.
+
+ using (SqlConnection conn = new SqlConnection(stringBuilder.ToString()))
+ {
+ conn.Open();
+
+ // when calling open, the connection could be reclaimed.
+ if (GC.GetGeneration(conn) == gcNumber)
+ {
+ Assert.Equal(rc + 1, SqlClientEventSourceProps.ReclaimedConnections);
+ }
+ else
+ {
+ Assert.Equal(rc, SqlClientEventSourceProps.ReclaimedConnections);
+ }
+ }
+ }
+
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
+ public void ConnectionPoolGroupsCounter_Functional()
+ {
+ SqlConnection.ClearAllPools();
+
+ var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true };
+
+ long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups;
+ long iacpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups;
+
+ using (SqlConnection conn = new SqlConnection(stringBuilder.ToString()))
+ {
+ conn.Open();
+
+ // when calling open, we have 1 more active connection pool group
+ Assert.Equal(acpg + 1, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
+
+ conn.Close();
+ }
+
+ SqlConnection.ClearAllPools();
+
+ // poolGroup state is changed from Active to Idle
+ PruneConnectionPoolGroups();
+
+ // poolGroup state is changed from Idle to Disabled
+ PruneConnectionPoolGroups();
+ Assert.Equal(acpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
+ Assert.Equal(iacpg + 1, SqlClientEventSourceProps.InactiveConnectionPoolGroups);
+
+ // Remove poolGroup from poolGroupsToRelease list
+ PruneConnectionPoolGroups();
+ Assert.Equal(iacpg, SqlClientEventSourceProps.ActiveConnectionPoolGroups);
+ }
+
+ private static InternalConnectionWrapper CreateEmancipatedConnection(string connectionString)
+ {
+ SqlConnection connection = new SqlConnection(connectionString);
+ connection.Open();
+ return new InternalConnectionWrapper(connection);
+ }
+
+ private void ClearConnectionPools()
+ {
+ //ClearAllPoos kills all the existing pooled connection thus deactivating all the active pools
+ long liveConnectionPools = SqlClientEventSourceProps.ActiveConnectionPools +
+ SqlClientEventSourceProps.InactiveConnectionPools;
+ SqlConnection.ClearAllPools();
+ Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, liveConnectionPools);
+ Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPools);
+
+ long icp = SqlClientEventSourceProps.InactiveConnectionPools;
+
+ // The 1st PruneConnectionPoolGroups call cleans the dangling inactive connection pools.
+ PruneConnectionPoolGroups();
+ // If the pool isn't empty, it's because there are active connections or distributed transactions that need it.
+ Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, icp);
+
+ //the 2nd call deactivates the dangling connection pool groups
+ long liveConnectionPoolGroups = SqlClientEventSourceProps.ActiveConnectionPoolGroups +
+ SqlClientEventSourceProps.InactiveConnectionPoolGroups;
+ long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups;
+ PruneConnectionPoolGroups();
+ Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, liveConnectionPoolGroups);
+ // If the pool entry isn't empty, it's because there are active pools that need it.
+ Assert.InRange(SqlClientEventSourceProps.ActiveConnectionPoolGroups, 0, acpg);
+
+ long icpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups;
+ //the 3rd call cleans the dangling connection pool groups
+ PruneConnectionPoolGroups();
+ Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, icpg);
+ }
+
+ private static void PruneConnectionPoolGroups()
+ {
+ FieldInfo connectionFactoryField = GetConnectionFactoryField();
+ MethodInfo pruneConnectionPoolGroupsMethod =
+ connectionFactoryField.FieldType.GetMethod("PruneConnectionPoolGroups",
+ BindingFlags.NonPublic | BindingFlags.Instance);
+ Debug.Assert(pruneConnectionPoolGroupsMethod != null);
+ pruneConnectionPoolGroupsMethod.Invoke(connectionFactoryField.GetValue(null), new[] { (object)null });
+ }
+
+ private static FieldInfo GetConnectionFactoryField()
+ {
+#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 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()
+ {
+ s_log = SqlClientEventSource.Metrics;
+
+#if NET
+ s_getActiveHardConnections = GenerateFieldGetter("_activeHardConnectionsCounter");
+ s_getHardConnects = GenerateFieldGetter("_hardConnectsCounter");
+ s_getHardDisconnects = GenerateFieldGetter("_hardDisconnectsCounter");
+ s_getActiveSoftConnections = GenerateFieldGetter("_activeSoftConnectionsCounter");
+ s_getSoftConnects = GenerateFieldGetter("_softConnectsCounter");
+ s_getSoftDisconnects = GenerateFieldGetter("_softDisconnectsCounter");
+ s_getNonPooledConnections = GenerateFieldGetter("_nonPooledConnectionsCounter");
+ s_getPooledConnections = GenerateFieldGetter("_pooledConnectionsCounter");
+ s_getActiveConnectionPoolGroups = GenerateFieldGetter("_activeConnectionPoolGroupsCounter");
+ s_getInactiveConnectionPoolGroups = GenerateFieldGetter("_inactiveConnectionPoolGroupsCounter");
+ s_getActiveConnectionPools = GenerateFieldGetter("_activeConnectionPoolsCounter");
+ s_getInactiveConnectionPools = GenerateFieldGetter("_inactiveConnectionPoolsCounter");
+ s_getActiveConnections = GenerateFieldGetter("_activeConnectionsCounter");
+ s_getFreeConnections = GenerateFieldGetter("_freeConnectionsCounter");
+ s_getStasisConnections = GenerateFieldGetter("_stasisConnectionsCounter");
+ s_getReclaimedConnections = GenerateFieldGetter("_reclaimedConnectionsCounter");
+
+ 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
+ Func notApplicableFunction = static () => -1;
+
+ // .NET Framework doesn't have performance counters for the number of hard and soft connections.
+ s_getActiveHardConnections = notApplicableFunction;
+ s_getHardConnects = GeneratePerformanceCounterGetter("_hardConnectsPerSecond");
+ s_getHardDisconnects = GeneratePerformanceCounterGetter("_hardDisconnectsPerSecond");
+ s_getActiveSoftConnections = notApplicableFunction;
+ s_getSoftConnects = GeneratePerformanceCounterGetter("_softConnectsPerSecond");
+ s_getSoftDisconnects = GeneratePerformanceCounterGetter("_softDisconnectsPerSecond");
+
+ s_getNonPooledConnections = GeneratePerformanceCounterGetter("_numberOfNonPooledConnections");
+ s_getPooledConnections = GeneratePerformanceCounterGetter("_numberOfPooledConnections");
+ s_getActiveConnectionPoolGroups = GeneratePerformanceCounterGetter("_numberOfActiveConnectionPoolGroups");
+ s_getInactiveConnectionPoolGroups = GeneratePerformanceCounterGetter("_numberOfInactiveConnectionPoolGroups");
+ s_getActiveConnectionPools = GeneratePerformanceCounterGetter("_numberOfActiveConnectionPools");
+ s_getInactiveConnectionPools = GeneratePerformanceCounterGetter("_numberOfInactiveConnectionPools");
+ s_getActiveConnections = GeneratePerformanceCounterGetter("_numberOfActiveConnections");
+ s_getFreeConnections = GeneratePerformanceCounterGetter("_numberOfFreeConnections");
+ s_getStasisConnections = GeneratePerformanceCounterGetter("_numberOfStasisConnections");
+ s_getReclaimedConnections = GeneratePerformanceCounterGetter("_numberOfReclaimedConnections");
+
+ static Func GeneratePerformanceCounterGetter(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 => s_getActiveHardConnections();
+
+ public static long HardConnects => s_getHardConnects();
+
+ public static long HardDisconnects => s_getHardDisconnects();
+
+ public static long ActiveSoftConnections => s_getActiveSoftConnections();
+
+ public static long SoftConnects => s_getSoftConnects();
+
+ public static long SoftDisconnects => s_getSoftDisconnects();
+
+ public static long NonPooledConnections => s_getNonPooledConnections();
+
+ public static long PooledConnections => s_getPooledConnections();
+
+ public static long ActiveConnectionPoolGroups => s_getActiveConnectionPoolGroups();
+
+ public static long InactiveConnectionPoolGroups => s_getInactiveConnectionPoolGroups();
+
+ public static long ActiveConnectionPools => s_getActiveConnectionPools();
+
+ public static long InactiveConnectionPools => s_getInactiveConnectionPools();
+
+ public static long ActiveConnections => s_getActiveConnections();
+
+ public static long FreeConnections => s_getFreeConnections();
+
+ public static long StasisConnections => s_getStasisConnections();
+
+ public static long ReclaimedConnections => s_getReclaimedConnections();
+ }
+}
From f6d3a6b146d586b787ed98e7e3e2b30e048bb81b Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Mon, 7 Apr 2025 22:19:32 +0100
Subject: [PATCH 4/5] Merge into one file, reduce conditional compilation
branches
---
.../src/Microsoft.Data.SqlClient.csproj | 3 -
.../netfx/src/Microsoft.Data.SqlClient.csproj | 3 -
.../SqlClient/Diagnostics/SqlClientMetrics.cs | 516 +++++++++++++-----
.../Diagnostics/SqlClientMetrics.netcore.cs | 178 ------
.../Diagnostics/SqlClientMetrics.netfx.cs | 193 -------
.../Data/SqlClient/SqlClientEventSource.cs | 4 +-
.../ManualTests/TracingTests/MetricsTest.cs | 64 +--
7 files changed, 398 insertions(+), 563 deletions(-)
delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
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 e90942685b..c1a80217fd 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -215,9 +215,6 @@
Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs
-
- Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.netcore.cs
-
Microsoft\Data\SqlClient\Diagnostics\SqlClientTransactionCommitAfter.netcore.cs
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index d6034754e9..13d68edbbb 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -310,9 +310,6 @@
Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.cs
-
- Microsoft\Data\SqlClient\Diagnostics\SqlClientMetrics.netfx.cs
-
Microsoft\Data\ProviderBase\DbMetaDataFactory.cs
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
index 504d7507a2..f62c65b122 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
@@ -2,43 +2,123 @@
// 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
{
- // This encapsulates three types of metrics:
- // * Default: These metrics are always enabled.
- // * Verbose: These metrics aren't enabled by default, but can be enabled on application startup if a trace switch is set to verbose.
- // * Trace: These metrics are enabled with the SqlClientEventSource.
- // Verbose metrics are only present for backwards compatibility.
+#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;
- EnableDefaultMetrics();
- }
- private void EnableDefaultMetrics()
- {
#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
}
- public void EnableTraceMetrics()
- {
#if NET
- EnableEventCounters();
+ 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
@@ -46,12 +126,9 @@ public void EnableTraceMetrics()
internal void HardConnectRequest()
{
#if NET
- Interlocked.Increment(ref _activeHardConnectionsCounter);
- Interlocked.Increment(ref _hardConnectsCounter);
-#endif
-#if NETFRAMEWORK
- _hardConnectsPerSecond?.Increment();
+ IncrementPlatformSpecificCounter(ref _activeHardConnections);
#endif
+ IncrementPlatformSpecificCounter(ref _hardConnectsRate);
}
///
@@ -60,12 +137,9 @@ internal void HardConnectRequest()
internal void HardDisconnectRequest()
{
#if NET
- Interlocked.Decrement(ref _activeHardConnectionsCounter);
- Interlocked.Increment(ref _hardDisconnectsCounter);
-#endif
-#if NETFRAMEWORK
- _hardDisconnectsPerSecond?.Increment();
+ DecrementPlatformSpecificCounter(ref _activeHardConnections);
#endif
+ IncrementPlatformSpecificCounter(ref _hardDisconnectsRate);
}
///
@@ -74,12 +148,9 @@ internal void HardDisconnectRequest()
internal void SoftConnectRequest()
{
#if NET
- Interlocked.Increment(ref _activeSoftConnectionsCounter);
- Interlocked.Increment(ref _softConnectsCounter);
-#endif
-#if NETFRAMEWORK
- _softConnectsPerSecond?.Increment();
+ IncrementPlatformSpecificCounter(ref _activeSoftConnections);
#endif
+ IncrementPlatformSpecificCounter(ref _softConnectsRate);
}
///
@@ -88,12 +159,9 @@ internal void SoftConnectRequest()
internal void SoftDisconnectRequest()
{
#if NET
- Interlocked.Decrement(ref _activeSoftConnectionsCounter);
- Interlocked.Increment(ref _softDisconnectsCounter);
-#endif
-#if NETFRAMEWORK
- _softDisconnectsPerSecond?.Increment();
+ DecrementPlatformSpecificCounter(ref _activeSoftConnections);
#endif
+ IncrementPlatformSpecificCounter(ref _softDisconnectsRate);
}
///
@@ -101,12 +169,7 @@ internal void SoftDisconnectRequest()
///
internal void EnterNonPooledConnection()
{
-#if NET
- Interlocked.Increment(ref _nonPooledConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfNonPooledConnections?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _nonPooledConnections);
}
///
@@ -114,12 +177,7 @@ internal void EnterNonPooledConnection()
///
internal void ExitNonPooledConnection()
{
-#if NET
- Interlocked.Decrement(ref _nonPooledConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfNonPooledConnections?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _nonPooledConnections);
}
///
@@ -127,12 +185,7 @@ internal void ExitNonPooledConnection()
///
internal void EnterPooledConnection()
{
-#if NET
- Interlocked.Increment(ref _pooledConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfPooledConnections?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _pooledConnections);
}
///
@@ -140,12 +193,7 @@ internal void EnterPooledConnection()
///
internal void ExitPooledConnection()
{
-#if NET
- Interlocked.Decrement(ref _pooledConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfPooledConnections?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _pooledConnections);
}
///
@@ -153,12 +201,7 @@ internal void ExitPooledConnection()
///
internal void EnterActiveConnectionPoolGroup()
{
-#if NET
- Interlocked.Increment(ref _activeConnectionPoolGroupsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnectionPoolGroups?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _activeConnectionPoolGroups);
}
///
@@ -166,12 +209,7 @@ internal void EnterActiveConnectionPoolGroup()
///
internal void ExitActiveConnectionPoolGroup()
{
-#if NET
- Interlocked.Decrement(ref _activeConnectionPoolGroupsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnectionPoolGroups?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _activeConnectionPoolGroups);
}
///
@@ -179,12 +217,7 @@ internal void ExitActiveConnectionPoolGroup()
///
internal void EnterInactiveConnectionPoolGroup()
{
-#if NET
- Interlocked.Increment(ref _inactiveConnectionPoolGroupsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfInactiveConnectionPoolGroups?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _inactiveConnectionPoolGroups);
}
///
@@ -192,12 +225,7 @@ internal void EnterInactiveConnectionPoolGroup()
///
internal void ExitInactiveConnectionPoolGroup()
{
-#if NET
- Interlocked.Decrement(ref _inactiveConnectionPoolGroupsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfInactiveConnectionPoolGroups?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _inactiveConnectionPoolGroups);
}
///
@@ -205,12 +233,7 @@ internal void ExitInactiveConnectionPoolGroup()
///
internal void EnterActiveConnectionPool()
{
-#if NET
- Interlocked.Increment(ref _activeConnectionPoolsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnectionPools?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _activeConnectionPools);
}
///
@@ -218,12 +241,7 @@ internal void EnterActiveConnectionPool()
///
internal void ExitActiveConnectionPool()
{
-#if NET
- Interlocked.Decrement(ref _activeConnectionPoolsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnectionPools?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _activeConnectionPools);
}
///
@@ -231,12 +249,7 @@ internal void ExitActiveConnectionPool()
///
internal void EnterInactiveConnectionPool()
{
-#if NET
- Interlocked.Increment(ref _inactiveConnectionPoolsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfInactiveConnectionPools?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _inactiveConnectionPools);
}
///
@@ -244,12 +257,7 @@ internal void EnterInactiveConnectionPool()
///
internal void ExitInactiveConnectionPool()
{
-#if NET
- Interlocked.Decrement(ref _inactiveConnectionPoolsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfInactiveConnectionPools?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _inactiveConnectionPools);
}
///
@@ -257,12 +265,7 @@ internal void ExitInactiveConnectionPool()
///
internal void EnterActiveConnection()
{
-#if NET
- Interlocked.Increment(ref _activeConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnections?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _activeConnections);
}
///
@@ -270,12 +273,7 @@ internal void EnterActiveConnection()
///
internal void ExitActiveConnection()
{
-#if NET
- Interlocked.Decrement(ref _activeConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfActiveConnections?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _activeConnections);
}
///
@@ -283,12 +281,7 @@ internal void ExitActiveConnection()
///
internal void EnterFreeConnection()
{
-#if NET
- Interlocked.Increment(ref _freeConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfFreeConnections?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _freeConnections);
}
///
@@ -296,12 +289,7 @@ internal void EnterFreeConnection()
///
internal void ExitFreeConnection()
{
-#if NET
- Interlocked.Decrement(ref _freeConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfFreeConnections?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _freeConnections);
}
///
@@ -309,12 +297,7 @@ internal void ExitFreeConnection()
///
internal void EnterStasisConnection()
{
-#if NET
- Interlocked.Increment(ref _stasisConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfStasisConnections?.Increment();
-#endif
+ IncrementPlatformSpecificCounter(ref _stasisConnections);
}
///
@@ -322,12 +305,7 @@ internal void EnterStasisConnection()
///
internal void ExitStasisConnection()
{
-#if NET
- Interlocked.Decrement(ref _stasisConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfStasisConnections?.Decrement();
-#endif
+ DecrementPlatformSpecificCounter(ref _stasisConnections);
}
///
@@ -335,12 +313,258 @@ internal void ExitStasisConnection()
///
internal void ReclaimedConnectionRequest()
{
+ IncrementPlatformSpecificCounter(ref _reclaimedConnections);
+ }
+
#if NET
- Interlocked.Increment(ref _reclaimedConnectionsCounter);
-#endif
-#if NETFRAMEWORK
- _numberOfReclaimedConnections?.Increment();
-#endif
+ 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/Diagnostics/SqlClientMetrics.netcore.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
deleted file mode 100644
index 78ad032b38..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netcore.cs
+++ /dev/null
@@ -1,178 +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;
-
-#nullable enable
-
-namespace Microsoft.Data.SqlClient.Diagnostics
-{
- internal sealed partial class SqlClientMetrics
- {
- 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;
-
- private void EnableEventCounters()
- {
- _activeHardConnections = _activeHardConnections ??
- new PollingCounter("active-hard-connections", _eventSource, () => _activeHardConnectionsCounter)
- {
- DisplayName = "Actual active connections currently made to servers",
- DisplayUnits = "count"
- };
-
- _hardConnectsPerSecond = _hardConnectsPerSecond ??
- new IncrementingPollingCounter("hard-connects", _eventSource, () => _hardConnectsCounter)
- {
- DisplayName = "Actual connection rate to servers",
- DisplayUnits = "count / sec",
- DisplayRateTimeScale = TimeSpan.FromSeconds(1)
- };
-
- _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ??
- new IncrementingPollingCounter("hard-disconnects", _eventSource, () => _hardDisconnectsCounter)
- {
- DisplayName = "Actual disconnection rate from servers",
- DisplayUnits = "count / sec",
- DisplayRateTimeScale = TimeSpan.FromSeconds(1)
- };
-
- _activeSoftConnections = _activeSoftConnections ??
- new PollingCounter("active-soft-connects", _eventSource, () => _activeSoftConnectionsCounter)
- {
- DisplayName = "Active connections retrieved from the connection pool",
- DisplayUnits = "count"
- };
-
- _softConnects = _softConnects ??
- new IncrementingPollingCounter("soft-connects", _eventSource, () => _softConnectsCounter)
- {
- DisplayName = "Rate of connections retrieved from the connection pool",
- DisplayUnits = "count / sec",
- DisplayRateTimeScale = TimeSpan.FromSeconds(1)
- };
-
- _softDisconnects = _softDisconnects ??
- new IncrementingPollingCounter("soft-disconnects", _eventSource, () => _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", _eventSource, () => _nonPooledConnectionsCounter)
- {
- DisplayName = "Number of connections not using connection pooling",
- DisplayUnits = "count"
- };
-
- _numberOfPooledConnections = _numberOfPooledConnections ??
- new PollingCounter("number-of-pooled-connections", _eventSource, () => _pooledConnectionsCounter)
- {
- DisplayName = "Number of connections managed by the connection pool",
- DisplayUnits = "count"
- };
-
- _numberOfActiveConnectionPoolGroups = _numberOfActiveConnectionPoolGroups ??
- new PollingCounter("number-of-active-connection-pool-groups", _eventSource, () => _activeConnectionPoolGroupsCounter)
- {
- DisplayName = "Number of active unique connection strings",
- DisplayUnits = "count"
- };
-
- _numberOfInactiveConnectionPoolGroups = _numberOfInactiveConnectionPoolGroups ??
- new PollingCounter("number-of-inactive-connection-pool-groups", _eventSource, () => _inactiveConnectionPoolGroupsCounter)
- {
- DisplayName = "Number of unique connection strings waiting for pruning",
- DisplayUnits = "count"
- };
-
- _numberOfActiveConnectionPools = _numberOfActiveConnectionPools ??
- new PollingCounter("number-of-active-connection-pools", _eventSource, () => _activeConnectionPoolsCounter)
- {
- DisplayName = "Number of active connection pools",
- DisplayUnits = "count"
- };
-
- _numberOfInactiveConnectionPools = _numberOfInactiveConnectionPools ??
- new PollingCounter("number-of-inactive-connection-pools", _eventSource, () => _inactiveConnectionPoolsCounter)
- {
- DisplayName = "Number of inactive connection pools",
- DisplayUnits = "count"
- };
-
- _numberOfActiveConnections = _numberOfActiveConnections ??
- new PollingCounter("number-of-active-connections", _eventSource, () => _activeConnectionsCounter)
- {
- DisplayName = "Number of active connections",
- DisplayUnits = "count"
- };
-
- _numberOfFreeConnections = _numberOfFreeConnections ??
- new PollingCounter("number-of-free-connections", _eventSource, () => _freeConnectionsCounter)
- {
- DisplayName = "Number of ready connections in the connection pool",
- DisplayUnits = "count"
- };
-
- _numberOfStasisConnections = _numberOfStasisConnections ??
- new PollingCounter("number-of-stasis-connections", _eventSource, () => _stasisConnectionsCounter)
- {
- DisplayName = "Number of connections currently waiting to be ready",
- DisplayUnits = "count"
- };
-
- _numberOfReclaimedConnections = _numberOfReclaimedConnections ??
- new IncrementingPollingCounter("number-of-reclaimed-connections", _eventSource, () => _reclaimedConnectionsCounter)
- {
- DisplayName = "Number of reclaimed connections from GC",
- DisplayUnits = "count",
- DisplayRateTimeScale = TimeSpan.FromSeconds(1)
- };
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
deleted file mode 100644
index cd2e42731b..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.netfx.cs
+++ /dev/null
@@ -1,193 +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;
-using System.Reflection;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.Versioning;
-using System.Security.Permissions;
-using Interop.Windows.Kernel32;
-using Microsoft.Data.Common;
-
-#nullable enable
-
-namespace Microsoft.Data.SqlClient.Diagnostics
-{
- [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
- internal sealed partial class SqlClientMetrics
- {
- private const string PerformanceCounterCategoryName = ".NET Data Provider for SqlServer";
- private const string PerformanceCounterCategoryHelp = "Counters for Microsoft.Data.SqlClient";
- private const int CounterInstanceNameMaxLength = 127;
-
- private PerformanceCounter? _hardConnectsPerSecond;
- private PerformanceCounter? _hardDisconnectsPerSecond;
- private PerformanceCounter? _softConnectsPerSecond;
- private PerformanceCounter? _softDisconnectsPerSecond;
- private PerformanceCounter? _numberOfNonPooledConnections;
- private PerformanceCounter? _numberOfPooledConnections;
- private PerformanceCounter? _numberOfActiveConnectionPoolGroups;
- private PerformanceCounter? _numberOfInactiveConnectionPoolGroups;
- private PerformanceCounter? _numberOfActiveConnectionPools;
- private PerformanceCounter? _numberOfInactiveConnectionPools;
- private PerformanceCounter? _numberOfActiveConnections;
- private PerformanceCounter? _numberOfFreeConnections;
- private PerformanceCounter? _numberOfStasisConnections;
- private PerformanceCounter? _numberOfReclaimedConnections;
-
- private static PerformanceCounter? CreatePerformanceCounter(string categoryName, string instanceName, string counterName, PerformanceCounterType counterType)
- {
- PerformanceCounter? instance = null;
-
- try
- {
- 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
- }
- catch (InvalidOperationException e)
- {
- ADP.TraceExceptionWithoutRethrow(e);
- }
-
- return instance;
- }
-
- [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;
- }
-
- // 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;
- }
-
- [PerformanceCounterPermission(SecurityAction.Assert, PermissionAccess = PerformanceCounterPermissionAccess.Write,
- MachineName = ".", CategoryName = PerformanceCounterCategoryName)]
- private void EnablePerformanceCounters()
- {
- AppDomain.CurrentDomain.DomainUnload += UnloadEventHandler;
- AppDomain.CurrentDomain.ProcessExit += ExitEventHandler;
- AppDomain.CurrentDomain.UnhandledException += ExceptionEventHandler;
-
- string instanceName = GetInstanceName();
-
- // level 0-3: hard connects/disconnects, plus basic pool/pool entry statistics
-
- _hardConnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "HardConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
- _hardDisconnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "HardDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
- _numberOfNonPooledConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfNonPooledConnections", PerformanceCounterType.NumberOfItems32);
- _numberOfPooledConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfPooledConnections", PerformanceCounterType.NumberOfItems32);
- _numberOfActiveConnectionPoolGroups = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32);
- _numberOfInactiveConnectionPoolGroups = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfInactiveConnectionPoolGroups", PerformanceCounterType.NumberOfItems32);
- _numberOfActiveConnectionPools = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnectionPools", PerformanceCounterType.NumberOfItems32);
- _numberOfInactiveConnectionPools = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfInactiveConnectionPools", PerformanceCounterType.NumberOfItems32);
- _numberOfStasisConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfStasisConnections", PerformanceCounterType.NumberOfItems32);
- _numberOfReclaimedConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfReclaimedConnections", PerformanceCounterType.NumberOfItems32);
-
- TraceSwitch perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters");
- if (TraceLevel.Verbose == perfCtrSwitch.Level)
- {
- _softConnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "SoftConnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
- _softDisconnectsPerSecond = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "SoftDisconnectsPerSecond", PerformanceCounterType.RateOfCountsPerSecond32);
- _numberOfActiveConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfActiveConnections", PerformanceCounterType.NumberOfItems32);
- _numberOfFreeConnections = CreatePerformanceCounter(PerformanceCounterCategoryName, instanceName, "NumberOfFreeConnections", PerformanceCounterType.NumberOfItems32);
- }
- }
-
- [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
- _hardConnectsPerSecond?.RemoveInstance();
- _hardDisconnectsPerSecond?.RemoveInstance();
- _softConnectsPerSecond?.RemoveInstance();
- _softDisconnectsPerSecond?.RemoveInstance();
- _numberOfNonPooledConnections?.RemoveInstance();
- _numberOfPooledConnections?.RemoveInstance();
- _numberOfActiveConnectionPoolGroups?.RemoveInstance();
- _numberOfInactiveConnectionPoolGroups?.RemoveInstance();
- _numberOfActiveConnectionPools?.RemoveInstance();
- _numberOfInactiveConnectionPools?.RemoveInstance();
- _numberOfActiveConnections?.RemoveInstance();
- _numberOfFreeConnections?.RemoveInstance();
- _numberOfStasisConnections?.RemoveInstance();
- _numberOfReclaimedConnections?.RemoveInstance();
- }
-
- [PrePrepareMethod]
- private void ExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
- {
- if (e != null && e.IsTerminating)
- {
- RemovePerformanceCounters();
- }
- }
-
- [PrePrepareMethod]
- private void ExitEventHandler(object sender, EventArgs e)
- {
- RemovePerformanceCounters();
- }
-
- [PrePrepareMethod]
- private void UnloadEventHandler(object sender, EventArgs e)
- {
- RemovePerformanceCounters();
- }
- }
-}
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 6a8d503e53..90a69b5670 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs
@@ -26,15 +26,17 @@ 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.EnableTraceMetrics();
+ Metrics.EnableEventCounters();
}
}
+#endif
#region Event IDs
// Initialized static Scope IDs
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
index 723be21bd5..ded0dee6cc 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
@@ -348,24 +348,31 @@ static SqlClientEventSourceProps()
{
s_log = SqlClientEventSource.Metrics;
-#if NET
- s_getActiveHardConnections = GenerateFieldGetter("_activeHardConnectionsCounter");
- s_getHardConnects = GenerateFieldGetter("_hardConnectsCounter");
- s_getHardDisconnects = GenerateFieldGetter("_hardDisconnectsCounter");
- s_getActiveSoftConnections = GenerateFieldGetter("_activeSoftConnectionsCounter");
- s_getSoftConnects = GenerateFieldGetter("_softConnectsCounter");
- s_getSoftDisconnects = GenerateFieldGetter("_softDisconnectsCounter");
- s_getNonPooledConnections = GenerateFieldGetter("_nonPooledConnectionsCounter");
- s_getPooledConnections = GenerateFieldGetter("_pooledConnectionsCounter");
- s_getActiveConnectionPoolGroups = GenerateFieldGetter("_activeConnectionPoolGroupsCounter");
- s_getInactiveConnectionPoolGroups = GenerateFieldGetter("_inactiveConnectionPoolGroupsCounter");
- s_getActiveConnectionPools = GenerateFieldGetter("_activeConnectionPoolsCounter");
- s_getInactiveConnectionPools = GenerateFieldGetter("_inactiveConnectionPoolsCounter");
- s_getActiveConnections = GenerateFieldGetter("_activeConnectionsCounter");
- s_getFreeConnections = GenerateFieldGetter("_freeConnectionsCounter");
- s_getStasisConnections = GenerateFieldGetter("_stasisConnectionsCounter");
- s_getReclaimedConnections = GenerateFieldGetter("_reclaimedConnectionsCounter");
+#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);
@@ -374,28 +381,7 @@ static Func GenerateFieldGetter(string fieldName)
return () => (long)counterField.GetValue(s_log)!;
}
#else
- Func notApplicableFunction = static () => -1;
-
- // .NET Framework doesn't have performance counters for the number of hard and soft connections.
- s_getActiveHardConnections = notApplicableFunction;
- s_getHardConnects = GeneratePerformanceCounterGetter("_hardConnectsPerSecond");
- s_getHardDisconnects = GeneratePerformanceCounterGetter("_hardDisconnectsPerSecond");
- s_getActiveSoftConnections = notApplicableFunction;
- s_getSoftConnects = GeneratePerformanceCounterGetter("_softConnectsPerSecond");
- s_getSoftDisconnects = GeneratePerformanceCounterGetter("_softDisconnectsPerSecond");
-
- s_getNonPooledConnections = GeneratePerformanceCounterGetter("_numberOfNonPooledConnections");
- s_getPooledConnections = GeneratePerformanceCounterGetter("_numberOfPooledConnections");
- s_getActiveConnectionPoolGroups = GeneratePerformanceCounterGetter("_numberOfActiveConnectionPoolGroups");
- s_getInactiveConnectionPoolGroups = GeneratePerformanceCounterGetter("_numberOfInactiveConnectionPoolGroups");
- s_getActiveConnectionPools = GeneratePerformanceCounterGetter("_numberOfActiveConnectionPools");
- s_getInactiveConnectionPools = GeneratePerformanceCounterGetter("_numberOfInactiveConnectionPools");
- s_getActiveConnections = GeneratePerformanceCounterGetter("_numberOfActiveConnections");
- s_getFreeConnections = GeneratePerformanceCounterGetter("_numberOfFreeConnections");
- s_getStasisConnections = GeneratePerformanceCounterGetter("_numberOfStasisConnections");
- s_getReclaimedConnections = GeneratePerformanceCounterGetter("_numberOfReclaimedConnections");
-
- static Func GeneratePerformanceCounterGetter(string fieldName)
+ static Func GenerateFieldGetter(string fieldName)
{
FieldInfo counterField = s_log.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(counterField != null);
From 845903a288fc918b269a8f1e2612d55331c85a55 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Fri, 11 Apr 2025 20:07:18 +0100
Subject: [PATCH 5/5] Account for the removal of InternalsVisibleTo
---
.../tests/ManualTests/TracingTests/MetricsTest.cs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
index ded0dee6cc..4e90bbf6c7 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/MetricsTest.cs
@@ -346,7 +346,13 @@ internal static class SqlClientEventSourceProps
static SqlClientEventSourceProps()
{
- s_log = SqlClientEventSource.Metrics;
+ Type sqlClientEventSourceType =
+ Assembly.GetAssembly(typeof(SqlConnection))!.GetType("Microsoft.Data.SqlClient.SqlClientEventSource");
+ Debug.Assert(sqlClientEventSourceType != 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;