From 6b6f7f9126740f303bcb6af8066aa55a5e0a999e Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Thu, 5 Mar 2020 12:59:37 -0800 Subject: [PATCH 01/13] EventSource NetCore --- src/Microsoft.Data.SqlClient.sln | 16 +- .../src/Microsoft/Data/Common/AdapterUtil.cs | 12 +- .../Data/Common/DbConnectionOptions.Common.cs | 25 +- .../Data/ProviderBase/DbConnectionFactory.cs | 67 +- .../Data/ProviderBase/DbConnectionInternal.cs | 8 + .../ProviderBase/DbConnectionPoolGroup.cs | 15 + .../src/Microsoft.Data.SqlClient.csproj | 2 + .../src/Microsoft/Data/DataException.cs | 21 + .../Data/ProviderBase/DbConnectionFactory.cs | 3 + .../Data/ProviderBase/DbConnectionInternal.cs | 37 +- .../Data/ProviderBase/DbConnectionPool.cs | 235 +++- ...rectoryAuthenticationTimeoutRetryHelper.cs | 22 +- ...veDirectoryNativeAuthenticationProvider.cs | 11 + ...estationBasedEnclaveProvider.NetCoreApp.cs | 1 - .../EnclaveProviderBase.NetCoreApp.cs | 1 - .../Data/SqlClient/LocalDBAPI.Common.cs | 2 + .../Data/SqlClient/LocalDBAPI.Windows.cs | 13 +- .../Data/SqlClient/Server/SmiMetaData.cs | 101 ++ .../SqlClient/Server/SmiMetaDataProperty.cs | 88 ++ .../SqlAuthenticationProviderManager.cs | 19 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 20 +- .../Data/SqlClient/SqlClientEventSource.cs | 614 +++++++++ .../Data/SqlClient/SqlClientLogger.cs | 39 + .../Microsoft/Data/SqlClient/SqlCommand.cs | 220 ++- .../Microsoft/Data/SqlClient/SqlCommandSet.cs | 39 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 469 ++++--- .../Data/SqlClient/SqlConnectionFactory.cs | 38 +- .../Data/SqlClient/SqlConnectionHelper.cs | 38 +- .../SqlConnectionPoolGroupProviderInfo.cs | 2 + .../Data/SqlClient/SqlDataAdapter.cs | 9 + .../Microsoft/Data/SqlClient/SqlDataReader.cs | 262 ++-- .../Data/SqlClient/SqlDelegatedTransaction.cs | 24 +- .../Microsoft/Data/SqlClient/SqlDependency.cs | 1013 ++++++++------ .../Data/SqlClient/SqlDependencyListener.cs | 1207 ++++++++++------- .../SqlClient/SqlDependencyUtils.AppDomain.cs | 20 +- .../Data/SqlClient/SqlDependencyUtils.cs | 594 ++++---- .../src/Microsoft/Data/SqlClient/SqlError.cs | 4 + .../Data/SqlClient/SqlInternalConnection.cs | 10 +- .../SqlClient/SqlInternalConnectionTds.cs | 91 +- .../Data/SqlClient/SqlInternalTransaction.cs | 40 +- .../Data/SqlClient/SqlTransaction.cs | 35 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 195 ++- .../Data/SqlClient/TdsParserHelperClasses.cs | 4 + .../Data/SqlClient/TdsParserSessionPool.cs | 68 +- .../Data/SqlClient/TdsParserStateObject.cs | 47 +- .../SqlClient/TdsParserStateObjectManaged.cs | 1 - .../Data/ProviderBase/DbConnectionFactory.cs | 4 +- .../Data/ProviderBase/DbConnectionPool.cs | 4 +- .../ProviderBase/DbConnectionPoolGroup.cs | 2 +- ...rectoryAuthenticationTimeoutRetryHelper.cs | 8 +- .../AzureAttestationBasedEnclaveProvider.cs | 2 - .../Data/SqlClient/EnclaveDelegate.cs | 2 - .../Data/SqlClient/EnclaveProviderBase.cs | 3 +- .../SqlAuthenticationProviderManager.cs | 2 + .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 8 +- .../Data/SqlClient/SqlClientEventSource.cs | 3 + .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 1 - .../Microsoft/Data/SqlClient/SqlDependency.cs | 3 - .../Data/SqlClient/SqlDependencyListener.cs | 7 +- .../Data/SqlClient/SqlDependencyUtils.cs | 6 +- .../SqlClient/SqlInternalConnectionTds.cs | 5 +- 62 files changed, 4133 insertions(+), 1731 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 6c9a8f59b8..0da3b93aa9 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,16 +767,16 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs index 10f277f020..4318d3d1e2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using System.Transactions; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.Common { @@ -30,7 +31,14 @@ internal static partial class ADP internal const CompareOptions DefaultCompareOptions = CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase; internal const int DefaultConnectionTimeout = DbConnectionStringDefaults.ConnectTimeout; - static partial void TraceException(string trace, Exception e); + static private void TraceException(string trace, Exception e) + { + Debug.Assert(null != e, "TraceException: null Exception"); + if (null != e) + { + SqlClientEventSource.Log.TraceEvent(trace, e.ToString()); // will include callstack if permission is available + } + } internal static void TraceExceptionAsReturnValue(Exception e) { @@ -40,7 +48,7 @@ internal static void TraceExceptionAsReturnValue(Exception e) internal static void TraceExceptionWithoutRethrow(Exception e) { Debug.Assert(ADP.IsCatchableExceptionType(e), "Invalid exception type, should have been re-thrown!"); - TraceException(" '%ls'\n", e); + TraceException(" '{0}'", e); } internal static ArgumentException Argument(string error) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs index 470425d3fb..d5b3b1062d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Text; using System.Text.RegularExpressions; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.Common { @@ -111,6 +112,8 @@ internal Dictionary Parsetable public string UsersConnectionString(bool hidePassword) => UsersConnectionString(hidePassword, false); + internal string UsersConnectionStringForTrace() => UsersConnectionString(true, true); + private string UsersConnectionString(bool hidePassword, bool forceHidePassword) { string connectionString = _usersConnectionString; @@ -157,7 +160,27 @@ private static bool CompareInsensitiveInvariant(string strvalue, string strconst (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst)); [System.Diagnostics.Conditional("DEBUG")] - static partial void DebugTraceKeyValuePair(string keyname, string keyvalue, Dictionary synonyms); + private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dictionary synonyms) + { + if (SqlClientEventSource.Log.IsAdvanceTraceOn()) + { + Debug.Assert(keyname == keyname.ToLower(CultureInfo.InvariantCulture), "missing ToLower"); + string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); + + if ((KEY.Password != realkeyname) && (SYNONYM.Pwd != realkeyname)) + { + // don't trace passwords ever! + if (null != keyvalue) + { + SqlClientEventSource.Log.AdvanceTrace(" KeyName='{0}', KeyValue='{1}'", keyname, keyvalue); + } + else + { + SqlClientEventSource.Log.AdvanceTrace(" KeyName='{0}'", keyname); + } + } + } + } private static string GetKeyName(StringBuilder buffer) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 619f681c0b..2aa715bdd3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Data.Common; using System; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.ProviderBase { @@ -21,6 +22,8 @@ internal abstract partial class DbConnectionFactory private const int PruningDueTime = 4 * 60 * 1000; // 4 minutes private const int PruningPeriod = 30 * 1000; // thirty seconds + private static int _objectTypeCount; // EventSource counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to // a maximum of Environment.ProcessorCount at a time. @@ -28,6 +31,8 @@ internal abstract partial class DbConnectionFactory private static Task[] s_pendingOpenNonPooled = new Task[Environment.ProcessorCount]; private static Task s_completedTask; + internal int ObjectID => _objectID; + protected DbConnectionFactory() { _connectionPoolGroups = new Dictionary(); @@ -45,25 +50,40 @@ abstract public DbProviderFactory ProviderFactory public void ClearAllPools() { - Dictionary connectionPoolGroups = _connectionPoolGroups; - foreach (KeyValuePair entry in connectionPoolGroups) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent("", "API"); + try { - DbConnectionPoolGroup poolGroup = entry.Value; - if (null != poolGroup) + Dictionary connectionPoolGroups = _connectionPoolGroups; + foreach (KeyValuePair entry in connectionPoolGroups) { - poolGroup.Clear(); + DbConnectionPoolGroup poolGroup = entry.Value; + if (null != poolGroup) + { + poolGroup.Clear(); + } } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } public void ClearPool(DbConnection connection) { ADP.CheckArgumentNull(connection, nameof(connection)); - - DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); - if (null != poolGroup) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", GetObjectId(connection)); + try { - poolGroup.Clear(); + DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); + if (null != poolGroup) + { + poolGroup.Clear(); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -71,12 +91,19 @@ public void ClearPool(DbConnectionPoolKey key) { Debug.Assert(key != null, "key cannot be null"); ADP.CheckArgumentNull(key.ConnectionString, nameof(key) + "." + nameof(key.ConnectionString)); - - DbConnectionPoolGroup poolGroup; - Dictionary connectionPoolGroups = _connectionPoolGroups; - if (connectionPoolGroups.TryGetValue(key, out poolGroup)) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" connectionString"); + try { - poolGroup.Clear(); + DbConnectionPoolGroup poolGroup; + Dictionary connectionPoolGroups = _connectionPoolGroups; + if (connectionPoolGroups.TryGetValue(key, out poolGroup)) + { + poolGroup.Clear(); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -100,6 +127,7 @@ internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConne { newConnection.MakeNonPooledObject(owningConnection); } + SqlClientEventSource.Log.TraceEvent(" {0}#, Non-pooled database connection created.", ObjectID); return newConnection; } @@ -113,6 +141,7 @@ internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbCo { newConnection.MakePooledConnection(pool); } + SqlClientEventSource.Log.TraceEvent(" {0}#, Pooled database connection created.", ObjectID); return newConnection; } @@ -167,6 +196,8 @@ private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnecti // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) { + SqlClientEventSource.Log.TraceEvent(" {0}#, DisabledPoolGroup={1}#", ObjectID, connectionPoolGroup.ObjectID); + // reusing existing pool option in case user originally used SetConnectionPoolOptions DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions; @@ -272,6 +303,9 @@ internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, D private void PruneConnectionPoolGroups(object state) { + // when debugging this method, expect multiple threads at the same time + SqlClientEventSource.Log.AdvanceTrace(" {0}#", ObjectID); + // First, walk the pool release list and attempt to clear each // pool, when the pool is finally empty, we dispose of it. If the // pool isn't empty, it's because there are active connections or @@ -290,6 +324,7 @@ private void PruneConnectionPoolGroups(object state) if (0 == pool.Count) { _poolsToRelease.Remove(pool); + SqlClientEventSource.Log.AdvanceTrace(" {0}#, ReleasePool={1}#", ObjectID, pool.ObjectID); } } } @@ -313,6 +348,7 @@ private void PruneConnectionPoolGroups(object state) if (0 == poolsLeft) { _poolGroupsToRelease.Remove(poolGroup); + SqlClientEventSource.Log.AdvanceTrace(" {0}#, ReleasePoolGroup={1}#", ObjectID, poolGroup.ObjectID); } } } @@ -375,6 +411,7 @@ internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { Debug.Assert(null != poolGroup, "null poolGroup?"); + SqlClientEventSource.Log.TraceEvent(" {0}#, poolGroup={1}#", ObjectID, poolGroup.ObjectID); lock (_poolGroupsToRelease) { @@ -427,6 +464,8 @@ protected virtual DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal i abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection); + abstract protected int GetObjectId(DbConnection conne); + abstract internal void PermissionDemand(DbConnection outerConnection); abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index b44d4251ae..8c73c45500 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; using System; using System.Data; using System.Data.Common; @@ -224,6 +225,7 @@ internal void DeactivateConnection() { // Internal method called from the connection pooler so we don't expose // the Deactivate method publicly. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Deactivating", ObjectID); #if DEBUG int activateCount = Interlocked.Decrement(ref _activateCount); @@ -247,12 +249,14 @@ internal void DeactivateConnection() protected internal void DoNotPoolThisConnection() { _cannotBePooled = true; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Marking pooled object as non-poolable so it will be disposed", ObjectID); } /// Ensure that this connection cannot be put back into the pool. protected internal void DoomThisConnection() { _connectionIsDoomed = true; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Dooming", ObjectID); } protected internal virtual DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions) @@ -372,6 +376,8 @@ internal void PrePush(object expectedOwner) { throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time } + + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Preparing to push into pool, owning connection {1}#, pooledCount={2}", ObjectID, 0, _pooledCount); _pooledCount++; _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% } @@ -400,6 +406,8 @@ internal void PostPop(object newOwner) } _owningObject.Target = newOwner; _pooledCount--; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Preparing to pop from pool, owning connection {1}#, pooledCount={2}", ObjectID, 0, _pooledCount); + //3 // The following tests are retail assertions of things we can't allow to happen. if (null != Pool) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 68c1cfb5f4..f0c63eca37 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -4,6 +4,7 @@ using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; using System.Collections.Concurrent; using System.Data.Common; using System.Diagnostics; @@ -39,6 +40,9 @@ sealed internal class DbConnectionPoolGroup private DbConnectionPoolGroupProviderInfo _providerInfo; private DbMetaDataFactory _metaDataFactory; + private static int _objectTypeCount; // EventSource counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + // always lock this before changing _state, we don't want to move out of the 'Disabled' state // PoolGroupStateUninitialized = 0; private const int PoolGroupStateActive = 1; // initial state, GetPoolGroup from cache, connection Open @@ -83,6 +87,14 @@ internal DbConnectionPoolGroupProviderInfo ProviderInfo internal bool IsDisabled => (PoolGroupStateDisabled == _state); + internal int ObjectID + { + get + { + return _objectID; + } + } + internal DbConnectionPoolGroupOptions PoolGroupOptions => _poolGroupOptions; internal DbMetaDataFactory MetaDataFactory @@ -223,6 +235,7 @@ private bool MarkPoolGroupAsActive() if (PoolGroupStateIdle == _state) { _state = PoolGroupStateActive; + SqlClientEventSource.Log.TraceEvent(" {0}#, Active", ObjectID); } return (PoolGroupStateActive == _state); } @@ -274,10 +287,12 @@ internal bool Prune() if (PoolGroupStateActive == _state) { _state = PoolGroupStateIdle; + SqlClientEventSource.Log.TraceEvent(" {0}#, Idle", ObjectID); } else if (PoolGroupStateIdle == _state) { _state = PoolGroupStateDisabled; + SqlClientEventSource.Log.TraceEvent(" {0}#, Disabled", ObjectID); } } return (PoolGroupStateDisabled == _state); 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 1b8b1bb014..3bbb55a663 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -566,6 +566,8 @@ + + True True diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs index 41d1022b2e..4a401b4c22 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; +using Microsoft.Data.SqlClient; namespace Microsoft.Data { @@ -23,8 +25,27 @@ internal static class ExceptionBuilder // The resource Data.txt will ensure proper string text based on the appropriate // locale. + static private void TraceException( + string trace, Exception e) + { + Debug.Assert(null != e, "TraceException: null Exception"); + if (null != e) + { + SqlClientEventSource.Log.AdvanceTrace(trace, e.Message); + try + { + SqlClientEventSource.Log.AdvanceTrace(" Environment StackTrace = '{0}'", Environment.StackTrace); + } + catch (System.Security.SecurityException) + { + // if you don't have permission - you don't get the stack trace + } + } + } + internal static void TraceExceptionAsReturnValue(Exception e) { + TraceException(" Message='{0}'", e); } // diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 65c608d700..2fb7649b13 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Transactions; using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.ProviderBase { @@ -162,6 +163,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour // connection creation failed on semaphore waiting or if max pool reached if (connectionPool.IsRunning) { + SqlClientEventSource.Log.TraceEvent(" {0}#, GetConnection failed because a pool timeout occurred.", ObjectID); // If GetConnection failed while the pool is running, the pool timeout occurred. throw ADP.PooledOpenTimeout(); } @@ -179,6 +181,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour if (connection == null) { + SqlClientEventSource.Log.TraceEvent(" {0}#, GetConnection failed because a pool timeout occurred and all retries were exhausted.", ObjectID); // exhausted all retries or timed out - give up throw ADP.PooledOpenTimeout(); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 2568e2a081..8350c765ee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -8,11 +8,15 @@ using System.Threading; using System.Transactions; using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.ProviderBase { internal abstract partial class DbConnectionInternal { + private static int _objectTypeCount; + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + private bool _isInStasis; private Transaction _enlistedTransaction; // [usage must be thread-safe] the transaction that we're enlisted in, either manually or automatically @@ -101,6 +105,7 @@ protected internal Transaction EnlistedTransaction if (null != value) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Enlisting.", ObjectID, value.GetHashCode()); TransactionOutcomeEnlist(value); } } @@ -157,6 +162,14 @@ internal bool IsTxRootWaitingForTxEnd } } + internal int ObjectID + { + get + { + return _objectID; + } + } + virtual protected bool UnbindOnTransactionCompletion { get @@ -196,6 +209,12 @@ internal void ActivateConnection(Transaction transaction) { // Internal method called from the connection pooler so we don't expose // the Activate method publicly. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Activating", ObjectID); + +#if DEBUG + int activateCount = Interlocked.Increment(ref _activateCount); + Debug.Assert(1 == activateCount, "activated multiple times?"); +#endif // DEBUG Activate(transaction); } @@ -240,7 +259,7 @@ internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFac //////////////////////////////////////////////////////////////// Debug.Assert(null != owningObject, "null owningObject"); Debug.Assert(null != connectionFactory, "null connectionFactory"); - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}# Closing.", ObjectID); // if an exception occurs after the state change but before the try block // the connection will be stuck in OpenBusy state. The commented out try-catch @@ -315,7 +334,7 @@ virtual internal void DelegatedTransactionEnded() // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - + SqlClientEventSource.Log.TraceEvent(" {0}#, Delegated Transaction Completed.", ObjectID); if (1 == _pooledCount) { @@ -407,6 +426,8 @@ internal void DetachCurrentTransactionIfEnded() // Detach transaction from connection. internal void DetachTransaction(Transaction transaction, bool isExplicitlyReleasing) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount={1})", ObjectID, _pooledCount); + // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should // be the exception, not the rule. @@ -445,9 +466,8 @@ internal void CleanupConnectionOnTransactionCompletion(Transaction transaction) void TransactionCompletedEvent(object sender, TransactionEventArgs e) { Transaction transaction = e.Transaction; - + SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount = {1})", ObjectID, _pooledCount); CleanupTransactionOnCompletion(transaction); - CleanupConnectionOnTransactionCompletion(transaction); } @@ -459,10 +479,19 @@ private void TransactionOutcomeEnlist(Transaction transaction) internal void SetInStasis() { _isInStasis = true; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.", ObjectID); } private void TerminateStasis(bool returningToPool) { + if (returningToPool) + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Delegated Transaction has ended, connection is closed. Returning to general pool.", ObjectID); + } + else + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Delegated Transaction has ended, connection is closed/leaked. Disposing.", ObjectID); + } _isInStasis = false; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 6a7231d0c8..3ad60ca40e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using System.Transactions; using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.ProviderBase { @@ -73,6 +74,7 @@ internal TransactedConnectionPool(DbConnectionPool pool) _pool = pool; _transactedCxns = new Dictionary(); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Constructed for connection pool {1}#", ObjectID, _pool.ObjectID); } internal int ObjectID @@ -129,6 +131,10 @@ internal DbConnectionInternal GetTransactedObject(Transaction transaction) } } + if (null != transactedObject) + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Popped.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); + } return transactedObject; } @@ -154,6 +160,7 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal lock (connections) { Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?"); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Pushing.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); connections.Add(transactedObject); } } @@ -188,11 +195,14 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal lock (connections) { Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?"); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Pushing.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); connections.Add(transactedObject); } } else { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Adding List to transacted pool.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); + // add the connection/transacted object to the list newConnections.Add(transactedObject); @@ -219,11 +229,13 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal } } } + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Added.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } } internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); TransactedConnectionList connections; int entry = -1; @@ -257,6 +269,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // safely remove the list from the transacted pool. if (0 >= connections.Count) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Removing List from transacted pool.", ObjectID, transaction.GetHashCode()); _transactedCxns.Remove(transaction); // we really need to dispose our connection list; it may have @@ -270,6 +283,10 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra connections.Dispose(); } } + else + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Transacted pool not yet created prior to transaction completing. Connection may be leaked.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); + } } // If (and only if) we found the connection in the list of @@ -382,6 +399,8 @@ internal WaitHandle[] GetHandles(bool withCreate) private readonly List _objectList; private int _totalObjects; + private static int _objectTypeCount; // EventSource counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // only created by DbConnectionPoolGroup.GetConnectionPool internal DbConnectionPool( @@ -424,6 +443,7 @@ internal DbConnectionPool( _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback _state = State.Running; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Constructed.", ObjectID); //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls // StartBackgroundCallbacks after pool is actually in the collection @@ -502,6 +522,14 @@ private int MinPoolSize get { return PoolGroupOptions.MinPoolSize; } } + internal int ObjectID + { + get + { + return _objectID; + } + } + internal DbConnectionPoolGroup PoolGroup { get { return _connectionPoolGroup; } @@ -558,7 +586,7 @@ private void CleanupCallback(object state) // // With this logic, objects are pruned from the pool if unused for // at least one period but not more than two periods. - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#", ObjectID); // Destroy free objects that put us above MinPoolSize from old stack. while (Count > MinPoolSize) @@ -630,6 +658,7 @@ private void CleanupCallback(object state) break; Debug.Assert(obj != null, "null connection is not expected"); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, ChangeStacks={1}#", ObjectID, obj.ObjectID); Debug.Assert(!obj.IsEmancipated, "pooled object not in pool"); Debug.Assert(obj.CanBePooled, "pooled object is not poolable"); @@ -644,6 +673,7 @@ private void CleanupCallback(object state) internal void Clear() { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Clearing.", ObjectID); DbConnectionInternal obj; // First, quickly doom everything. @@ -677,6 +707,7 @@ internal void Clear() // Finally, reclaim everything that's emancipated (which, because // it's been doomed, will cause it to be disposed of as well) ReclaimEmancipatedObjects(); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Cleared.", ObjectID); } private Timer CreateCleanupTimer() => @@ -727,6 +758,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio } } } + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Added to pool.", ObjectID, newObj != null ? newObj?.ObjectID.ToString() ?? "null" : "null"); // Reset the error wait: _errorWait = ERROR_WAIT_DEFAULT; @@ -784,6 +816,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio private void DeactivateObject(DbConnectionInternal obj) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Deactivating.", ObjectID, obj.ObjectID); obj.DeactivateConnection(); bool returnToGeneralPool = false; @@ -916,8 +949,13 @@ internal void DestroyObject(DbConnectionInternal obj) // we simply leave it alone; when the transaction completes, it will // come back through PutObjectFromTransactedPool, which will call us // again. - if (!obj.IsTxRootWaitingForTxEnd) + if (obj.IsTxRootWaitingForTxEnd) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Has Delegated Transaction, waiting to Dispose.", ObjectID, obj.ObjectID); + } + else + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Removing from pool.", ObjectID, obj.ObjectID); bool removed = false; lock (_objectList) { @@ -926,12 +964,18 @@ internal void DestroyObject(DbConnectionInternal obj) _totalObjects = _objectList.Count; } + if (removed) + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Removed from pool.", ObjectID, obj.ObjectID); + } obj.Dispose(); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Disposed.", ObjectID, obj.ObjectID); } } private void ErrorCallback(object state) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Resetting Error handling.", ObjectID); _errorOccurred = false; _waitHandles.ErrorEvent.Reset(); @@ -1062,6 +1106,7 @@ internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource {0}#, DbConnectionInternal State != Running.", ObjectID); connection = null; return true; } @@ -1101,6 +1146,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { DbConnectionInternal obj = null; Transaction transaction = null; + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Getting connection.", ObjectID); // If automatic transaction enlistment is required, then we try to // get the connection from the transacted connection pool first. @@ -1135,16 +1181,19 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj switch (waitResult) { case WaitHandle.WaitTimeout: + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Wait timed out.", ObjectID); Interlocked.Decrement(ref _waitCount); connection = null; return false; case ERROR_HANDLE: // Throw the error that PoolCreateRequest stashed. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Errors are set.", ObjectID); Interlocked.Decrement(ref _waitCount); throw TryCloneCachedException(); case CREATION_HANDLE: + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Creating new connection.", ObjectID); try { obj = UserCreateRequest(owningObject, userOptions); @@ -1194,6 +1243,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj if ((obj != null) && (!obj.IsConnectionAlive())) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, found dead and removed.", ObjectID, obj.ObjectID); DestroyObject(obj); obj = null; // Setting to null in case creating a new object fails @@ -1203,6 +1253,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { try { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Creating new connection.", ObjectID); obj = UserCreateRequest(owningObject, userOptions); } finally @@ -1213,13 +1264,16 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj else { // Timeout waiting for creation semaphore - return null + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Wait timed out.", ObjectID); connection = null; return false; } } } break; + default: + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, WaitForMultipleObjects={1}", ObjectID, waitResult); Interlocked.Decrement(ref _waitCount); throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult); } @@ -1271,6 +1325,7 @@ private void PrepareConnection(DbConnection owningObject, DbConnectionInternal o /// A new inner connection that is attached to the internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, replacing connection.", ObjectID); DbConnectionInternal newConnection = UserCreateRequest(owningObject, userOptions, oldConnection); if (newConnection != null) @@ -1311,6 +1366,7 @@ private DbConnectionInternal GetFromGeneralPool() if (null != obj) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Popped from general pool.", ObjectID, obj.ObjectID); } return (obj); } @@ -1326,6 +1382,8 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) if (null != obj) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Popped from transacted pool.", ObjectID, obj.ObjectID); + if (obj.IsTransactionRoot) { try @@ -1334,12 +1392,14 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) } catch { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, found dead and removed.", ObjectID, obj.ObjectID); DestroyObject(obj); throw; } } else if (!obj.IsConnectionAlive()) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, found dead and removed.", ObjectID, obj.ObjectID); DestroyObject(obj); obj = null; } @@ -1352,105 +1412,128 @@ private void PoolCreateRequest(object state) { // called by pooler to ensure pool requests are currently being satisfied - // creation mutex has not been obtained - - if (State.Running == _state) + long scopeID = SqlClientEventSource.Log.PoolerScopeEnterEvent(" {0}#", ObjectID); + try { - // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls, - // start it back up again - if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) + if (State.Running == _state) { - Thread waitOpenThread = new Thread(WaitForPendingOpen); - waitOpenThread.IsBackground = true; - waitOpenThread.Start(); - } + // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls, + // start it back up again + if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) + { + Thread waitOpenThread = new Thread(WaitForPendingOpen); + waitOpenThread.IsBackground = true; + waitOpenThread.Start(); + } - // Before creating any new objects, reclaim any released objects that were - // not closed. - ReclaimEmancipatedObjects(); + // Before creating any new objects, reclaim any released objects that were + // not closed. + ReclaimEmancipatedObjects(); - if (!ErrorOccurred) - { - if (NeedToReplenish) + if (!ErrorOccurred) { - // Check to see if pool was created using integrated security and if so, make - // sure the identity of current user matches that of user that created pool. - // If it doesn't match, do not create any objects on the ThreadPool thread, - // since either Open will fail or we will open a object for this pool that does - // not belong in this pool. The side effect of this is that if using integrated - // security min pool size cannot be guaranteed. - if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) + if (NeedToReplenish) { - return; - } - int waitResult = BOGUS_HANDLE; - try - { - try - { } - finally + // Check to see if pool was created using integrated security and if so, make + // sure the identity of current user matches that of user that created pool. + // If it doesn't match, do not create any objects on the ThreadPool thread, + // since either Open will fail or we will open a object for this pool that does + // not belong in this pool. The side effect of this is that if using integrated + // security min pool size cannot be guaranteed. + if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) { - waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(withCreate: true), CreationTimeout); + return; } - if (CREATION_HANDLE == waitResult) + int waitResult = BOGUS_HANDLE; + try { - DbConnectionInternal newObj; - - // Check ErrorOccurred again after obtaining mutex - if (!ErrorOccurred) + try + { } + finally + { + waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(withCreate: true), CreationTimeout); + } + if (CREATION_HANDLE == waitResult) { - while (NeedToReplenish) + DbConnectionInternal newObj; + + // Check ErrorOccurred again after obtaining mutex + if (!ErrorOccurred) { - try - { - // Don't specify any user options because there is no outer connection associated with the new connection - newObj = CreateObject(owningObject: null, userOptions: null, oldConnection: null); - } - catch - { - // Catch all the exceptions occuring during CreateObject so that they - // don't emerge as unhandled on the thread pool and don't crash applications - // The error is handled in CreateObject and surfaced to the caller of the Connection Pool - // using the ErrorEvent. Hence it is OK to swallow all exceptions here. - break; - } - // We do not need to check error flag here, since we know if - // CreateObject returned null, we are in error case. - if (null != newObj) + while (NeedToReplenish) { - PutNewObject(newObj); - } - else - { - break; + try + { + // Don't specify any user options because there is no outer connection associated with the new connection + newObj = CreateObject(owningObject: null, userOptions: null, oldConnection: null); + } + catch + { + // Catch all the exceptions occuring during CreateObject so that they + // don't emerge as unhandled on the thread pool and don't crash applications + // The error is handled in CreateObject and surfaced to the caller of the Connection Pool + // using the ErrorEvent. Hence it is OK to swallow all exceptions here. + break; + } + // We do not need to check error flag here, since we know if + // CreateObject returned null, we are in error case. + if (null != newObj) + { + PutNewObject(newObj); + } + else + { + break; + } } } } + else if (WaitHandle.WaitTimeout == waitResult) + { + // do not wait forever and potential block this worker thread + // instead wait for a period of time and just requeue to try again + QueuePoolCreateRequest(); + } + else + { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, PoolCreateRequest called WaitForSingleObject failed {1}", ObjectID, waitResult); + } } - else if (WaitHandle.WaitTimeout == waitResult) + catch (Exception e) { - // do not wait forever and potential block this worker thread - // instead wait for a period of time and just requeue to try again - QueuePoolCreateRequest(); + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + + // Now that CreateObject can throw, we need to catch the exception and discard it. + // There is no further action we can take beyond tracing. The error will be + // thrown to the user the next time they request a connection. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, PoolCreateRequest called CreateConnection which threw an exception: {1}", ObjectID, e); } - } - finally - { - if (CREATION_HANDLE == waitResult) + finally { - // reuse waitResult and ignore its value - _waitHandles.CreationSemaphore.Release(1); + if (CREATION_HANDLE == waitResult) + { + // reuse waitResult and ignore its value + _waitHandles.CreationSemaphore.Release(1); + } } } } } } + finally + { + SqlClientEventSource.Log.PoolerScopeLeaveEvent(scopeID); + } } internal void PutNewObject(DbConnectionInternal obj) { Debug.Assert(null != obj, "why are we adding a null object to the pool?"); // Debug.Assert(obj.CanBePooled, "non-poolable object in pool"); - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Pushing to general pool.", ObjectID, obj.ObjectID); _stackNew.Push(obj); _waitHandles.PoolSemaphore.Release(1); @@ -1494,6 +1577,7 @@ internal void PutObjectFromTransactedPool(DbConnectionInternal obj) // method, we can safely presume that the caller is the only person // that is using the connection, and that all pre-push logic has been // done and all transactions are ended. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Transaction has ended.", ObjectID, obj.ObjectID); if (_state == State.Running && obj.CanBePooled) { @@ -1518,7 +1602,7 @@ private void QueuePoolCreateRequest() private bool ReclaimEmancipatedObjects() { bool emancipatedObjectFound = false; - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#", ObjectID); List reclaimedObjects = new List(); int count; @@ -1570,7 +1654,7 @@ private bool ReclaimEmancipatedObjects() for (int i = 0; i < count; ++i) { DbConnectionInternal obj = reclaimedObjects[i]; - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Connection {1}#, Reclaiming.", ObjectID, obj.ObjectID); emancipatedObjectFound = true; obj.DetachCurrentTransactionIfEnded(); @@ -1581,6 +1665,7 @@ private bool ReclaimEmancipatedObjects() internal void Startup() { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, CleanupWait={1}", ObjectID, _cleanupWait); _cleanupTimer = CreateCleanupTimer(); if (NeedToReplenish) { @@ -1590,6 +1675,7 @@ internal void Startup() internal void Shutdown() { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#", ObjectID); _state = State.ShuttingDown; // deactivate timer callbacks @@ -1609,13 +1695,14 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra { Debug.Assert(null != transaction, "null transaction?"); Debug.Assert(null != transactedObject, "null transactedObject?"); + // Note: connection may still be associated with transaction due to Explicit Unbinding requirement. + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction {1}#, Connection {2}#, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); // called by the internal connection when it get's told that the // transaction is completed. We tell the transacted pool to remove // the connection from it's list, then we put the connection back in // general circulation. - TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool; if (null != transactedConnectionPool) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs index 8e7fca8dcf..473b3b638a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs @@ -9,7 +9,6 @@ namespace Microsoft.Data.SqlClient { - /// /// AD auth retry states. /// @@ -28,6 +27,7 @@ internal class ActiveDirectoryAuthenticationTimeoutRetryHelper private ActiveDirectoryAuthenticationTimeoutRetryState _state = ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted; private SqlFedAuthToken _token; private readonly string _typeName; + private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); /// /// Constructor. @@ -65,6 +65,10 @@ public ActiveDirectoryAuthenticationTimeoutRetryState State default: throw new InvalidOperationException($"Unsupported state: {value}."); } + if (_sqlAuthLogger.IsLoggingEnabled) + { + _sqlAuthLogger.LogInfo(_typeName, "SetState", $"State changed from {_state} to {value}."); + } _state = value; } } @@ -76,10 +80,18 @@ public SqlFedAuthToken CachedToken { get { + if (_sqlAuthLogger.IsLoggingEnabled) + { + _sqlAuthLogger.LogInfo(_typeName, "GetCachedToken", $"Retrieved cached token {GetTokenHash(_token)}."); + } return _token; } set { + if (_sqlAuthLogger.IsLoggingEnabled) + { + _sqlAuthLogger.LogInfo(_typeName, "SetCachedToken", $"CachedToken changed from {GetTokenHash(_token)} to {GetTokenHash(value)}."); + } _token = value; } } @@ -89,10 +101,12 @@ public SqlFedAuthToken CachedToken /// public bool CanRetryWithSqlException(SqlException sqlex) { - if (_state == ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted - && CachedToken != null - && IsConnectTimeoutError(sqlex)) + var methodName = "CheckCanRetry"; + if (_sqlAuthLogger.LogAssert(_state == ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted, _typeName, methodName, $"Cannot retry due to state == {_state}.") + && _sqlAuthLogger.LogAssert(CachedToken != null, _typeName, methodName, $"Cannot retry when cached token is null.") + && _sqlAuthLogger.LogAssert(IsConnectTimeoutError(sqlex), _typeName, methodName, $"Cannot retry when exception is not timeout.")) { + _sqlAuthLogger.LogInfo(_typeName, methodName, "All checks passed."); return true; } return false; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryNativeAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryNativeAuthenticationProvider.cs index 4f0f5165a5..0d4a5af3ba 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryNativeAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryNativeAuthenticationProvider.cs @@ -16,6 +16,7 @@ internal class ActiveDirectoryNativeAuthenticationProvider : SqlAuthenticationPr { private static readonly string s_defaultScopeSuffix = "/.default"; private readonly string _type = typeof(ActiveDirectoryNativeAuthenticationProvider).Name; + private readonly SqlClientLogger _logger = new SqlClientLogger(); /// /// Get token. @@ -52,5 +53,15 @@ public override bool IsSupported(SqlAuthenticationMethod authentication) { return authentication == SqlAuthenticationMethod.ActiveDirectoryPassword; } + + public override void BeforeLoad(SqlAuthenticationMethod authentication) + { + _logger.LogInfo(_type, "BeforeLoad", $"being loaded into SqlAuthProviders for {authentication}."); + } + + public override void BeforeUnload(SqlAuthenticationMethod authentication) + { + _logger.LogInfo(_type, "BeforeUnload", $"being unloaded from SqlAuthProviders for {authentication}."); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs index 1fcb95a3a8..7bab4d6303 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.NetCoreApp.cs @@ -17,7 +17,6 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; - // Azure Attestation Protocol Flow // To start the attestation process, Sql Client sends the Protocol Id (i.e. 1), Nonce, Attestation Url and ECDH Public Key // Sql Server uses attestation Url to attest the enclave and send the JWT to Sql client. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index 32f9555c8c..3b38ec607a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -65,7 +65,6 @@ namespace Microsoft.Data.SqlClient { // Base class for Enclave provider - internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs index bc1dcc0b0e..e088b627bb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs @@ -36,7 +36,9 @@ private static LocalDBFormatMessageDelegate LocalDBFormatMessage if (functionAddr == IntPtr.Zero) { + // SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossibe to get this error. int hResult = Marshal.GetLastWin32Error(); + SqlClientEventSource.Log.TraceEvent(" GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult); throw CreateLocalDBException(errorMessage: SR.LocalDB_MethodNotFound); } s_localDBFormatMessage = Marshal.GetDelegateForFunctionPointer(functionAddr); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs index 597a88517f..af8aec7a73 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs @@ -24,9 +24,16 @@ private static IntPtr UserInstanceDLLHandle SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle); if (s_userInstanceDLLHandle == IntPtr.Zero) { - SNINativeMethodWrapper.SNI_Error sniError; - SNINativeMethodWrapper.SNIGetLastError(out sniError); - throw CreateLocalDBException(errorMessage: SRHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); + if (s_userInstanceDLLHandle != IntPtr.Zero) + { + SqlClientEventSource.Log.TraceEvent(" LocalDB - handle obtained"); + } + else + { + SNINativeMethodWrapper.SNI_Error sniError; + SNINativeMethodWrapper.SNIGetLastError(out sniError); + throw CreateLocalDBException(errorMessage: SRHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); + } } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaData.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaData.cs index a855d73d4a..c06738cfe3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaData.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaData.cs @@ -674,6 +674,59 @@ private void SetDefaultsForType(SqlDbType dbType) _fieldMetaData = smdDflt._fieldMetaData; // This is ok due to immutability _extendedProperties = smdDflt._extendedProperties; // This is ok due to immutability } + + internal string TraceString() + { + return TraceString(0); + } + + virtual internal string TraceString(int indent) + { + string indentStr = new String(' ', indent); + string fields = String.Empty; + if (null != _fieldMetaData) + { + foreach (SmiMetaData fieldMd in _fieldMetaData) + { + fields = String.Format(CultureInfo.InvariantCulture, + "{0}{1}\n\t", fields, fieldMd.TraceString(indent + 5)); + } + } + + string properties = string.Empty; + if (null != _extendedProperties) + { + foreach (SmiMetaDataProperty property in _extendedProperties.Values) + { + properties = String.Format(CultureInfo.InvariantCulture, + "{0}{1} {2}\n\t", properties, indentStr, property.TraceString()); + } + } + + return String.Format(CultureInfo.InvariantCulture, "\n\t" + + "{0} SqlDbType={1:g}\n\t" + + "{0} MaxLength={2:d}\n\t" + + "{0} Precision={3:d}\n\t" + + "{0} Scale={4:d}\n\t" + + "{0} LocaleId={5:x}\n\t" + + "{0} CompareOptions={6:g}\n\t" + + "{0} Type={7}\n\t" + + "{0} MultiValued={8}\n\t" + + "{0} fields=\n\t{9}" + + "{0} properties=\n\t{10}", + indentStr, + SqlDbType, + MaxLength, + Precision, + Scale, + LocaleId, + CompareOptions, + (null != Type) ? Type.ToString() : "", + IsMultiValued, + fields, + properties); + + } } // SmiExtendedMetaData @@ -799,6 +852,22 @@ internal SmiExtendedMetaData( internal string TypeSpecificNamePart2 => _typeSpecificNamePart2; internal string TypeSpecificNamePart3 => _typeSpecificNamePart3; + + internal override string TraceString(int indent) + { + return String.Format(CultureInfo.InvariantCulture, + "{2} Name={0}" + + "{1}" + + "{2}TypeSpecificNamePart1='{3}'\n\t" + + "{2}TypeSpecificNamePart2='{4}'\n\t" + + "{2}TypeSpecificNamePart3='{5}'\n\t", + (null != _name) ? _name : "", + base.TraceString(indent), + new String(' ', indent), + (null != TypeSpecificNamePart1) ? TypeSpecificNamePart1 : "", + (null != TypeSpecificNamePart2) ? TypeSpecificNamePart2 : "", + (null != TypeSpecificNamePart3) ? TypeSpecificNamePart3 : ""); + } } // SmiParameterMetaData @@ -889,6 +958,15 @@ internal SmiParameterMetaData( } internal ParameterDirection Direction => _direction; + + internal override string TraceString(int indent) + { + return String.Format(CultureInfo.InvariantCulture, "{0}" + + "{1} Direction={2:g}\n\t", + base.TraceString(indent), + new String(' ', indent), + Direction); + } } // SmiStorageMetaData @@ -1082,6 +1160,29 @@ internal SmiStorageMetaData( internal bool IsIdentity => _isIdentity; internal bool IsColumnSet => _isColumnSet; + + internal override string TraceString(int indent) + { + return String.Format(CultureInfo.InvariantCulture, "{0}" + + "{1} AllowsDBNull={2}\n\t" + + "{1} ServerName='{3}'\n\t" + + "{1} CatalogName='{4}'\n\t" + + "{1} SchemaName='{5}'\n\t" + + "{1} TableName='{6}'\n\t" + + "{1} ColumnName='{7}'\n\t" + + "{1} IsKey={8}\n\t" + + "{1} IsIdentity={9}\n\t", + base.TraceString(indent), + new String(' ', indent), + AllowsDBNull, + (null != ServerName) ? ServerName : "", + (null != CatalogName) ? CatalogName : "", + (null != SchemaName) ? SchemaName : "", + (null != TableName) ? TableName : "", + (null != ColumnName) ? ColumnName : "", + IsKey, + IsIdentity); + } } // SmiQueryMetaData diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaDataProperty.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaDataProperty.cs index da866f272d..5d4993aee1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaDataProperty.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiMetaDataProperty.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient.Server @@ -75,6 +76,13 @@ internal bool IsReadOnly } } + internal IEnumerable Values + { + get + { + return new List(_properties); + } + } // Allow switching to read only, but not back. internal void SetReadOnly() @@ -94,6 +102,7 @@ private void EnsureWritable() // Base class for properties internal abstract class SmiMetaDataProperty { + internal abstract string TraceString(); } // Property defining a list of column ordinals that define a unique key @@ -129,6 +138,30 @@ internal void CheckCount(int countToMatch) "SmiDefaultFieldsProperty.CheckCount: DefaultFieldsProperty size (" + _columns.Count + ") not equal to checked size (" + countToMatch + ")"); } + + internal override string TraceString() + { + string returnValue = "UniqueKey("; + bool delimit = false; + for (int columnOrd = 0; columnOrd < _columns.Count; columnOrd++) + { + if (delimit) + { + returnValue += ","; + } + else + { + delimit = true; + } + if (_columns[columnOrd]) + { + returnValue += columnOrd.ToString(CultureInfo.InvariantCulture); + } + } + returnValue += ")"; + + return returnValue; + } } // Property defining a sort order for a set of columns (by ordinal and ASC/DESC). @@ -138,6 +171,11 @@ internal struct SmiColumnOrder { internal int SortOrdinal; internal SortOrder Order; + + internal string TraceString() + { + return string.Format(CultureInfo.InvariantCulture, "{0} {1}", SortOrdinal, Order); + } } private IList _columns; @@ -175,6 +213,31 @@ internal void CheckCount(int countToMatch) "SmiDefaultFieldsProperty.CheckCount: DefaultFieldsProperty size (" + _columns.Count + ") not equal to checked size (" + countToMatch + ")"); } + + internal override string TraceString() + { + string returnValue = "SortOrder("; + bool delimit = false; + foreach (SmiColumnOrder columnOrd in _columns) + { + if (delimit) + { + returnValue += ","; + } + else + { + delimit = true; + } + + if (Microsoft.Data.SqlClient.SortOrder.Unspecified != columnOrd.Order) + { + returnValue += columnOrd.TraceString(); + } + } + returnValue += ")"; + + return returnValue; + } } // property defining inheritance relationship(s) @@ -216,6 +279,31 @@ internal void CheckCount(int countToMatch) ") not equal to checked size (" + countToMatch + ")"); } + internal override string TraceString() + { + string returnValue = "DefaultFields("; + bool delimit = false; + for (int columnOrd = 0; columnOrd < _defaults.Count; columnOrd++) + { + if (delimit) + { + returnValue += ","; + } + else + { + delimit = true; + } + + if (_defaults[columnOrd]) + { + returnValue += columnOrd; + } + } + returnValue += ")"; + + return returnValue; + } + #endregion } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index f9c63ea336..0d382aa512 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -41,6 +41,7 @@ static SqlAuthenticationProviderManager() private readonly SqlAuthenticationInitializer _initializer; private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; private readonly ConcurrentDictionary _providers; + private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); /// /// Constructor. @@ -48,12 +49,14 @@ static SqlAuthenticationProviderManager() public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSection configSection) { _typeName = GetType().Name; + var methodName = "Ctor"; _providers = new ConcurrentDictionary(); var authenticationsWithAppSpecifiedProvider = new HashSet(); _authenticationsWithAppSpecifiedProvider = authenticationsWithAppSpecifiedProvider; if (configSection == null) { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No SqlAuthProviders configuration section found."); return; } @@ -71,6 +74,11 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe { throw SQL.CannotCreateSqlAuthInitializer(configSection.InitializerType, e); } + _sqlAuthLogger.LogInfo(_typeName, methodName, "Created user-defined SqlAuthenticationInitializer."); + } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined SqlAuthenticationInitializer found."); } // add user-defined providers, if any. @@ -95,8 +103,13 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe _providers[authentication] = provider; authenticationsWithAppSpecifiedProvider.Add(authentication); + _sqlAuthLogger.LogInfo(_typeName, methodName, string.Format("Added user-defined auth provider: {0} for authentication {1}.", providerSettings?.Type, authentication)); } } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined auth providers."); + } } /// @@ -119,10 +132,13 @@ public SqlAuthenticationProvider GetProvider(SqlAuthenticationMethod authenticat public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider) { if (!provider.IsSupported(authenticationMethod)) + { throw SQL.UnsupportedAuthenticationByProvider(authenticationMethod.ToString(), provider.GetType().Name); - + } + var methodName = "SetProvider"; if (_authenticationsWithAppSpecifiedProvider.Contains(authenticationMethod)) { + _sqlAuthLogger.LogError(_typeName, methodName, $"Failed to add provider {GetProviderType(provider)} because a user-defined provider with type {GetProviderType(_providers[authenticationMethod])} already existed for authentication {authenticationMethod}."); } _providers.AddOrUpdate(authenticationMethod, provider, (key, oldProvider) => { @@ -134,6 +150,7 @@ public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthent { provider.BeforeLoad(authenticationMethod); } + _sqlAuthLogger.LogInfo(_typeName, methodName, $"Added auth provider {GetProviderType(provider)}, overriding existed provider {GetProviderType(oldProvider)} for authentication {authenticationMethod}."); return provider; }); return true; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index d64b478f1a..18d81e10eb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -17,8 +17,8 @@ namespace Microsoft.Data.SqlClient { - // This internal class helps us to associate the metadata from the target - // with the ColumnOrdinals from the source. + // This internal class helps us to associate the metadata from the target. + // with ColumnOrdinals from the source. internal sealed class _ColumnMapping { internal int _sourceColumnOrdinal; @@ -236,6 +236,9 @@ private int RowNumber private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; + private static int _objectTypeCount; // Bid counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + // Newly added member variables for Async modification, m = member variable to bcp. private int _savedBatchSize = 0; // Save the batchsize so that changes are not affected unexpectedly. private bool _hasMoreRowToCopy = false; @@ -406,6 +409,14 @@ public int NotifyAfter } } + internal int ObjectID + { + get + { + return _objectID; + } + } + /// public event SqlRowsCopiedEventHandler SqlRowsCopied { @@ -525,7 +536,8 @@ private string CreateInitialQuery() private Task CreateAndExecuteInitialQueryAsync(out BulkCopySimpleResultSet result) { string TDSCommand = CreateInitialQuery(); - + SqlClientEventSource.Log.TraceEvent(" Initial Query: '{0}'", TDSCommand); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, this.BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); if (executeTask == null) @@ -790,6 +802,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i private Task SubmitUpdateBulkCommand(string TDSCommand) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, this.BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); if (executeTask == null) @@ -2296,6 +2309,7 @@ private void CheckAndRaiseNotification() // It's also the user's chance to cause an exception. _stateObj.BcpLock = true; abortOperation = FireRowsCopiedEvent(_rowsCopied); + SqlClientEventSource.Log.TraceEvent("", "INFO"); // In case the target connection is closed accidentally. if (ConnectionState.Open != _connection.State) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs new file mode 100644 index 0000000000..5e90796ade --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -0,0 +1,614 @@ +// 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.Text; +using System.Diagnostics.Tracing; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + [EventSource(Name = "Microsoft.Data.SqlClient.EventSource")] + internal class SqlClientEventSource : EventSource + { + // Defines the singleton instance for the Resources ETW provider + internal static readonly SqlClientEventSource Log = new SqlClientEventSource(); + + private static long s_nextScopeId = 0; + private static long s_nextNotificationScopeId = 0; + private static long s_nextPoolerScopeId = 0; + + /// + /// Defines EventId for BeginExecute (Reader, Scalar, NonQuery, XmlReader). + /// + private const int BeginExecuteEventId = 1; + + /// + /// Defines EventId for EndExecute (Reader, Scalar, NonQuery, XmlReader). + /// + private const int EndExecuteEventId = 2; + private const int TraceEventId = 3; + private const int EnterScopeId = 4; + private const int ExitScopeId = 5; + private const int TraceBinId = 6; + private const int CorrelationTraceId = 7; + private const int NotificationsScopeEnterId = 8; + private const int NotificationsTraceId = 9; + private const int PoolerScopeEnterId = 10; + private const int PoolerTraceId = 11; + + /// + /// Keyword definitions. These represent logical groups of events that can be turned on and off independently + /// Often each task has a keyword, but where tasks are determined by subsystem, keywords are determined by + /// usefulness to end users to filter. Generally users don't mind extra events if they are not high volume + /// so grouping low volume events together in a single keywords is OK (users can post-filter by task if desired) + /// + /// The visibility of the enum has to be public, otherwise there will be an ArgumentException on calling related WriteEvent method. the Keywords class has to be a nested class. + /// Each keyword must be a power of 2. + /// + /// + #region Keywords + public class Keywords + { + internal const EventKeywords SqlClient = 0; + + internal const EventKeywords Trace = (EventKeywords)1; + + internal const EventKeywords TraceBin = (EventKeywords)2; + + internal const EventKeywords Scope = (EventKeywords)4; + + internal const EventKeywords NotificationTrace = (EventKeywords)8; + + internal const EventKeywords Pooling = (EventKeywords)16; + + internal const EventKeywords Correlation = (EventKeywords)32; + + internal const EventKeywords NotificationScope = (EventKeywords)64; + + internal const EventKeywords PoolerScope = (EventKeywords)128; + + internal const EventKeywords PoolerTrace = (EventKeywords)256; + + internal const EventKeywords Advanced = (EventKeywords)512; + + internal const EventKeywords StateDump = (EventKeywords)1024; + } + #endregion + + public static class Tasks // this name is important for EventSource + { + /// Task that tracks sql command execute. + public const EventTask ExecuteCommand = (EventTask)1; + } + + #region Enable/Disable Events + [NonEvent] + internal bool IsTraceEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.Trace); + + [NonEvent] + internal bool IsTraceBinEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.TraceBin); + + [NonEvent] + internal bool IsScopeEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.Scope); + + [NonEvent] + internal bool IsPoolerScopeEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.PoolerScope); + + [NonEvent] + internal bool IsCorrelationEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.Correlation); + + [NonEvent] + internal bool IsNotificationScopeEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.NotificationScope); + + [NonEvent] + internal bool IsPoolingEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.Pooling); + + [NonEvent] + internal bool IsNotificationTraceEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.NotificationTrace); + + [NonEvent] + internal bool IsPoolerTraceEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.PoolerTrace); + + [NonEvent] + internal bool IsAdvanceTraceOn() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.Advanced); + + [NonEvent] + internal bool IsStateDumpEnabled() => SqlClientEventSource.Log.IsEnabled(EventLevel.Informational, Keywords.StateDump); + + [NonEvent] + internal bool IsSqlClientEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.SqlClient); + #endregion + + #region overloads + //Never use event writer directly as they are not checking for enabled/disabled situations. Always use overloads. + [NonEvent] + internal void TraceEvent(string message, T0 args0) + { + if (Log.IsTraceEnabled()) + { + TraceEvent(string.Format(message, args0)); + } + } + + [NonEvent] + internal void TraceEvent(string message) + { + if (Log.IsTraceEnabled()) + { + Trace(message); + } + } + + [NonEvent] + internal void TraceEvent(string message, T0 args0, T1 args1) + { + if (Log.IsTraceEnabled()) + { + Trace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void TraceEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsTraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2)); + } + } + + [NonEvent] + internal void TraceEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsTraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2, args3)); + } + } + + [NonEvent] + internal void TraceEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4, T5 args5) + { + if (Log.IsTraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2, args3, args4, args5)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(message); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1, args2)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1, args2, args3)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4, T5 args5) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1, args2, args3, args4, args5)); + } + } + + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4, T5 args5, T6 args6, T7 args7) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1, args2, args3, args4, args5, args6, args7)); + } + } + + [NonEvent] + internal void AdvanceTraceBin(string message, T0 args0, T1 args1) + { + if (Log.IsAdvanceTraceOn()) + { + TraceBin(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal long ScopeEnterEvent(string message, T0 args0) + { + if (Log.IsScopeEnabled()) + { + return ScopeEnter(string.Format(message, args0)); + } + return 0; + } + + [NonEvent] + internal long AdvanceScopeEnter(string message, T0 args0) + { + if (Log.IsAdvanceTraceOn()) + { + return ScopeEnter(string.Format(message, args0)); + } + return 0; + } + + [NonEvent] + internal long ScopeEnterEvent(string message) + { + if (Log.IsScopeEnabled()) + { + return ScopeEnter(message); + } + return 0; + } + + [NonEvent] + internal long ScopeEnterEvent(string message, T0 args0, T1 args1) + { + if (Log.IsScopeEnabled()) + { + return ScopeEnter(string.Format(message, args0, args1)); + } + return 0; + } + + [NonEvent] + internal long ScopeEnterEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsScopeEnabled()) + { + return ScopeEnter(string.Format(message, args0, args1, args2)); + } + return 0; + } + + [NonEvent] + internal long ScopeEnterEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsScopeEnabled()) + { + return ScopeEnter(string.Format(message, args0, args1, args2, args3)); + } + return 0; + } + + [NonEvent] + internal long PoolerScopeEnterEvent(string message, T0 args0) + { + if (Log.IsPoolerScopeEnabled()) + { + return PoolerScopeEnter(string.Format(message, args0)); + } + return 0; + } + + [NonEvent] + internal long NotificationsScopeEnterEvent(string message, T0 args0) + { + if (Log.IsNotificationScopeEnabled()) + { + return NotificationsScopeEnter(string.Format(message, args0)); + } + return 0; + } + + [NonEvent] + internal long NotificationsScopeEnterEvent(string message, T0 args0, T1 args1) + { + if (Log.IsNotificationScopeEnabled()) + { + return NotificationsScopeEnter(string.Format(message, args0, args1)); + } + return 0; + } + + [NonEvent] + internal long NotificationsScopeEnterEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsNotificationScopeEnabled()) + { + return NotificationsScopeEnter(string.Format(message, args0, args1, args2)); + } + return 0; + } + + [NonEvent] + internal long NotificationsScopeEnterEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsNotificationScopeEnabled()) + { + return NotificationsScopeEnter(string.Format(message, args0, args1, args2, args3)); + } + return 0; + } + + [NonEvent] + internal void PoolerTraceEvent(string message, T0 args0) + { + if (Log.IsPoolerTraceEnabled()) + { + PoolerTrace(string.Format(message, args0)); + } + } + + [NonEvent] + internal void PoolerTraceEvent(string message, T0 args0, T1 args1) + { + if (Log.IsPoolerTraceEnabled()) + { + PoolerTrace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void PoolerTraceEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsPoolerTraceEnabled()) + { + PoolerTrace(string.Format(message, args0, args1, args2)); + } + } + + [NonEvent] + internal void PoolerTraceEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsPoolerTraceEnabled()) + { + PoolerTrace(string.Format(message, args0, args1, args2, args3)); + } + } + + [NonEvent] + internal void CorrelationTraceEvent(string message, T0 args0) + { + if (Log.IsCorrelationEnabled()) + { + CorrelationTrace(string.Format(message, args0)); + } + } + + [NonEvent] + internal void CorrelationTraceEvent(string message, T0 args0, T1 args1) + { + if (Log.IsCorrelationEnabled()) + { + CorrelationTrace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void CorrelationTraceEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsCorrelationEnabled()) + { + CorrelationTrace(string.Format(message, args0, args1, args2)); + } + } + + [NonEvent] + internal void NotificationsTraceEvent(string message) + { + if (Log.IsNotificationTraceEnabled()) + { + NotificationsTrace(message); + } + } + + [NonEvent] + internal void NotificationsTraceEvent(string message, T0 args0) + { + if (Log.IsNotificationTraceEnabled()) + { + NotificationsTrace(string.Format(message, args0)); + } + } + + [NonEvent] + internal void NotificationsTraceEvent(string message, T0 args0, T1 args1) + { + if (Log.IsNotificationTraceEnabled()) + { + NotificationsTrace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void NotificationsTraceEvent(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsNotificationTraceEnabled()) + { + NotificationsTrace(string.Format(message, args0, args1, args2)); + } + } + + [NonEvent] + internal void NotificationsTraceEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsNotificationTraceEnabled()) + { + NotificationsTrace(string.Format(message, args0, args1, args2, args3)); + } + } + + [NonEvent] + internal void TraceBinEvent(string message, T0 args0, T1 args1) + { + if (Log.IsTraceBinEnabled()) + { + TraceBin(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void StateDumpEvent(string message, T0 args0, T1 args1) + { + if (Log.IsStateDumpEnabled()) + { + Trace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void ScopeLeaveEvent(long scopeId) + { + if (Log.IsScopeEnabled()) + { + ScopeLeave(scopeId); + } + } + + [NonEvent] + internal void NotificationsScopeLeaveEvent(long scopeId) + { + if (Log.IsNotificationScopeEnabled()) + { + ScopeLeave(scopeId); + } + } + + [NonEvent] + internal void PoolerScopeLeaveEvent(long scopeId) + { + if (Log.IsPoolerScopeEnabled()) + { + ScopeLeave(scopeId); + } + } + + [NonEvent] + internal void AdvanceScopeLeave(long scopeId) + { + if (Log.IsAdvanceTraceOn()) + { + ScopeLeave(scopeId); + } + } + #endregion + + #region Events + [Event(TraceEventId, Level = EventLevel.Informational, Keywords = Keywords.Trace)] + internal void Trace(string message) + { + WriteEvent(TraceEventId, message); + } + + [Event(EnterScopeId, Level = EventLevel.Verbose, Keywords = Keywords.Scope)] + internal long ScopeEnter(string message) + { + long scopeId = Interlocked.Increment(ref s_nextScopeId); + WriteEvent(EnterScopeId, message); + return scopeId; + } + + [Event(ExitScopeId, Level = EventLevel.Verbose, Keywords = Keywords.Scope)] + internal void ScopeLeave(long scopeId) + { + WriteEvent(ExitScopeId, scopeId); + } + + [Event(TraceBinId, Level = EventLevel.Informational, Keywords = Keywords.Trace)] + internal void TraceBin(string message) + { + WriteEvent(TraceBinId, message); + } + + [Event(CorrelationTraceId, Level = EventLevel.Informational, Keywords = Keywords.Correlation, Opcode = EventOpcode.Start)] + internal void CorrelationTrace(string message) + { + WriteEvent(CorrelationTraceId, message); + } + + [Event(NotificationsScopeEnterId, Level = EventLevel.Informational, Opcode = EventOpcode.Start, Keywords = Keywords.NotificationScope)] + internal long NotificationsScopeEnter(string message) + { + long scopeId = Interlocked.Increment(ref s_nextNotificationScopeId); + WriteEvent(NotificationsScopeEnterId, message); + return scopeId; + } + + [Event(PoolerScopeEnterId, Level = EventLevel.Informational, Opcode = EventOpcode.Start, Keywords = Keywords.PoolerScope)] + internal long PoolerScopeEnter(string message) + { + long scopeId = Interlocked.Increment(ref s_nextPoolerScopeId); + WriteEvent(PoolerScopeEnterId, message); + return scopeId; + } + + [Event(NotificationsTraceId, Level = EventLevel.Informational, Keywords = Keywords.Trace)] + internal void NotificationsTrace(string message) + { + WriteEvent(NotificationsTraceId, message); + } + + [Event(PoolerTraceId, Level = EventLevel.Informational, Keywords = Keywords.PoolerTrace)] + internal void PoolerTrace(string message) + { + WriteEvent(PoolerTraceId, message); + } + + // unfortunately these are not marked as Start/Stop opcodes. The reason is that we dont want them to participate in + // the EventSource activity IDs (because they currently don't use tasks and this simply confuses the logic) and + // because of versioning requirements we don't have ActivityOptions capability (because mscorlib and System.Data version + // at different rates) Sigh... + [Event(BeginExecuteEventId, Keywords = Keywords.SqlClient, Task = Tasks.ExecuteCommand, Opcode = EventOpcode.Start)] + public void BeginExecute(int objectId, string dataSource, string database, string commandText) + { + // we do not use unsafe code for better performance optization here because optimized helpers make the code unsafe where that would not be the case otherwise. + // This introduces the question of partial trust, which is complex in the SQL case (there are a lot of scenarios and SQL has special security support). + if (Log.IsSqlClientEnabled()) + { + WriteEvent(BeginExecuteEventId, objectId, dataSource, database, commandText); + } + } + + // unfortunately these are not marked as Start/Stop opcodes. The reason is that we dont want them to participate in + // the EventSource activity IDs (because they currently don't use tasks and this simply confuses the logic) and + // because of versioning requirements we don't have ActivityOptions capability (because mscorlib and System.Data version + // at different rates) Sigh... + [Event(EndExecuteEventId, Keywords = Keywords.SqlClient, Task = Tasks.ExecuteCommand, Opcode = EventOpcode.Stop)] + public void EndExecute(int objectId, int compositeState, int sqlExceptionNumber) + { + if (Log.IsSqlClientEnabled()) + { + WriteEvent(EndExecuteEventId, objectId, compositeState, sqlExceptionNumber); + } + } + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs new file mode 100644 index 0000000000..c150399783 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient +{ + /// + public class SqlClientLogger + { + internal enum LogLevel + { + Info = 0, + Error, + } + + /// + public void LogInfo(string type, string method, string message) + { + SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); + } + + /// + public void LogError(string type, string method, string message) + { + SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); + } + + /// + public bool LogAssert(bool value, string type, string method, string message) + { + if (!value) + LogError(type, method, message); + return value; + } + + /// + public bool IsLoggingEnabled => SqlClientEventSource.Log.IsEnabled(); + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index a675e738bc..06170f299b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -25,7 +25,9 @@ namespace Microsoft.Data.SqlClient /// public sealed partial class SqlCommand : DbCommand, ICloneable { - private string _commandText; + private static int _objectTypeCount; // Bid counter + internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText; + private CommandType _commandType; private int _commandTimeout = ADP.DefaultCommandTimeout; private UpdateRowSource _updatedRowSource = UpdateRowSource.Both; @@ -412,6 +414,7 @@ private SqlCommand(SqlCommand from) : this() } _activeConnection = value; + SqlClientEventSource.Log.TraceEvent(" {0}#, {1}#", ObjectID, (null != value) ? value.ObjectID : -1); } } @@ -445,6 +448,7 @@ public SqlNotificationRequest Notification } set { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); _sqlDep = null; _notification = value; } @@ -488,7 +492,7 @@ internal SqlStatistics Statistics throw SQL.CannotModifyPropertyAsyncOperationInProgress(); } } - + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); _transaction = value; } } @@ -516,6 +520,7 @@ override public string CommandText } set { + SqlClientEventSource.Log.TraceEvent(" {0}#, String Value = '{1}'", ObjectID, value); if (_commandText != value) { PropertyChanging(); @@ -536,6 +541,7 @@ override public int CommandTimeout } set { + SqlClientEventSource.Log.TraceEvent(" {0}#, {1}", ObjectID, value); if (value < 0) { throw ADP.InvalidCommandTimeout(value); @@ -568,6 +574,7 @@ override public CommandType CommandType } set { + SqlClientEventSource.Log.TraceEvent(" {0}#, {1}{2}", ObjectID, (int)value, _commandType); if (_commandType != value) { switch (value) @@ -672,6 +679,7 @@ internal void OnStatementCompleted(int recordCount) { try { + SqlClientEventSource.Log.TraceEvent(" {0}#, recordCount={1}", ObjectID, recordCount); handler(this, new StatementCompletedEventArgs(recordCount)); } catch (Exception e) @@ -698,6 +706,8 @@ override public void Prepare() _pendingCancel = false; SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -761,6 +771,7 @@ override public void Prepare() finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -810,6 +821,7 @@ internal void Unprepare() } _cachedMetaData = null; + SqlClientEventSource.Log.TraceEvent(" {0}#, Command unprepared.", ObjectID); } // Cancel is supposed to be multi-thread safe. @@ -819,6 +831,8 @@ internal void Unprepare() /// override public void Cancel() { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); SqlStatistics statistics = null; try { @@ -895,6 +909,7 @@ override public void Cancel() finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -932,23 +947,38 @@ override public object ExecuteScalar() SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + Exception e = null; + bool success = false; + int? sqlExceptionNumber = null; + try { statistics = SqlStatistics.StartTimer(Statistics); SqlDataReader ds; ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, returnStream: true); + success = true; + return CompleteExecuteScalar(ds, false); } catch (Exception ex) { + if (ex is SqlException) + { + SqlException exception = (SqlException)ex; + sqlExceptionNumber = exception.Number; + } + e = ex; throw; } finally { SqlStatistics.StopTimer(statistics); - + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { _diagnosticListener.WriteCommandError(operationId, this, e); @@ -1001,6 +1031,8 @@ override public int ExecuteNonQuery() SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Exception e = null; try { @@ -1016,7 +1048,7 @@ override public int ExecuteNonQuery() finally { SqlStatistics.StopTimer(statistics); - + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { _diagnosticListener.WriteCommandError(operationId, this, e); @@ -1038,6 +1070,7 @@ public IAsyncResult BeginExecuteNonQuery() /// public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false); } @@ -1226,7 +1259,17 @@ private void WaitForAsyncResults(IAsyncResult asyncResult, bool isInternal) } /// - public int EndExecuteNonQuery(IAsyncResult asyncResult) => EndExecuteNonQueryInternal(asyncResult); + public int EndExecuteNonQuery(IAsyncResult asyncResult) + { + try + { + return EndExecuteNonQueryInternal(asyncResult); + } + finally + { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + } + } private void ThrowIfReconnectionHasBeenCanceled() { @@ -1243,6 +1286,7 @@ private void ThrowIfReconnectionHasBeenCanceled() /// public int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -1279,10 +1323,13 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { SqlStatistics statistics = null; int? sqlExceptionNumber = null; + bool success = false; + try { statistics = SqlStatistics.StartTimer(Statistics); int result = (int)InternalEndExecuteNonQuery(asyncResult, isInternal: false, endMethod: nameof(EndExecuteNonQuery)); + success = true; return result; } catch (SqlException e) @@ -1306,6 +1353,7 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) finally { SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false); } } @@ -1438,6 +1486,8 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, bo { // otherwise, use a full-fledged execute that can handle params and stored procs Debug.Assert(!sendToPipe, "trying to send non-context command to pipe"); + SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as RPC.", ObjectID); + SqlDataReader reader = RunExecuteReader(0, RunBehavior.UntilDone, false, completion, timeout, out task, out usedCache, asyncWrite, inRetry, methodName); if (null != reader) { @@ -1454,6 +1504,7 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, bo } } } + Debug.Assert(isAsync || null == _stateObj, "non-null state object in InternalExecuteNonQuery"); return task; } @@ -1464,10 +1515,14 @@ public XmlReader ExecuteXmlReader() // Reset _pendingCancel upon entry into any Execute - used to synchronize state // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; + bool success = false; Guid operationId = _diagnosticListener.WriteCommandBefore(this); SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + int? sqlExceptionNumber = null; Exception e = null; try @@ -1477,17 +1532,25 @@ public XmlReader ExecuteXmlReader() // use the reader to consume metadata SqlDataReader ds; ds = RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true); + success = true; return CompleteXmlReader(ds); } catch (Exception ex) { e = ex; + if (ex is SqlException) + { + SqlException exception = (SqlException)ex; + sqlExceptionNumber = exception.Number; + } + throw; } finally { SqlStatistics.StopTimer(statistics); - + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { _diagnosticListener.WriteCommandError(operationId, this, e); @@ -1509,6 +1572,7 @@ public IAsyncResult BeginExecuteXmlReader() /// public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false); } @@ -1625,11 +1689,19 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource /// public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) { - return EndExecuteXmlReaderInternal(asyncResult); + try + { + return EndExecuteXmlReaderInternal(asyncResult); + } + finally + { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + } } private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -1659,12 +1731,20 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) private XmlReader EndExecuteXmlReaderInternal(IAsyncResult asyncResult) { + bool success = false; + int? sqlExceptionNumber = null; try { + success = true; return CompleteXmlReader(InternalEndExecuteReader(asyncResult, false, nameof(EndExecuteXmlReader))); } catch (Exception e) { + if (e is SqlException) + { + SqlException ex = (SqlException)e; + sqlExceptionNumber = ex.Number; + } if (cachedAsyncState != null) { cachedAsyncState.ResetAsyncState(); @@ -1675,6 +1755,10 @@ private XmlReader EndExecuteXmlReaderInternal(IAsyncResult asyncResult) }; throw; } + finally + { + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false); + } } private XmlReader CompleteXmlReader(SqlDataReader ds, bool isAsync = false) @@ -1722,11 +1806,16 @@ private XmlReader CompleteXmlReader(SqlDataReader ds, bool isAsync = false) public IAsyncResult BeginExecuteReader(CommandBehavior behavior) => BeginExecuteReader(null, null, behavior); /// - public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) => BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); + public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) + { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); + } /// override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); return ExecuteReader(behavior); } @@ -1734,6 +1823,8 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) new public SqlDataReader ExecuteReader() { SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1742,6 +1833,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -1755,7 +1847,8 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) Guid operationId = _diagnosticListener.WriteCommandBefore(this); SqlStatistics statistics = null; - + bool success = false; + int? sqlExceptionNumber = null; Exception e = null; try { @@ -1764,12 +1857,19 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) } catch (Exception ex) { + if (ex is SqlException) + { + SqlException exception = (SqlException)ex; + sqlExceptionNumber = exception.Number; + } + e = ex; throw; } finally { SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { @@ -1785,12 +1885,20 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) /// public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) { - return EndExecuteReaderInternal(asyncResult); + try + { + return EndExecuteReaderInternal(asyncResult); + } + finally + { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + } } internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -1820,13 +1928,22 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) { SqlStatistics statistics = null; + bool success = false; + int? sqlExceptionNumber = null; try { + success = true; statistics = SqlStatistics.StartTimer(Statistics); return InternalEndExecuteReader(asyncResult, false, nameof(EndExecuteReader)); } catch (Exception e) { + if (e is SqlException) + { + SqlException exception = (SqlException)e; + sqlExceptionNumber = exception.Number; + } + if (cachedAsyncState != null) { cachedAsyncState.ResetAsyncState(); @@ -1840,6 +1957,7 @@ private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) finally { SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false); } } @@ -2129,6 +2247,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Guid operationId = _diagnosticListener.WriteCommandBefore(this); TaskCompletionSource source = new TaskCompletionSource(); @@ -2215,6 +2334,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b /// new public Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); Guid operationId = default(Guid); if (!_parentOperationStarted) operationId = _diagnosticListener.WriteCommandBefore(this); @@ -2365,6 +2485,7 @@ public Task ExecuteXmlReaderAsync() /// public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); Guid operationId = _diagnosticListener.WriteCommandBefore(this); TaskCompletionSource source = new TaskCompletionSource(); @@ -2906,6 +3027,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, // no parameters are sent over // no data reader is returned // use this overload for "batch SQL" tds token type + SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as SQLBATCH.", ObjectID); Task executeTask = _stateObj.Parser.TdsExecuteSQLBatch(this.CommandText, timeout, this.Notification, _stateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); @@ -3541,18 +3663,18 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques { SqlParameter param = originalRpcRequest.userParams[i]; SqlParameter paramCopy = new SqlParameter( - param.ParameterName, - param.SqlDbType, - param.Size, - param.Direction, - param.Precision, - param.Scale, - param.SourceColumn, + param.ParameterName, + param.SqlDbType, + param.Size, + param.Direction, + param.Precision, + param.Scale, + param.SourceColumn, param.SourceVersion, - param.SourceColumnNullMapping, - param.Value, - param.XmlSchemaCollectionDatabase, - param.XmlSchemaCollectionOwningSchema, + param.SourceColumnNullMapping, + param.Value, + param.XmlSchemaCollectionDatabase, + param.XmlSchemaCollectionOwningSchema, param.XmlSchemaCollectionName ); paramCopy.CompareInfo = param.CompareInfo; @@ -3584,7 +3706,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques } //@parameters - + SqlParameter paramsParam = describeParameterEncryptionRequest.systemParams[1]; paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; @@ -3734,7 +3856,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi { keysToBeSentToEnclave = new Dictionary(); keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); - } + } else if (!keysToBeSentToEnclave.ContainsKey(currentOrdinal)) { keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); @@ -3789,7 +3911,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // When the RPC object gets reused, the parameter array has more parameters that the valid params for the command. // Null is used to indicate the end of the valid part of the array. Refer to GetRPCObject(). - + for (int index = 0; index < userParamCount; index++) { SqlParameter sqlParameter = rpc.userParams[index]; @@ -4002,7 +4124,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } @@ -4045,7 +4167,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior if (ShouldUseEnclaveBasedWorkflow && this.enclavePackage != null) { - EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, + EnclaveDelegate.Instance.InvalidateEnclaveSession(this._activeConnection.AttestationProtocol, this._activeConnection.Parser.EnclaveType, this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this.enclavePackage.EnclaveSession); } @@ -4252,6 +4374,11 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi { // Send over SQL Batch command if we are not a stored proc and have no parameters Debug.Assert(!IsUserPrepared, "CommandType.Text with no params should not be prepared!"); + + if (returnStream) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as SQLBATCH.", ObjectID); + } string text = GetCommandText(cmdBehavior) + GetResetOptionsString(cmdBehavior); //If the query requires enclave computations, pass the enclavepackage in the SQLBatch TDS stream if (requiresEnclaveComputations) @@ -4308,8 +4435,11 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi BuildExecuteSql(cmdBehavior, null, _parameters, ref rpc); } - // if shiloh, then set NOMETADATA_UNLESSCHANGED flag rpc.options = TdsEnums.RPC_NOMETADATA; + if (returnStream) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as RPC.", ObjectID); + } Debug.Assert(_rpcArrayOf1[0] == rpc); writeTask = _stateObj.Parser.TdsExecuteRPC(this, _rpcArrayOf1, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite); @@ -4324,6 +4454,11 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi // then batch sql them over. This is inefficient (3 round trips) but the only way we can get metadata only from // a stored proc optionSettings = GetSetOptionsString(cmdBehavior); + if (returnStream) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as RPC.", ObjectID); + } + // turn set options ON if (null != optionSettings) { @@ -4749,7 +4884,7 @@ internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateOb } internal void OnDoneProc() - { + { // called per rpc batch complete if (BatchRPCMode) { @@ -5983,6 +6118,33 @@ private void NotifyDependency() /// - public SqlCommand Clone() => new SqlCommand(this); + public SqlCommand Clone() + { + SqlCommand clone = new SqlCommand(this); + SqlClientEventSource.Log.TraceEvent(" {0}#, clone={1}#", ObjectID, clone.ObjectID); + return clone; + } + + private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool synchronous) + { + if (SqlClientEventSource.Log.IsSqlClientEnabled()) + { + // SqlEventSource.WriteEvent(int, int, int, int) is faster than provided overload SqlEventSource.WriteEvent(int, object[]). + // that's why trying to fit several booleans in one integer value + + // success state is stored the first bit in compositeState 0x01 + int successFlag = success ? 1 : 0; + + // isSqlException is stored in the 2nd bit in compositeState 0x100 + int isSqlExceptionFlag = sqlExceptionNumber.HasValue ? 2 : 0; + + // synchronous state is stored in the second bit in compositeState 0x10 + int synchronousFlag = synchronous ? 4 : 0; + + int compositeState = successFlag | isSqlExceptionFlag | synchronousFlag; + + SqlClientEventSource.Log.EndExecute(GetHashCode(), compositeState, sqlExceptionNumber.GetValueOrDefault()); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs index 4d51c01f86..2d649f194c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs @@ -22,6 +22,9 @@ internal sealed class SqlCommandSet private SqlCommand _batchCommand; + private static int _objectTypeCount; // Bid counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private sealed class LocalCommand { internal readonly string CommandText; @@ -102,10 +105,18 @@ internal SqlTransaction Transaction } } + internal int ObjectID + { + get + { + return _objectID; + } + } + internal void Append(SqlCommand command) { ADP.CheckArgumentNull(command, nameof(command)); - + SqlClientEventSource.Log.TraceEvent(" {0}#, command={1}, parameterCount={2}", ObjectID, command.ObjectID, command.Parameters.Count); string cmdText = command.CommandText; if (string.IsNullOrEmpty(cmdText)) { @@ -237,6 +248,7 @@ internal static void BuildStoredProcedureName(StringBuilder builder, string part internal void Clear() { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); DbCommand batchCommand = BatchCommand; if (null != batchCommand) { @@ -252,6 +264,7 @@ internal void Clear() internal void Dispose() { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); SqlCommand command = _batchCommand; _commandList = null; _batchCommand = null; @@ -265,17 +278,25 @@ internal void Dispose() internal int ExecuteNonQuery() { ValidateCommandBehavior(nameof(ExecuteNonQuery), CommandBehavior.Default); + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - BatchCommand.BatchRPCMode = true; - BatchCommand.ClearBatchCommand(); - BatchCommand.Parameters.Clear(); - for (int ii = 0; ii < _commandList.Count; ii++) + try { - LocalCommand cmd = _commandList[ii]; - BatchCommand.AddBatchCommand(cmd.CommandText, cmd.Parameters, cmd.CmdType, cmd.ColumnEncryptionSetting); - } + BatchCommand.BatchRPCMode = true; + BatchCommand.ClearBatchCommand(); + BatchCommand.Parameters.Clear(); + for (int ii = 0; ii < _commandList.Count; ii++) + { + LocalCommand cmd = _commandList[ii]; + BatchCommand.AddBatchCommand(cmd.CommandText, cmd.Parameters, cmd.CmdType, cmd.ColumnEncryptionSetting); + } - return BatchCommand.ExecuteBatchRPCCommand(); + return BatchCommand.ExecuteBatchRPCCommand(); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal SqlParameter GetParameter(int commandIndex, int parameterIndex) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 96789dc1d2..71b7a39441 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -782,15 +782,23 @@ public SqlTransaction BeginTransaction(string transactionName) [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] override protected DbTransaction BeginDbTransaction(System.Data.IsolationLevel isolationLevel) { - DbTransaction transaction = BeginTransaction(isolationLevel); + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#, isolationLevel={1}", ObjectID, (int)isolationLevel); + try + { + DbTransaction transaction = BeginTransaction(isolationLevel); - // InnerConnection doesn't maintain a ref on the outer connection (this) and - // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction - // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable - // until the completion of BeginTransaction with KeepAlive - GC.KeepAlive(this); + // InnerConnection doesn't maintain a ref on the outer connection (this) and + // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction + // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable + // until the completion of BeginTransaction with KeepAlive + GC.KeepAlive(this); - return transaction; + return transaction; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -798,7 +806,7 @@ public SqlTransaction BeginTransaction(System.Data.IsolationLevel iso, string tr { WaitForPendingReconnection(); SqlStatistics statistics = null; - + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#, iso={1}, transactionName='{2}'", ObjectID, (int)iso, (string.IsNullOrEmpty(transactionName) ? "None" : transactionName)); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -823,6 +831,7 @@ public SqlTransaction BeginTransaction(System.Data.IsolationLevel iso, string tr finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -831,6 +840,7 @@ public override void ChangeDatabase(string database) { SqlStatistics statistics = null; RepairInnerConnection(); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -875,78 +885,87 @@ private void CloseInnerConnection() /// public override void Close() { - ConnectionState previousState = State; - Guid operationId = default(Guid); - Guid clientConnectionId = default(Guid); - - // during the call to Dispose() there is a redundant call to - // Close(). because of this, the second time Close() is invoked the - // connection is already in a closed state. this doesn't seem to be a - // problem except for logging, as we'll get duplicate Before/After/Error - // log entries - if (previousState != ConnectionState.Closed) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + try { - operationId = s_diagnosticListener.WriteConnectionCloseBefore(this); - // we want to cache the ClientConnectionId for After/Error logging, as when the connection - // is closed then we will lose this identifier - // - // note: caching this is only for diagnostics logging purposes - clientConnectionId = ClientConnectionId; - } + ConnectionState previousState = State; + Guid operationId = default(Guid); + Guid clientConnectionId = default(Guid); - SqlStatistics statistics = null; + // during the call to Dispose() there is a redundant call to + // Close(). because of this, the second time Close() is invoked the + // connection is already in a closed state. this doesn't seem to be a + // problem except for logging, as we'll get duplicate Before/After/Error + // log entries + if (previousState != ConnectionState.Closed) + { + operationId = s_diagnosticListener.WriteConnectionCloseBefore(this); + // we want to cache the ClientConnectionId for After/Error logging, as when the connection + // is closed then we will lose this identifier + // + // note: caching this is only for diagnostics logging purposes + clientConnectionId = ClientConnectionId; + } - Exception e = null; - try - { - statistics = SqlStatistics.StartTimer(Statistics); + SqlStatistics statistics = null; - Task reconnectTask = _currentReconnectionTask; - if (reconnectTask != null && !reconnectTask.IsCompleted) + Exception e = null; + try { - CancellationTokenSource cts = _reconnectionCancellationSource; - if (cts != null) + statistics = SqlStatistics.StartTimer(Statistics); + + Task reconnectTask = _currentReconnectionTask; + if (reconnectTask != null && !reconnectTask.IsCompleted) { - cts.Cancel(); + CancellationTokenSource cts = _reconnectionCancellationSource; + if (cts != null) + { + cts.Cancel(); + } + AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection + if (State != ConnectionState.Open) + {// if we cancelled before the connection was opened + OnStateChange(DbConnectionInternal.StateChangeClosed); + } } - AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection - if (State != ConnectionState.Open) - {// if we cancelled before the connection was opened - OnStateChange(DbConnectionInternal.StateChangeClosed); + CancelOpenAndWait(); + CloseInnerConnection(); + GC.SuppressFinalize(this); + + if (null != Statistics) + { + ADP.TimerCurrent(out _statistics._closeTimestamp); } } - CancelOpenAndWait(); - CloseInnerConnection(); - GC.SuppressFinalize(this); - - if (null != Statistics) + catch (Exception ex) { - ADP.TimerCurrent(out _statistics._closeTimestamp); + e = ex; + throw; } - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - - // we only want to log this if the previous state of the - // connection is open, as that's the valid use-case - if (previousState != ConnectionState.Closed) + finally { - if (e != null) - { - s_diagnosticListener.WriteConnectionCloseError(operationId, clientConnectionId, this, e); - } - else + SqlStatistics.StopTimer(statistics); + + // we only want to log this if the previous state of the + // connection is open, as that's the valid use-case + if (previousState != ConnectionState.Closed) { - s_diagnosticListener.WriteConnectionCloseAfter(operationId, clientConnectionId, this); + if (e != null) + { + s_diagnosticListener.WriteConnectionCloseError(operationId, clientConnectionId, this, e); + } + else + { + s_diagnosticListener.WriteConnectionCloseAfter(operationId, clientConnectionId, this); + } } } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -980,40 +999,49 @@ private void DisposeMe(bool disposing) /// public override void Open() { - Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); - - PrepareStatisticsForNewConnection(); - - SqlStatistics statistics = null; - - Exception e = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { - statistics = SqlStatistics.StartTimer(Statistics); + Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); - if (!TryOpen(null)) + PrepareStatisticsForNewConnection(); + + SqlStatistics statistics = null; + + Exception e = null; + try { - throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending); - } - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); + statistics = SqlStatistics.StartTimer(Statistics); - if (e != null) + if (!TryOpen(null)) + { + throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending); + } + } + catch (Exception ex) { - s_diagnosticListener.WriteConnectionOpenError(operationId, this, e); + e = ex; + throw; } - else + finally { - s_diagnosticListener.WriteConnectionOpenAfter(operationId, this); + SqlStatistics.StopTimer(statistics); + + if (e != null) + { + s_diagnosticListener.WriteConnectionOpenError(operationId, this, e); + } + else + { + s_diagnosticListener.WriteConnectionOpenAfter(operationId, this); + } } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal void RegisterWaitingForReconnect(Task waitingTask) @@ -1046,6 +1074,7 @@ private async Task ReconnectAsync(int timeout) { if (ctoken.IsCancellationRequested) { + SqlClientEventSource.Log.TraceEvent(" Orginal ClientConnectionID: {0} - reconnection cancelled.", _originalConnectionId.ToString()); return; } try @@ -1064,12 +1093,15 @@ private async Task ReconnectAsync(int timeout) { ForceNewConnection = false; } + SqlClientEventSource.Log.TraceEvent(" Reconnection suceeded. ClientConnectionID {0} -> {1}", _originalConnectionId.ToString(), ClientConnectionId.ToString()); return; } catch (SqlException e) { + SqlClientEventSource.Log.TraceEvent(" Orginal ClientConnectionID {0} - reconnection attempt failed error {1}", _originalConnectionId, e.Message); if (attempt == retryCount - 1) { + SqlClientEventSource.Log.TraceEvent(" Orginal ClientConnectionID {0} - give up reconnection", _originalConnectionId.ToString()); throw SQL.CR_AllAttemptsFailed(e, _originalConnectionId); } if (timeout > 0 && ADP.TimerRemaining(commandTimeoutExpiration) < ADP.TimerFromSeconds(ConnectRetryInterval)) @@ -1139,8 +1171,10 @@ internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) if (runningReconnect == null) { if (cData._unrecoverableStatesCount == 0) - { // could change since the first check, but now is stable since connection is know to be broken + { + // could change since the first check, but now is stable since connection is know to be broken _originalConnectionId = ClientConnectionId; + SqlClientEventSource.Log.TraceEvent(" Connection ClientConnectionID {0} is invalid, reconnecting", _originalConnectionId.ToString()); _recoverySessionData = cData; if (beforeDisconnect != null) { @@ -1233,82 +1267,91 @@ private void CancelOpenAndWait() /// public override Task OpenAsync(CancellationToken cancellationToken) { - Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); - - PrepareStatisticsForNewConnection(); - - SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.PoolerScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { - statistics = SqlStatistics.StartTimer(Statistics); + Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); - System.Transactions.Transaction transaction = ADP.GetCurrentTransaction(); - TaskCompletionSource completion = new TaskCompletionSource(transaction); - TaskCompletionSource result = new TaskCompletionSource(); + PrepareStatisticsForNewConnection(); - if (s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterOpenConnection) || - s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlErrorOpenConnection)) + SqlStatistics statistics = null; + try { - result.Task.ContinueWith((t) => + statistics = SqlStatistics.StartTimer(Statistics); + + System.Transactions.Transaction transaction = ADP.GetCurrentTransaction(); + TaskCompletionSource completion = new TaskCompletionSource(transaction); + TaskCompletionSource result = new TaskCompletionSource(); + + if (s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterOpenConnection) || + s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlErrorOpenConnection)) { - if (t.Exception != null) - { - s_diagnosticListener.WriteConnectionOpenError(operationId, this, t.Exception); - } - else + result.Task.ContinueWith((t) => { - s_diagnosticListener.WriteConnectionOpenAfter(operationId, this); - } - }, TaskScheduler.Default); - } + if (t.Exception != null) + { + s_diagnosticListener.WriteConnectionOpenError(operationId, this, t.Exception); + } + else + { + s_diagnosticListener.WriteConnectionOpenAfter(operationId, this); + } + }, TaskScheduler.Default); + } - if (cancellationToken.IsCancellationRequested) - { - result.SetCanceled(); - return result.Task; - } + if (cancellationToken.IsCancellationRequested) + { + result.SetCanceled(); + return result.Task; + } - bool completed; + bool completed; + + try + { + completed = TryOpen(completion); + } + catch (Exception e) + { + s_diagnosticListener.WriteConnectionOpenError(operationId, this, e); + result.SetException(e); + return result.Task; + } + + if (completed) + { + result.SetResult(null); + } + else + { + CancellationTokenRegistration registration = new CancellationTokenRegistration(); + if (cancellationToken.CanBeCanceled) + { + registration = cancellationToken.Register(s_openAsyncCancel, completion); + } + OpenAsyncRetry retry = new OpenAsyncRetry(this, completion, result, registration); + _currentCompletion = new Tuple, Task>(completion, result.Task); + completion.Task.ContinueWith(retry.Retry, TaskScheduler.Default); + return result.Task; + } - try - { - completed = TryOpen(completion); - } - catch (Exception e) - { - s_diagnosticListener.WriteConnectionOpenError(operationId, this, e); - result.SetException(e); return result.Task; } - - if (completed) + catch (Exception ex) { - result.SetResult(null); + s_diagnosticListener.WriteConnectionOpenError(operationId, this, ex); + throw; } - else + finally { - CancellationTokenRegistration registration = new CancellationTokenRegistration(); - if (cancellationToken.CanBeCanceled) - { - registration = cancellationToken.Register(s_openAsyncCancel, completion); - } - OpenAsyncRetry retry = new OpenAsyncRetry(this, completion, result, registration); - _currentCompletion = new Tuple, Task>(completion, result.Task); - completion.Task.ContinueWith(retry.Retry, TaskScheduler.Default); - return result.Task; + SqlStatistics.StopTimer(statistics); } - - return result.Task; - } - catch (Exception ex) - { - s_diagnosticListener.WriteConnectionOpenError(operationId, this, ex); - throw; } finally { - SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.PoolerScopeLeaveEvent(scopeID); } } @@ -1352,6 +1395,7 @@ public OpenAsyncRetry(SqlConnection parent, TaskCompletionSource retryTask) { + SqlClientEventSource.Log.TraceEvent(" {0}#", _parent.ObjectID); _registration.Dispose(); try { @@ -1609,6 +1653,7 @@ internal void OnError(SqlException exception, bool breakConnection, Action {0}#, Connection broken.", ObjectID); Close(); } }; @@ -1617,6 +1662,7 @@ internal void OnError(SqlException exception, bool breakConnection, Action {0}#, Connection broken.", ObjectID); Close(); } } @@ -1667,6 +1713,7 @@ internal void OnInfoMessage(SqlInfoMessageEventArgs imevent) internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Message='{1}'", ObjectID, (null != imevent) ? imevent.Message : ""); SqlInfoMessageEventHandler handler = InfoMessage; if (null != handler) { @@ -1692,84 +1739,102 @@ internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) /// public static void ChangePassword(string connectionString, string newPassword) { - if (string.IsNullOrEmpty(connectionString)) - { - throw SQL.ChangePasswordArgumentMissing(nameof(newPassword)); - } - if (string.IsNullOrEmpty(newPassword)) - { - throw SQL.ChangePasswordArgumentMissing(nameof(newPassword)); - } - if (TdsEnums.MAXLEN_NEWPASSWORD < newPassword.Length) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(""); + SqlClientEventSource.Log.CorrelationTraceEvent(" ActivityID {0}", ActivityCorrelator.Current.ToString()); + try { - throw ADP.InvalidArgumentLength(nameof(newPassword), TdsEnums.MAXLEN_NEWPASSWORD); - } + if (string.IsNullOrEmpty(connectionString)) + { + throw SQL.ChangePasswordArgumentMissing(nameof(newPassword)); + } + if (string.IsNullOrEmpty(newPassword)) + { + throw SQL.ChangePasswordArgumentMissing(nameof(newPassword)); + } + if (TdsEnums.MAXLEN_NEWPASSWORD < newPassword.Length) + { + throw ADP.InvalidArgumentLength(nameof(newPassword), TdsEnums.MAXLEN_NEWPASSWORD); + } - SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null); + SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null); - SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key); - if (connectionOptions.IntegratedSecurity) - { - throw SQL.ChangePasswordConflictsWithSSPI(); + SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key); + if (connectionOptions.IntegratedSecurity) + { + throw SQL.ChangePasswordConflictsWithSSPI(); + } + if (!string.IsNullOrEmpty(connectionOptions.AttachDBFilename)) + { + throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename); + } + + ChangePassword(connectionString, connectionOptions, null, newPassword, null); } - if (!string.IsNullOrEmpty(connectionOptions.AttachDBFilename)) + finally { - throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - ChangePassword(connectionString, connectionOptions, null, newPassword, null); } /// public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) { - if (string.IsNullOrEmpty(connectionString)) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(""); + SqlClientEventSource.Log.CorrelationTraceEvent(" ActivityID {0}", ActivityCorrelator.Current.ToString()); + try { - throw SQL.ChangePasswordArgumentMissing(nameof(connectionString)); - } + if (string.IsNullOrEmpty(connectionString)) + { + throw SQL.ChangePasswordArgumentMissing(nameof(connectionString)); + } - // check credential; not necessary to check the length of password in credential as the check is done by SqlCredential class - if (credential == null) - { - throw SQL.ChangePasswordArgumentMissing(nameof(credential)); - } + // check credential; not necessary to check the length of password in credential as the check is done by SqlCredential class + if (credential == null) + { + throw SQL.ChangePasswordArgumentMissing(nameof(credential)); + } - if (newSecurePassword == null || newSecurePassword.Length == 0) - { - throw SQL.ChangePasswordArgumentMissing(nameof(newSecurePassword)); - } + if (newSecurePassword == null || newSecurePassword.Length == 0) + { + throw SQL.ChangePasswordArgumentMissing(nameof(newSecurePassword)); + } - if (!newSecurePassword.IsReadOnly()) - { - throw ADP.MustBeReadOnly(nameof(newSecurePassword)); - } + if (!newSecurePassword.IsReadOnly()) + { + throw ADP.MustBeReadOnly(nameof(newSecurePassword)); + } - if (TdsEnums.MAXLEN_NEWPASSWORD < newSecurePassword.Length) - { - throw ADP.InvalidArgumentLength(nameof(newSecurePassword), TdsEnums.MAXLEN_NEWPASSWORD); - } + if (TdsEnums.MAXLEN_NEWPASSWORD < newSecurePassword.Length) + { + throw ADP.InvalidArgumentLength(nameof(newSecurePassword), TdsEnums.MAXLEN_NEWPASSWORD); + } - SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null); + SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null); - SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key); + SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key); - // Check for connection string values incompatible with SqlCredential - if (!string.IsNullOrEmpty(connectionOptions.UserID) || !string.IsNullOrEmpty(connectionOptions.Password)) - { - throw ADP.InvalidMixedArgumentOfSecureAndClearCredential(); - } + // Check for connection string values incompatible with SqlCredential + if (!string.IsNullOrEmpty(connectionOptions.UserID) || !string.IsNullOrEmpty(connectionOptions.Password)) + { + throw ADP.InvalidMixedArgumentOfSecureAndClearCredential(); + } - if (connectionOptions.IntegratedSecurity) - { - throw SQL.ChangePasswordConflictsWithSSPI(); - } + if (connectionOptions.IntegratedSecurity) + { + throw SQL.ChangePasswordConflictsWithSSPI(); + } + + if (!string.IsNullOrEmpty(connectionOptions.AttachDBFilename)) + { + throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename); + } - if (!string.IsNullOrEmpty(connectionOptions.AttachDBFilename)) + ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword); + } + finally { - throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword); } private static void ChangePassword(string connectionString, SqlConnectionString connectionOptions, SqlCredential credential, string newPassword, SecureString newSecurePassword) @@ -1873,6 +1938,10 @@ private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) Debug.Assert(TypeSystemAssemblyVersion != null, "TypeSystemAssembly should be set !"); if (string.Equals(asmRef.Name, "Microsoft.SqlServer.Types", StringComparison.OrdinalIgnoreCase)) { + if (asmRef.Version != TypeSystemAssemblyVersion && SqlClientEventSource.Log.IsTraceEnabled()) + { + SqlClientEventSource.Log.TraceEvent(" SQL CLR type version change: Server sent {0}, client will instantiate {1}", asmRef.Version.ToString(), TypeSystemAssemblyVersion.ToString()); + } asmRef.Version = TypeSystemAssemblyVersion; } try diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index cd746c2859..03fdf79d0b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -165,17 +165,34 @@ override protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions int connectionTimeout = opt.ConnectTimeout; if ((0 < connectionTimeout) && (connectionTimeout < int.MaxValue / 1000)) + { connectionTimeout *= 1000; + } else if (connectionTimeout >= int.MaxValue / 1000) + { connectionTimeout = int.MaxValue; + } + if (opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive) + { + // interactive mode will always have pool's CreateTimeout = 10 x ConnectTimeout. + if (connectionTimeout >= Int32.MaxValue / 10) + { + connectionTimeout = Int32.MaxValue; + } + else + { + connectionTimeout *= 10; + } + SqlClientEventSource.Log.TraceEvent("Set connection pool CreateTimeout={0} when AD Interactive is in use.", connectionTimeout); + } poolingOptions = new DbConnectionPoolGroupOptions( - opt.IntegratedSecurity, - opt.MinPoolSize, - opt.MaxPoolSize, - connectionTimeout, - opt.LoadBalanceTimeout, - opt.Enlist); + opt.IntegratedSecurity, + opt.MinPoolSize, + opt.MaxPoolSize, + connectionTimeout, + opt.LoadBalanceTimeout, + opt.Enlist); } return poolingOptions; } @@ -222,6 +239,15 @@ override internal DbConnectionInternal GetInnerConnection(DbConnection connectio return null; } + override protected int GetObjectId(DbConnection connection) + { + SqlConnection c = (connection as SqlConnection); + if (null != c) + { + return c.ObjectID; + } + return 0; + } override internal void PermissionDemand(DbConnection outerConnection) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index 2b4ffa16bd..20eb919642 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -23,6 +23,9 @@ public sealed partial class SqlConnection : DbConnection private DbConnectionInternal _innerConnection; private int _closeCount; + private static int _objectTypeCount; // Bid counter + internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); + /// public SqlConnection() : base() { @@ -57,6 +60,7 @@ internal DbConnectionOptions ConnectionOptions private string ConnectionString_Get() { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); bool hidePassword = InnerConnection.ShouldHidePassword; DbConnectionOptions connectionOptions = UserConnectionOptions; return ((null != connectionOptions) ? connectionOptions.UsersConnectionString(hidePassword) : ""); @@ -82,6 +86,8 @@ private void ConnectionString_Set(DbConnectionPoolKey key) { throw ADP.OpenConnectionPropertySet(nameof(ConnectionString), connectionInternal.State); } + string cstr = ((null != connectionOptions) ? connectionOptions.UsersConnectionStringForTrace() : ""); + SqlClientEventSource.Log.TraceEvent(" {0}#, '{1}'", ObjectID, cstr); } internal DbConnectionInternal InnerConnection @@ -123,6 +129,18 @@ internal void Abort(Exception e) Interlocked.CompareExchange(ref _innerConnection, DbConnectionClosedPreviouslyOpened.SingletonInstance, innerConnection); innerConnection.DoomThisConnection(); } + + // NOTE: we put the tracing last, because the ToString() calls (and + // the SqlClientEventSource.SqlClientEventSource.Log.Trace, for that matter) have no reliability contract and + // will end the reliable try... + if (e is OutOfMemoryException) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Aborting operation due to asynchronous exception: {'OutOfMemory'}", ObjectID); + } + else + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Aborting operation due to asynchronous exception: {1}", ObjectID, e.ToString()); + } } internal void AddWeakReference(object value, int tag) @@ -133,11 +151,19 @@ internal void AddWeakReference(object value, int tag) /// override protected DbCommand CreateDbCommand() { - DbCommand command = null; - DbProviderFactory providerFactory = ConnectionFactory.ProviderFactory; - command = providerFactory.CreateCommand(); - command.Connection = this; - return command; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + try + { + DbCommand command = null; + DbProviderFactory providerFactory = ConnectionFactory.ProviderFactory; + command = providerFactory.CreateCommand(); + command.Connection = this; + return command; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -158,6 +184,8 @@ override protected void Dispose(bool disposing) /// public override void EnlistTransaction(Transaction transaction) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection enlisting in a transaction.", ObjectID); + // If we're currently enlisted in a transaction and we were called // on the EnlistTransaction method (Whidbey) we're not allowed to // enlist in a different transaction. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs index bac95978c2..874c44287f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs @@ -53,6 +53,7 @@ internal void AliasCheck(string server) } else if (_alias != server) { + SqlClientEventSource.Log.TraceEvent(" alias change detected. Clearing PoolGroup"); base.PoolGroup.Clear(); _alias = server; } @@ -65,6 +66,7 @@ internal void FailoverCheck(SqlInternalConnection connection, bool actualUseFail { if (UseFailoverPartner != actualUseFailoverPartner) { + SqlClientEventSource.Log.TraceEvent(" Failover detected. failover partner='{0}'. Clearing PoolGroup", actualFailoverPartner); base.PoolGroup.Clear(); _useFailoverPartner = actualUseFailoverPartner; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs index c33cd99361..47c3d97f83 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs @@ -6,6 +6,7 @@ using System.Data; using System.Data.Common; using System.Diagnostics; +using System.Threading; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient @@ -21,6 +22,11 @@ public sealed class SqlDataAdapter : DbDataAdapter, IDbDataAdapter, ICloneable private SqlCommandSet _commandSet; private int _updateBatchSize = 1; + private static int _objectTypeCount; // Bid counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + + internal int ObjectID => _objectID; + /// public SqlDataAdapter() : base() { @@ -121,6 +127,7 @@ public override int UpdateBatchSize throw ADP.ArgumentOutOfRange(nameof(UpdateBatchSize)); } _updateBatchSize = value; + SqlClientEventSource.Log.TraceEvent(" {0}#, {1}", ObjectID, value); } } @@ -142,6 +149,7 @@ protected override void ClearBatch() protected override int ExecuteBatch() { Debug.Assert(null != _commandSet && (0 < _commandSet.CommandCount), "no commands"); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); return _commandSet.ExecuteNonQuery(); } @@ -164,6 +172,7 @@ protected override bool GetBatchedRecordsAffected(int commandIdentifier, out int /// protected override void InitializeBatching() { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); _commandSet = new SqlCommandSet(); SqlCommand command = SelectCommand; if (null == command) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index de121b9f8f..78f4cd718b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -71,9 +71,9 @@ internal class SharedState private FieldNameLookup _fieldNameLookup; private CommandBehavior _commandBehavior; - private static int s_objectTypeCount; // Bid counter + private static int s_objectTypeCount; // Eventsource counter private static readonly ReadOnlyCollection s_emptySchema = new ReadOnlyCollection(Array.Empty()); - internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount); + internal static readonly int ObjectID = Interlocked.Increment(ref s_objectTypeCount); // metadata (no explicit table, use 'Table') private MultiPartTableName[] _tableNames = null; @@ -844,6 +844,7 @@ protected override void Dispose(bool disposing) /// public override void Close() { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); SqlStatistics statistics = null; try { @@ -926,6 +927,7 @@ public override void Close() finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -1465,6 +1467,7 @@ override public int GetProviderSpecificValues(object[] values) public override DataTable GetSchemaTable() { SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1481,6 +1484,7 @@ public override DataTable GetSchemaTable() finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -3257,6 +3261,7 @@ override public bool NextResult() private bool TryNextResult(out bool more) { SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -3387,6 +3392,7 @@ private bool TryNextResult(out bool more) finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -3416,6 +3422,7 @@ override public bool Read() private bool TryReadInternal(bool setTimeout, out bool more) { SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -3568,6 +3575,7 @@ private bool TryReadInternal(bool setTimeout, out bool more) finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -3945,6 +3953,7 @@ private void RestoreServerSettings(TdsParser parser, TdsParserStateObject stateO // broken connection, so check state first. if (parser.State == TdsParserState.OpenLoggedIn) { + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID '{1}'", ObjectID, ActivityCorrelator.Current.ToString()); Task executeTask = parser.TdsExecuteSQLBatch(_resetOptionsString, (_command != null) ? _command.CommandTimeout : 0, null, stateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); @@ -4249,41 +4258,49 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn /// public override Task NextResultAsync(CancellationToken cancellationToken) { - TaskCompletionSource source = new TaskCompletionSource(); - - if (IsClosed) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + try { - source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed())); - return source.Task; - } + TaskCompletionSource source = new TaskCompletionSource(); - IDisposable registration = null; - if (cancellationToken.CanBeCanceled) - { - if (cancellationToken.IsCancellationRequested) + if (IsClosed) + { + source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed())); + return source.Task; + } + + IDisposable registration = null; + if (cancellationToken.CanBeCanceled) + { + if (cancellationToken.IsCancellationRequested) + { + source.SetCanceled(); + return source.Task; + } + registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); + } + + Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); + if (original != null) + { + source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); + return source.Task; + } + + // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) + if (_cancelAsyncOnCloseToken.IsCancellationRequested) { source.SetCanceled(); + _currentTask = null; return source.Task; } - registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); - } - Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); - if (original != null) - { - source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); - return source.Task; + return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registration)); } - - // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) - if (_cancelAsyncOnCloseToken.IsCancellationRequested) + finally { - source.SetCanceled(); - _currentTask = null; - return source.Task; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registration)); } private static Task NextResultAsyncExecute(Task task, object state) @@ -4291,6 +4308,7 @@ private static Task NextResultAsyncExecute(Task task, object state) HasNextResultAsyncCallContext context = (HasNextResultAsyncCallContext)state; if (task != null) { + SqlClientEventSource.Log.TraceEvent(" attempt retry {0}#", ObjectID); context._reader.PrepareForAsyncContinuation(); } @@ -4566,126 +4584,134 @@ private Task GetBytesAsyncReadDataStage(GetBytesAsyncCallContext context, b /// public override Task ReadAsync(CancellationToken cancellationToken) { - if (IsClosed) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + try { - return Task.FromException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed())); - } + if (IsClosed) + { + return Task.FromException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed())); + } - // If user's token is canceled, return a canceled task - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } + // If user's token is canceled, return a canceled task + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } - // Check for existing async - if (_currentTask != null) - { - return Task.FromException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); - } + // Check for existing async + if (_currentTask != null) + { + return Task.FromException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); + } - // These variables will be captured in moreFunc so that we can skip searching for a row token once one has been read - bool rowTokenRead = false; - bool more = false; + // These variables will be captured in moreFunc so that we can skip searching for a row token once one has been read + bool rowTokenRead = false; + bool more = false; - // Shortcut, do we have enough data to immediately do the ReadAsync? - try - { - // First, check if we can finish reading the current row - // NOTE: If we are in SingleRow mode and we've read that single row (i.e. _haltRead == true), then skip the shortcut - if ((!_haltRead) && ((!_sharedState._dataReady) || (WillHaveEnoughData(_metaData.Length - 1)))) + // Shortcut, do we have enough data to immediately do the ReadAsync? + try { -#if DEBUG - try + // First, check if we can finish reading the current row + // NOTE: If we are in SingleRow mode and we've read that single row (i.e. _haltRead == true), then skip the shortcut + if ((!_haltRead) && ((!_sharedState._dataReady) || (WillHaveEnoughData(_metaData.Length - 1)))) { - _stateObj._shouldHaveEnoughData = true; -#endif - if (_sharedState._dataReady) - { - // Clean off current row - CleanPartialReadReliable(); - } - - // If there a ROW token ready (as well as any metadata for the row) - if (_stateObj.IsRowTokenReady()) +#if DEBUG + try { - // Read the ROW token - bool result = TryReadInternal(true, out more); - Debug.Assert(result, "Should not have run out of data"); + _stateObj._shouldHaveEnoughData = true; +#endif + if (_sharedState._dataReady) + { + // Clean off current row + CleanPartialReadReliable(); + } - rowTokenRead = true; - if (more) + // If there a ROW token ready (as well as any metadata for the row) + if (_stateObj.IsRowTokenReady()) { - // Sequential mode, nothing left to do - if (IsCommandBehavior(CommandBehavior.SequentialAccess)) + // Read the ROW token + bool result = TryReadInternal(true, out more); + Debug.Assert(result, "Should not have run out of data"); + + rowTokenRead = true; + if (more) { - return ADP.TrueTask; + // Sequential mode, nothing left to do + if (IsCommandBehavior(CommandBehavior.SequentialAccess)) + { + return ADP.TrueTask; + } + // For non-sequential, check if we can read the row data now + else if (WillHaveEnoughData(_metaData.Length - 1)) + { + // Read row data + result = TryReadColumn(_metaData.Length - 1, setTimeout: true); + Debug.Assert(result, "Should not have run out of data"); + return ADP.TrueTask; + } } - // For non-sequential, check if we can read the row data now - else if (WillHaveEnoughData(_metaData.Length - 1)) + else { - // Read row data - result = TryReadColumn(_metaData.Length - 1, setTimeout: true); - Debug.Assert(result, "Should not have run out of data"); - return ADP.TrueTask; + // No data left, return + return ADP.FalseTask; } } - else - { - // No data left, return - return ADP.FalseTask; - } - } #if DEBUG + } + finally + { + _stateObj._shouldHaveEnoughData = false; + } +#endif } - finally + } + catch (Exception ex) + { + if (!ADP.IsCatchableExceptionType(ex)) { - _stateObj._shouldHaveEnoughData = false; + throw; } -#endif + return Task.FromException(ex); } - } - catch (Exception ex) - { - if (!ADP.IsCatchableExceptionType(ex)) + + TaskCompletionSource source = new TaskCompletionSource(); + Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); + if (original != null) { - throw; + source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); + return source.Task; } - return Task.FromException(ex); - } - - TaskCompletionSource source = new TaskCompletionSource(); - Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); - if (original != null) - { - source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists())); - return source.Task; - } - // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) - if (_cancelAsyncOnCloseToken.IsCancellationRequested) - { - source.SetCanceled(); - _currentTask = null; - return source.Task; - } + // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) + if (_cancelAsyncOnCloseToken.IsCancellationRequested) + { + source.SetCanceled(); + _currentTask = null; + return source.Task; + } - IDisposable registration = null; - if (cancellationToken.CanBeCanceled) - { - registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); - } + IDisposable registration = null; + if (cancellationToken.CanBeCanceled) + { + registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); + } - var context = Interlocked.Exchange(ref _cachedReadAsyncContext, null) ?? new ReadAsyncCallContext(); + var context = Interlocked.Exchange(ref _cachedReadAsyncContext, null) ?? new ReadAsyncCallContext(); - Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ReadAsyncCallContext was not properly disposed"); + Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ReadAsyncCallContext was not properly disposed"); - context.Set(this, source, registration); - context._hasMoreData = more; - context._hasReadRowToken = rowTokenRead; + context.Set(this, source, registration); + context._hasMoreData = more; + context._hasReadRowToken = rowTokenRead; - PrepareAsyncInvocation(useSnapshot: true); + PrepareAsyncInvocation(useSnapshot: true); - return InvokeAsyncCall(context); + return InvokeAsyncCall(context); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } private static Task ReadAsyncExecute(Task task, object state) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index d8a580f4a7..fb4d16c502 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -14,15 +14,9 @@ namespace Microsoft.Data.SqlClient sealed internal partial class SqlDelegatedTransaction : IPromotableSinglePhaseNotification { private static int _objectTypeCount; - private readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + private const int _globalTransactionsTokenVersionSizeInBytes = 4; // the size of the version in the PromotedDTCToken for Global Transactions - internal int ObjectID - { - get - { - return _objectID; - } - } // WARNING!!! Multithreaded object! // Locking strategy: Any potentailly-multithreaded operation must first lock the associated connection, then @@ -86,12 +80,15 @@ public void Initialize() // transaction. SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; - + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, delegating transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { if (connection.IsEnlistedInTransaction) - { // defect first + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, was enlisted, now defecting.", ObjectID, connection.ObjectID); + + // defect first connection.EnlistNull(); } @@ -144,7 +141,7 @@ public byte[] Promote() Exception promoteException; byte[] returnValue = null; SqlConnection usersConnection = connection.Connection; - + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, promoting transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -223,7 +220,7 @@ public void Rollback(SinglePhaseEnlistment enlistment) SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; - + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, aborting transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -296,7 +293,7 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) SqlInternalConnection connection = GetValidConnection(); SqlConnection usersConnection = connection.Connection; - + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, committing transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -407,6 +404,7 @@ internal void TransactionEnded(Transaction transaction) if (connection != null) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Connection {1}#, transaction completed externally.", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependency.cs index b7807538bd..eb93cf8463 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -228,6 +228,9 @@ private static void InvokeCallback(object eventContextPair) private static readonly string s_assemblyName = (typeof(SqlDependencyProcessDispatcher)).Assembly.FullName; private static readonly string s_typeName = (typeof(SqlDependencyProcessDispatcher)).FullName; + private static int _objectTypeCount; // EventSourceCounter counter + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + /// // Constructors public SqlDependency() : this(null, null, SQL.SqlDependencyTimeoutDefault) @@ -242,19 +245,27 @@ public SqlDependency(SqlCommand command) : this(command, null, SQL.SqlDependency /// public SqlDependency(SqlCommand command, string options, int timeout) { - if (timeout < 0) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, options: '{1}', timeout: '{2}'", ObjectID, options, timeout); + try { - throw SQL.InvalidSqlDependencyTimeout(nameof(timeout)); - } - _timeout = timeout; + if (timeout < 0) + { + throw SQL.InvalidSqlDependencyTimeout(nameof(timeout)); + } + _timeout = timeout; - if (null != options) - { // Ignore null value - will force to default. - _options = options; - } + if (null != options) + { // Ignore null value - will force to default. + _options = options; + } - AddCommandInternal(command); - SqlDependencyPerAppDomainDispatcher.SingletonInstance.AddDependencyEntry(this); // Add dep to hashtable with Id. + AddCommandInternal(command); + SqlDependencyPerAppDomainDispatcher.SingletonInstance.AddDependencyEntry(this); // Add dep to hashtable with Id. + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Public Properties @@ -283,50 +294,66 @@ public event OnChangeEventHandler OnChange // EventHandlers to be fired when dependency is notified. add { - if (null != value) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - SqlNotificationEventArgs sqlNotificationEvent = null; - - lock (_eventHandlerLock) + if (null != value) { - if (_dependencyFired) - { // If fired, fire the new event immediately. - sqlNotificationEvent = new SqlNotificationEventArgs(SqlNotificationType.Subscribe, SqlNotificationInfo.AlreadyChanged, SqlNotificationSource.Client); - } - else + SqlNotificationEventArgs sqlNotificationEvent = null; + + lock (_eventHandlerLock) { - EventContextPair pair = new EventContextPair(value, this); - if (!_eventList.Contains(pair)) - { - _eventList.Add(pair); + if (_dependencyFired) + { // If fired, fire the new event immediately. + sqlNotificationEvent = new SqlNotificationEventArgs(SqlNotificationType.Subscribe, SqlNotificationInfo.AlreadyChanged, SqlNotificationSource.Client); } else { - throw SQL.SqlDependencyEventNoDuplicate(); + EventContextPair pair = new EventContextPair(value, this); + if (!_eventList.Contains(pair)) + { + _eventList.Add(pair); + } + else + { + throw SQL.SqlDependencyEventNoDuplicate(); + } } } - } - if (null != sqlNotificationEvent) - { // Delay firing the event until outside of lock. - value(this, sqlNotificationEvent); + if (null != sqlNotificationEvent) + { // Delay firing the event until outside of lock. + value(this, sqlNotificationEvent); + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } remove { - if (null != value) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - EventContextPair pair = new EventContextPair(value, this); - lock (_eventHandlerLock) + if (null != value) { - int index = _eventList.IndexOf(pair); - if (0 <= index) + EventContextPair pair = new EventContextPair(value, this); + lock (_eventHandlerLock) { - _eventList.RemoveAt(index); + int index = _eventList.IndexOf(pair); + if (0 <= index) + { + _eventList.RemoveAt(index); + } } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } } @@ -335,13 +362,21 @@ public event OnChangeEventHandler OnChange public void AddCommandDependency(SqlCommand command) { // Adds command to dependency collection so we automatically create the SqlNotificationsRequest object - // and listen for a notification for the added commands. - if (command == null) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - throw ADP.ArgumentNull(nameof(command)); - } + // and listen for a notification for the added commands. + if (command == null) + { + throw ADP.ArgumentNull(nameof(command)); + } - AddCommandInternal(command); + AddCommandInternal(command); + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Static Methods - public & internal @@ -361,114 +396,126 @@ public static bool Start(string connectionString, string queue) internal static bool Start(string connectionString, string queue, bool useDefaults) { - if (string.IsNullOrEmpty(connectionString)) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" AppDomainKey: '{0}', queue: '{1}'", AppDomainKey, queue); + try { - if (null == connectionString) + if (string.IsNullOrEmpty(connectionString)) { - throw ADP.ArgumentNull(nameof(connectionString)); - } - else - { - throw ADP.Argument(nameof(connectionString)); + if (null == connectionString) + { + throw ADP.ArgumentNull(nameof(connectionString)); + } + else + { + throw ADP.Argument(nameof(connectionString)); + } } - } - if (!useDefaults && string.IsNullOrEmpty(queue)) - { // If specified but null or empty, use defaults. - useDefaults = true; - queue = null; // Force to null - for proper hashtable comparison for default case. - } + if (!useDefaults && string.IsNullOrEmpty(queue)) + { // If specified but null or empty, use defaults. + useDefaults = true; + queue = null; // Force to null - for proper hashtable comparison for default case. + } - // End duplicate Start/Stop logic. + // End duplicate Start/Stop logic. - bool errorOccurred = false; - bool result = false; + bool errorOccurred = false; + bool result = false; - lock (s_startStopLock) - { - try + lock (s_startStopLock) { - if (null == s_processDispatcher) - { // Ensure _processDispatcher reference is present - inside lock. - s_processDispatcher = SqlDependencyProcessDispatcher.SingletonProcessDispatcher; - } + try + { + if (null == s_processDispatcher) + { // Ensure _processDispatcher reference is present - inside lock. + s_processDispatcher = SqlDependencyProcessDispatcher.SingletonProcessDispatcher; + } - if (useDefaults) - { // Default listener. - string server = null; - DbConnectionPoolIdentity identity = null; - string user = null; - string database = null; - string service = null; - bool appDomainStart = false; + if (useDefaults) + { // Default listener. + string server = null; + DbConnectionPoolIdentity identity = null; + string user = null; + string database = null; + string service = null; + bool appDomainStart = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { // CER to ensure that if Start succeeds we add to hash completing setup. - // Start using process wide default service/queue & database from connection string. - result = s_processDispatcher.StartWithDefault( - connectionString, - out server, - out identity, - out user, - out database, - ref service, - s_appDomainKey, - SqlDependencyPerAppDomainDispatcher.SingletonInstance, - out errorOccurred, - out appDomainStart); - } - finally - { - if (appDomainStart && !errorOccurred) - { // If success, add to hashtable. - IdentityUserNamePair identityUser = new IdentityUserNamePair(identity, user); - DatabaseServicePair databaseService = new DatabaseServicePair(database, service); - if (!AddToServerUserHash(server, identityUser, databaseService)) - { - try + RuntimeHelpers.PrepareConstrainedRegions(); + try + { // CER to ensure that if Start succeeds we add to hash completing setup. + // Start using process wide default service/queue & database from connection string. + result = s_processDispatcher.StartWithDefault( + connectionString, + out server, + out identity, + out user, + out database, + ref service, + s_appDomainKey, + SqlDependencyPerAppDomainDispatcher.SingletonInstance, + out errorOccurred, + out appDomainStart); + SqlClientEventSource.Log.NotificationsTraceEvent(" Start (defaults) returned: '{0}', with service: '{1}', server: '{2}', database: '{3}'", result, service, server, database); + } + finally + { + if (appDomainStart && !errorOccurred) + { // If success, add to hashtable. + IdentityUserNamePair identityUser = new IdentityUserNamePair(identity, user); + DatabaseServicePair databaseService = new DatabaseServicePair(database, service); + if (!AddToServerUserHash(server, identityUser, databaseService)) { - Stop(connectionString, queue, useDefaults, true); - } - catch (Exception e) - { // Discard stop failure! - if (!ADP.IsCatchableExceptionType(e)) + try { - throw; + Stop(connectionString, queue, useDefaults, true); } - - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + catch (Exception e) + { // Discard stop failure! + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + SqlClientEventSource.Log.NotificationsTrace(" Exception occurred from Stop() after duplicate was found on Start()."); + } + throw SQL.SqlDependencyDuplicateStart(); } - throw SQL.SqlDependencyDuplicateStart(); } } } + else + { // Start with specified service/queue & database. + result = s_processDispatcher.Start( + connectionString, + queue, + s_appDomainKey, + SqlDependencyPerAppDomainDispatcher.SingletonInstance); + SqlClientEventSource.Log.NotificationsTraceEvent(" Start (user provided queue) returned: '{0}'", result); + + // No need to call AddToServerDatabaseHash since if not using default queue user is required + // to provide options themselves. + } } - else - { // Start with specified service/queue & database. - result = s_processDispatcher.Start( - connectionString, - queue, - s_appDomainKey, - SqlDependencyPerAppDomainDispatcher.SingletonInstance); - // No need to call AddToServerDatabaseHash since if not using default queue user is required - // to provide options themselves. - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) + catch (Exception e) { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + SqlClientEventSource.Log.NotificationsTraceEvent(" Exception occurred from _processDispatcher.Start(...), calling Invalidate(...)."); throw; } - - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. - - throw; } - } - return result; + return result; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } /// @@ -485,185 +532,222 @@ public static bool Stop(string connectionString, string queue) internal static bool Stop(string connectionString, string queue, bool useDefaults, bool startFailed) { - if (string.IsNullOrEmpty(connectionString)) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" AppDomainKey: '{0}', queue: '{1}'", AppDomainKey, queue); + try { - if (null == connectionString) - { - throw ADP.ArgumentNull(nameof(connectionString)); - } - else + if (string.IsNullOrEmpty(connectionString)) { - throw ADP.Argument(nameof(connectionString)); + if (null == connectionString) + { + throw ADP.ArgumentNull(nameof(connectionString)); + } + else + { + throw ADP.Argument(nameof(connectionString)); + } } - } - if (!useDefaults && string.IsNullOrEmpty(queue)) - { // If specified but null or empty, use defaults. - useDefaults = true; - queue = null; // Force to null - for proper hashtable comparison for default case. - } - - // End duplicate Start/Stop logic. + if (!useDefaults && string.IsNullOrEmpty(queue)) + { // If specified but null or empty, use defaults. + useDefaults = true; + queue = null; // Force to null - for proper hashtable comparison for default case. + } - bool result = false; + // End duplicate Start/Stop logic. - lock (s_startStopLock) - { - if (null != s_processDispatcher) - { // If _processDispatcher null, no Start has been called. - try - { - string server = null; - DbConnectionPoolIdentity identity = null; - string user = null; - string database = null; - string service = null; + bool result = false; - if (useDefaults) + lock (s_startStopLock) + { + if (null != s_processDispatcher) + { // If _processDispatcher null, no Start has been called. + try { - bool appDomainStop = false; + string server = null; + DbConnectionPoolIdentity identity = null; + string user = null; + string database = null; + string service = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { // CER to ensure that if Stop succeeds we remove from hash completing teardown. - // Start using process wide default service/queue & database from connection string. + if (useDefaults) + { + bool appDomainStop = false; + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { // CER to ensure that if Stop succeeds we remove from hash completing teardown. + // Start using process wide default service/queue & database from connection string. + result = s_processDispatcher.Stop( + connectionString, + out server, + out identity, + out user, + out database, + ref service, + s_appDomainKey, + out appDomainStop); + } + finally + { + if (appDomainStop && !startFailed) + { // If success, remove from hashtable. + Debug.Assert(!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(database), "Server or Database null/Empty upon successfull Stop()!"); + IdentityUserNamePair identityUser = new IdentityUserNamePair(identity, user); + DatabaseServicePair databaseService = new DatabaseServicePair(database, service); + RemoveFromServerUserHash(server, identityUser, databaseService); + } + } + } + else + { result = s_processDispatcher.Stop( connectionString, out server, out identity, out user, out database, - ref service, + ref queue, s_appDomainKey, - out appDomainStop); - } - finally - { - if (appDomainStop && !startFailed) - { // If success, remove from hashtable. - Debug.Assert(!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(database), "Server or Database null/Empty upon successfull Stop()!"); - IdentityUserNamePair identityUser = new IdentityUserNamePair(identity, user); - DatabaseServicePair databaseService = new DatabaseServicePair(database, service); - RemoveFromServerUserHash(server, identityUser, databaseService); - } + out bool ignored); + // No need to call RemoveFromServerDatabaseHash since if not using default queue user is required + // to provide options themselves. } } - else + catch (Exception e) { - result = s_processDispatcher.Stop( - connectionString, - out server, - out identity, - out user, - out database, - ref queue, - s_appDomainKey, - out bool ignored); - // No need to call RemoveFromServerDatabaseHash since if not using default queue user is required - // to provide options themselves. - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + } } } + return result; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - return result; } // General static utility functions private static bool AddToServerUserHash(string server, IdentityUserNamePair identityUser, DatabaseServicePair databaseService) { - bool result = false; - - lock (s_serverUserHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" server: '{0}', database: '{1}', service: '{2}'", server, databaseService.Database, databaseService.Service); + try { - Dictionary> identityDatabaseHash; + bool result = false; - if (!s_serverUserHash.ContainsKey(server)) + lock (s_serverUserHash) { - identityDatabaseHash = new Dictionary>(); - s_serverUserHash.Add(server, identityDatabaseHash); - } - else - { - identityDatabaseHash = s_serverUserHash[server]; - } + Dictionary> identityDatabaseHash; - List databaseServiceList; + if (!s_serverUserHash.ContainsKey(server)) + { + SqlClientEventSource.Log.NotificationsTrace(" Hash did not contain server, adding."); + identityDatabaseHash = new Dictionary>(); + s_serverUserHash.Add(server, identityDatabaseHash); + } + else + { + identityDatabaseHash = s_serverUserHash[server]; + } - if (!identityDatabaseHash.ContainsKey(identityUser)) - { - databaseServiceList = new List(); - identityDatabaseHash.Add(identityUser, databaseServiceList); - } - else - { - databaseServiceList = identityDatabaseHash[identityUser]; - } + List databaseServiceList; - if (!databaseServiceList.Contains(databaseService)) - { - databaseServiceList.Add(databaseService); - result = true; + if (!identityDatabaseHash.ContainsKey(identityUser)) + { + SqlClientEventSource.Log.NotificationsTrace(" Hash contained server but not user, adding user."); + databaseServiceList = new List(); + identityDatabaseHash.Add(identityUser, databaseServiceList); + } + else + { + databaseServiceList = identityDatabaseHash[identityUser]; + } + + if (!databaseServiceList.Contains(databaseService)) + { + SqlClientEventSource.Log.NotificationsTrace(" Adding database."); + databaseServiceList.Add(databaseService); + result = true; + } + else + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - hash already contained server, user, and database - we will throw!."); + } } - } - return result; + return result; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private static void RemoveFromServerUserHash(string server, IdentityUserNamePair identityUser, DatabaseServicePair databaseService) { - lock (s_serverUserHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" server: '{0}', database: '{1}', service: '{2}'", server, databaseService.Database, databaseService.Service); + try { - Dictionary> identityDatabaseHash; - - if (s_serverUserHash.ContainsKey(server)) + lock (s_serverUserHash) { - identityDatabaseHash = s_serverUserHash[server]; + Dictionary> identityDatabaseHash; - List databaseServiceList; - - if (identityDatabaseHash.ContainsKey(identityUser)) + if (s_serverUserHash.ContainsKey(server)) { - databaseServiceList = identityDatabaseHash[identityUser]; + identityDatabaseHash = s_serverUserHash[server]; - int index = databaseServiceList.IndexOf(databaseService); - if (index >= 0) + List databaseServiceList; + + if (identityDatabaseHash.ContainsKey(identityUser)) { - databaseServiceList.RemoveAt(index); + databaseServiceList = identityDatabaseHash[identityUser]; - if (databaseServiceList.Count == 0) + int index = databaseServiceList.IndexOf(databaseService); + if (index >= 0) { - identityDatabaseHash.Remove(identityUser); + SqlClientEventSource.Log.NotificationsTrace(" Hash contained server, user, and database - removing database."); + databaseServiceList.RemoveAt(index); - if (identityDatabaseHash.Count == 0) + if (databaseServiceList.Count == 0) { - s_serverUserHash.Remove(server); + SqlClientEventSource.Log.NotificationsTrace(" databaseServiceList count 0, removing the list for this server and user."); + identityDatabaseHash.Remove(identityUser); + + if (identityDatabaseHash.Count == 0) + { + SqlClientEventSource.Log.NotificationsTrace(" identityDatabaseHash count 0, removing the hash for this server."); + s_serverUserHash.Remove(server); + } } } + else + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - hash contained server and user but not database!"); + Debug.Fail("Unexpected state - hash did not contain database!"); + } } else { - Debug.Fail("Unexpected state - hash did not contain database!"); + SqlClientEventSource.Log.NotificationsTrace(" ERROR - hash contained server but not user!"); + Debug.Fail("Unexpected state - hash did not contain user!"); } } else { - Debug.Fail("Unexpected state - hash did not contain user!"); + SqlClientEventSource.Log.NotificationsTrace(" ERROR - hash did not contain server!"); + Debug.Fail("Unexpected state - hash did not contain server!"); } } - else - { - Debug.Fail("Unexpected state - hash did not contain server!"); - } + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } @@ -671,86 +755,104 @@ internal static string GetDefaultComposedOptions(string server, string failoverS { // Server must be an exact match, but user and database only needs to match exactly if there is more than one // for the given user or database passed. That is ambiguious and we must fail. - string result; - - lock (s_serverUserHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" server: '{0}', failoverServer: '{1}', database: '{2}'", server, failoverServer, database); + try { - if (!s_serverUserHash.ContainsKey(server)) + string result; + + lock (s_serverUserHash) { - if (0 == s_serverUserHash.Count) - { // Special error for no calls to start. - throw SQL.SqlDepDefaultOptionsButNoStart(); - } - else if (!string.IsNullOrEmpty(failoverServer) && s_serverUserHash.ContainsKey(failoverServer)) - { - server = failoverServer; - } - else + if (!s_serverUserHash.ContainsKey(server)) { - throw SQL.SqlDependencyNoMatchingServerStart(); + if (0 == s_serverUserHash.Count) + { + // Special error for no calls to start. + SqlClientEventSource.Log.NotificationsTrace(" ERROR - no start calls have been made, about to throw."); + throw SQL.SqlDepDefaultOptionsButNoStart(); + } + else if (!string.IsNullOrEmpty(failoverServer) && s_serverUserHash.ContainsKey(failoverServer)) + { + SqlClientEventSource.Log.NotificationsTrace(" using failover server instead\n"); + server = failoverServer; + } + else + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - not listening to this server, about to throw."); + throw SQL.SqlDependencyNoMatchingServerStart(); + } } - } - Dictionary> identityDatabaseHash = s_serverUserHash[server]; + Dictionary> identityDatabaseHash = s_serverUserHash[server]; - List databaseList = null; + List databaseList = null; - if (!identityDatabaseHash.ContainsKey(identityUser)) - { - if (identityDatabaseHash.Count > 1) + if (!identityDatabaseHash.ContainsKey(identityUser)) { - throw SQL.SqlDependencyNoMatchingServerStart(); + if (identityDatabaseHash.Count > 1) + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - not listening for this user, " + + "but listening to more than one other user, about to throw."); + throw SQL.SqlDependencyNoMatchingServerStart(); + } + else + { + // Since only one user, - use that. + // Foreach - but only one value present. + foreach (KeyValuePair> entry in identityDatabaseHash) + { + databaseList = entry.Value; + break; // Only iterate once. + } + } } else { - // Since only one user, - use that. - // Foreach - but only one value present. - foreach (KeyValuePair> entry in identityDatabaseHash) - { - databaseList = entry.Value; - break; // Only iterate once. - } + databaseList = identityDatabaseHash[identityUser]; } - } - else - { - databaseList = identityDatabaseHash[identityUser]; - } - DatabaseServicePair pair = new DatabaseServicePair(database, null); - DatabaseServicePair resultingPair = null; - int index = databaseList.IndexOf(pair); - if (index != -1) - { // Exact match found, use it. - resultingPair = databaseList[index]; - } + DatabaseServicePair pair = new DatabaseServicePair(database, null); + DatabaseServicePair resultingPair = null; + int index = databaseList.IndexOf(pair); + if (index != -1) + { // Exact match found, use it. + resultingPair = databaseList[index]; + } - if (null != resultingPair) - { // Exact database match. - database = FixupServiceOrDatabaseName(resultingPair.Database); // Fixup in place. - string quotedService = FixupServiceOrDatabaseName(resultingPair.Service); - result = "Service=" + quotedService + ";Local Database=" + database; - } - else - { // No exact database match found. - if (databaseList.Count == 1) - { // If only one database for this server/user, use it. - object[] temp = databaseList.ToArray(); // Must copy, no other choice but foreach. - resultingPair = (DatabaseServicePair)temp[0]; - Debug.Assert(temp.Length == 1, "If databaseList.Count==1, why does copied array have length other than 1?"); - string quotedDatabase = FixupServiceOrDatabaseName(resultingPair.Database); + if (null != resultingPair) + { // Exact database match. + database = FixupServiceOrDatabaseName(resultingPair.Database); // Fixup in place. string quotedService = FixupServiceOrDatabaseName(resultingPair.Service); - result = "Service=" + quotedService + ";Local Database=" + quotedDatabase; + result = "Service=" + quotedService + ";Local Database=" + database; } else - { // More than one database for given server, ambiguous - fail the default case! - throw SQL.SqlDependencyNoMatchingServerDatabaseStart(); + { // No exact database match found. + if (databaseList.Count == 1) + { + // If only one database for this server/user, use it. + object[] temp = databaseList.ToArray(); // Must copy, no other choice but foreach. + resultingPair = (DatabaseServicePair)temp[0]; + Debug.Assert(temp.Length == 1, "If databaseList.Count==1, why does copied array have length other than 1?"); + string quotedDatabase = FixupServiceOrDatabaseName(resultingPair.Database); + string quotedService = FixupServiceOrDatabaseName(resultingPair.Service); + result = "Service=" + quotedService + ";Local Database=" + quotedDatabase; + } + else + { + // More than one database for given server, ambiguous - fail the default case! + SqlClientEventSource.Log.NotificationsTrace(" ERROR - SqlDependency.Start called multiple times for this server/user, but no matching database."); + throw SQL.SqlDependencyNoMatchingServerDatabaseStart(); + } } } - } - Debug.Assert(!string.IsNullOrEmpty(result), "GetDefaultComposedOptions should never return null or empty string!"); - return result; + Debug.Assert(!string.IsNullOrEmpty(result), "GetDefaultComposedOptions should never return null or empty string!"); + SqlClientEventSource.Log.NotificationsTraceEvent(" resulting options: '{0}'.", result); + return result; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Internal Methods @@ -759,16 +861,24 @@ internal static string GetDefaultComposedOptions(string server, string failoverS // use this list for a reverse lookup based on server. internal void AddToServerList(string server) { - lock (_serverList) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, server: '{1}'", ObjectID, server); + try { - int index = _serverList.BinarySearch(server, StringComparer.OrdinalIgnoreCase); - if (0 > index) - { // If less than 0, item was not found in list. - index = ~index; // BinarySearch returns the 2's compliment of where the item should be inserted to preserver a sorted list after insertion. - _serverList.Insert(index, server); + lock (_serverList) + { + int index = _serverList.BinarySearch(server, StringComparer.OrdinalIgnoreCase); + if (0 > index) + { // If less than 0, item was not found in list. + index = ~index; // BinarySearch returns the 2's compliment of where the item should be inserted to preserver a sorted list after insertion. + _serverList.Insert(index, server); + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } internal bool ContainsServer(string server) @@ -781,70 +891,105 @@ internal bool ContainsServer(string server) internal string ComputeHashAndAddToDispatcher(SqlCommand command) { - // Create a string representing the concatenation of the connection string, command text and .ToString on all parameter values. - // This string will then be mapped to unique notification ID (new GUID). We add the guid and the hash to the app domain - // dispatcher to be able to map back to the dependency that needs to be fired for a notification of this - // command. + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlCommand: {1}#", ObjectID, command.ObjectID); + try + { + // Create a string representing the concatenation of the connection string, command text and .ToString on all parameter values. + // This string will then be mapped to unique notification ID (new GUID). We add the guid and the hash to the app domain + // dispatcher to be able to map back to the dependency that needs to be fired for a notification of this + // command. - // Add Connection string to prevent redundant notifications when same command is running against different databases or SQL servers - string commandHash = ComputeCommandHash(command.Connection.ConnectionString, command); // calculate the string representation of command + // Add Connection string to prevent redundant notifications when same command is running against different databases or SQL servers + string commandHash = ComputeCommandHash(command.Connection.ConnectionString, command); // calculate the string representation of command - string idString = SqlDependencyPerAppDomainDispatcher.SingletonInstance.AddCommandEntry(commandHash, this); // Add to map. - return idString; + string idString = SqlDependencyPerAppDomainDispatcher.SingletonInstance.AddCommandEntry(commandHash, this); // Add to map. + SqlClientEventSource.Log.NotificationsTraceEvent(" computed id string: '{0}'.", idString); + return idString; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } internal void Invalidate(SqlNotificationType type, SqlNotificationInfo info, SqlNotificationSource source) { - List eventList = null; - - lock (_eventHandlerLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - if (_dependencyFired && - SqlNotificationInfo.AlreadyChanged != info && - SqlNotificationSource.Client != source) + List eventList = null; + + lock (_eventHandlerLock) { + if (_dependencyFired && + SqlNotificationInfo.AlreadyChanged != info && + SqlNotificationSource.Client != source) + { - if (ExpirationTime >= DateTime.UtcNow) + if (ExpirationTime >= DateTime.UtcNow) + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - notification received twice - we should never enter this state!"); + Debug.Fail("Received notification twice - we should never enter this state!"); + } + else + { + // There is a small window in which SqlDependencyPerAppDomainDispatcher.TimeoutTimerCallback + // raises Timeout event but before removing this event from the list. If notification is received from + // server in this case, we will hit this code path. + // It is safe to ignore this race condition because no event is sent to user and no leak happens. + SqlClientEventSource.Log.NotificationsTrace(" ignore notification received after timeout!"); + } + } + else { - Debug.Fail("Received notification twice - we should never enter this state!"); + // It is the invalidators responsibility to remove this dependency from the app domain static hash. + _dependencyFired = true; + eventList = _eventList; + _eventList = new List(); // Since we are firing the events, null so we do not fire again. } } - else + + if (eventList != null) { - // It is the invalidators responsibility to remove this dependency from the app domain static hash. - _dependencyFired = true; - eventList = _eventList; - _eventList = new List(); // Since we are firing the events, null so we do not fire again. + SqlClientEventSource.Log.NotificationsTrace(" Firing events."); + foreach (EventContextPair pair in eventList) + { + pair.Invoke(new SqlNotificationEventArgs(type, info, source)); + } } } - - if (eventList != null) + finally { - foreach (EventContextPair pair in eventList) - { - pair.Invoke(new SqlNotificationEventArgs(type, info, source)); - } + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } // This method is used by SqlCommand. internal void StartTimer(SqlNotificationRequest notificationRequest) { - if (_expirationTime == DateTime.MaxValue) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - int seconds = SQL.SqlDependencyServerTimeout; - if (0 != _timeout) + if (_expirationTime == DateTime.MaxValue) { - seconds = _timeout; - } - if (notificationRequest != null && notificationRequest.Timeout < seconds && notificationRequest.Timeout != 0) - { - seconds = notificationRequest.Timeout; - } + int seconds = SQL.SqlDependencyServerTimeout; + if (0 != _timeout) + { + seconds = _timeout; + } + if (notificationRequest != null && notificationRequest.Timeout < seconds && notificationRequest.Timeout != 0) + { + seconds = notificationRequest.Timeout; + } - // We use UTC to check if SqlDependency is expired, need to use it here as well. - _expirationTime = DateTime.UtcNow.AddSeconds(seconds); - SqlDependencyPerAppDomainDispatcher.SingletonInstance.StartTimer(this); + // We use UTC to check if SqlDependency is expired, need to use it here as well. + _expirationTime = DateTime.UtcNow.AddSeconds(seconds); + SqlDependencyPerAppDomainDispatcher.SingletonInstance.StartTimer(this); + } + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } @@ -854,115 +999,135 @@ private void AddCommandInternal(SqlCommand cmd) { if (cmd != null) { - SqlConnection connection = cmd.Connection; - - if (cmd.Notification != null) + // Don't bother with EventSource if command null. + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlCommand: {1}#", ObjectID, cmd.ObjectID); + try { - // Fail if cmd has notification that is not already associated with this dependency. - if (cmd._sqlDep == null || cmd._sqlDep != this) + SqlConnection connection = cmd.Connection; + + if (cmd.Notification != null) { - throw SQL.SqlCommandHasExistingSqlNotificationRequest(); + // Fail if cmd has notification that is not already associated with this dependency. + if (cmd._sqlDep == null || cmd._sqlDep != this) + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR - throwing command has existing SqlNotificationRequest exception."); + throw SQL.SqlCommandHasExistingSqlNotificationRequest(); + } } - } - else - { - bool needToInvalidate = false; - - lock (_eventHandlerLock) + else { - if (!_dependencyFired) + bool needToInvalidate = false; + + lock (_eventHandlerLock) { - cmd.Notification = new SqlNotificationRequest() + if (!_dependencyFired) { - Timeout = _timeout - }; + cmd.Notification = new SqlNotificationRequest() + { + Timeout = _timeout + }; - // Add the command - A dependancy should always map to a set of commands which haven't fired. - if (null != _options) - { // Assign options if user provided. - cmd.Notification.Options = _options; - } + // Add the command - A dependancy should always map to a set of commands which haven't fired. + if (null != _options) + { // Assign options if user provided. + cmd.Notification.Options = _options; + } - cmd._sqlDep = this; - } - else - { - // We should never be able to enter this state, since if we've fired our event list is cleared - // and the event method will immediately fire if a new event is added. So, we should never have - // an event to fire in the event list once we've fired. - Debug.Assert(0 == _eventList.Count, "How can we have an event at this point?"); - if (0 == _eventList.Count) - { // Keep logic just in case. - needToInvalidate = true; // Delay invalidation until outside of lock. + cmd._sqlDep = this; + } + else + { + // We should never be able to enter this state, since if we've fired our event list is cleared + // and the event method will immediately fire if a new event is added. So, we should never have + // an event to fire in the event list once we've fired. + Debug.Assert(0 == _eventList.Count, "How can we have an event at this point?"); + if (0 == _eventList.Count) + { + // Keep logic just in case. + SqlClientEventSource.Log.NotificationsTrace(" ERROR - firing events, though it is unexpected we have events at this point."); + needToInvalidate = true; // Delay invalidation until outside of lock. + } } } - } - if (needToInvalidate) - { - Invalidate(SqlNotificationType.Subscribe, SqlNotificationInfo.AlreadyChanged, SqlNotificationSource.Client); + if (needToInvalidate) + { + Invalidate(SqlNotificationType.Subscribe, SqlNotificationInfo.AlreadyChanged, SqlNotificationSource.Client); + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } } private string ComputeCommandHash(string connectionString, SqlCommand command) { - // Create a string representing the concatenation of the connection string, the command text and .ToString on all its parameter values. - // This string will then be mapped to the notification ID. + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlCommand: {1}#", ObjectID, command.ObjectID); + try + { + // Create a string representing the concatenation of the connection string, the command text and .ToString on all its parameter values. + // This string will then be mapped to the notification ID. - // All types should properly support a .ToString for the values except - // byte[], char[], and XmlReader. + // All types should properly support a .ToString for the values except + // byte[], char[], and XmlReader. - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(); - // add the Connection string and the Command text - builder.AppendFormat("{0};{1}", connectionString, command.CommandText); + // add the Connection string and the Command text + builder.AppendFormat("{0};{1}", connectionString, command.CommandText); - // append params - for (int i = 0; i < command.Parameters.Count; i++) - { - object value = command.Parameters[i].Value; - - if (value == null || value == DBNull.Value) - { - builder.Append("; NULL"); - } - else + // append params + for (int i = 0; i < command.Parameters.Count; i++) { - Type type = value.GetType(); + object value = command.Parameters[i].Value; - if (type == typeof(byte[])) - { - builder.Append(";"); - byte[] temp = (byte[])value; - for (int j = 0; j < temp.Length; j++) - { - builder.Append(temp[j].ToString("x2", CultureInfo.InvariantCulture)); - } - } - else if (type == typeof(char[])) - { - builder.Append((char[])value); - } - else if (type == typeof(XmlReader)) + if (value == null || value == DBNull.Value) { - builder.Append(";"); - // Cannot .ToString XmlReader - just allocate GUID. - // This means if XmlReader is used, we will not reuse IDs. - builder.Append(Guid.NewGuid().ToString()); + builder.Append("; NULL"); } else { - builder.Append(";"); - builder.Append(value.ToString()); + Type type = value.GetType(); + + if (type == typeof(byte[])) + { + builder.Append(";"); + byte[] temp = (byte[])value; + for (int j = 0; j < temp.Length; j++) + { + builder.Append(temp[j].ToString("x2", CultureInfo.InvariantCulture)); + } + } + else if (type == typeof(char[])) + { + builder.Append((char[])value); + } + else if (type == typeof(XmlReader)) + { + builder.Append(";"); + // Cannot .ToString XmlReader - just allocate GUID. + // This means if XmlReader is used, we will not reuse IDs. + builder.Append(Guid.NewGuid().ToString()); + } + else + { + builder.Append(";"); + builder.Append(value.ToString()); + } } } - } - string result = builder.ToString(); - - return result; + string result = builder.ToString(); + SqlClientEventSource.Log.NotificationsTraceEvent(" ComputeCommandHash result: '{0}'.", result); + return result; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Basic copy of function in SqlConnection.cs for ChangeDatabase and similar functionality. Since this will diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index fc523fb4fa..b342713762 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -48,10 +48,14 @@ private class SqlConnectionContainer private Timer _retryTimer = null; private Dictionary _appDomainKeyHash = null; // AppDomainKey->Open RefCount + private static int _objectTypeCount; // EventSource counter + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + // Constructor internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, string appDomainKey, bool useDefaults) { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, queue: '{1}'", ObjectID, HashHelper?.Queue); bool setupCompleted = false; try { @@ -70,6 +74,10 @@ internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, str { _queue = _hashHelper.Queue; } +#if DEBUG + SqlConnectionString connectionStringOptions = new SqlConnectionString(_hashHelper.ConnectionStringBuilder.ConnectionString); + SqlClientEventSource.Log.NotificationsTraceEvent(" Modified connection string: '{0}'", connectionStringOptions.UsersConnectionStringForTrace()); +#endif // Always use ConnectionStringBuilder since in default case it is different from the // connection string used in the hashHelper. @@ -165,6 +173,10 @@ internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, str } throw; } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Properties @@ -195,42 +207,71 @@ internal string Database // SqlDependencyProcessDispatcher.QueueAppDomainUnload on AppDomain.Unload. internal bool AppDomainUnload(string appDomainKey) { - Debug.Assert(!string.IsNullOrEmpty(appDomainKey), "Unexpected empty appDomainKey!"); - - // Dictionary used to track how many times start has been called per app domain. - // For each decrement, subtract from count, and delete if we reach 0. - lock (_appDomainKeyHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, AppDomainKey: '{1}'", ObjectID, appDomainKey); + try { - if (_appDomainKeyHash.ContainsKey(appDomainKey)) - { // Do nothing if AppDomain did not call Start! - int value = _appDomainKeyHash[appDomainKey]; - Debug.Assert(value > 0, "Why is value 0 or less?"); + Debug.Assert(!string.IsNullOrEmpty(appDomainKey), "Unexpected empty appDomainKey!"); - bool ignored = false; - while (value > 0) + // Dictionary used to track how many times start has been called per app domain. + // For each decrement, subtract from count, and delete if we reach 0. + lock (_appDomainKeyHash) + { + if (_appDomainKeyHash.ContainsKey(appDomainKey)) { - Debug.Assert(!_stopped, "We should not yet be stopped!"); - Stop(appDomainKey, out ignored); // Stop will decrement value and remove if necessary from _appDomainKeyHash. - value--; - } + // Do nothing if AppDomain did not call Start! + SqlClientEventSource.Log.NotificationsTraceEvent(" _appDomainKeyHash contained AppDomainKey: '{0}'.", appDomainKey); + int value = _appDomainKeyHash[appDomainKey]; + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP> _appDomainKeyHash for AppDomainKey: '{0}' count: '{1}'.", appDomainKey, value); + Debug.Assert(value > 0, "Why is value 0 or less?"); + + bool ignored = false; + while (value > 0) + { + Debug.Assert(!_stopped, "We should not yet be stopped!"); + Stop(appDomainKey, out ignored); // Stop will decrement value and remove if necessary from _appDomainKeyHash. + value--; + } + + // Stop will remove key when decremented to 0 for this AppDomain, which should now be the case. + Debug.Assert(0 == value, "We did not reach 0 at end of loop in AppDomainUnload!"); + Debug.Assert(!_appDomainKeyHash.ContainsKey(appDomainKey), "Key not removed after AppDomainUnload!"); - // Stop will remove key when decremented to 0 for this AppDomain, which should now be the case. - Debug.Assert(0 == value, "We did not reach 0 at end of loop in AppDomainUnload!"); - Debug.Assert(!_appDomainKeyHash.ContainsKey(appDomainKey), "Key not removed after AppDomainUnload!"); + if (_appDomainKeyHash.ContainsKey(appDomainKey)) + { + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP|ERR> ERROR - after the Stop() loop, _appDomainKeyHash for AppDomainKey: '{0}' entry not removed from hash. Count: {1}'", appDomainKey, _appDomainKeyHash[appDomainKey]); + } + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP> _appDomainKeyHash did not contain AppDomainKey: '{0}'.", appDomainKey); + } } + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP> Exiting, _stopped: '{0}'.", _stopped); + return _stopped; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - return _stopped; } private void AsynchronouslyQueryServiceBrokerQueue() { - AsyncCallback callback = new AsyncCallback(AsyncResultCallback); - _com.BeginExecuteReader(callback, null, CommandBehavior.Default); // NO LOCK NEEDED + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + AsyncCallback callback = new AsyncCallback(AsyncResultCallback); + _com.BeginExecuteReader(callback, null, CommandBehavior.Default); // NO LOCK NEEDED + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private void AsyncResultCallback(IAsyncResult asyncResult) { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); try { using (SqlDataReader reader = _com.EndExecuteReader(asyncResult)) @@ -256,6 +297,7 @@ private void AsyncResultCallback(IAsyncResult asyncResult) _errorState = true; throw; } + SqlClientEventSource.Log.NotificationsTrace(" Exception occurred."); if (!_stop) { // Only assert if not in cancel path. @@ -273,163 +315,187 @@ private void AsyncResultCallback(IAsyncResult asyncResult) Restart(null); // Error code path. Will Invalidate based on server if 1st retry fails. } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private void CreateQueueAndService(bool restart) { - SqlCommand com = new SqlCommand() - { - Connection = _con - }; - SqlTransaction trans = null; - + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); try { - trans = _con.BeginTransaction(); // Since we cannot batch proc creation, start transaction. - com.Transaction = trans; + SqlCommand com = new SqlCommand() + { + Connection = _con + }; + SqlTransaction trans = null; - string nameLiteral = SqlServerEscapeHelper.MakeStringLiteral(_queue); + try + { + trans = _con.BeginTransaction(); // Since we cannot batch proc creation, start transaction. + com.Transaction = trans; - com.CommandText = - "CREATE PROCEDURE " + _sprocName + " AS" - + " BEGIN" - + " BEGIN TRANSACTION;" - + " RECEIVE TOP(0) conversation_handle FROM " + _escapedQueueName + ";" - + " IF (SELECT COUNT(*) FROM " + _escapedQueueName + " WHERE message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer') > 0" + string nameLiteral = SqlServerEscapeHelper.MakeStringLiteral(_queue); + + com.CommandText = + "CREATE PROCEDURE " + _sprocName + " AS" + " BEGIN" - + " if ((SELECT COUNT(*) FROM sys.services WHERE name = " + nameLiteral + ") > 0)" - + " DROP SERVICE " + _escapedQueueName + ";" - + " if (OBJECT_ID(" + nameLiteral + ", 'SQ') IS NOT NULL)" - + " DROP QUEUE " + _escapedQueueName + ";" - + " DROP PROCEDURE " + _sprocName + ";" // Don't need conditional because this is self - + " END" - + " COMMIT TRANSACTION;" - + " END"; - - if (!restart) - { - com.ExecuteNonQuery(); - } - else - { // Upon restart, be resilient to the user dropping queue, service, or procedure. - try + + " BEGIN TRANSACTION;" + + " RECEIVE TOP(0) conversation_handle FROM " + _escapedQueueName + ";" + + " IF (SELECT COUNT(*) FROM " + _escapedQueueName + " WHERE message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer') > 0" + + " BEGIN" + + " if ((SELECT COUNT(*) FROM sys.services WHERE name = " + nameLiteral + ") > 0)" + + " DROP SERVICE " + _escapedQueueName + ";" + + " if (OBJECT_ID(" + nameLiteral + ", 'SQ') IS NOT NULL)" + + " DROP QUEUE " + _escapedQueueName + ";" + + " DROP PROCEDURE " + _sprocName + ";" // Don't need conditional because this is self + + " END" + + " COMMIT TRANSACTION;" + + " END"; + + if (!restart) { - com.ExecuteNonQuery(); // Cannot add 'IF OBJECT_ID' to create procedure query - wrap and discard failure. + com.ExecuteNonQuery(); } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - ADP.TraceExceptionWithoutRethrow(e); - + else + { // Upon restart, be resilient to the user dropping queue, service, or procedure. try - { // Since the failure will result in a rollback, rollback our object. - if (null != trans) - { - trans.Rollback(); - trans = null; - } + { + com.ExecuteNonQuery(); // Cannot add 'IF OBJECT_ID' to create procedure query - wrap and discard failure. } - catch (Exception f) + catch (Exception e) { - if (!ADP.IsCatchableExceptionType(f)) + if (!ADP.IsCatchableExceptionType(e)) { throw; } - ADP.TraceExceptionWithoutRethrow(f); // Discard failure, but trace for now. + ADP.TraceExceptionWithoutRethrow(e); + + try + { // Since the failure will result in a rollback, rollback our object. + if (null != trans) + { + trans.Rollback(); + trans = null; + } + } + catch (Exception f) + { + if (!ADP.IsCatchableExceptionType(f)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(f); // Discard failure, but trace for now. + } } - } - if (null == trans) - { // Create a new transaction for next operations. - trans = _con.BeginTransaction(); - com.Transaction = trans; + if (null == trans) + { // Create a new transaction for next operations. + trans = _con.BeginTransaction(); + com.Transaction = trans; + } } - } - com.CommandText = - "IF OBJECT_ID(" + nameLiteral + ", 'SQ') IS NULL" - + " BEGIN" - + " CREATE QUEUE " + _escapedQueueName + " WITH ACTIVATION (PROCEDURE_NAME=" + _sprocName + ", MAX_QUEUE_READERS=1, EXECUTE AS OWNER);" - + " END;" - + " IF (SELECT COUNT(*) FROM sys.services WHERE NAME=" + nameLiteral + ") = 0" - + " BEGIN" - + " CREATE SERVICE " + _escapedQueueName + " ON QUEUE " + _escapedQueueName + " ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);" - + " IF (SELECT COUNT(*) FROM sys.database_principals WHERE name='sql_dependency_subscriber' AND type='R') <> 0" - + " BEGIN" - + " GRANT SEND ON SERVICE::" + _escapedQueueName + " TO sql_dependency_subscriber;" - + " END; " - + " END;" - + " BEGIN DIALOG @dialog_handle FROM SERVICE " + _escapedQueueName + " TO SERVICE " + nameLiteral; - - SqlParameter param = new SqlParameter() - { - ParameterName = "@dialog_handle", - DbType = DbType.Guid, - Direction = ParameterDirection.Output - }; - com.Parameters.Add(param); - com.ExecuteNonQuery(); - - // Finish setting up queries and state. For re-start, we need to ensure we begin a new dialog above and reset - // our queries to use the new dialogHandle. - _dialogHandle = ((Guid)param.Value).ToString(); - _beginConversationQuery = "BEGIN CONVERSATION TIMER ('" + _dialogHandle + "') TIMEOUT = 120; " + _receiveQuery; - _com.CommandText = _beginConversationQuery; - _endConversationQuery = "END CONVERSATION @p1; "; - _concatQuery = _endConversationQuery + _com.CommandText; - - trans.Commit(); - trans = null; - _serviceQueueCreated = true; - } - finally - { - if (null != trans) - { - try + com.CommandText = + "IF OBJECT_ID(" + nameLiteral + ", 'SQ') IS NULL" + + " BEGIN" + + " CREATE QUEUE " + _escapedQueueName + " WITH ACTIVATION (PROCEDURE_NAME=" + _sprocName + ", MAX_QUEUE_READERS=1, EXECUTE AS OWNER);" + + " END;" + + " IF (SELECT COUNT(*) FROM sys.services WHERE NAME=" + nameLiteral + ") = 0" + + " BEGIN" + + " CREATE SERVICE " + _escapedQueueName + " ON QUEUE " + _escapedQueueName + " ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);" + + " IF (SELECT COUNT(*) FROM sys.database_principals WHERE name='sql_dependency_subscriber' AND type='R') <> 0" + + " BEGIN" + + " GRANT SEND ON SERVICE::" + _escapedQueueName + " TO sql_dependency_subscriber;" + + " END; " + + " END;" + + " BEGIN DIALOG @dialog_handle FROM SERVICE " + _escapedQueueName + " TO SERVICE " + nameLiteral; + + SqlParameter param = new SqlParameter() { - trans.Rollback(); - trans = null; - } - catch (Exception e) + ParameterName = "@dialog_handle", + DbType = DbType.Guid, + Direction = ParameterDirection.Output + }; + com.Parameters.Add(param); + com.ExecuteNonQuery(); + + // Finish setting up queries and state. For re-start, we need to ensure we begin a new dialog above and reset + // our queries to use the new dialogHandle. + _dialogHandle = ((Guid)param.Value).ToString(); + _beginConversationQuery = "BEGIN CONVERSATION TIMER ('" + _dialogHandle + "') TIMEOUT = 120; " + _receiveQuery; + _com.CommandText = _beginConversationQuery; + _endConversationQuery = "END CONVERSATION @p1; "; + _concatQuery = _endConversationQuery + _com.CommandText; + + trans.Commit(); + trans = null; + _serviceQueueCreated = true; + } + finally + { + if (null != trans) { - if (!ADP.IsCatchableExceptionType(e)) + try { - throw; + trans.Rollback(); + trans = null; + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } internal void IncrementStartCount(string appDomainKey, out bool appDomainStart) { - appDomainStart = false; // Reset out param. - int result = Interlocked.Increment(ref _startCount); // Add to refCount. - - // Dictionary used to track how many times start has been called per app domain. - // For each increment, add to count, and create entry if not present. - lock (_appDomainKeyHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - if (_appDomainKeyHash.ContainsKey(appDomainKey)) - { - _appDomainKeyHash[appDomainKey] = _appDomainKeyHash[appDomainKey] + 1; - } - else + appDomainStart = false; // Reset out param. + int result = Interlocked.Increment(ref _startCount); // Add to refCount. + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.IncrementStartCount|DEP> {0}#, incremented _startCount: {1}", s_staticInstance.ObjectID, result); + + // Dictionary used to track how many times start has been called per app domain. + // For each increment, add to count, and create entry if not present. + lock (_appDomainKeyHash) { - _appDomainKeyHash[appDomainKey] = 1; - appDomainStart = true; + if (_appDomainKeyHash.ContainsKey(appDomainKey)) + { + _appDomainKeyHash[appDomainKey] = _appDomainKeyHash[appDomainKey] + 1; + SqlClientEventSource.Log.NotificationsTraceEvent("SqlConnectionContainer.IncrementStartCount|DEP> _appDomainKeyHash contained AppDomainKey: '{0}', incremented count: '{1}'.", appDomainKey, _appDomainKeyHash[appDomainKey]); + } + else + { + _appDomainKeyHash[appDomainKey] = 1; + appDomainStart = true; + SqlClientEventSource.Log.NotificationsTraceEvent(" _appDomainKeyHash did not contain AppDomainKey: '{0}', added to hashtable and value set to 1.", appDomainKey); + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private void ProcessNotificationResults(SqlDataReader reader) { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); Guid handle = Guid.Empty; // Conversation_handle. Always close this! try { @@ -437,7 +503,18 @@ private void ProcessNotificationResults(SqlDataReader reader) { while (reader.Read()) { + SqlClientEventSource.Log.NotificationsTrace(" Row read."); +#if DEBUG + if (SqlClientEventSource.Log.IsNotificationTraceEnabled()) + { + for (int i = 0; i < reader.FieldCount; i++) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" column: {0}, value: {1}", reader.GetName(i), reader.GetValue(i).ToString()); + } + } +#endif string msgType = reader.GetString(0); + SqlClientEventSource.Log.NotificationsTraceEvent(" msgType: '{0}'", msgType); handle = reader.GetGuid(1); // Only process QueryNotification messages. @@ -450,6 +527,7 @@ private void ProcessNotificationResults(SqlDataReader reader) if (null != notification) { string key = notification.Key; + SqlClientEventSource.Log.NotificationsTraceEvent(" Key: '{0}'", key); int index = key.IndexOf(';'); // Our format is simple: "AppDomainKey;commandHash" if (index >= 0) @@ -478,26 +556,31 @@ private void ProcessNotificationResults(SqlDataReader reader) else { Debug.Fail("Received notification but do not have an associated PerAppDomainDispatcher!"); + SqlClientEventSource.Log.NotificationsTrace(" Received notification but do not have an associated PerAppDomainDispatcher!"); } } else { Debug.Fail("Unexpected ID format received!"); + SqlClientEventSource.Log.NotificationsTrace(" Unexpected ID format received!"); } } else { Debug.Fail("Null notification returned from ProcessMessage!"); + SqlClientEventSource.Log.NotificationsTrace(" Null notification returned from ProcessMessage!"); } } else { Debug.Fail("Null payload for QN notification type!"); + SqlClientEventSource.Log.NotificationsTrace(" Null payload for QN notification type!"); } } else { handle = Guid.Empty; + SqlClientEventSource.Log.NotificationsTrace(" Unexpected message format received!"); } } } @@ -525,336 +608,381 @@ private void ProcessNotificationResults(SqlDataReader reader) } Debug.Assert(_com.Parameters.Count == 2, "Unexpected number of parameters!"); } + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } private void Restart(object unused) - { // Unused arg required by TimerCallback. + { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); try { - lock (this) + // Unused arg required by TimerCallback. + try { - if (!_stop) - { // Only execute if we are still in running state. - try - { - _con.Close(); - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) + lock (this) + { + if (!_stop) + { // Only execute if we are still in running state. + try { - throw; + _con.Close(); + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard close failure, if it occurs. Only trace it. } - ADP.TraceExceptionWithoutRethrow(e); // Discard close failure, if it occurs. Only trace it. } } - } - // Rather than one long lock - take lock 3 times for shorter periods. + // Rather than one long lock - take lock 3 times for shorter periods. - lock (this) - { - if (!_stop) + lock (this) { - _con.Open(); + if (!_stop) + { + _con.Open(); + } } - } - lock (this) - { - if (!_stop) + lock (this) { - if (_serviceQueueCreated) + if (!_stop) { - bool failure = false; - - try + if (_serviceQueueCreated) { - CreateQueueAndService(true); // Ensure service, queue, etc is present, if we created it. - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) + bool failure = false; + + try { - throw; + CreateQueueAndService(true); // Ensure service, queue, etc is present, if we created it. + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. + failure = true; } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace for now. - failure = true; - } - if (failure) - { - // If we failed to re-created queue, service, sproc - invalidate! - s_staticInstance.Invalidate(Server, - new SqlNotification(SqlNotificationInfo.Error, - SqlNotificationSource.Client, - SqlNotificationType.Change, - null)); + if (failure) + { + // If we failed to re-created queue, service, sproc - invalidate! + s_staticInstance.Invalidate(Server, + new SqlNotification(SqlNotificationInfo.Error, + SqlNotificationSource.Client, + SqlNotificationType.Change, + null)); + } } } } - } - lock (this) - { - if (!_stop) + lock (this) { - _timeoutParam.Value = 0; // Reset timeout to zero - we do not want to block. - SynchronouslyQueryServiceBrokerQueue(); - // If the above succeeds, we are back in success case - requeue for async call. - _timeoutParam.Value = _defaultWaitforTimeout; // If success, reset to default for re-queue. - AsynchronouslyQueryServiceBrokerQueue(); - _errorState = false; - Timer retryTimer = _retryTimer; - if (retryTimer != null) + if (!_stop) { - _retryTimer = null; - retryTimer.Dispose(); + _timeoutParam.Value = 0; // Reset timeout to zero - we do not want to block. + SynchronouslyQueryServiceBrokerQueue(); + // If the above succeeds, we are back in success case - requeue for async call. + _timeoutParam.Value = _defaultWaitforTimeout; // If success, reset to default for re-queue. + AsynchronouslyQueryServiceBrokerQueue(); + _errorState = false; + Timer retryTimer = _retryTimer; + if (retryTimer != null) + { + _retryTimer = null; + retryTimer.Dispose(); + } } } - } - - if (_stop) - { - TearDownAndDispose(); // Function will lock(this). - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - ADP.TraceExceptionWithoutRethrow(e); - try - { - // If unexpected query or connection failure, invalidate all dependencies against this server. - // We may over-notify if only some of the connections to a particular database were affected, - // but this should not be frequent enough to be a concern. - // NOTE - we invalidate after failure first occurs and then retry fails. We will then continue - // to invalidate every time the retry fails. - s_staticInstance.Invalidate(Server, - new SqlNotification(SqlNotificationInfo.Error, - SqlNotificationSource.Client, - SqlNotificationType.Change, - null)); + if (_stop) + { + TearDownAndDispose(); // Function will lock(this). + } } - catch (Exception f) + catch (Exception e) { - if (!ADP.IsCatchableExceptionType(f)) + if (!ADP.IsCatchableExceptionType(e)) { throw; } - ADP.TraceExceptionWithoutRethrow(f); // Discard exception from Invalidate. User events can throw. - } + ADP.TraceExceptionWithoutRethrow(e); - try - { - _con.Close(); - } - catch (Exception f) - { - if (!ADP.IsCatchableExceptionType(f)) + try { - throw; + // If unexpected query or connection failure, invalidate all dependencies against this server. + // We may over-notify if only some of the connections to a particular database were affected, + // but this should not be frequent enough to be a concern. + // NOTE - we invalidate after failure first occurs and then retry fails. We will then continue + // to invalidate every time the retry fails. + s_staticInstance.Invalidate(Server, + new SqlNotification(SqlNotificationInfo.Error, + SqlNotificationSource.Client, + SqlNotificationType.Change, + null)); + } + catch (Exception f) + { + if (!ADP.IsCatchableExceptionType(f)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(f); // Discard exception from Invalidate. User events can throw. } - ADP.TraceExceptionWithoutRethrow(f); // Discard close failure, if it occurs. Only trace it. - } - if (!_stop) - { - // Create a timer to callback in one minute, retrying the call to Restart(). - _retryTimer = new Timer(new TimerCallback(Restart), null, _defaultWaitforTimeout, Timeout.Infinite); - // We will retry this indefinitely, until success - or Stop(); + try + { + _con.Close(); + } + catch (Exception f) + { + if (!ADP.IsCatchableExceptionType(f)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(f); // Discard close failure, if it occurs. Only trace it. + } + + if (!_stop) + { + // Create a timer to callback in one minute, retrying the call to Restart(). + _retryTimer = new Timer(new TimerCallback(Restart), null, _defaultWaitforTimeout, Timeout.Infinite); + // We will retry this indefinitely, until success - or Stop(); + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } internal bool Stop(string appDomainKey, out bool appDomainStop) { - appDomainStop = false; + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + appDomainStop = false; - // Dictionary used to track how many times start has been called per app domain. - // For each decrement, subtract from count, and delete if we reach 0. + // Dictionary used to track how many times start has been called per app domain. + // For each decrement, subtract from count, and delete if we reach 0. - if (null != appDomainKey) - { - // If null, then this was called from SqlDependencyProcessDispatcher, we ignore appDomainKeyHash. - lock (_appDomainKeyHash) + if (null != appDomainKey) { - if (_appDomainKeyHash.ContainsKey(appDomainKey)) - { // Do nothing if AppDomain did not call Start! - int value = _appDomainKeyHash[appDomainKey]; + // If null, then this was called from SqlDependencyProcessDispatcher, we ignore appDomainKeyHash. + lock (_appDomainKeyHash) + { + if (_appDomainKeyHash.ContainsKey(appDomainKey)) + { // Do nothing if AppDomain did not call Start! + int value = _appDomainKeyHash[appDomainKey]; - Debug.Assert(value > 0, "Unexpected count for appDomainKey"); + Debug.Assert(value > 0, "Unexpected count for appDomainKey"); + SqlClientEventSource.Log.NotificationsTraceEvent(" _appDomainKeyHash contained AppDomainKey: '{0}', pre-decrement Count: '{1}'.", appDomainKey, value); - if (value > 0) - { - _appDomainKeyHash[appDomainKey] = value - 1; + if (value > 0) + { + _appDomainKeyHash[appDomainKey] = value - 1; + } + else + { + SqlClientEventSource.Log.NotificationsTrace(" ERROR pre-decremented count <= 0!"); + Debug.Fail("Unexpected AppDomainKey count in Stop()"); + } + + if (1 == value) + { // Remove from dictionary if pre-decrement count was one. + _appDomainKeyHash.Remove(appDomainKey); + appDomainStop = true; + } } else { - Debug.Fail("Unexpected AppDomainKey count in Stop()"); + SqlClientEventSource.Log.NotificationsTrace(" ERROR appDomainKey not null and not found in hash!"); + Debug.Fail("Unexpected state on Stop() - no AppDomainKey entry in hashtable!"); } - - if (1 == value) - { // Remove from dictionary if pre-decrement count was one. - _appDomainKeyHash.Remove(appDomainKey); - appDomainStop = true; - } - } - else - { - Debug.Fail("Unexpected state on Stop() - no AppDomainKey entry in hashtable!"); } } - } - Debug.Assert(_startCount > 0, "About to decrement _startCount less than 0!"); - int result = Interlocked.Decrement(ref _startCount); + Debug.Assert(_startCount > 0, "About to decrement _startCount less than 0!"); + int result = Interlocked.Decrement(ref _startCount); - if (0 == result) - { // If we've reached refCount 0, destroy. - // Lock to ensure Cancel() complete prior to other thread calling TearDown. - lock (this) + if (0 == result) { - try - { - // Race condition with executing thread - will throw if connection is closed due to failure. - // Rather than fighting the race condition, just call it and discard any potential failure. - _com.Cancel(); // Cancel the pending command. No-op if connection closed. - } - catch (Exception e) + // If we've reached refCount 0, destroy. + // Lock to ensure Cancel() complete prior to other thread calling TearDown. + SqlClientEventSource.Log.NotificationsTrace(" Reached 0 count, cancelling and waiting."); + + lock (this) { - if (!ADP.IsCatchableExceptionType(e)) + try { - throw; + // Race condition with executing thread - will throw if connection is closed due to failure. + // Rather than fighting the race condition, just call it and discard any potential failure. + _com.Cancel(); // Cancel the pending command. No-op if connection closed. + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard failure, if it should occur. } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure, if it should occur. + _stop = true; } - _stop = true; - } - // Wait until stopped and service & queue are dropped. - Stopwatch retryStopwatch = Stopwatch.StartNew(); - while (true) - { - lock (this) + // Wait until stopped and service & queue are dropped. + Stopwatch retryStopwatch = Stopwatch.StartNew(); + while (true) { - if (_stopped) + lock (this) { - break; - } + if (_stopped) + { + break; + } - // If we are in error state (_errorState is true), force a tear down. - // Likewise, if we have exceeded the maximum retry period (30 seconds) waiting for cleanup, force a tear down. - // In rare cases during app domain unload, the async cleanup performed by AsyncResultCallback - // may fail to execute TearDownAndDispose, leaving this method in an infinite loop. - // To avoid the infinite loop, we force the cleanup here after 30 seconds. Since we have reached - // refcount of 0, either this method call or the thread running AsyncResultCallback is responsible for calling - // TearDownAndDispose when transitioning to the _stopped state. Failing to call TearDownAndDispose means we leak - // the service broker objects created by this SqlDependency instance, so we make a best effort here to call - // TearDownAndDispose in the maximum retry period case as well as in the _errorState case. - if (_errorState || retryStopwatch.Elapsed.Seconds >= 30) - { - Timer retryTimer = _retryTimer; - _retryTimer = null; - if (retryTimer != null) + // If we are in error state (_errorState is true), force a tear down. + // Likewise, if we have exceeded the maximum retry period (30 seconds) waiting for cleanup, force a tear down. + // In rare cases during app domain unload, the async cleanup performed by AsyncResultCallback + // may fail to execute TearDownAndDispose, leaving this method in an infinite loop. + // To avoid the infinite loop, we force the cleanup here after 30 seconds. Since we have reached + // refcount of 0, either this method call or the thread running AsyncResultCallback is responsible for calling + // TearDownAndDispose when transitioning to the _stopped state. Failing to call TearDownAndDispose means we leak + // the service broker objects created by this SqlDependency instance, so we make a best effort here to call + // TearDownAndDispose in the maximum retry period case as well as in the _errorState case. + if (_errorState || retryStopwatch.Elapsed.Seconds >= 30) { - retryTimer.Dispose(); // Dispose timer - stop retry loop! + SqlClientEventSource.Log.NotificationsTraceEvent(" forcing cleanup. elapsedSeconds: '{0}', _errorState: '{1}'.", retryStopwatch.Elapsed.Seconds, _errorState); + Timer retryTimer = _retryTimer; + _retryTimer = null; + if (retryTimer != null) + { + retryTimer.Dispose(); // Dispose timer - stop retry loop! + } + TearDownAndDispose(); // Will not hit server unless connection open! + break; } - TearDownAndDispose(); // Will not hit server unless connection open! - break; } - } - // Yield the thread since the stop has not yet completed. - // To avoid CPU spikes while waiting, yield and wait for at least one millisecond - Thread.Sleep(1); + // Yield the thread since the stop has not yet completed. + // To avoid CPU spikes while waiting, yield and wait for at least one millisecond + Thread.Sleep(1); + } + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" _startCount not 0 after decrement. _startCount: '{0}'.", _startCount); } - } - Debug.Assert(0 <= _startCount, "Invalid start count state"); + Debug.Assert(0 <= _startCount, "Invalid start count state"); - return _stopped; + return _stopped; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private void SynchronouslyQueryServiceBrokerQueue() { - using (SqlDataReader reader = _com.ExecuteReader()) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + using (SqlDataReader reader = _com.ExecuteReader()) + { + ProcessNotificationResults(reader); + } + } + finally { - ProcessNotificationResults(reader); + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } [SuppressMessage("Microsoft.Security", "CA2100:ReviewSqlQueriesForSecurityVulnerabilities")] private void TearDownAndDispose() { - lock (this) - { // Lock to ensure Stop() (with Cancel()) complete prior to TearDown. - try - { - // Only execute if connection is still up and open. - if (ConnectionState.Closed != _con.State && ConnectionState.Broken != _con.State) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + lock (this) + { // Lock to ensure Stop() (with Cancel()) complete prior to TearDown. + try { - if (_com.Parameters.Count > 1) - { // Need to close dialog before completing. - // In the normal case, the "End Conversation" query is executed before a - // receive query and upon return we will clear the state. However, unless - // a non notification query result is returned, we will not clear it. That - // means a query is generally always executing with an "end conversation" on - // the wire. Rather than synchronize for success of the other "end conversation", - // simply re-execute. - try - { - _com.CommandText = _endConversationQuery; - _com.Parameters.Remove(_timeoutParam); - _com.ExecuteNonQuery(); - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) + // Only execute if connection is still up and open. + if (ConnectionState.Closed != _con.State && ConnectionState.Broken != _con.State) + { + if (_com.Parameters.Count > 1) + { // Need to close dialog before completing. + // In the normal case, the "End Conversation" query is executed before a + // receive query and upon return we will clear the state. However, unless + // a non notification query result is returned, we will not clear it. That + // means a query is generally always executing with an "end conversation" on + // the wire. Rather than synchronize for success of the other "end conversation", + // simply re-execute. + try { - throw; + _com.CommandText = _endConversationQuery; + _com.Parameters.Remove(_timeoutParam); + _com.ExecuteNonQuery(); + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard failure. } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure. } - } - if (_serviceQueueCreated && !_errorState) - { - /* - BEGIN TRANSACTION; - DROP SERVICE "+_escapedQueueName+"; - DROP QUEUE "+_escapedQueueName+"; - DROP PROCEDURE "+_sprocName+"; - COMMIT TRANSACTION; - */ - _com.CommandText = "BEGIN TRANSACTION; DROP SERVICE " + _escapedQueueName + "; DROP QUEUE " + _escapedQueueName + "; DROP PROCEDURE " + _sprocName + "; COMMIT TRANSACTION;"; - try - { - _com.ExecuteNonQuery(); - } - catch (Exception e) + if (_serviceQueueCreated && !_errorState) { - if (!ADP.IsCatchableExceptionType(e)) + /* + BEGIN TRANSACTION; + DROP SERVICE "+_escapedQueueName+"; + DROP QUEUE "+_escapedQueueName+"; + DROP PROCEDURE "+_sprocName+"; + COMMIT TRANSACTION; + */ + _com.CommandText = "BEGIN TRANSACTION; DROP SERVICE " + _escapedQueueName + "; DROP QUEUE " + _escapedQueueName + "; DROP PROCEDURE " + _sprocName + "; COMMIT TRANSACTION;"; + try { - throw; + _com.ExecuteNonQuery(); + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); // Discard failure. } - ADP.TraceExceptionWithoutRethrow(e); // Discard failure. } } } - } - finally - { - _stopped = true; - _con.Dispose(); // Close and dispose connection. + finally + { + _stopped = true; + _con.Dispose(); // Close and dispose connection. + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } } @@ -988,36 +1116,42 @@ internal static SqlNotification ProcessMessage(SqlXml xmlMessage) catch (ArgumentException e) { ADP.TraceExceptionWithoutRethrow(e); // Discard failure, but trace. + SqlClientEventSource.Log.TraceEvent(" Exception thrown - Enum.Parse failed to parse the value '{0}' of the attribute '{1}'.", xmlReader.Value, xmlReader.LocalName); return null; } } if (MessageAttributes.All != messageAttributes) { + SqlClientEventSource.Log.TraceEvent(" Not all expected attributes in Message; messageAttributes = '{0}'.", (int)messageAttributes); return null; } // Proceed to the "Message" node. if (!xmlReader.Read()) { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } // Verify state after Read(). if ((XmlNodeType.Element != xmlReader.NodeType) || (0 != string.Compare(xmlReader.LocalName, MessageNode, StringComparison.OrdinalIgnoreCase))) { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } // Proceed to the Text Node. if (!xmlReader.Read()) { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } // Verify state after Read(). if (xmlReader.NodeType != XmlNodeType.Text) { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } @@ -1027,6 +1161,7 @@ internal static SqlNotification ProcessMessage(SqlXml xmlMessage) // Proceed to the Text Node. if (!xmlMessageReader.Read()) { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } @@ -1037,6 +1172,7 @@ internal static SqlNotification ProcessMessage(SqlXml xmlMessage) } else { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; } } @@ -1045,6 +1181,7 @@ internal static SqlNotification ProcessMessage(SqlXml xmlMessage) } else { + SqlClientEventSource.Log.TraceEvent(" unexpected Read failure on xml or unexpected structure of xml."); return null; // failure } } @@ -1165,15 +1302,28 @@ public override int GetHashCode() private Dictionary _connectionContainers; // NT_ID+ConStr+Service->Container private Dictionary _sqlDependencyPerAppDomainDispatchers; // AppDomainKey->Callback + private static int _objectTypeCount; //EventSource counter + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); // Constructors // Private constructor - only called by public constructor for static initialization. private SqlDependencyProcessDispatcher(object dummyVariable) { Debug.Assert(null == s_staticInstance, "Real constructor called with static instance already created!"); - - _connectionContainers = new Dictionary(); - _sqlDependencyPerAppDomainDispatchers = new Dictionary(); + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { +#if DEBUG + // Possibly expensive, limit to debug. + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, AppDomain.CurrentDomain.FriendlyName: {1}", ObjectID, AppDomain.CurrentDomain.FriendlyName); +#endif + _connectionContainers = new Dictionary(); + _sqlDependencyPerAppDomainDispatchers = new Dictionary(); + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Constructor is only called by remoting. @@ -1181,6 +1331,18 @@ private SqlDependencyProcessDispatcher(object dummyVariable) public SqlDependencyProcessDispatcher() { // Empty constructor and object - dummy to obtain singleton. + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { +#if DEBUG + // Possibly expensive, limit to debug. + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, AppDomain.CurrentDomain.FriendlyName: {1}", ObjectID, AppDomain.CurrentDomain.FriendlyName); +#endif + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Properties @@ -1196,34 +1358,42 @@ private static SqlConnectionContainerHashHelper GetHashHelper( out string user, string queue) { - // Force certain connection string properties to be used by SqlDependencyProcessDispatcher. - // This logic is done here to enable us to have the complete connection string now to be used - // for tracing as we flow through the logic. - connectionStringBuilder = new SqlConnectionStringBuilder(connectionString) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, queue: {1}", s_staticInstance.ObjectID, queue); + try { - Pooling = false, - Enlist = false, - ConnectRetryCount = 0 - }; - if (null != queue) - { // User provided! - connectionStringBuilder.ApplicationName = queue; // ApplicationName will be set to queue name. - } + // Force certain connection string properties to be used by SqlDependencyProcessDispatcher. + // This logic is done here to enable us to have the complete connection string now to be used + // for tracing as we flow through the logic. + connectionStringBuilder = new SqlConnectionStringBuilder(connectionString) + { + Pooling = false, + Enlist = false, + ConnectRetryCount = 0 + }; + if (null != queue) + { // User provided! + connectionStringBuilder.ApplicationName = queue; // ApplicationName will be set to queue name. + } - if (connectionStringBuilder.IntegratedSecurity) - { - // Use existing identity infrastructure for error cases and proper hash value. - identity = DbConnectionPoolIdentity.GetCurrent(); - user = null; + if (connectionStringBuilder.IntegratedSecurity) + { + // Use existing identity infrastructure for error cases and proper hash value. + identity = DbConnectionPoolIdentity.GetCurrent(); + user = null; + } + else + { + identity = null; + user = connectionStringBuilder.UserID; + } + + return new SqlConnectionContainerHashHelper(identity, connectionStringBuilder.ConnectionString, + queue, connectionStringBuilder); } - else + finally { - identity = null; - user = connectionStringBuilder.UserID; + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - return new SqlConnectionContainerHashHelper(identity, connectionStringBuilder.ConnectionString, - queue, connectionStringBuilder); } // Needed for remoting to prevent lifetime issues and default GC cleanup. @@ -1234,32 +1404,40 @@ public override object InitializeLifetimeService() private void Invalidate(string server, SqlNotification sqlNotification) { - Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); - lock (_sqlDependencyPerAppDomainDispatchers) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, server: {1}", ObjectID, server); + try { - - foreach (KeyValuePair entry in _sqlDependencyPerAppDomainDispatchers) + Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); + lock (_sqlDependencyPerAppDomainDispatchers) { - SqlDependencyPerAppDomainDispatcher perAppDomainDispatcher = entry.Value; - try - { - perAppDomainDispatcher.InvalidateServer(server, sqlNotification); - } - catch (Exception f) + + foreach (KeyValuePair entry in _sqlDependencyPerAppDomainDispatchers) { - // Since we are looping over dependency dispatchers, do not allow one Invalidate - // that results in a throw prevent us from invalidating all dependencies - // related to this server. - // NOTE - SqlDependencyPerAppDomainDispatcher already wraps individual dependency invalidates - // with try/catch, but we should be careful and do the same here. - if (!ADP.IsCatchableExceptionType(f)) + SqlDependencyPerAppDomainDispatcher perAppDomainDispatcher = entry.Value; + try { - throw; + perAppDomainDispatcher.InvalidateServer(server, sqlNotification); + } + catch (Exception f) + { + // Since we are looping over dependency dispatchers, do not allow one Invalidate + // that results in a throw prevent us from invalidating all dependencies + // related to this server. + // NOTE - SqlDependencyPerAppDomainDispatcher already wraps individual dependency invalidates + // with try/catch, but we should be careful and do the same here. + if (!ADP.IsCatchableExceptionType(f)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(f); // Discard failure, but trace. } - ADP.TraceExceptionWithoutRethrow(f); // Discard failure, but trace. } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Clean-up method initiated by other AppDomain.Unloads @@ -1273,31 +1451,39 @@ internal void QueueAppDomainUnloading(string appDomainKey) // This method is only called by queued work-items from the method above. private void AppDomainUnloading(object state) { - string appDomainKey = (string)state; - - Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); - lock (_connectionContainers) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try { - List containersToRemove = new List(); + string appDomainKey = (string)state; - foreach (KeyValuePair entry in _connectionContainers) + Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); + lock (_connectionContainers) { - SqlConnectionContainer container = entry.Value; - if (container.AppDomainUnload(appDomainKey)) - { // Perhaps wrap in try catch. - containersToRemove.Add(container.HashHelper); + List containersToRemove = new List(); + + foreach (KeyValuePair entry in _connectionContainers) + { + SqlConnectionContainer container = entry.Value; + if (container.AppDomainUnload(appDomainKey)) + { // Perhaps wrap in try catch. + containersToRemove.Add(container.HashHelper); + } + } + + foreach (SqlConnectionContainerHashHelper hashHelper in containersToRemove) + { + _connectionContainers.Remove(hashHelper); } } - foreach (SqlConnectionContainerHashHelper hashHelper in containersToRemove) - { - _connectionContainers.Remove(hashHelper); + lock (_sqlDependencyPerAppDomainDispatchers) + { // Remove from global Dictionary. + _sqlDependencyPerAppDomainDispatchers.Remove(appDomainKey); } } - - lock (_sqlDependencyPerAppDomainDispatchers) - { // Remove from global Dictionary. - _sqlDependencyPerAppDomainDispatchers.Remove(appDomainKey); + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } @@ -1367,61 +1553,77 @@ private bool Start( bool useDefaults) { Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); - server = null; // Reset out params. - identity = null; - user = null; - database = null; - errorOccurred = false; - appDomainStart = false; - - lock (_sqlDependencyPerAppDomainDispatchers) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, queue: '{1}', appDomainKey: '{2}', perAppDomainDispatcher ID: '{3}'", ObjectID, queueService, appDomainKey, dispatcher.ObjectID); + try { - if (!_sqlDependencyPerAppDomainDispatchers.ContainsKey(appDomainKey)) + server = null; // Reset out params. + identity = null; + user = null; + database = null; + errorOccurred = false; + appDomainStart = false; + + lock (_sqlDependencyPerAppDomainDispatchers) { - _sqlDependencyPerAppDomainDispatchers[appDomainKey] = dispatcher; + if (!_sqlDependencyPerAppDomainDispatchers.ContainsKey(appDomainKey)) + { + _sqlDependencyPerAppDomainDispatchers[appDomainKey] = dispatcher; + } } - } - SqlConnectionContainerHashHelper hashHelper = GetHashHelper(connectionString, - out SqlConnectionStringBuilder connectionStringBuilder, - out identity, - out user, - queueService); + SqlConnectionContainerHashHelper hashHelper = GetHashHelper(connectionString, + out SqlConnectionStringBuilder connectionStringBuilder, + out identity, + out user, + queueService); +#if DEBUG + SqlConnectionString connectionStringOptions = new SqlConnectionString(connectionStringBuilder.ConnectionString); + SqlClientEventSource.Log.NotificationsTraceEvent(" Modified connection string: '{0}'", connectionStringOptions.UsersConnectionStringForTrace()); +#endif - bool started = false; + bool started = false; - SqlConnectionContainer container = null; - lock (_connectionContainers) - { - if (!_connectionContainers.ContainsKey(hashHelper)) + SqlConnectionContainer container = null; + lock (_connectionContainers) { - container = new SqlConnectionContainer(hashHelper, appDomainKey, useDefaults); - _connectionContainers.Add(hashHelper, container); - started = true; - appDomainStart = true; - } - else - { - container = _connectionContainers[hashHelper]; - if (container.InErrorState) + if (!_connectionContainers.ContainsKey(hashHelper)) { - errorOccurred = true; // Set outparam errorOccurred true so we invalidate on Start(). + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, hashtable miss, creating new container.", ObjectID); + container = new SqlConnectionContainer(hashHelper, appDomainKey, useDefaults); + _connectionContainers.Add(hashHelper, container); + started = true; + appDomainStart = true; } else { - container.IncrementStartCount(appDomainKey, out appDomainStart); + container = _connectionContainers[hashHelper]; + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, hashtable hit, container: {1}", ObjectID, container.ObjectID); + if (container.InErrorState) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, container: {1} is in error state!", ObjectID, container.ObjectID); + errorOccurred = true; // Set outparam errorOccurred true so we invalidate on Start(). + } + else + { + container.IncrementStartCount(appDomainKey, out appDomainStart); + } } } - } - if (useDefaults && !errorOccurred) - { // Return server, database, and queue for use by SqlDependency. - server = container.Server; - database = container.Database; - queueService = container.Queue; + if (useDefaults && !errorOccurred) + { // Return server, database, and queue for use by SqlDependency. + server = container.Server; + database = container.Database; + queueService = container.Queue; + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, default service: '{1}', server: '{2}', database: '{3}'", ObjectID, queueService, server, database); + } + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, started: {1}", ObjectID, started); + return started; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - return started; } // Stop methods @@ -1436,38 +1638,55 @@ internal bool Stop( string appDomainKey, out bool appDomainStop) { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, queue: '{1}'", ObjectID, queueService); Debug.Assert(this == s_staticInstance, "Instance method called on non _staticInstance instance!"); - server = null; // Reset out param. - identity = null; - user = null; - database = null; - appDomainStop = false; + try + { + server = null; // Reset out param. + identity = null; + user = null; + database = null; + appDomainStop = false; - SqlConnectionContainerHashHelper hashHelper = GetHashHelper(connectionString, - out SqlConnectionStringBuilder connectionStringBuilder, - out identity, - out user, - queueService); + SqlConnectionContainerHashHelper hashHelper = GetHashHelper(connectionString, + out SqlConnectionStringBuilder connectionStringBuilder, + out identity, + out user, + queueService); +#if DEBUG + SqlConnectionString connectionStringOptions = new SqlConnectionString(connectionStringBuilder.ConnectionString); + SqlClientEventSource.Log.NotificationsTraceEvent(" Modified connection string: '{0}'", connectionStringOptions.UsersConnectionStringForTrace()); +#endif - bool stopped = false; + bool stopped = false; - lock (_connectionContainers) - { - if (_connectionContainers.ContainsKey(hashHelper)) + lock (_connectionContainers) { - SqlConnectionContainer container = _connectionContainers[hashHelper]; - server = container.Server; // Return server, database, and queue info for use by calling SqlDependency. - database = container.Database; - queueService = container.Queue; - - if (container.Stop(appDomainKey, out appDomainStop)) - { // Stop can be blocking if refCount == 0 on container. - stopped = true; - _connectionContainers.Remove(hashHelper); // Remove from collection. + if (_connectionContainers.ContainsKey(hashHelper)) + { + SqlConnectionContainer container = _connectionContainers[hashHelper]; + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, hashtable hit, container: {1}", ObjectID, container.ObjectID); + server = container.Server; // Return server, database, and queue info for use by calling SqlDependency. + database = container.Database; + queueService = container.Queue; + + if (container.Stop(appDomainKey, out appDomainStop)) + { // Stop can be blocking if refCount == 0 on container. + stopped = true; + _connectionContainers.Remove(hashHelper); // Remove from collection. + } + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, hashtable miss.", ObjectID); } } + SqlClientEventSource.Log.NotificationsTraceEvent(" {0}#, stopped: {1}", ObjectID, stopped); + return stopped; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - return stopped; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs index 792b16accf..c75553cf49 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs @@ -19,12 +19,20 @@ private void SubscribeToAppDomainUnload() private void UnloadEventHandler(object sender, EventArgs e) { - // Make non-blocking call to ProcessDispatcher to ThreadPool.QueueUserWorkItem to complete - // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, - // this will just be a ref-count subtract. For non-shared containers, we will close the container - // and clean-up. - var dispatcher = SqlDependency.ProcessDispatcher; - dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + // Make non-blocking call to ProcessDispatcher to ThreadPool.QueueUserWorkItem to complete + // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, + // this will just be a ref-count subtract. For non-shared containers, we will close the container + // and clean-up. + var dispatcher = SqlDependency.ProcessDispatcher; + dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index 50c78d26bb..abb7d8425c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -75,19 +75,30 @@ internal DependencyList(string commandHash) // a timeout. We'll enable this only on demand. private Timer _timeoutTimer; + private static int _objectTypeCount; // EventSource counter + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + private SqlDependencyPerAppDomainDispatcher() { - _dependencyIdToDependencyHash = new Dictionary(); - _notificationIdToDependenciesHash = new Dictionary(); - _commandHashToNotificationId = new Dictionary(); + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#", ObjectID); + try + { + _dependencyIdToDependencyHash = new Dictionary(); + _notificationIdToDependenciesHash = new Dictionary(); + _commandHashToNotificationId = new Dictionary(); - _timeoutTimer = ADP.UnsafeCreateTimer( - new TimerCallback(TimeoutTimerCallback), - null, - Timeout.Infinite, - Timeout.Infinite); + _timeoutTimer = ADP.UnsafeCreateTimer( + new TimerCallback(TimeoutTimerCallback), + null, + Timeout.Infinite, + Timeout.Infinite); - SubscribeToAppDomainUnload(); + SubscribeToAppDomainUnload(); + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // When remoted across appdomains, MarshalByRefObject links by default time out if there is no activity @@ -102,94 +113,184 @@ public override object InitializeLifetimeService() // This method is called upon SqlDependency constructor. internal void AddDependencyEntry(SqlDependency dep) { - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlDependency: {1}#", ObjectID, dep.ObjectID); + try { - _dependencyIdToDependencyHash.Add(dep.Id, dep); + lock (_instanceLock) + { + _dependencyIdToDependencyHash.Add(dep.Id, dep); + } + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } // This method is called upon Execute of a command associated with a SqlDependency object. internal string AddCommandEntry(string commandHash, SqlDependency dep) { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, commandHash: '{1}', SqlDependency: {2}#", ObjectID, commandHash, dep.ObjectID); string notificationId = string.Empty; - lock (_instanceLock) + try { - if (_dependencyIdToDependencyHash.ContainsKey(dep.Id)) + lock (_instanceLock) { - // check if we already have notification associated with given command hash - if (_commandHashToNotificationId.TryGetValue(commandHash, out notificationId)) + if (!_dependencyIdToDependencyHash.ContainsKey(dep.Id)) { - // we have one or more SqlDependency instances with same command hash - - DependencyList dependencyList = null; - if (!_notificationIdToDependenciesHash.TryGetValue(notificationId, out dependencyList)) + // Determine if depId->dep hashtable contains dependency. If not, it's been invalidated. + SqlClientEventSource.Log.NotificationsTraceEvent(" Dependency not present in depId->dep hash, must have been invalidated."); + } + else + { + // check if we already have notification associated with given command hash + if (_commandHashToNotificationId.TryGetValue(commandHash, out notificationId)) { - // this should not happen since _commandHashToNotificationId and _notificationIdToDependenciesHash are always - // updated together - Debug.Fail("_commandHashToNotificationId has entries that were removed from _notificationIdToDependenciesHash. Remember to keep them in sync"); - throw ADP.InternalError(ADP.InternalErrorCode.SqlDependencyCommandHashIsNotAssociatedWithNotification); + // we have one or more SqlDependency instances with same command hash + + DependencyList dependencyList = null; + if (!_notificationIdToDependenciesHash.TryGetValue(notificationId, out dependencyList)) + { + // this should not happen since _commandHashToNotificationId and _notificationIdToDependenciesHash are always + // updated together + Debug.Fail("_commandHashToNotificationId has entries that were removed from _notificationIdToDependenciesHash. Remember to keep them in sync"); + throw ADP.InternalError(ADP.InternalErrorCode.SqlDependencyCommandHashIsNotAssociatedWithNotification); + } + + // join the new dependency to the list + if (!dependencyList.Contains(dep)) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Dependency not present for commandHash, adding."); + dependencyList.Add(dep); + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Dependency already present for commandHash."); + } } - - // join the new dependency to the list - if (!dependencyList.Contains(dep)) + else { + // we did not find notification ID with the same app domain and command hash, create a new one + // use unique guid to avoid duplicate IDs + // prepend app domain ID to the key - SqlConnectionContainer::ProcessNotificationResults (SqlDependencyListener.cs) + // uses this app domain ID to route the message back to the app domain in which this SqlDependency was created + notificationId = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{0};{1}", + SqlDependency.AppDomainKey, // must be first + Guid.NewGuid().ToString("D", System.Globalization.CultureInfo.InvariantCulture) + ); + SqlClientEventSource.Log.NotificationsTraceEvent(" Creating new Dependencies list for commandHash."); + DependencyList dependencyList = new DependencyList(commandHash); dependencyList.Add(dep); + + // map command hash to notification we just created to reuse it for the next client + _commandHashToNotificationId.Add(commandHash, notificationId); + _notificationIdToDependenciesHash.Add(notificationId, dependencyList); } - } - else - { - // we did not find notification ID with the same app domain and command hash, create a new one - // use unique guid to avoid duplicate IDs - // prepend app domain ID to the key - SqlConnectionContainer::ProcessNotificationResults (SqlDependencyListener.cs) - // uses this app domain ID to route the message back to the app domain in which this SqlDependency was created - notificationId = string.Format(System.Globalization.CultureInfo.InvariantCulture, - "{0};{1}", - SqlDependency.AppDomainKey, // must be first - Guid.NewGuid().ToString("D", System.Globalization.CultureInfo.InvariantCulture) - ); - - DependencyList dependencyList = new DependencyList(commandHash); - dependencyList.Add(dep); - - // map command hash to notification we just created to reuse it for the next client - _commandHashToNotificationId.Add(commandHash, notificationId); - _notificationIdToDependenciesHash.Add(notificationId, dependencyList); - } - Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); + Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); + } } - } - return notificationId; + return notificationId; + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // This method is called by the ProcessDispatcher upon a notification for this AppDomain. internal void InvalidateCommandID(SqlNotification sqlNotification) { - List dependencyList = null; - - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, commandHash: '{1}'", ObjectID, sqlNotification.Key); + try { - dependencyList = LookupCommandEntryWithRemove(sqlNotification.Key); + List dependencyList = null; + + lock (_instanceLock) + { + dependencyList = LookupCommandEntryWithRemove(sqlNotification.Key); + + if (null != dependencyList) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" commandHash found in hashtable."); + foreach (SqlDependency dependency in dependencyList) + { + // Ensure we remove from process static app domain hash for dependency initiated invalidates. + LookupDependencyEntryWithRemove(dependency.Id); + + // Completely remove Dependency from commandToDependenciesHash. + RemoveDependencyFromCommandToDependenciesHash(dependency); + } + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" commandHash NOT found in hashtable."); + } + } if (null != dependencyList) { + // After removal from hashtables, invalidate. foreach (SqlDependency dependency in dependencyList) { - // Ensure we remove from process static app domain hash for dependency initiated invalidates. + SqlClientEventSource.Log.NotificationsTraceEvent(" Dependency found in commandHash dependency ArrayList - calling invalidate."); + try + { + dependency.Invalidate(sqlNotification.Type, sqlNotification.Info, sqlNotification.Source); + } + catch (Exception e) + { + // Since we are looping over dependencies, do not allow one Invalidate + // that results in a throw prevent us from invalidating all dependencies + // related to this server. + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + ADP.TraceExceptionWithoutRethrow(e); + } + } + } + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } + } + + // This method is called when a connection goes down or other unknown error occurs in the ProcessDispatcher. + internal void InvalidateServer(string server, SqlNotification sqlNotification) + { + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, server: '{1}'", ObjectID, server); + try + { + List dependencies = new List(); + + lock (_instanceLock) + { // Copy inside of lock, but invalidate outside of lock. + foreach (KeyValuePair entry in _dependencyIdToDependencyHash) + { + SqlDependency dependency = entry.Value; + if (dependency.ContainsServer(server)) + { + dependencies.Add(dependency); + } + } + + foreach (SqlDependency dependency in dependencies) + { // Iterate over resulting list removing from our hashes. + // Ensure we remove from process static app domain hash for dependency initiated invalidates. LookupDependencyEntryWithRemove(dependency.Id); // Completely remove Dependency from commandToDependenciesHash. RemoveDependencyFromCommandToDependenciesHash(dependency); } } - } - if (null != dependencyList) - { - // After removal from hashtables, invalidate. - foreach (SqlDependency dependency in dependencyList) - { + foreach (SqlDependency dependency in dependencies) + { // Iterate and invalidate. try { dependency.Invalidate(sqlNotification.Type, sqlNotification.Info, sqlNotification.Source); @@ -207,154 +308,160 @@ internal void InvalidateCommandID(SqlNotification sqlNotification) } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } - // This method is called when a connection goes down or other unknown error occurs in the ProcessDispatcher. - internal void InvalidateServer(string server, SqlNotification sqlNotification) + // This method is called by SqlCommand to enable ASP.NET scenarios - map from ID to Dependency. + internal SqlDependency LookupDependencyEntry(string id) { - List dependencies = new List(); - - lock (_instanceLock) - { // Copy inside of lock, but invalidate outside of lock. - foreach (KeyValuePair entry in _dependencyIdToDependencyHash) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, Key: '{1}'", ObjectID, id); + try + { + if (null == id) { - SqlDependency dependency = entry.Value; - if (dependency.ContainsServer(server)) - { - dependencies.Add(dependency); - } - } - - foreach (SqlDependency dependency in dependencies) - { // Iterate over resulting list removing from our hashes. - // Ensure we remove from process static app domain hash for dependency initiated invalidates. - LookupDependencyEntryWithRemove(dependency.Id); - - // Completely remove Dependency from commandToDependenciesHash. - RemoveDependencyFromCommandToDependenciesHash(dependency); + throw ADP.ArgumentNull(nameof(id)); } - } - - foreach (SqlDependency dependency in dependencies) - { // Iterate and invalidate. - try + if (string.IsNullOrEmpty(id)) { - dependency.Invalidate(sqlNotification.Type, sqlNotification.Info, sqlNotification.Source); + throw SQL.SqlDependencyIdMismatch(); } - catch (Exception e) + + SqlDependency entry = null; + + lock (_instanceLock) { - // Since we are looping over dependencies, do not allow one Invalidate - // that results in a throw prevent us from invalidating all dependencies - // related to this server. - if (!ADP.IsCatchableExceptionType(e)) + if (_dependencyIdToDependencyHash.ContainsKey(id)) { - throw; + entry = _dependencyIdToDependencyHash[id]; + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" ERROR - dependency ID mismatch - not throwing."); } - ADP.TraceExceptionWithoutRethrow(e); } - } - } - // This method is called by SqlCommand to enable ASP.NET scenarios - map from ID to Dependency. - internal SqlDependency LookupDependencyEntry(string id) - { - if (null == id) - { - throw ADP.ArgumentNull(nameof(id)); + return entry; } - if (string.IsNullOrEmpty(id)) + finally { - throw SQL.SqlDependencyIdMismatch(); + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - SqlDependency entry = null; - - lock (_instanceLock) - { - if (_dependencyIdToDependencyHash.ContainsKey(id)) - { - entry = _dependencyIdToDependencyHash[id]; - } - } - - return entry; } // Remove the dependency from the hashtable with the passed id. private void LookupDependencyEntryWithRemove(string id) { - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, id: '{1}'", ObjectID, id); + try { - if (_dependencyIdToDependencyHash.ContainsKey(id)) + lock (_instanceLock) { - _dependencyIdToDependencyHash.Remove(id); + if (_dependencyIdToDependencyHash.ContainsKey(id)) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Entry found in hashtable - removing."); + _dependencyIdToDependencyHash.Remove(id); - // if there are no more dependencies then we can dispose the timer. - if (0 == _dependencyIdToDependencyHash.Count) + // if there are no more dependencies then we can dispose the timer. + if (0 == _dependencyIdToDependencyHash.Count) + { + _timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); + _sqlDependencyTimeOutTimerStarted = false; + } + } + else { - _timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); - _sqlDependencyTimeOutTimerStarted = false; + SqlClientEventSource.Log.NotificationsTraceEvent(" Entry NOT found in hashtable."); } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } // Find and return arraylist, and remove passed hash value. private List LookupCommandEntryWithRemove(string notificationId) { - DependencyList entry = null; - - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, commandHash: '{1}'", ObjectID, notificationId); + try { - if (_notificationIdToDependenciesHash.TryGetValue(notificationId, out entry)) + DependencyList entry = null; + + lock (_instanceLock) { - // update the tables - _notificationIdToDependenciesHash.Remove(notificationId); - // Cleanup the map between the command hash and associated notification ID - _commandHashToNotificationId.Remove(entry.CommandHash); + if (_notificationIdToDependenciesHash.TryGetValue(notificationId, out entry)) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Entries found in hashtable - removing."); + + // update the tables + _notificationIdToDependenciesHash.Remove(notificationId); + // Cleanup the map between the command hash and associated notification ID + _commandHashToNotificationId.Remove(entry.CommandHash); + } + else + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Entries NOT found in hashtable."); + } + + Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); } - Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); + return entry; // DependencyList inherits from List + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } - - return entry; // DependencyList inherits from List } // Remove from commandToDependenciesHash all references to the passed dependency. private void RemoveDependencyFromCommandToDependenciesHash(SqlDependency dependency) { - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlDependency: {1}#", ObjectID, dependency.ObjectID); + try { - List notificationIdsToRemove = new List(); - List commandHashesToRemove = new List(); - - foreach (KeyValuePair entry in _notificationIdToDependenciesHash) + lock (_instanceLock) { - DependencyList dependencies = entry.Value; - if (dependencies.Remove(dependency)) + List notificationIdsToRemove = new List(); + List commandHashesToRemove = new List(); + + foreach (KeyValuePair entry in _notificationIdToDependenciesHash) { - if (dependencies.Count == 0) + DependencyList dependencies = entry.Value; + if (dependencies.Remove(dependency)) { - // this dependency was the last associated with this notification ID, remove the entry - // note: cannot do it inside foreach over dictionary - notificationIdsToRemove.Add(entry.Key); - commandHashesToRemove.Add(entry.Value.CommandHash); + SqlClientEventSource.Log.NotificationsTraceEvent(" Removed SqlDependency: {0}#, with ID: '{1}'.", dependency.ObjectID, dependency.Id); + if (dependencies.Count == 0) + { + // this dependency was the last associated with this notification ID, remove the entry + // note: cannot do it inside foreach over dictionary + notificationIdsToRemove.Add(entry.Key); + commandHashesToRemove.Add(entry.Value.CommandHash); + } } + + // same SqlDependency can be associated with more than one command, so we have to continue till the end... } - // same SqlDependency can be associated with more than one command, so we have to continue till the end... - } + Debug.Assert(commandHashesToRemove.Count == notificationIdsToRemove.Count, "maps should be kept in sync"); + for (int i = 0; i < notificationIdsToRemove.Count; i++) + { + // cleanup the entry outside of foreach + _notificationIdToDependenciesHash.Remove(notificationIdsToRemove[i]); + // Cleanup the map between the command hash and associated notification ID + _commandHashToNotificationId.Remove(commandHashesToRemove[i]); + } - Debug.Assert(commandHashesToRemove.Count == notificationIdsToRemove.Count, "maps should be kept in sync"); - for (int i = 0; i < notificationIdsToRemove.Count; i++) - { - // cleanup the entry outside of foreach - _notificationIdToDependenciesHash.Remove(notificationIdsToRemove[i]); - // Cleanup the map between the command hash and associated notification ID - _commandHashToNotificationId.Remove(commandHashesToRemove[i]); + Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); } - - Debug.Assert(_notificationIdToDependenciesHash.Count == _commandHashToNotificationId.Count, "always keep these maps in sync!"); + } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } @@ -362,109 +469,132 @@ private void RemoveDependencyFromCommandToDependenciesHash(SqlDependency depende internal void StartTimer(SqlDependency dep) { - // If this dependency expires sooner than the current next timeout, change - // the timeout and enable timer callback as needed. Note that we change _nextTimeout - // only inside the hashtable syncroot. - lock (_instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlDependency: {1}#", ObjectID, dep.ObjectID); + try { - // Enable the timer if needed (disable when empty, enable on the first addition). - if (!_sqlDependencyTimeOutTimerStarted) + // If this dependency expires sooner than the current next timeout, change + // the timeout and enable timer callback as needed. Note that we change _nextTimeout + // only inside the hashtable syncroot. + lock (_instanceLock) { - _timeoutTimer.Change(15000 /* 15 secs */, 15000 /* 15 secs */); + // Enable the timer if needed (disable when empty, enable on the first addition). + if (!_sqlDependencyTimeOutTimerStarted) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Timer not yet started, starting."); + _timeoutTimer.Change(15000 /* 15 secs */, 15000 /* 15 secs */); - // Save this as the earlier timeout to come. - _nextTimeout = dep.ExpirationTime; - _sqlDependencyTimeOutTimerStarted = true; - } - else if (_nextTimeout > dep.ExpirationTime) - { - // Save this as the earlier timeout to come. - _nextTimeout = dep.ExpirationTime; + // Save this as the earlier timeout to come. + _nextTimeout = dep.ExpirationTime; + _sqlDependencyTimeOutTimerStarted = true; + } + else if (_nextTimeout > dep.ExpirationTime) + { + SqlClientEventSource.Log.NotificationsTraceEvent(" Timer already started, resetting time."); + + // Save this as the earlier timeout to come. + _nextTimeout = dep.ExpirationTime; + } } } + finally + { + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); + } } private static void TimeoutTimerCallback(object state) { - SqlDependency[] dependencies; - - // Only take the lock for checking whether there is work to do - // if we do have work, we'll copy the hashtable and scan it after releasing - // the lock. - lock (SingletonInstance._instanceLock) + long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" AppDomainKey: '{0}'", SqlDependency.AppDomainKey); + try { - if (0 == SingletonInstance._dependencyIdToDependencyHash.Count) - { - // Nothing to check. - return; - } - if (SingletonInstance._nextTimeout > DateTime.UtcNow) + SqlDependency[] dependencies; + + // Only take the lock for checking whether there is work to do + // if we do have work, we'll copy the hashtable and scan it after releasing + // the lock. + lock (SingletonInstance._instanceLock) { - // No dependency timed-out yet. - return; - } + if (0 == SingletonInstance._dependencyIdToDependencyHash.Count) + { + SqlClientEventSource.Log.NotificationsTrace(" No dependencies, exiting."); - // If at least one dependency timed-out do a scan of the table. - // NOTE: we could keep a shadow table sorted by expiration time, but - // given the number of typical simultaneously alive dependencies it's - // probably not worth the optimization. - dependencies = new SqlDependency[SingletonInstance._dependencyIdToDependencyHash.Count]; - SingletonInstance._dependencyIdToDependencyHash.Values.CopyTo(dependencies, 0); - } + // Nothing to check. + return; + } + if (SingletonInstance._nextTimeout > DateTime.UtcNow) + { + SqlClientEventSource.Log.NotificationsTrace(" No timeouts expired, exiting."); - // Scan the active dependencies if needed. - DateTime now = DateTime.UtcNow; - DateTime newNextTimeout = DateTime.MaxValue; + // No dependency timed-out yet. + return; + } - for (int i = 0; i < dependencies.Length; i++) - { - // If expired fire the change notification. - if (dependencies[i].ExpirationTime <= now) + // If at least one dependency timed-out do a scan of the table. + // NOTE: we could keep a shadow table sorted by expiration time, but + // given the number of typical simultaneously alive dependencies it's + // probably not worth the optimization. + dependencies = new SqlDependency[SingletonInstance._dependencyIdToDependencyHash.Count]; + SingletonInstance._dependencyIdToDependencyHash.Values.CopyTo(dependencies, 0); + } + + // Scan the active dependencies if needed. + DateTime now = DateTime.UtcNow; + DateTime newNextTimeout = DateTime.MaxValue; + + for (int i = 0; i < dependencies.Length; i++) { - try + // If expired fire the change notification. + if (dependencies[i].ExpirationTime <= now) { - // This invokes user-code which may throw exceptions. - // NOTE: this is intentionally outside of the lock, we don't want - // to invoke user-code while holding an internal lock. - dependencies[i].Invalidate(SqlNotificationType.Change, SqlNotificationInfo.Error, SqlNotificationSource.Timeout); + try + { + // This invokes user-code which may throw exceptions. + // NOTE: this is intentionally outside of the lock, we don't want + // to invoke user-code while holding an internal lock. + dependencies[i].Invalidate(SqlNotificationType.Change, SqlNotificationInfo.Error, SqlNotificationSource.Timeout); + } + catch (Exception e) + { + if (!ADP.IsCatchableExceptionType(e)) + { + throw; + } + + // This is an exception in user code, and we're in a thread-pool thread + // without user's code up in the stack, no much we can do other than + // eating the exception. + ADP.TraceExceptionWithoutRethrow(e); + } } - catch (Exception e) + else { - if (!ADP.IsCatchableExceptionType(e)) + if (dependencies[i].ExpirationTime < newNextTimeout) { - throw; + newNextTimeout = dependencies[i].ExpirationTime; // Track the next earlier timeout. } - - // This is an exception in user code, and we're in a thread-pool thread - // without user's code up in the stack, no much we can do other than - // eating the exception. - ADP.TraceExceptionWithoutRethrow(e); + dependencies[i] = null; // Null means "don't remove it from the hashtable" in the loop below. } } - else + + // Remove timed-out dependencies from the hashtable. + lock (SingletonInstance._instanceLock) { - if (dependencies[i].ExpirationTime < newNextTimeout) + for (int i = 0; i < dependencies.Length; i++) + { + if (null != dependencies[i]) + { + SingletonInstance._dependencyIdToDependencyHash.Remove(dependencies[i].Id); + } + } + if (newNextTimeout < SingletonInstance._nextTimeout) { - newNextTimeout = dependencies[i].ExpirationTime; // Track the next earlier timeout. + SingletonInstance._nextTimeout = newNextTimeout; // We're inside the lock so ok to update. } - dependencies[i] = null; // Null means "don't remove it from the hashtable" in the loop below. } } - - // Remove timed-out dependencies from the hashtable. - lock (SingletonInstance._instanceLock) + finally { - for (int i = 0; i < dependencies.Length; i++) - { - if (null != dependencies[i]) - { - SingletonInstance._dependencyIdToDependencyHash.Remove(dependencies[i].Id); - } - } - if (newNextTimeout < SingletonInstance._nextTimeout) - { - SingletonInstance._nextTimeout = newNextTimeout; // We're inside the lock so ok to update. - } + SqlClientEventSource.Log.NotificationsScopeLeaveEvent(scopeID); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs index 15e313ad30..358ee385f2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs @@ -37,6 +37,10 @@ internal SqlError(int infoNumber, byte errorState, byte errorClass, string serve _lineNumber = lineNumber; _win32ErrorCode = 0; _exception = exception; + if (errorClass != 0) + { + SqlClientEventSource.Log.TraceEvent(" infoNumber={0}, errorState={1}, errorClass={2}, errorMessage='{3}', procedure='{4}', lineNumber={5}", infoNumber, (int)errorState, (int)errorClass, errorMessage, procedure ?? "None", (int)lineNumber); + } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index 7c71dcdedb..6eebfcf36c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -249,6 +249,7 @@ override protected void Deactivate() { try { + SqlClientEventSource.Log.AdvanceTrace(" {0}# deactivating", ObjectID); SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection; if (null != referenceCollection) { @@ -328,11 +329,12 @@ protected void Enlist(Transaction tx) private void EnlistNonNull(Transaction tx) { Debug.Assert(null != tx, "null transaction?"); - + SqlClientEventSource.Log.AdvanceTrace(" {0}#, transaction {1}#.", ObjectID, tx.GetHashCode()); bool hasDelegatedTransaction = false; // Promotable transactions are only supported on Yukon // servers or newer. + SqlClientEventSource.Log.AdvanceTrace(" {0}#, attempting to delegate", ObjectID); SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx); try @@ -396,6 +398,7 @@ private void EnlistNonNull(Transaction tx) if (hasDelegatedTransaction) { this.DelegatedTransaction = delegatedTransaction; + SqlClientEventSource.Log.AdvanceTrace(" {0}#, delegated to transaction {1}# with transactionId=0x{2}", ObjectID, null != CurrentTransaction ? CurrentTransaction.ObjectID : 0, null != CurrentTransaction ? CurrentTransaction.TransactionId : SqlInternalTransaction.NullTransactionId); } } catch (SqlException e) @@ -425,6 +428,7 @@ private void EnlistNonNull(Transaction tx) if (!hasDelegatedTransaction) { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, delegation not possible, enlisting.", ObjectID); byte[] cookie = null; if (_isGlobalTransaction) @@ -455,6 +459,7 @@ private void EnlistNonNull(Transaction tx) PropagateTransactionCookie(cookie); _isEnlistedInTransaction = true; + SqlClientEventSource.Log.AdvanceTrace(" {0}#, enlisted with transaction {1}# with transactionId=0x{2}", ObjectID, null != CurrentTransaction ? CurrentTransaction.ObjectID : 0, null != CurrentTransaction ? CurrentTransaction.TransactionId : SqlInternalTransaction.NullTransactionId); } EnlistedTransaction = tx; // Tell the base class about our enlistment @@ -478,6 +483,7 @@ private void EnlistNonNull(Transaction tx) internal void EnlistNull() { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, unenlisting.", ObjectID); // We were in a transaction, but now we are not - so send // message to server with empty transaction - confirmed proper // behavior from Sameet Agarwal @@ -495,6 +501,8 @@ internal void EnlistNull() _isEnlistedInTransaction = false; EnlistedTransaction = null; // Tell the base class about our enlistment + SqlClientEventSource.Log.AdvanceTrace(" {0}#, unenlisted.", ObjectID); + // The EnlistTransaction above will return an TransactionEnded event, // which causes the TdsParser or SmiEventSink should to clear the // current transaction. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 1dade03a64..18a244e196 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -462,6 +462,7 @@ internal SqlInternalConnectionTds( ThreadHasParserLockForClose = false; _parserLock.Release(); } + SqlClientEventSource.Log.AdvanceTrace(" {0}#, constructed new TDS internal connection", ObjectID); } // Returns true if the Sql error is a transient. @@ -645,6 +646,7 @@ protected override void ChangeDatabaseInternal(string database) public override void Dispose() { + SqlClientEventSource.Log.AdvanceTrace(" {0}# disposing", ObjectID); try { TdsParser parser = Interlocked.Exchange(ref _parser, null); // guard against multiple concurrent dispose calls -- Delegated Transactions might cause this. @@ -1088,11 +1090,18 @@ private void CompleteLogin(bool enlistOK) _parser.Run(RunBehavior.UntilDone, null, null, null, _parser._physicalStateObj); if (RoutingInfo == null) - { // ROR should not affect state of connection recovery + { + // ROR should not affect state of connection recovery if (_federatedAuthenticationRequested && !_federatedAuthenticationAcknowledged) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Server did not acknowledge the federated authentication request", ObjectID); throw SQL.ParsingError(ParsingErrorState.FedAuthNotAcknowledged); } + if (_federatedAuthenticationInfoRequested && !_federatedAuthenticationInfoReceived) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Server never sent the requested federated authentication info", ObjectID); + throw SQL.ParsingError(ParsingErrorState.FedAuthInfoNotReceived); + } if (!_sessionRecoveryAcknowledged) { _currentSessionData = null; @@ -1127,6 +1136,7 @@ private void CompleteLogin(bool enlistOK) _parser.EnableMars(); _fConnectionOpen = true; // mark connection as open + SqlClientEventSource.Log.AdvanceTrace(" Post-Login Phase: Server connection obtained."); // for non-pooled connections, enlist in a distributed transaction // if present - and user specified to enlist @@ -1241,6 +1251,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, private void LoginFailure() { + SqlClientEventSource.Log.TraceEvent(" {0}#", ObjectID); + // If the parser was allocated and we failed, then we must have failed on // either the Connect or Login, either way we should call Disconnect. // Disconnect can be called if the connection is already closed - becomes @@ -1355,7 +1367,7 @@ private void LoginNoFailover(ServerInfo serverInfo, Debug.Assert(object.ReferenceEquals(connectionOptions, this.ConnectionOptions), "ConnectionOptions argument and property must be the same"); // consider removing the argument int routingAttempts = 0; ServerInfo originalServerInfo = serverInfo; // serverInfo may end up pointing to new object due to routing, original object is used to set CurrentDatasource - + SqlClientEventSource.Log.AdvanceTrace(" {0}#, host={1}", ObjectID, serverInfo.UserServerName); int sleepInterval = 100; //milliseconds to sleep (back off) between attempts. ResolveExtendedServerName(serverInfo, !redirectedUserInstance, connectionOptions); @@ -1424,6 +1436,7 @@ private void LoginNoFailover(ServerInfo serverInfo, if (RoutingInfo != null) { + SqlClientEventSource.Log.TraceEvent(" Routed to {0}", serverInfo.ExtendedServerName); if (routingAttempts > 0) { throw SQL.ROR_RecursiveRoutingNotSupported(this); @@ -1504,6 +1517,7 @@ private void LoginNoFailover(ServerInfo serverInfo, // Sleep for a bit to prevent clogging the network with requests, // then update sleep interval for next iteration (max 1 second interval) + SqlClientEventSource.Log.AdvanceTrace(" {0}#, sleeping {1}[milisec]", ObjectID, sleepInterval); Thread.Sleep(sleepInterval); sleepInterval = (sleepInterval < 500) ? sleepInterval * 2 : 1000; } @@ -1547,6 +1561,7 @@ TimeoutTimer timeout ) { Debug.Assert(!connectionOptions.MultiSubnetFailover, "MultiSubnetFailover should not be set if failover partner is used"); + SqlClientEventSource.Log.AdvanceTrace(" {0}#, useFailover={1}[bool], primary={2}, failover={3}", ObjectID, useFailoverHost, primaryServerInfo.UserServerName, failoverHost ?? "null"); int sleepInterval = 100; //milliseconds to sleep (back off) between attempts. long timeoutUnitInterval; @@ -1605,6 +1620,7 @@ TimeoutTimer timeout // Primary server may give us a different failover partner than the connection string indicates. Update it if (null != ServerProvidedFailOverPartner && failoverServerInfo.ResolvedServerName != ServerProvidedFailOverPartner) { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, new failover partner={1}", ObjectID, ServerProvidedFailOverPartner); failoverServerInfo.SetDerivedNames(string.Empty, ServerProvidedFailOverPartner); } currentServerInfo = failoverServerInfo; @@ -1636,7 +1652,7 @@ TimeoutTimer timeout // In any case, server should not have sent the routing info. throw SQL.ROR_UnexpectedRoutingInfo(this); } - + SqlClientEventSource.Log.TraceEvent(" Routed to {0}", RoutingInfo.ServerName); break; // leave the while loop -- we've successfully connected } catch (SqlException sqlex) @@ -1669,6 +1685,7 @@ TimeoutTimer timeout // the network with requests, then update sleep interval for next iteration (max 1 second interval) if (1 == attemptNumber % 2) { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, sleeping {1}[milisec]", ObjectID, sleepInterval); Thread.Sleep(sleepInterval); sleepInterval = (sleepInterval < 500) ? sleepInterval * 2 : 1000; } @@ -1723,6 +1740,7 @@ private void AttemptOneLogin( TimeoutTimer timeout, bool withFailover = false) { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, timout={1}[msec], server={2}", ObjectID, timeout.MillisecondsRemaining, serverInfo.ExtendedServerName); RoutingInfo = null; // forget routing information _parser._physicalStateObj.SniContext = SniContext.Snix_Connect; @@ -1825,6 +1843,7 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = internal void BreakConnection() { var connection = Connection; + SqlClientEventSource.Log.TraceEvent(" {0}#, Breaking connection.", ObjectID); DoomThisConnection(); // Mark connection as unusable, so it will be destroyed if (null != connection) { @@ -1930,6 +1949,7 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_ROUTING: + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received routing info", ObjectID); if (string.IsNullOrEmpty(rec.newRoutingInfo.ServerName) || rec.newRoutingInfo.Protocol != 0 || rec.newRoutingInfo.Port == 0) { throw SQL.ROR_InvalidRoutingInfo(this); @@ -1972,7 +1992,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); Debug.Assert(_dbConnectionPoolAuthenticationContextKey == null, "_dbConnectionPoolAuthenticationContextKey should be null."); - + SqlClientEventSource.Log.TraceEvent(" {0}#, Generating federated authentication token", ObjectID); DbConnectionPoolAuthenticationContext dbConnectionPoolAuthenticationContext = null; // We want to refresh the token without taking a lock on the context, allowed when the access token is expiring within the next 10 mins. @@ -2003,6 +2023,9 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // And on successful login, try to update the cache with the new token. if (contextValidity <= _dbAuthenticationContextUnLockedRefreshTimeSpan) { + SqlClientEventSource.Log.TraceEvent(" {0}#, " + + "The expiration time is less than 10 mins, so trying to get new access token regardless of if an other thread is also trying to update it." + + "The expiration time is {1}. Current Time is {2}.", ObjectID, dbConnectionPoolAuthenticationContext.ExpirationTime.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString()); attemptRefreshTokenUnLocked = true; } @@ -2022,6 +2045,10 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // If a thread is already doing the refresh, just use the existing token in the cache and proceed. else if (contextValidity <= _dbAuthenticationContextLockedRefreshTimeSpan) { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, " + + "The authentication context needs a refresh.The expiration time is {1}. " + + "Current Time is {2}.", ObjectID, dbConnectionPoolAuthenticationContext.ExpirationTime.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString()); + // Call the function which tries to acquire a lock over the authentication context before trying to update. // If the lock could not be obtained, it will return false, without attempting to fetch a new token. attemptRefreshTokenLocked = TryGetFedAuthTokenLocked(fedAuthInfo, dbConnectionPoolAuthenticationContext, out fedAuthToken); @@ -2030,7 +2057,14 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // If there was an exception in retrieving the new token, TryGetFedAuthTokenLocked should have thrown, so we won't be here. Debug.Assert(!attemptRefreshTokenLocked || fedAuthToken != null, "Either Lock should not have been obtained or fedAuthToken should not be null."); Debug.Assert(!attemptRefreshTokenLocked || _newDbConnectionPoolAuthenticationContext != null, "Either Lock should not have been obtained or _newDbConnectionPoolAuthenticationContext should not be null."); + + // Indicate in EventSource Trace that we are successful with the update. + if (attemptRefreshTokenLocked) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, The attempt to get a new access token succeeded under the locked mode.", ObjectID); + } } + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Found an authentication context in the cache that does not need a refresh at this time. Re-using the cached token.", ObjectID); } } @@ -2090,8 +2124,15 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP // Else some other thread is already updating it, so just proceed forward with the existing token in the cache. if (dbConnectionPoolAuthenticationContext.LockToUpdate()) { + SqlClientEventSource.Log.TraceEvent(" {0}#, " + + "Acquired the lock to update the authentication context.The expiration time is {1}. " + + "Current Time is {2}.", ObjectID, dbConnectionPoolAuthenticationContext.ExpirationTime.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString()); authenticationContextLocked = true; } + else + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Refreshing the context is already in progress by another thread.", ObjectID); + } if (authenticationContextLocked) { @@ -2248,6 +2289,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) || _timeout.IsExpired || _timeout.MillisecondsRemaining <= sleepInterval) { + SqlClientEventSource.Log.TraceEvent(" {0}#", msalException.ErrorCode); // Error[0] SqlErrorCollection sqlErs = new SqlErrorCollection(); sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, SRHelper.GetString(SR.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); @@ -2265,6 +2307,9 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) throw exc; } + SqlClientEventSource.Log.AdvanceTrace(" {0}#, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); + SqlClientEventSource.Log.AdvanceTrace(" {0}#, remaining {1}[Milliseconds]", ObjectID, _timeout.MillisecondsRemaining); + Thread.Sleep(sleepInterval); sleepInterval *= 2; } @@ -2279,7 +2324,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) DateTime expirationTime = DateTime.FromFileTimeUtc(fedAuthToken.expirationFileTime); _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(fedAuthToken.accessToken, expirationTime); } - + SqlClientEventSource.Log.TraceEvent(" {0}#, Finished generating federated authentication token.", ObjectID); return fedAuthToken; } @@ -2343,8 +2388,10 @@ internal void OnFeatureExtAck(int featureId, byte[] data) case TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS: { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received feature extension acknowledgement for GlobalTransactions", ObjectID); if (data.Length < 1) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown version number for GlobalTransactions", ObjectID); throw SQL.ParsingError(); } @@ -2357,8 +2404,10 @@ internal void OnFeatureExtAck(int featureId, byte[] data) } case TdsEnums.FEATUREEXT_FEDAUTH: { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received feature extension acknowledgement for federated authentication", ObjectID); if (!_federatedAuthenticationRequested) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Did not request federated authentication", ObjectID); throw SQL.ParsingErrorFeatureId(ParsingErrorState.UnrequestedFeatureAckReceived, featureId); } @@ -2371,27 +2420,53 @@ internal void OnFeatureExtAck(int featureId, byte[] data) // The server shouldn't have sent any additional data with the ack (like a nonce) if (data.Length != 0) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Federated authentication feature extension ack for MSAL and Security Token includes extra data", ObjectID); throw SQL.ParsingError(ParsingErrorState.FedAuthFeatureAckContainsExtraData); } break; default: Debug.Fail("Unknown _fedAuthLibrary type"); + SqlClientEventSource.Log.TraceEvent(" {0}#, Attempting to use unknown federated authentication library", ObjectID); throw SQL.ParsingErrorLibraryType(ParsingErrorState.FedAuthFeatureAckUnknownLibraryType, (int)_fedAuthFeatureExtensionData.Value.libraryType); } _federatedAuthenticationAcknowledged = true; + + if (_newDbConnectionPoolAuthenticationContext != null) + { + Debug.Assert(_dbConnectionPool != null, "_dbConnectionPool should not be null when _newDbConnectionPoolAuthenticationContext != null."); + + DbConnectionPoolAuthenticationContext newAuthenticationContextInCacheAfterAddOrUpdate = _dbConnectionPool.AuthenticationContexts.AddOrUpdate(_dbConnectionPoolAuthenticationContextKey, _newDbConnectionPoolAuthenticationContext, + (key, oldValue) => DbConnectionPoolAuthenticationContext.ChooseAuthenticationContextToUpdate(oldValue, _newDbConnectionPoolAuthenticationContext)); + + Debug.Assert(newAuthenticationContextInCacheAfterAddOrUpdate != null, "newAuthenticationContextInCacheAfterAddOrUpdate should not be null."); +#if DEBUG + // For debug purposes, assert and trace if we ended up updating the cache with the new one or some other thread's context won the expiration race. + if (newAuthenticationContextInCacheAfterAddOrUpdate == _newDbConnectionPoolAuthenticationContext) + { + SqlClientEventSource.Log.TraceEvent(" {0}#, Updated the new dbAuthenticationContext in the _dbConnectionPool.AuthenticationContexts.", ObjectID); + } + else + { + SqlClientEventSource.Log.TraceEvent(" {0}#, AddOrUpdate attempted on _dbConnectionPool.AuthenticationContexts, but it did not update the new value.", ObjectID); + } +#endif + } break; } case TdsEnums.FEATUREEXT_TCE: { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received feature extension acknowledgement for TCE", ObjectID); if (data.Length < 1) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown version number for TCE", ObjectID); throw SQL.ParsingError(ParsingErrorState.TceUnknownVersion); } byte supportedTceVersion = data[0]; if (0 == supportedTceVersion || supportedTceVersion > TdsEnums.MAX_SUPPORTED_TCE_VERSION) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Invalid version number for TCE", ObjectID); throw SQL.ParsingErrorValue(ParsingErrorState.TceInvalidVersion, supportedTceVersion); } @@ -2411,26 +2486,32 @@ internal void OnFeatureExtAck(int featureId, byte[] data) case TdsEnums.FEATUREEXT_UTF8SUPPORT: { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received feature extension acknowledgement for UTF8 support", ObjectID); if (data.Length < 1) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown value for UTF8 support", ObjectID); throw SQL.ParsingError(); } break; } case TdsEnums.FEATUREEXT_DATACLASSIFICATION: { + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Received feature extension acknowledgement for DATACLASSIFICATION", ObjectID); if (data.Length < 1) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown token for DATACLASSIFICATION", ObjectID); throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } byte supportedDataClassificationVersion = data[0]; if ((0 == supportedDataClassificationVersion) || (supportedDataClassificationVersion > TdsEnums.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION)) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Invalid version number for DATACLASSIFICATION", ObjectID); throw SQL.ParsingErrorValue(ParsingErrorState.DataClassificationInvalidVersion, supportedDataClassificationVersion); } if (data.Length != 2) { + SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown token for DATACLASSIFICATION", ObjectID); throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } byte enabled = data[1]; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs index 5ca53cfa3a..b972b47444 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs @@ -40,6 +40,8 @@ sealed internal class SqlInternalTransaction private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. + private static int _objectTypeCount; // EventSource counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal bool RestoreBrokenConnection { get; set; } internal bool ConnectionHasBeenRestored { get; set; } @@ -50,6 +52,7 @@ internal SqlInternalTransaction(SqlInternalConnection innerConnection, Transacti internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Created for connection {1}#, outer transaction {2}#, Type {3}", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); _innerConnection = innerConnection; _transactionType = type; @@ -177,6 +180,13 @@ internal bool IsZombied } } + internal int ObjectID + { + get + { + return _objectID; + } + } internal int OpenResultsCount { @@ -244,7 +254,7 @@ internal void CloseFromConnection() SqlInternalConnection innerConnection = _innerConnection; Debug.Assert(innerConnection != null, "How can we be here if the connection is null?"); - + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Closing", ObjectID); bool processFinallyBlock = true; try { @@ -270,6 +280,8 @@ internal void CloseFromConnection() internal void Commit() { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); @@ -296,6 +308,10 @@ internal void Commit() throw; } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal void Completed(TransactionState transactionState) @@ -323,6 +339,7 @@ internal void Dispose() private /*protected override*/ void Dispose(bool disposing) { + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Disposing", ObjectID); if (disposing) { if (null != _innerConnection) @@ -373,6 +390,7 @@ internal void InitParent(SqlTransaction transaction) internal void Rollback() { + var scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); @@ -406,10 +424,15 @@ internal void Rollback() throw; } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal void Rollback(string transactionName) { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#, transactionName='{transactionName}'", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); @@ -437,10 +460,15 @@ internal void Rollback(string transactionName) } throw; } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal void Save(string savePointName) { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#, savePointName='{savePointName}'", ObjectID); _innerConnection.ValidateConnectionForExecute(null); // ROLLBACK takes either a save point name or a transaction name. It will rollback the @@ -465,6 +493,10 @@ internal void Save(string savePointName) throw; } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } internal void Zombie() @@ -511,5 +543,11 @@ private void ZombieParent() _parent = null; } } + + internal string TraceString() + { + return String.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", + ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 14c9f3081f..442856fd33 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -16,6 +16,8 @@ public sealed class SqlTransaction : DbTransaction { private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + private static int _objectTypeCount; // Bid counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; private SqlInternalTransaction _internalTransaction; @@ -104,6 +106,13 @@ internal bool IsZombied } } + internal int ObjectID + { + get + { + return _objectID; + } + } internal SqlStatistics Statistics { @@ -133,6 +142,8 @@ override public void Commit() ZombieCheck(); SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -161,6 +172,8 @@ override public void Commit() } finally { + SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { s_diagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connection, e); @@ -171,8 +184,6 @@ override public void Commit() } _isFromAPI = false; - - SqlStatistics.StopTimer(statistics); } } @@ -198,6 +209,7 @@ override public void Rollback() if (IsYukonPartialZombie) { // Put something in the trace in case a customer has an issue + SqlClientEventSource.Log.AdvanceTrace(" {0}# partial zombie no rollback required", ObjectID); _internalTransaction = null; // yukon zombification } else @@ -205,6 +217,8 @@ override public void Rollback() ZombieCheck(); SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -220,6 +234,8 @@ override public void Rollback() } finally { + SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, null, e); @@ -229,8 +245,6 @@ override public void Rollback() s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, null); } _isFromAPI = false; - - SqlStatistics.StopTimer(statistics); } } } @@ -242,7 +256,7 @@ public void Rollback(string transactionName) Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, transactionName); ZombieCheck(); - + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}# transactionName='{1}'", ObjectID, transactionName); SqlStatistics statistics = null; try { @@ -259,6 +273,8 @@ public void Rollback(string transactionName) } finally { + SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, transactionName, e); @@ -270,7 +286,6 @@ public void Rollback(string transactionName) _isFromAPI = false; - SqlStatistics.StopTimer(statistics); } } @@ -280,6 +295,7 @@ public void Save(string savePointName) ZombieCheck(); SqlStatistics statistics = null; + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}# savePointName='{1}'", ObjectID, savePointName); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -289,6 +305,7 @@ public void Save(string savePointName) finally { SqlStatistics.StopTimer(statistics); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -304,7 +321,11 @@ internal void Zombie() // Of course, if the connection is already closed, // then we're free to zombify... SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - if (internalConnection == null || _isFromAPI) + if (null != internalConnection && !_isFromAPI) + { + SqlClientEventSource.Log.AdvanceTrace(" {0}# yukon deferred zombie", ObjectID); + } + else { _internalTransaction = null; // pre-yukon zombification } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 3c827f323e..1dfb67f4ba 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -38,6 +38,10 @@ internal struct SNIErrorDetails // and surfacing objects to the user. internal sealed partial class TdsParser { + private static int _objectTypeCount; // EventSource counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + internal int ObjectID => _objectID; + // Default state object for parser internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI. @@ -285,6 +289,14 @@ internal SqlStatistics Statistics } } + private bool IncludeTraceHeader + { + get + { + return (_isDenali && SqlClientEventSource.Log.IsEnabled()); + } + } + internal int IncrementNonTransactedOpenResultCount() { // IMPORTANT - this increments the connection wide open result count for all @@ -342,12 +354,34 @@ internal void Connect( ThrowExceptionAndWarning(_physicalStateObj); Debug.Fail("SNI returned status != success, but no error thrown?"); } + else + { + _sniSpnBuffer = null; + if (authType == SqlAuthenticationMethod.ActiveDirectoryPassword) + { + SqlClientEventSource.Log.TraceEvent(" Active Directory Password authentication", "SEC"); + } + else if (authType == SqlAuthenticationMethod.SqlPassword) + { + + SqlClientEventSource.Log.TraceEvent(" SQL Password authentication", "SEC"); + } + else if (authType == SqlAuthenticationMethod.ActiveDirectoryInteractive) + { + SqlClientEventSource.Log.TraceEvent(" Active Directory Interactive authentication", "SEC"); + } + else + { + SqlClientEventSource.Log.TraceEvent(" SQL authentication", "SEC"); + } + } _sniSpnBuffer = null; if (integratedSecurity) { LoadSSPILibrary(); + SqlClientEventSource.Log.TraceEvent(" SSPI or Active Directory Authentication Library for SQL Server based integrated authentication", "SEC"); } byte[] instanceName = null; @@ -370,6 +404,7 @@ internal void Connect( // don't, the memory for the connection object might not be accurate and thus // a bad error could be returned (as it was when it was freed to early for me). _physicalStateObj.Dispose(); + SqlClientEventSource.Log.TraceEvent(" Login failure", "ERR|SEC"); ThrowExceptionAndWarning(_physicalStateObj); Debug.Fail("SNI returned status != success, but no error thrown?"); } @@ -397,18 +432,19 @@ internal void Connect( uint result = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); - + SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); SendPreLoginHandshake(instanceName, encrypt); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _physicalStateObj.SniContext = SniContext.Snix_PreLogin; - + SqlClientEventSource.Log.TraceEvent(" Consuming prelogin handshake", "SEC"); PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); if (status == PreLoginHandshakeStatus.InstanceFailure) { + SqlClientEventSource.Log.TraceEvent(" Prelogin handshake unsuccessful. Reattempting prelogin handshake", "SEC"); _physicalStateObj.Dispose(); // Close previous connection // On Instance failure re-connect and flush SNI named instance cache. @@ -419,12 +455,14 @@ internal void Connect( if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + SqlClientEventSource.Log.TraceEvent(" Login failure", "ERR|SEC"); ThrowExceptionAndWarning(_physicalStateObj); } uint retCode = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId); Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); + SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); SendPreLoginHandshake(instanceName, encrypt); status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); @@ -433,6 +471,7 @@ internal void Connect( // one pre-login packet and know we are connecting to Shiloh. if (status == PreLoginHandshakeStatus.InstanceFailure) { + SqlClientEventSource.Log.TraceEvent(" Prelogin handshake unsuccessful. Login failure", "ERR|SEC"); throw SQL.InstanceFailure(); } } @@ -492,7 +531,9 @@ internal void EnableMars() internal TdsParserStateObject CreateSession() { - return TdsParserStateObjectFactory.Singleton.CreateSessionObject(this, _pMarsPhysicalConObj, true); + TdsParserStateObject session = TdsParserStateObjectFactory.Singleton.CreateSessionObject(this, _pMarsPhysicalConObj, true); + SqlClientEventSource.Log.AdvanceTrace(" {0}# created session {1}", ObjectID, session.ObjectID); + return session; } internal TdsParserStateObject GetSession(object owner) @@ -503,10 +544,12 @@ internal TdsParserStateObject GetSession(object owner) session = _sessionPool.GetSession(owner); Debug.Assert(!session.HasPendingData, "pending data on a pooled MARS session"); + SqlClientEventSource.Log.AdvanceTrace(" {0}# getting session {1} from pool", ObjectID, session.ObjectID); } else { session = _physicalStateObj; + SqlClientEventSource.Log.AdvanceTrace(" {0}# getting physical session {1}", ObjectID, session.ObjectID); } Debug.Assert(session._outputPacketNumber == 1, "The packet number is expected to be 1"); return session; @@ -666,6 +709,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) int actIdSize = GUID_SIZE + sizeof(uint); offset += actIdSize; optionDataSize += actIdSize; + SqlClientEventSource.Log.TraceEvent(" ClientConnectionID {0}, ActivityID {1}", _connHandler._clientConnectionId.ToString(), actId.ToString()); break; case (int)PreLoginOptions.FEDAUTHREQUIRED: @@ -897,6 +941,8 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus // Only 0x00 and 0x01 are accepted values from the server. if (payload[payloadOffset] != 0x00 && payload[payloadOffset] != 0x01) { + SqlClientEventSource.Log.TraceEvent(" {0}#, " + + "Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was {1}.", ObjectID, (int)payload[payloadOffset]); throw SQL.ParsingErrorValue(ParsingErrorState.FedAuthRequiredPreLoginResponseInvalidValue, (int)payload[payloadOffset]); } @@ -932,7 +978,8 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus internal void Deactivate(bool connectionIsDoomed) { // Called when the connection that owns us is deactivated. - + SqlClientEventSource.Log.AdvanceTrace(" {0}# deactivating", ObjectID); + SqlClientEventSource.Log.StateDumpEvent(" {0}# {1}", ObjectID, TraceString()); if (MARSOn) { @@ -1090,6 +1137,10 @@ internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool calle // can be deleted) SqlErrorCollection temp = stateObj.GetFullErrorAndWarningCollection(out breakConnection); + if (temp.Count == 0) + { + SqlClientEventSource.Log.TraceEvent(" Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock {0}#", ObjectID); + } Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: null errors collection!"); Debug.Assert(temp.Count > 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!"); Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!"); @@ -1784,6 +1835,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead Debug.Fail($"unexpected token; token = {token,-2:X2}"); _state = TdsParserState.Broken; _connHandler.BreakConnection(); + SqlClientEventSource.Log.TraceEvent(" Potential multi-threaded misuse of connection, unexpected TDS token found {0}#", ObjectID); throw SQL.ParsingError(); } @@ -2107,6 +2159,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead } case TdsEnums.SQLLOGINACK: { + SqlClientEventSource.Log.TraceEvent(" Received login acknowledgement token", "SEC"); SqlLoginAck ack; if (!TryProcessLoginAck(stateObj, out ack)) { @@ -3503,9 +3556,11 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, SqlFedAuthInfo tempFedAuthInfo = new SqlFedAuthInfo(); // Skip reading token length, since it has already been read in caller + SqlClientEventSource.Log.AdvanceTrace(" FEDAUTHINFO token stream length = {0}", tokenLen); if (tokenLen < sizeof(uint)) { // the token must at least contain a DWORD indicating the number of info IDs + SqlClientEventSource.Log.TraceEvent(" FEDAUTHINFO token stream length too short for CountOfInfoIDs."); throw SQL.ParsingErrorLength(ParsingErrorState.FedAuthInfoLengthTooShortForCountOfInfoIds, tokenLen); } @@ -3513,19 +3568,22 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, uint optionsCount; if (!stateObj.TryReadUInt32(out optionsCount)) { + SqlClientEventSource.Log.TraceEvent(" Failed to read CountOfInfoIDs in FEDAUTHINFO token stream.", "ERR"); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadCountOfInfoIds); } tokenLen -= sizeof(uint); // remaining length is shortened since we read optCount - + SqlClientEventSource.Log.AdvanceTrace(" CountOfInfoIDs = {0}", optionsCount.ToString(CultureInfo.InvariantCulture)); if (tokenLen > 0) { // read the rest of the token byte[] tokenData = new byte[tokenLen]; int totalRead = 0; bool successfulRead = stateObj.TryReadByteArray(tokenData, tokenLen, out totalRead); + SqlClientEventSource.Log.AdvanceTrace(" Read rest of FEDAUTHINFO token stream: {0}", BitConverter.ToString(tokenData, 0, totalRead)); if (!successfulRead || totalRead != tokenLen) { + SqlClientEventSource.Log.TraceEvent(" Failed to read FEDAUTHINFO token stream. Attempted to read {0} bytes, actually read {1}", tokenLen, totalRead); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadTokenStream); } @@ -3546,6 +3604,7 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, byte id = tokenData[currentOptionOffset]; uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1))); uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5))); + SqlClientEventSource.Log.AdvanceTrace(" FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}", id, dataLen.ToString(CultureInfo.InvariantCulture), dataOffset.ToString(CultureInfo.InvariantCulture)); // offset is measured from optCount, so subtract to make offset measured // from the beginning of tokenData @@ -3557,6 +3616,7 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, // if dataOffset points to a region within FedAuthInfoOpt or after the end of the token, throw if (dataOffset < totalOptionsSize || dataOffset >= tokenLen) { + SqlClientEventSource.Log.TraceEvent(" FedAuthInfoDataOffset points to an invalid location.", "ERR"); throw SQL.ParsingErrorOffset(ParsingErrorState.FedAuthInfoInvalidOffset, unchecked((int)dataOffset)); } @@ -3568,12 +3628,15 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, } catch (ArgumentOutOfRangeException e) { + SqlClientEventSource.Log.TraceEvent(" Failed to read FedAuthInfoData.", "ERR"); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadData, e); } catch (ArgumentException e) { + SqlClientEventSource.Log.TraceEvent(" FedAuthInfoData is not in unicode format.", "ERR"); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDataNotUnicode, e); } + SqlClientEventSource.Log.AdvanceTrace(" FedAuthInfoData: {0}", data); // store data in tempFedAuthInfo switch ((TdsEnums.FedAuthInfoId)id) @@ -3581,22 +3644,28 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, case TdsEnums.FedAuthInfoId.Spn: tempFedAuthInfo.spn = data; break; + case TdsEnums.FedAuthInfoId.Stsurl: tempFedAuthInfo.stsurl = data; break; + default: + SqlClientEventSource.Log.AdvanceTrace(" Ignoring unknown federated authentication info option: {0}", id); break; } } } else { + SqlClientEventSource.Log.TraceEvent(" FEDAUTHINFO token stream is not long enough to contain the data it claims to.", "ERR"); throw SQL.ParsingErrorLength(ParsingErrorState.FedAuthInfoLengthTooShortForData, tokenLen); } + SqlClientEventSource.Log.TraceEvent(" Processed FEDAUTHINFO token stream: {0}", tempFedAuthInfo.ToString()); if (String.IsNullOrWhiteSpace(tempFedAuthInfo.stsurl) || String.IsNullOrWhiteSpace(tempFedAuthInfo.spn)) { // We should be receiving both stsurl and spn + SqlClientEventSource.Log.TraceEvent(" FEDAUTHINFO token stream does not contain both STSURL and SPN.", "ERR"); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDoesNotContainStsurlAndSpn); } @@ -3963,6 +4032,7 @@ internal bool TryProcessTceCryptoMetadata(TdsParserStateObject stateObj, // validate the index (ordinal passed) if (index >= cipherTable.Value.Size) { + SqlClientEventSource.Log.TraceEvent(" Incorrect ordinal received {0}, max tab size: {1}", index, cipherTable.Value.Size); throw SQL.ParsingErrorValue(ParsingErrorState.TceInvalidOrdinalIntoCipherInfoTable, index); } } @@ -7916,6 +7986,7 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures } WriteInt(log7Flags, _physicalStateObj); + SqlClientEventSource.Log.AdvanceTrace(" {0}#, TDS Login7 flags = {1}:", ObjectID, log7Flags); WriteInt(0, _physicalStateObj); // ClientTimeZone is not used WriteInt(0, _physicalStateObj); // LCID is unused by server @@ -8033,6 +8104,11 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures // write ibFeatureExtLong if (useFeatureExt) { + if ((requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0) + { + SqlClientEventSource.Log.TraceEvent(" Sending federated authentication feature request", "SEC"); + } + WriteInt(feOffset, _physicalStateObj); } @@ -8119,7 +8195,7 @@ internal void SendFedAuthToken(SqlFedAuthToken fedAuthToken) { Debug.Assert(fedAuthToken != null, "fedAuthToken cannot be null"); Debug.Assert(fedAuthToken.accessToken != null, "fedAuthToken.accessToken cannot be null"); - + SqlClientEventSource.Log.TraceEvent(" Sending federated authentication token", "SEC"); _physicalStateObj._outputMessageType = TdsEnums.MT_FEDAUTH; byte[] accessToken = fedAuthToken.accessToken; @@ -8493,7 +8569,7 @@ bool isDelegateControlRequest internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) { int old_outputPacketNumber = stateObj._outputPacketNumber; - + SqlClientEventSource.Log.TraceEvent(" Exception caught on ExecuteXXX: '{0}'", e.ToString()); if (stateObj.HasOpenResult) { // Need to decrement openResultCount if operation failed. @@ -8524,6 +8600,7 @@ internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) // Reset the ThreadHasParserLock value in case our caller expects it to be set\not set _connHandler.ThreadHasParserLockForClose = originalThreadHasParserLock; } + SqlClientEventSource.Log.TraceEvent(" Exception rethrown.", "ERR"); } } @@ -8578,6 +8655,11 @@ internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationReques _connHandler.CheckEnlistedTransactionBinding(); stateObj.SetTimeoutSeconds(timeout); + + if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) + { + SqlClientEventSource.Log.TraceEvent(" Potential multi-threaded misuse of connection, non-MARs connection with an open result {0}#", ObjectID); + } stateObj.SniContext = SniContext.Snix_Execute; WriteRPCBatchHeaders(stateObj, notificationRequest); @@ -8703,6 +8785,11 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo stateObj.SetTimeoutSeconds(timeout); + if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) + { + SqlClientEventSource.Log.TraceEvent(" Potential multi-threaded misuse of connection, non-MARs connection with an open result {0}#", ObjectID); + } + stateObj.SniContext = SniContext.Snix_Execute; WriteRPCBatchHeaders(stateObj, notificationRequest); @@ -9520,6 +9607,8 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(metaData.SqlDbType, metaData.IsMultiValued, value, null); } + var sendDefaultValue = sendDefault ? 1 : 0; + SqlClientEventSource.Log.AdvanceTrace(" {0}#, Sending parameter '{1}', default flag={2}, metadata:{3}", ObjectID, param.ParameterName, sendDefaultValue, metaData.TraceString(3)); // // Write parameter metadata @@ -10558,6 +10647,7 @@ private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificatio Debug.Assert(ushort.MaxValue >= service.Length, "Service length is out of range"); Debug.Assert(-1 <= timeout, "Timeout"); + SqlClientEventSource.Log.NotificationsTraceEvent(" NotificationRequest: userData: '{0}', options: '{1}', timeout: '{2}'", notificationRequest.UserData, notificationRequest.Options, notificationRequest.Timeout); WriteShort(TdsEnums.HEADERTYPE_QNOTIFICATION, stateObj); // Query notifications Type WriteShort(callbackId.Length * 2, stateObj); // Length in bytes @@ -10569,6 +10659,22 @@ private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificatio WriteInt(timeout, stateObj); } + private void WriteTraceHeaderData(TdsParserStateObject stateObj) + { + Debug.Assert(this.IncludeTraceHeader, "WriteTraceHeaderData can only be called on a Denali or higher version server and bid trace with the control bit are on"); + + // We may need to update the trace header length if trace header is changed in the future + + ActivityCorrelator.ActivityId actId = ActivityCorrelator.Current; + + WriteShort(TdsEnums.HEADERTYPE_TRACE, stateObj); // Trace Header Type + + stateObj.WriteByteArray(actId.Id.ToByteArray(), GUID_SIZE, 0); // Id (Guid) + WriteUnsignedInt(actId.Sequence, stateObj); // sequence number + + SqlClientEventSource.Log.TraceEvent(" ActivityID {0}", actId.ToString()); + } + private void WriteRPCBatchHeaders(TdsParserStateObject stateObj, SqlNotificationRequest notificationRequest) { /* Header: @@ -12472,5 +12578,80 @@ private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObjec return true; } + + const string StateTraceFormatString = "\n\t" + + " _physicalStateObj = {0}\n\t" + + " _pMarsPhysicalConObj = {1}\n\t" + + " _state = {2}\n\t" + + " _server = {3}\n\t" + + " _fResetConnection = {4}\n\t" + + " _defaultCollation = {5}\n\t" + + " _defaultCodePage = {6}\n\t" + + " _defaultLCID = {7}\n\t" + + " _defaultEncoding = {8}\n\t" + + " _encryptionOption = {9}\n\t" + + " _currentTransaction = {10}\n\t" + + " _pendingTransaction = {11}\n\t" + + " _retainedTransactionId = {12}\n\t" + + " _nonTransactedOpenResultCount = {13}\n\t" + + " _connHandler = {14}\n\t" + + " _fMARS = {15}\n\t" + + " _sessionPool = {16}\n\t" + + " _isYukon = {17}\n\t" + + " _sniSpnBuffer = {18}\n\t" + + " _errors = {19}\n\t" + + " _warnings = {20}\n\t" + + " _attentionErrors = {21}\n\t" + + " _attentionWarnings = {22}\n\t" + + " _statistics = {23}\n\t" + + " _statisticsIsInTransaction = {24}\n\t" + + " _fPreserveTransaction = {25}" + + " _fParallel = {26}" + ; + internal string TraceString() + { + return String.Format(/*IFormatProvider*/ null, + StateTraceFormatString, + null == _physicalStateObj, + null == _pMarsPhysicalConObj, + _state, + _server, + _fResetConnection, + null == _defaultCollation ? "(null)" : _defaultCollation.TraceString(), + _defaultCodePage, + _defaultLCID, + TraceObjectClass(_defaultEncoding), + "", + _encryptionOption, + null == _currentTransaction ? "(null)" : _currentTransaction.TraceString(), + null == _pendingTransaction ? "(null)" : _pendingTransaction.TraceString(), + _retainedTransactionId, + _nonTransactedOpenResultCount, + null == _connHandler ? "(null)" : _connHandler.ObjectID.ToString((IFormatProvider)null), + _fMARS, + null == _sessionPool ? "(null)" : _sessionPool.TraceString(), + _isYukon, + null == _sniSpnBuffer ? "(null)" : _sniSpnBuffer.Length.ToString((IFormatProvider)null), + _physicalStateObj != null ? "(null)" : _physicalStateObj.ErrorCount.ToString((IFormatProvider)null), + _physicalStateObj != null ? "(null)" : _physicalStateObj.WarningCount.ToString((IFormatProvider)null), + _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionErrorCount.ToString((IFormatProvider)null), + _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionWarningCount.ToString((IFormatProvider)null), + null == _statistics, + _statisticsIsInTransaction, + _fPreserveTransaction, + null == _connHandler ? "(null)" : _connHandler.ConnectionOptions.MultiSubnetFailover.ToString((IFormatProvider)null)); + } + + private string TraceObjectClass(object instance) + { + if (null == instance) + { + return "(null)"; + } + else + { + return instance.GetType().ToString(); + } + } } // tdsparser }//namespace diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index b87e83137e..1e24817b85 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -250,6 +250,10 @@ internal SqlCompareOptions SqlCompareOptions } } + internal string TraceString() + { + return string.Format(/*IFormatProvider*/ null, "(LCID={0}, Opts={1})", LCID, (int)SqlCompareOptions); + } internal static bool AreSame(SqlCollation a, SqlCollation b) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs index 789afde0ce..8717267d61 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.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 System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Data.Common; @@ -20,6 +21,8 @@ internal class TdsParserSessionPool private const int MaxInactiveCount = 10; // pick something, preferably small... + private static int _objectTypeCount; // Bid counter + private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private readonly TdsParser _parser; // parser that owns us private readonly List _cache; // collection of all known sessions @@ -33,6 +36,7 @@ internal TdsParserSessionPool(TdsParser parser) _cache = new List(); _freeStateObjects = new TdsParserStateObject[MaxInactiveCount]; _freeStateObjectCount = 0; + SqlClientEventSource.Log.AdvanceTrace(" {0}# created session pool for parser {1}", ObjectID, parser.ObjectID); } private bool IsDisposed @@ -43,42 +47,56 @@ private bool IsDisposed } } + internal int ObjectID + { + get + { + return _objectID; + } + } internal void Deactivate() { // When being deactivated, we check all the sessions in the // cache to make sure they're cleaned up and then we dispose of // sessions that are past what we want to keep around. - - lock (_cache) + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}# deactivating cachedCount={1}", ObjectID, _cachedCount); + try { - // NOTE: The PutSession call below may choose to remove the - // session from the cache, which will throw off our - // enumerator. We avoid that by simply indexing backward - // through the array. - - for (int i = _cache.Count - 1; i >= 0; i--) + lock (_cache) { - TdsParserStateObject session = _cache[i]; + // NOTE: The PutSession call below may choose to remove the + // session from the cache, which will throw off our + // enumerator. We avoid that by simply indexing backward + // through the array. - if (null != session) + for (int i = _cache.Count - 1; i >= 0; i--) { - if (session.IsOrphaned) - { - // TODO: consider adding a performance counter for the number of sessions we reclaim + TdsParserStateObject session = _cache[i]; - - PutSession(session); + if (null != session) + { + if (session.IsOrphaned) + { + // TODO: consider adding a performance counter for the number of sessions we reclaim + SqlClientEventSource.Log.AdvanceTrace(" {0}# reclaiming session {1}", ObjectID, session.ObjectID); + PutSession(session); + } } } + // TODO: re-enable this assert when the connection isn't doomed. + //Debug.Assert (_cachedCount < MaxInactiveCount, "non-orphaned connection past initial allocation?"); } - // TODO: re-enable this assert when the connection isn't doomed. - //Debug.Assert (_cachedCount < MaxInactiveCount, "non-orphaned connection past initial allocation?"); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } internal void Dispose() { + SqlClientEventSource.Log.AdvanceTrace(" {0}# disposing cachedCount={1}", ObjectID, _cachedCount); lock (_cache) { // Dispose free sessions @@ -136,7 +154,7 @@ internal TdsParserStateObject GetSession(object owner) { // No free objects, create a new one session = _parser.CreateSession(); - + SqlClientEventSource.Log.AdvanceTrace(" {0}# adding session {1} to pool", ObjectID, session.ObjectID); _cache.Add(session); _cachedCount = _cache.Count; @@ -145,7 +163,7 @@ internal TdsParserStateObject GetSession(object owner) session.Activate(owner); } - + SqlClientEventSource.Log.AdvanceTrace(" {0}# using session {1}", ObjectID, session.ObjectID); return session; } @@ -167,6 +185,7 @@ internal void PutSession(TdsParserStateObject session) else if ((okToReuse) && (_freeStateObjectCount < MaxInactiveCount)) { // Session is good to re-use and our cache has space + SqlClientEventSource.Log.AdvanceTrace(" {0}# keeping session {1} cachedCount={2}", ObjectID, session.ObjectID, _cachedCount); Debug.Assert(!session.HasPendingData, "pending data on a pooled session?"); _freeStateObjects[_freeStateObjectCount] = session; @@ -175,6 +194,7 @@ internal void PutSession(TdsParserStateObject session) else { // Either the session is bad, or we have no cache space - so dispose the session and remove it + SqlClientEventSource.Log.AdvanceTrace(" {0}# disposing session {1} cachedCount={2}", ObjectID, session.ObjectID, _cachedCount); bool removed = _cache.Remove(session); Debug.Assert(removed, "session not in pool?"); @@ -194,6 +214,16 @@ internal int ActiveSessionsCount return _cachedCount - _freeStateObjectCount; } } + + internal string TraceString() + { + return string.Format(/*IFormatProvider*/ null, + "(ObjID={0}, free={1}, cached={2}, total={3})", + _objectID, + null == _freeStateObjects ? "(null)" : _freeStateObjectCount.ToString((IFormatProvider)null), + _cachedCount, + _cache.Count); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 2056a22b35..32f96f3205 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -22,6 +22,9 @@ sealed internal class LastIOTimer internal abstract class TdsParserStateObject { + private static int _objectTypeCount; // Bid counter + internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + [Flags] internal enum SnapshottedStateFlags : byte { @@ -537,7 +540,8 @@ internal bool TryInitialize(TdsParserStateObject stateObj, int columnsCount) return false; } - + SqlClientEventSource.Log.TraceEvent(" {0}#, NBCROW bitmap received, column count = {1}", stateObj.ObjectID, columnsCount); + SqlClientEventSource.Log.TraceBinEvent(" NBCROW bitmap data: ", _nullBitmap, (ushort)_nullBitmap.Length); return true; } @@ -865,9 +869,8 @@ internal void DecrementOpenResultCount() internal int DecrementPendingCallbacks(bool release) { int remaining = Interlocked.Decrement(ref _pendingCallbacks); - + SqlClientEventSource.Log.AdvanceTrace(" {0}#, after decrementing _pendingCallbacks: {1}", ObjectID, _pendingCallbacks); FreeGcHandle(remaining, release); - // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it Debug.Assert((remaining == -1 && SessionHandle.IsNull) || (0 <= remaining && remaining < 3), $"_pendingCallbacks values is invalid after decrementing: {remaining}"); @@ -2005,7 +2008,7 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int tota } if (_longlenleft == 0) - { + { // Read the next chunk or cleanup state if hit the end if (!TryReadPlpLength(false, out ignored)) { @@ -3723,6 +3726,42 @@ internal int WarningCount } protected abstract PacketHandle EmptyReadPacket { get; } + internal int ObjectID => _objectID; + + internal int PreAttentionErrorCount + { + get + { + int count = 0; + lock (_errorAndWarningsLock) + { + if (_preAttentionErrors != null) + { + count = _preAttentionErrors.Count; + } + } + return count; + } + } + + /// + /// Gets the number of errors currently in the pre-attention warning collection + /// + internal int PreAttentionWarningCount + { + get + { + int count = 0; + lock (_errorAndWarningsLock) + { + if (_preAttentionWarnings != null) + { + count = _preAttentionWarnings.Count; + } + } + return count; + } + } /// /// Gets the full list of errors and warnings (including the pre-attention ones), then wipes all error and warning lists diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index cce005ab5b..042cbb184f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Data.Common; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 75df4299ec..eaac2585c6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -25,7 +25,7 @@ internal abstract class DbConnectionFactory private const int PruningDueTime = 4 * 60 * 1000; // 4 minutes private const int PruningPeriod = 30 * 1000; // thirty seconds - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to @@ -106,7 +106,7 @@ public void ClearPool(DbConnectionPoolKey key) { Debug.Assert(key != null, "key cannot be null"); ADP.CheckArgumentNull(key.ConnectionString, "key.ConnectionString"); - long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" connectionString", "API"); + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" connectionString"); try { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 33630971e2..74522ea5ff 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -475,7 +475,7 @@ protected override bool ReleaseHandle() private readonly List _objectList; private int _totalObjects; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // only created by DbConnectionPoolGroup.GetConnectionPool @@ -1971,7 +1971,6 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern // transaction is completed. We tell the transacted pool to remove // the connection from it's list, then we put the connection back in // general circulation. - TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool; if (null != transactedConnectionPool) { @@ -1979,7 +1978,6 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern } } - private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection = null) { // called by user when they were not able to obtain a free object but diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 89f6b04ec3..f250f44a4f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -39,7 +39,7 @@ sealed internal class DbConnectionPoolGroup private DbConnectionPoolGroupProviderInfo _providerInfo; private DbMetaDataFactory _metaDataFactory; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // always lock this before changing _state, we don't want to move out of the 'Disabled' state diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs index f0ed91dc24..473b3b638a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs @@ -66,8 +66,9 @@ public ActiveDirectoryAuthenticationTimeoutRetryState State throw new InvalidOperationException($"Unsupported state: {value}."); } if (_sqlAuthLogger.IsLoggingEnabled) + { _sqlAuthLogger.LogInfo(_typeName, "SetState", $"State changed from {_state} to {value}."); - + } _state = value; } } @@ -80,14 +81,17 @@ public SqlFedAuthToken CachedToken get { if (_sqlAuthLogger.IsLoggingEnabled) + { _sqlAuthLogger.LogInfo(_typeName, "GetCachedToken", $"Retrieved cached token {GetTokenHash(_token)}."); + } return _token; } set { if (_sqlAuthLogger.IsLoggingEnabled) + { _sqlAuthLogger.LogInfo(_typeName, "SetCachedToken", $"CachedToken changed from {GetTokenHash(_token)} to {GetTokenHash(value)}."); - + } _token = value; } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index f530600bb3..12dfce5977 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -63,7 +63,6 @@ internal class AzureAttestationEnclaveProvider : EnclaveProviderBase #endregion #region Public methods - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. public override void GetEnclaveSession(string servername, string attestationUrl, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) @@ -81,7 +80,6 @@ public override SqlEnclaveAttestationParameters GetAttestationParameters(string return new SqlEnclaveAttestationParameters(AzureBasedAttestationProtocolId, attestationParam, clientDHKey); } - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. public override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellmanCng clientDHKey, string attestationUrl, string servername, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs index d57bc47067..24903f1eae 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs @@ -10,13 +10,11 @@ namespace Microsoft.Data.SqlClient { - /// /// A delegate for communicating with secure enclave /// internal class EnclaveDelegate { - private static readonly SqlAeadAes256CbcHmac256Factory SqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory(); private static readonly string GetAttestationInfoQueryString = String.Format(@"Select GetTrustedModuleIdentityAndAttestationInfo({0}) as attestationInfo", 0); private static readonly EnclaveDelegate _EnclaveDelegate = new EnclaveDelegate(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 953faaa018..3b38ec607a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -7,7 +7,6 @@ using System.Security.Cryptography; using System.Threading; - // Enclave session locking model // 1. For doing the enclave attestation, driver makes either 1, 2 or 3 API calls(in order) // - GetEnclaveSession @@ -65,6 +64,7 @@ namespace Microsoft.Data.SqlClient { + // Base class for Enclave provider internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants @@ -89,7 +89,6 @@ internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider #endregion #region Public methods - // Helper method to get the enclave session from the cache if present protected void GetEnclaveSessionHelper(string servername, string attestationUrl, bool shouldGenerateNonce, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 32356f85ba..b331e5e335 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -135,7 +135,9 @@ public SqlAuthenticationProvider GetProvider(SqlAuthenticationMethod authenticat public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider) { if (!provider.IsSupported(authenticationMethod)) + { throw SQL.UnsupportedAuthenticationByProvider(authenticationMethod.ToString(), provider.GetType().Name); + } var methodName = "SetProvider"; if (_authenticationsWithAppSpecifiedProvider.Contains(authenticationMethod)) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index c6a922f454..36b11ef896 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -24,11 +24,9 @@ namespace Microsoft.Data.SqlClient { - // ------------------------------------------------------------------------------------------------- - // this internal class helps us to associate the metadata (from the target) - // with columnordinals (from the source) - // - sealed internal class _ColumnMapping + // This internal class helps us to associate the metadata from the target. + // with ColumnOrdinals from the source. + internal sealed class _ColumnMapping { internal int _sourceColumnOrdinal; internal _SqlMetaData _metadata; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 5e90796ade..4f02f43dd7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -12,6 +12,9 @@ namespace Microsoft.Data.SqlClient internal class SqlClientEventSource : EventSource { // Defines the singleton instance for the Resources ETW provider + /// + /// + /// internal static readonly SqlClientEventSource Log = new SqlClientEventSource(); private static long s_nextScopeId = 0; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 2fe57230ad..e2ffc12caf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -35,7 +35,6 @@ namespace Microsoft.Data.SqlClient ] public sealed class SqlCommand : DbCommand, ICloneable { - private static int _objectTypeCount; // Bid counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); @@ -5338,6 +5337,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi { SqlClientEventSource.Log.TraceEvent(" {0}#, Command executed as RPC.", ObjectID); } + // turn set options ON if (null != optionSettings) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index a7d7c4847a..f4f7831acd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1817,7 +1817,6 @@ internal void Retry(Task retryTask) private bool TryOpen(TaskCompletionSource retry) { SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions; - _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs index 1f3c4a1ea3..3698926c8d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -929,7 +929,6 @@ internal static bool Stop(string connectionString, string queue, bool useDefault private static bool AddToServerUserHash(string server, IdentityUserNamePair identityUser, DatabaseServicePair databaseService) { long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" server: '{0}', database: '{1}', service: '{2}'", server, databaseService.Database, databaseService.Service); - try { bool result = false; @@ -985,7 +984,6 @@ private static bool AddToServerUserHash(string server, IdentityUserNamePair iden private static void RemoveFromServerUserHash(string server, IdentityUserNamePair identityUser, DatabaseServicePair databaseService) { long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" server: '{0}', database: '{1}', service: '{2}'", server, databaseService.Database, databaseService.Service); - try { lock (_serverUserHash) @@ -1367,7 +1365,6 @@ private void AddCommandInternal(SqlCommand cmd) private string ComputeCommandHash(string connectionString, SqlCommand command) { long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlCommand: {1}#", ObjectID, command.ObjectID); - try { // Create a string representing the concatenation of the connection string, the command text and .ToString on all its parameter values. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index f15dca1967..99a9d4fcda 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -583,7 +583,7 @@ private void ProcessNotificationResults(SqlDataReader reader) string msgType = reader.GetString(0); SqlClientEventSource.Log.NotificationsTraceEvent(" msgType: '{0}'", msgType); handle = reader.GetGuid(1); - // SqlClientEventSource.SqlClientEventSource.Log.NotificationsTrace($"SqlConnectionContainer.ProcessNotificationResults(SqlDataReader)|DEP> conversationHandle: '%p(GUID)'", conversationHandle); + //SqlClientEventSource.Log.NotificationsTrace("SqlConnectionContainer.ProcessNotificationResults(SqlDataReader)|DEP> conversationHandle: '%p(GUID)'", conversationHandle); // Only process QueryNotification messages. if (0 == String.Compare(msgType, "http://schemas.microsoft.com/SQL/Notifications/QueryNotification", StringComparison.OrdinalIgnoreCase)) @@ -911,8 +911,9 @@ internal bool Stop(string appDomainKey, out bool appDomainStop) int result = Interlocked.Decrement(ref _startCount); if (0 == result) - { // If we've reached refCount 0, destroy. - // Lock to ensure Cancel() complete prior to other thread calling TearDown. + { + // If we've reached refCount 0, destroy. + // Lock to ensure Cancel() complete prior to other thread calling TearDown. SqlClientEventSource.Log.NotificationsTrace(" Reached 0 count, cancelling and waiting."); lock (this) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index 170e740b6c..bba7960aa8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -470,9 +470,7 @@ private List LookupCommandEntryWithRemove(string notificationId) // Remove from commandToDependenciesHash all references to the passed dependency. private void RemoveDependencyFromCommandToDependenciesHash(SqlDependency dependency) { - long scopeID = SqlClientEventSource.Log.NotificationsScopeEnterEvent(" {0}#, SqlDependency: {1}#", ObjectID, dependency.ObjectID); - try { lock (this) @@ -539,7 +537,7 @@ internal void StartTimer(SqlDependency dep) // Enable the timer if needed (disable when empty, enable on the first addition). if (!_SqlDependencyTimeOutTimerStarted) { - SqlClientEventSource.Log.NotificationsTrace(" Timer not yet started, starting."); + SqlClientEventSource.Log.NotificationsTraceEvent(" Timer not yet started, starting."); _timeoutTimer.Change(15000 /* 15 secs */, 15000 /* 15 secs */); // Save this as the earlier timeout to come. @@ -548,7 +546,7 @@ internal void StartTimer(SqlDependency dep) } else if (_nextTimeout > dep.ExpirationTime) { - SqlClientEventSource.Log.NotificationsTrace(" Timer already started, resetting time."); + SqlClientEventSource.Log.NotificationsTraceEvent(" Timer already started, resetting time."); // Save this as the earlier timeout to come. _nextTimeout = dep.ExpirationTime; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 1e2ef55a62..f0039d046a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1359,7 +1359,8 @@ private void CompleteLogin(bool enlistOK) _parser.Run(RunBehavior.UntilDone, null, null, null, _parser._physicalStateObj); if (_routingInfo == null) - { // ROR should not affect state of connection recovery + { + // ROR should not affect state of connection recovery if (_federatedAuthenticationRequested && !_federatedAuthenticationAcknowledged) { SqlClientEventSource.Log.TraceEvent(" {0}#, Server did not acknowledge the federated authentication request", ObjectID); @@ -2542,7 +2543,6 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { SqlClientEventSource.Log.TraceEvent(" {0}#, The attempt to get a new access token succeeded under the locked mode.", ObjectID); } - } SqlClientEventSource.Log.AdvanceTrace(" {0}#, Found an authentication context in the cache that does not need a refresh at this time. Re-using the cached token.", ObjectID); } @@ -2960,7 +2960,6 @@ internal void OnFeatureExtAck(int featureId, byte[] data) if (data.Length < 1) { SqlClientEventSource.Log.TraceEvent(" {0}#, Unknown version number for GlobalTransactions", ObjectID); - throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } From 96c51b95cfe1889d794cdfd05abfec247eb05d44 Mon Sep 17 00:00:00 2001 From: Javad Date: Mon, 9 Mar 2020 14:21:32 -0700 Subject: [PATCH 02/13] Update Microsoft.Data.SqlClient.sln --- src/Microsoft.Data.SqlClient.sln | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 0da3b93aa9..6c9a8f59b8 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,16 +767,16 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp2.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU From 893434b6a8e656c2f0045ce8bfc64657b8ba733d Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 9 Mar 2020 14:49:36 -0700 Subject: [PATCH 03/13] SNI EventSource --- .../Data/SqlClient/SNI/LocalDB.Windows.cs | 217 ++++++----- .../Microsoft/Data/SqlClient/SNI/SNICommon.cs | 72 ++-- .../Data/SqlClient/SNI/SNILoadHandle.cs | 1 + .../Data/SqlClient/SNI/SNIMarsConnection.cs | 282 +++++++++----- .../Data/SqlClient/SNI/SNIMarsHandle.cs | 359 +++++++++++------- .../Data/SqlClient/SNI/SNINpHandle.cs | 334 +++++++++------- .../src/Microsoft/Data/SqlClient/SNI/SSRP.cs | 75 ++-- .../Data/SqlClient/SqlClientEventSource.cs | 57 ++- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 203 +++++----- 9 files changed, 986 insertions(+), 614 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs index 1aa4341ab2..76fc48e984 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs @@ -64,16 +64,26 @@ internal static uint MapLocalDBErrorStateToCode(LocalDBErrorState errorState) switch (errorState) { case LocalDBErrorState.NO_INSTALLATION: + SqlClientEventSource.Log.SNITrace(" LocalDB is not installed. Error State ={0}", errorState); return SNICommon.LocalDBNoInstallation; + case LocalDBErrorState.INVALID_CONFIG: + SqlClientEventSource.Log.SNITrace(" Invalid configuration. Error State ={0}", errorState); return SNICommon.LocalDBInvalidConfig; + case LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH: + SqlClientEventSource.Log.SNITrace(" No SQL user instance path. Error State ={0}", errorState); return SNICommon.LocalDBNoSqlUserInstanceDllPath; + case LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH: + SqlClientEventSource.Log.SNITrace(" Invalid SQL user instance path. Error State ={0}", errorState); return SNICommon.LocalDBInvalidSqlUserInstanceDllPath; + case LocalDBErrorState.NONE: return 0; + default: + SqlClientEventSource.Log.SNITrace(" Invalid configuration. Error State ={0}", errorState); return SNICommon.LocalDBInvalidConfig; } } @@ -83,72 +93,84 @@ internal static uint MapLocalDBErrorStateToCode(LocalDBErrorState errorState) /// private bool LoadUserInstanceDll() { - // Check in a non thread-safe way if the handle is already set for performance. - if (_sqlUserInstanceLibraryHandle != null) - { - return true; - } - - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { + // Check in a non thread-safe way if the handle is already set for performance. if (_sqlUserInstanceLibraryHandle != null) { return true; } - //Get UserInstance Dll path - LocalDBErrorState registryQueryErrorState; - - // Get the LocalDB instance dll path from the registry - string dllPath = GetUserInstanceDllPath(out registryQueryErrorState); - // If there was no DLL path found, then there is an error. - if (dllPath == null) + lock (this) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, MapLocalDBErrorStateToCode(registryQueryErrorState), string.Empty); - return false; - } + if (_sqlUserInstanceLibraryHandle != null) + { + return true; + } + //Get UserInstance Dll path + LocalDBErrorState registryQueryErrorState; - // In case the registry had an empty path for dll - if (string.IsNullOrWhiteSpace(dllPath)) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBInvalidSqlUserInstanceDllPath, string.Empty); - return false; - } + // Get the LocalDB instance dll path from the registry + string dllPath = GetUserInstanceDllPath(out registryQueryErrorState); - // Load the dll - SafeLibraryHandle libraryHandle = Interop.Kernel32.LoadLibraryExW(dllPath.Trim(), IntPtr.Zero, 0); + // If there was no DLL path found, then there is an error. + if (dllPath == null) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, MapLocalDBErrorStateToCode(registryQueryErrorState), string.Empty); + SqlClientEventSource.Log.SNITrace("User instance DLL path is null."); + return false; + } - if (libraryHandle.IsInvalid) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBFailedToLoadDll, string.Empty); - libraryHandle.Dispose(); - return false; - } + // In case the registry had an empty path for dll + if (string.IsNullOrWhiteSpace(dllPath)) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBInvalidSqlUserInstanceDllPath, string.Empty); + SqlClientEventSource.Log.SNITrace(" User instance DLL path is invalid. DLL path ={0}", dllPath); + return false; + } - // Load the procs from the DLLs - _startInstanceHandle = Interop.Kernel32.GetProcAddress(libraryHandle, ProcLocalDBStartInstance); + // Load the dll + SafeLibraryHandle libraryHandle = Interop.Kernel32.LoadLibraryExW(dllPath.Trim(), IntPtr.Zero, 0); - if (_startInstanceHandle == IntPtr.Zero) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); - libraryHandle.Dispose(); - return false; - } + if (libraryHandle.IsInvalid) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBFailedToLoadDll, string.Empty); + SqlClientEventSource.Log.SNITrace(" Library Handle is invalid. Could not load the dll."); + libraryHandle.Dispose(); + return false; + } - // Set the delegate the invoke. - localDBStartInstanceFunc = (LocalDBStartInstance)Marshal.GetDelegateForFunctionPointer(_startInstanceHandle, typeof(LocalDBStartInstance)); + // Load the procs from the DLLs + _startInstanceHandle = Interop.Kernel32.GetProcAddress(libraryHandle, ProcLocalDBStartInstance); - if (localDBStartInstanceFunc == null) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); - libraryHandle.Dispose(); - _startInstanceHandle = IntPtr.Zero; - return false; - } + if (_startInstanceHandle == IntPtr.Zero) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); + SqlClientEventSource.Log.SNITrace(" Was not able to load the PROC from DLL. Bad Runtime."); + libraryHandle.Dispose(); + return false; + } - _sqlUserInstanceLibraryHandle = libraryHandle; + // Set the delegate the invoke. + localDBStartInstanceFunc = (LocalDBStartInstance)Marshal.GetDelegateForFunctionPointer(_startInstanceHandle, typeof(LocalDBStartInstance)); - return true; + if (localDBStartInstanceFunc == null) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); + libraryHandle.Dispose(); + _startInstanceHandle = IntPtr.Zero; + return false; + } + + _sqlUserInstanceLibraryHandle = libraryHandle; + SqlClientEventSource.Log.SNITrace(" User Instance DLL was loaded successfully."); + return true; + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -159,68 +181,81 @@ private bool LoadUserInstanceDll() /// private string GetUserInstanceDllPath(out LocalDBErrorState errorState) { - string dllPath = null; - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(LocalDBInstalledVersionRegistryKey)) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" GetUserInstanceDllPath"); + try { - if (key == null) + string dllPath = null; + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(LocalDBInstalledVersionRegistryKey)) { - errorState = LocalDBErrorState.NO_INSTALLATION; - return null; - } + if (key == null) + { + errorState = LocalDBErrorState.NO_INSTALLATION; + SqlClientEventSource.Log.SNITrace(" not installed. Error state ={0}.", errorState); + return null; + } - Version zeroVersion = new Version(); + Version zeroVersion = new Version(); - Version latestVersion = zeroVersion; + Version latestVersion = zeroVersion; - foreach (string subKey in key.GetSubKeyNames()) - { - Version currentKeyVersion; + foreach (string subKey in key.GetSubKeyNames()) + { + Version currentKeyVersion; + + if (!Version.TryParse(subKey, out currentKeyVersion)) + { + errorState = LocalDBErrorState.INVALID_CONFIG; + SqlClientEventSource.Log.SNITrace(" Invalid Configuration. state ={0}.", errorState); + return null; + } + + if (latestVersion.CompareTo(currentKeyVersion) < 0) + { + latestVersion = currentKeyVersion; + } + } - if (!Version.TryParse(subKey, out currentKeyVersion)) + // If no valid versions are found, then error out + if (latestVersion.Equals(zeroVersion)) { errorState = LocalDBErrorState.INVALID_CONFIG; + SqlClientEventSource.Log.SNITrace(" Invalid Configuration. state ={0}.", errorState); return null; } - if (latestVersion.CompareTo(currentKeyVersion) < 0) + // Use the latest version to get the DLL path + using (RegistryKey latestVersionKey = key.OpenSubKey(latestVersion.ToString())) { - latestVersion = currentKeyVersion; - } - } - // If no valid versions are found, then error out - if (latestVersion.Equals(zeroVersion)) - { - errorState = LocalDBErrorState.INVALID_CONFIG; - return null; - } + object instanceAPIPathRegistryObject = latestVersionKey.GetValue(InstanceAPIPathValueName); - // Use the latest version to get the DLL path - using (RegistryKey latestVersionKey = key.OpenSubKey(latestVersion.ToString())) - { + if (instanceAPIPathRegistryObject == null) + { + errorState = LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH; + SqlClientEventSource.Log.SNITrace(" No SQL user instance DLL. Instance API Path REgistry Object Error. state ={0}.", errorState); + return null; + } - object instanceAPIPathRegistryObject = latestVersionKey.GetValue(InstanceAPIPathValueName); + RegistryValueKind valueKind = latestVersionKey.GetValueKind(InstanceAPIPathValueName); - if (instanceAPIPathRegistryObject == null) - { - errorState = LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH; - return null; - } + if (valueKind != RegistryValueKind.String) + { + errorState = LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH; + SqlClientEventSource.Log.SNITrace(" No SQL user instance DLL. state ={0}. Registry value kind error.", errorState); + return null; + } - RegistryValueKind valueKind = latestVersionKey.GetValueKind(InstanceAPIPathValueName); + dllPath = (string)instanceAPIPathRegistryObject; - if (valueKind != RegistryValueKind.String) - { - errorState = LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH; - return null; + errorState = LocalDBErrorState.NONE; + return dllPath; } - - dllPath = (string)instanceAPIPathRegistryObject; - - errorState = LocalDBErrorState.NONE; - return dllPath; } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs index 03e3d4dc8e..c5acfdf474 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs @@ -136,51 +136,61 @@ internal class SNICommon /// True if certificate is valid internal static bool ValidateSslServerCertificate(string targetServerName, object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors) { - if (policyErrors == SslPolicyErrors.None) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - return true; - } - - if ((policyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) - { - string certServerName = cert.Subject.Substring(cert.Subject.IndexOf('=') + 1); - - // Verify that target server name matches subject in the certificate - if (targetServerName.Length > certServerName.Length) + if (policyErrors == SslPolicyErrors.None) { - return false; + SqlClientEventSource.Log.SNITrace(" SSL Server certificate validated."); + return true; } - else if (targetServerName.Length == certServerName.Length) + + if ((policyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) { - // Both strings have the same length, so targetServerName must be a FQDN - if (!targetServerName.Equals(certServerName, StringComparison.OrdinalIgnoreCase)) + SqlClientEventSource.Log.SNITrace(" SSL Remote certificate name mismatched."); + string certServerName = cert.Subject.Substring(cert.Subject.IndexOf('=') + 1); + + // Verify that target server name matches subject in the certificate + if (targetServerName.Length > certServerName.Length) { return false; } - } - else - { - if (string.Compare(targetServerName, 0, certServerName, 0, targetServerName.Length, StringComparison.OrdinalIgnoreCase) != 0) + else if (targetServerName.Length == certServerName.Length) { - return false; + // Both strings have the same length, so targetServerName must be a FQDN + if (!targetServerName.Equals(certServerName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } } - - // Server name matches cert name for its whole length, so ensure that the - // character following the server name is a '.'. This will avoid - // having server name "ab" match "abc.corp.company.com" - // (Names have different lengths, so the target server can't be a FQDN.) - if (certServerName[targetServerName.Length] != '.') + else { - return false; + if (string.Compare(targetServerName, 0, certServerName, 0, targetServerName.Length, StringComparison.OrdinalIgnoreCase) != 0) + { + return false; + } + + // Server name matches cert name for its whole length, so ensure that the + // character following the server name is a '.'. This will avoid + // having server name "ab" match "abc.corp.company.com" + // (Names have different lengths, so the target server can't be a FQDN.) + if (certServerName[targetServerName.Length] != '.') + { + return false; + } } } + else + { + // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch + return false; + } + return true; } - else + finally { - // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch - return false; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - return true; } /// @@ -193,6 +203,7 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec /// internal static uint ReportSNIError(SNIProviders provider, uint nativeError, uint sniError, string errorMessage) { + SqlClientEventSource.Log.SNITrace(" Provider ={0}, native Error ={1}, SNI Error ={2}, Error Message ={3}", provider, nativeError, sniError, errorMessage); return ReportSNIError(new SNIError(provider, nativeError, sniError, errorMessage)); } @@ -205,6 +216,7 @@ internal static uint ReportSNIError(SNIProviders provider, uint nativeError, uin /// internal static uint ReportSNIError(SNIProviders provider, uint sniError, Exception sniException) { + SqlClientEventSource.Log.SNITrace(" Provider ={0}, SNI Error ={2}, Exception ={3}", provider, sniError, sniException.Message); return ReportSNIError(new SNIError(provider, sniError, sniException)); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs index 1fa6c58a3f..90734a2f1a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs @@ -30,6 +30,7 @@ public SNIError LastError set { + SqlClientEventSource.Log.SNITrace(" Last Error Value = {0}", value); _lastError.Value = value; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs index 481f3ce10b..01a7317c7d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs @@ -62,14 +62,23 @@ public SNIMarsHandle CreateMarsSession(object callbackObject, bool async) /// public uint StartReceive() { - SNIPacket packet = null; + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" StartReceive"); + try + { + SNIPacket packet = null; - if (ReceiveAsync(ref packet) == TdsEnums.SNI_SUCCESS_IO_PENDING) + if (ReceiveAsync(ref packet) == TdsEnums.SNI_SUCCESS_IO_PENDING) + { + SqlClientEventSource.Log.SNITrace(" Success IO pending."); + return TdsEnums.SNI_SUCCESS_IO_PENDING; + } + SqlClientEventSource.Log.SNITrace(" Connection not useable."); + return SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnNotUsableError, string.Empty); + } + finally { - return TdsEnums.SNI_SUCCESS_IO_PENDING; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - return SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnNotUsableError, string.Empty); } /// @@ -79,9 +88,17 @@ public uint StartReceive() /// SNI error code public uint Send(SNIPacket packet) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" Send"); + try + { + lock (this) + { + return _lowerHandle.Send(packet); + } + } + finally { - return _lowerHandle.Send(packet); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -93,9 +110,17 @@ public uint Send(SNIPacket packet) /// SNI error code public uint SendAsync(SNIPacket packet, SNIAsyncCallback callback) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" SendAsync"); + try { - return _lowerHandle.SendAsync(packet, false, callback); + lock (this) + { + return _lowerHandle.SendAsync(packet, false, callback); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -106,15 +131,23 @@ public uint SendAsync(SNIPacket packet, SNIAsyncCallback callback) /// SNI error code public uint ReceiveAsync(ref SNIPacket packet) { - if (packet != null) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" SendAsync"); + try { - packet.Release(); - packet = null; - } + if (packet != null) + { + packet.Release(); + packet = null; + } - lock (this) + lock (this) + { + return _lowerHandle.ReceiveAsync(ref packet); + } + } + finally { - return _lowerHandle.ReceiveAsync(ref packet); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -124,9 +157,17 @@ public uint ReceiveAsync(ref SNIPacket packet) /// SNI error status public uint CheckConnection() { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - return _lowerHandle.CheckConnection(); + lock (this) + { + return _lowerHandle.CheckConnection(); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -136,6 +177,7 @@ public uint CheckConnection() public void HandleReceiveError(SNIPacket packet) { Debug.Assert(Monitor.IsEntered(this), "HandleReceiveError was called without being locked."); + SqlClientEventSource.Log.SNITrace(" HandleReceiveError was called without being locked."); foreach (SNIMarsHandle handle in _sessions.Values) { if (packet.HasCompletionCallback) @@ -163,133 +205,143 @@ public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) /// SNI error code public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) { - SNISMUXHeader currentHeader = null; - SNIPacket currentPacket = null; - SNIMarsHandle currentSession = null; - - if (sniErrorCode != TdsEnums.SNI_SUCCESS) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - lock (this) + SNISMUXHeader currentHeader = null; + SNIPacket currentPacket = null; + SNIMarsHandle currentSession = null; + + if (sniErrorCode != TdsEnums.SNI_SUCCESS) { - HandleReceiveError(packet); - return; + lock (this) + { + HandleReceiveError(packet); + SqlClientEventSource.Log.SNITrace(" not successfull."); + return; + } } - } - while (true) - { - lock (this) + while (true) { - if (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) + lock (this) { - currentHeader = null; - currentPacket = null; - currentSession = null; - - while (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) + if (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { - int bytesTaken = packet.TakeData(_headerBytes, _currentHeaderByteCount, SNISMUXHeader.HEADER_LENGTH - _currentHeaderByteCount); - _currentHeaderByteCount += bytesTaken; + currentHeader = null; + currentPacket = null; + currentSession = null; - if (bytesTaken == 0) + while (_currentHeaderByteCount != SNISMUXHeader.HEADER_LENGTH) { - sniErrorCode = ReceiveAsync(ref packet); + int bytesTaken = packet.TakeData(_headerBytes, _currentHeaderByteCount, SNISMUXHeader.HEADER_LENGTH - _currentHeaderByteCount); + _currentHeaderByteCount += bytesTaken; - if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + if (bytesTaken == 0) { + sniErrorCode = ReceiveAsync(ref packet); + + if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + { + SqlClientEventSource.Log.SNITrace(" not successfull IO Pending."); + return; + } + + HandleReceiveError(packet); return; } - - HandleReceiveError(packet); - return; } - } - _currentHeader.Read(_headerBytes); + _currentHeader.Read(_headerBytes); - _dataBytesLeft = (int)_currentHeader.length; - _currentPacket = new SNIPacket(headerSize: 0, dataSize: (int)_currentHeader.length); - } + _dataBytesLeft = (int)_currentHeader.length; + _currentPacket = new SNIPacket(headerSize: 0, dataSize: (int)_currentHeader.length); + } - currentHeader = _currentHeader; - currentPacket = _currentPacket; + currentHeader = _currentHeader; + currentPacket = _currentPacket; - if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) - { - if (_dataBytesLeft > 0) + if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { - int length = packet.TakeData(_currentPacket, _dataBytesLeft); - _dataBytesLeft -= length; - if (_dataBytesLeft > 0) { - sniErrorCode = ReceiveAsync(ref packet); + int length = packet.TakeData(_currentPacket, _dataBytesLeft); + _dataBytesLeft -= length; - if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + if (_dataBytesLeft > 0) { + sniErrorCode = ReceiveAsync(ref packet); + + if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + { + return; + } + + HandleReceiveError(packet); return; } - - HandleReceiveError(packet); - return; } } - } - _currentHeaderByteCount = 0; + _currentHeaderByteCount = 0; - if (!_sessions.ContainsKey(_currentHeader.sessionId)) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, string.Empty); - HandleReceiveError(packet); - _lowerHandle.Dispose(); - _lowerHandle = null; - return; - } + if (!_sessions.ContainsKey(_currentHeader.sessionId)) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, string.Empty); + HandleReceiveError(packet); + _lowerHandle.Dispose(); + _lowerHandle = null; + return; + } - if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) - { - _sessions.Remove(_currentHeader.sessionId); - } - else - { - currentSession = _sessions[_currentHeader.sessionId]; + if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) + { + _sessions.Remove(_currentHeader.sessionId); + } + else + { + currentSession = _sessions[_currentHeader.sessionId]; + } } - } - - if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) - { - currentSession.HandleReceiveComplete(currentPacket, currentHeader); - } - if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) - { - try + if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { - currentSession.HandleAck(currentHeader.highwater); + currentSession.HandleReceiveComplete(currentPacket, currentHeader); } - catch (Exception e) + + if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) { - SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); + try + { + currentSession.HandleAck(currentHeader.highwater); + } + catch (Exception e) + { + SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); + } } - } - lock (this) - { - if (packet.DataLeft == 0) + lock (this) { - sniErrorCode = ReceiveAsync(ref packet); - - if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + if (packet.DataLeft == 0) { + sniErrorCode = ReceiveAsync(ref packet); + + if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) + { + return; + } + + HandleReceiveError(packet); return; } - - HandleReceiveError(packet); - return; } } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -297,7 +349,15 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) /// public uint EnableSsl(uint options) { - return _lowerHandle.EnableSsl(options); + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + return _lowerHandle.EnableSsl(options); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -305,7 +365,15 @@ public uint EnableSsl(uint options) /// public void DisableSsl() { - _lowerHandle.DisableSsl(); + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + _lowerHandle.DisableSsl(); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } #if DEBUG @@ -314,7 +382,15 @@ public void DisableSsl() /// public void KillConnection() { - _lowerHandle.KillConnection(); + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + _lowerHandle.KillConnection(); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } #endif } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs index 00af84d656..5510a80d26 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs @@ -51,14 +51,20 @@ internal sealed class SNIMarsHandle : SNIHandle /// public override void Dispose() { + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); try { SendControlPacket(SNISMUXFlags.SMUX_FIN); } catch (Exception e) { + SqlClientEventSource.Log.SNITrace(" internal exception error = {0}", e.Message); SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -83,16 +89,24 @@ public SNIMarsHandle(SNIMarsConnection connection, ushort sessionId, object call /// SMUX header flags private void SendControlPacket(SNISMUXFlags flags) { - SNIPacket packet = new SNIPacket(headerSize: SNISMUXHeader.HEADER_LENGTH, dataSize: 0); + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + SNIPacket packet = new SNIPacket(headerSize: SNISMUXHeader.HEADER_LENGTH, dataSize: 0); - lock (this) + lock (this) + { + SetupSMUXHeader(0, flags); + _currentHeader.Write(packet.GetHeaderBuffer(SNISMUXHeader.HEADER_LENGTH)); + packet.SetHeaderActive(); + } + + _connection.Send(packet); + } + finally { - SetupSMUXHeader(0, flags); - _currentHeader.Write(packet.GetHeaderBuffer(SNISMUXHeader.HEADER_LENGTH)); - packet.SetHeaderActive(); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - _connection.Send(packet); } private void SetupSMUXHeader(int length, SNISMUXFlags flags) @@ -132,31 +146,38 @@ private SNIPacket SetPacketSMUXHeader(SNIPacket packet) public override uint Send(SNIPacket packet) { Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without mux reservation in Send"); - - while (true) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - lock (this) + while (true) { - if (_sequenceNumber < _sendHighwater) + lock (this) { - break; + if (_sequenceNumber < _sendHighwater) + { + break; + } } - } - _ackEvent.Wait(); + _ackEvent.Wait(); + lock (this) + { + _ackEvent.Reset(); + } + } + + SNIPacket muxedPacket = null; lock (this) { - _ackEvent.Reset(); + muxedPacket = SetPacketSMUXHeader(packet); } + return _connection.Send(muxedPacket); } - - SNIPacket muxedPacket = null; - lock (this) + finally { - muxedPacket = SetPacketSMUXHeader(packet); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - return _connection.Send(muxedPacket); } /// @@ -168,17 +189,25 @@ public override uint Send(SNIPacket packet) private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) { Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without mux reservation in InternalSendAsync"); - - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - if (_sequenceNumber >= _sendHighwater) + lock (this) { - return TdsEnums.SNI_QUEUE_FULL; - } + if (_sequenceNumber >= _sendHighwater) + { + SqlClientEventSource.Log.SNITrace(" SNI Queue is full"); + return TdsEnums.SNI_QUEUE_FULL; + } - SNIPacket muxedPacket = SetPacketSMUXHeader(packet); - muxedPacket.SetCompletionCallback(callback ?? HandleSendComplete); - return _connection.SendAsync(muxedPacket, callback); + SNIPacket muxedPacket = SetPacketSMUXHeader(packet); + muxedPacket.SetCompletionCallback(callback ?? HandleSendComplete); + return _connection.SendAsync(muxedPacket, callback); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -188,38 +217,46 @@ private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) /// SNI error code private uint SendPendingPackets() { + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); SNIMarsQueuedPacket packet = null; - - while (true) + try { - lock (this) + while (true) { - if (_sequenceNumber < _sendHighwater) + lock (this) { - if (_sendPacketQueue.Count != 0) + if (_sequenceNumber < _sendHighwater) { - packet = _sendPacketQueue.Peek(); - uint result = InternalSendAsync(packet.Packet, packet.Callback); + if (_sendPacketQueue.Count != 0) + { + packet = _sendPacketQueue.Peek(); + uint result = InternalSendAsync(packet.Packet, packet.Callback); + + if (result != TdsEnums.SNI_SUCCESS && result != TdsEnums.SNI_SUCCESS_IO_PENDING) + { + SqlClientEventSource.Log.SNITrace(" InternalSendAsync result is not SNI_SUCCESS and is not SNI_SUCCESS_IO_PENDING"); + return result; + } - if (result != TdsEnums.SNI_SUCCESS && result != TdsEnums.SNI_SUCCESS_IO_PENDING) + _sendPacketQueue.Dequeue(); + continue; + } + else { - return result; + _ackEvent.Set(); } - - _sendPacketQueue.Dequeue(); - continue; } - else - { - _ackEvent.Set(); - } - } - break; + break; + } } - } - return TdsEnums.SNI_SUCCESS; + return TdsEnums.SNI_SUCCESS; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -231,13 +268,21 @@ private uint SendPendingPackets() /// SNI error code public override uint SendAsync(SNIPacket packet, bool disposePacketAfterSendAsync, SNIAsyncCallback callback = null) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - _sendPacketQueue.Enqueue(new SNIMarsQueuedPacket(packet, callback != null ? callback : HandleSendComplete)); - } + lock (this) + { + _sendPacketQueue.Enqueue(new SNIMarsQueuedPacket(packet, callback != null ? callback : HandleSendComplete)); + } - SendPendingPackets(); - return TdsEnums.SNI_SUCCESS_IO_PENDING; + SendPendingPackets(); + return TdsEnums.SNI_SUCCESS_IO_PENDING; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -247,36 +292,44 @@ public override uint SendAsync(SNIPacket packet, bool disposePacketAfterSendAsyn /// SNI error code public override uint ReceiveAsync(ref SNIPacket packet) { - lock (_receivedPacketQueue) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - int queueCount = _receivedPacketQueue.Count; - - if (_connectionError != null) + lock (_receivedPacketQueue) { - return SNICommon.ReportSNIError(_connectionError); - } + int queueCount = _receivedPacketQueue.Count; - if (queueCount == 0) - { - _asyncReceives++; - return TdsEnums.SNI_SUCCESS_IO_PENDING; - } + if (_connectionError != null) + { + return SNICommon.ReportSNIError(_connectionError); + } - packet = _receivedPacketQueue.Dequeue(); + if (queueCount == 0) + { + _asyncReceives++; + return TdsEnums.SNI_SUCCESS_IO_PENDING; + } - if (queueCount == 1) + packet = _receivedPacketQueue.Dequeue(); + + if (queueCount == 1) + { + _packetEvent.Reset(); + } + } + + lock (this) { - _packetEvent.Reset(); + _receiveHighwater++; } - } - lock (this) + SendAckIfNecessary(); + return TdsEnums.SNI_SUCCESS; + } + finally { - _receiveHighwater++; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - SendAckIfNecessary(); - return TdsEnums.SNI_SUCCESS; } /// @@ -284,13 +337,21 @@ public override uint ReceiveAsync(ref SNIPacket packet) /// public void HandleReceiveError(SNIPacket packet) { - lock (_receivedPacketQueue) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - _connectionError = SNILoadHandle.SingletonInstance.LastError; - _packetEvent.Set(); - } + lock (_receivedPacketQueue) + { + _connectionError = SNILoadHandle.SingletonInstance.LastError; + _packetEvent.Set(); + } ((TdsParserStateObject)_callbackObject).ReadAsyncCallback(PacketHandle.FromManagedPacket(packet), 1); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -300,11 +361,19 @@ public void HandleReceiveError(SNIPacket packet) /// SNI error code public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - Debug.Assert(_callbackObject != null); + lock (this) + { + Debug.Assert(_callbackObject != null); - ((TdsParserStateObject)_callbackObject).WriteAsyncCallback(PacketHandle.FromManagedPacket(packet), sniErrorCode); + ((TdsParserStateObject)_callbackObject).WriteAsyncCallback(PacketHandle.FromManagedPacket(packet), sniErrorCode); + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -314,14 +383,22 @@ public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) /// Send highwater mark public void HandleAck(uint highwater) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - if (_sendHighwater != highwater) + lock (this) { - _sendHighwater = highwater; - SendPendingPackets(); + if (_sendHighwater != highwater) + { + _sendHighwater = highwater; + SendPendingPackets(); + } } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// @@ -331,35 +408,43 @@ public void HandleAck(uint highwater) /// SMUX header public void HandleReceiveComplete(SNIPacket packet, SNISMUXHeader header) { - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - if (_sendHighwater != header.highwater) - { - HandleAck(header.highwater); - } - - lock (_receivedPacketQueue) + lock (this) { - if (_asyncReceives == 0) + if (_sendHighwater != header.highwater) { - _receivedPacketQueue.Enqueue(packet); - _packetEvent.Set(); - return; + HandleAck(header.highwater); } - _asyncReceives--; - Debug.Assert(_callbackObject != null); + lock (_receivedPacketQueue) + { + if (_asyncReceives == 0) + { + _receivedPacketQueue.Enqueue(packet); + _packetEvent.Set(); + return; + } + + _asyncReceives--; + Debug.Assert(_callbackObject != null); - ((TdsParserStateObject)_callbackObject).ReadAsyncCallback(PacketHandle.FromManagedPacket(packet), 0); + ((TdsParserStateObject)_callbackObject).ReadAsyncCallback(PacketHandle.FromManagedPacket(packet), 0); + } } - } - lock (this) + lock (this) + { + _receiveHighwater++; + } + + SendAckIfNecessary(); + } + finally { - _receiveHighwater++; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - SendAckIfNecessary(); } /// @@ -390,51 +475,59 @@ private void SendAckIfNecessary() /// SNI error code public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) { - packet = null; - int queueCount; - uint result = TdsEnums.SNI_SUCCESS_IO_PENDING; - - while (true) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - lock (_receivedPacketQueue) + packet = null; + int queueCount; + uint result = TdsEnums.SNI_SUCCESS_IO_PENDING; + + while (true) { - if (_connectionError != null) + lock (_receivedPacketQueue) { - return SNICommon.ReportSNIError(_connectionError); - } + if (_connectionError != null) + { + return SNICommon.ReportSNIError(_connectionError); + } - queueCount = _receivedPacketQueue.Count; + queueCount = _receivedPacketQueue.Count; - if (queueCount > 0) - { - packet = _receivedPacketQueue.Dequeue(); + if (queueCount > 0) + { + packet = _receivedPacketQueue.Dequeue(); + + if (queueCount == 1) + { + _packetEvent.Reset(); + } - if (queueCount == 1) + result = TdsEnums.SNI_SUCCESS; + } + } + + if (result == TdsEnums.SNI_SUCCESS) + { + lock (this) { - _packetEvent.Reset(); + _receiveHighwater++; } - result = TdsEnums.SNI_SUCCESS; + SendAckIfNecessary(); + return result; } - } - if (result == TdsEnums.SNI_SUCCESS) - { - lock (this) + if (!_packetEvent.Wait(timeoutInMilliseconds)) { - _receiveHighwater++; + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnTimeoutError, string.Empty); + return TdsEnums.SNI_WAIT_TIMEOUT; } - - SendAckIfNecessary(); - return result; - } - - if (!_packetEvent.Wait(timeoutInMilliseconds)) - { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnTimeoutError, string.Empty); - return TdsEnums.SNI_WAIT_TIMEOUT; } } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs index 659b1a6e6f..99f9bf0654 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs @@ -40,56 +40,68 @@ internal sealed class SNINpHandle : SNIHandle public SNINpHandle(string serverName, string pipeName, long timerExpire, object callbackObject) { - _sendSync = new object(); - _targetServer = serverName; - _callbackObject = callbackObject; - + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(" Constructor"); + SqlClientEventSource.Log.SNITrace(" Constructor. server name = {0}, pipe name = {1}", serverName, pipeName); try { - _pipeStream = new NamedPipeClientStream( - serverName, - pipeName, - PipeDirection.InOut, - PipeOptions.Asynchronous | PipeOptions.WriteThrough); - - bool isInfiniteTimeOut = long.MaxValue == timerExpire; - if (isInfiniteTimeOut) + _sendSync = new object(); + _targetServer = serverName; + _callbackObject = callbackObject; + + try + { + _pipeStream = new NamedPipeClientStream( + serverName, + pipeName, + PipeDirection.InOut, + PipeOptions.Asynchronous | PipeOptions.WriteThrough); + + bool isInfiniteTimeOut = long.MaxValue == timerExpire; + if (isInfiniteTimeOut) + { + _pipeStream.Connect(System.Threading.Timeout.Infinite); + } + else + { + TimeSpan ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; + ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; + + _pipeStream.Connect((int)ts.TotalMilliseconds); + } + } + catch (TimeoutException te) { - _pipeStream.Connect(System.Threading.Timeout.Infinite); + SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, te); + _status = TdsEnums.SNI_ERROR; + SqlClientEventSource.Log.SNITrace(" Timed out. Exception = {0}", te.Message); + return; } - else + catch (IOException ioe) { - TimeSpan ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; - ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; + SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, ioe); + _status = TdsEnums.SNI_ERROR; + SqlClientEventSource.Log.SNITrace(" IOException = {0}", ioe.Message); + return; + } - _pipeStream.Connect((int)ts.TotalMilliseconds); + if (!_pipeStream.IsConnected || !_pipeStream.CanWrite || !_pipeStream.CanRead) + { + SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.ConnOpenFailedError, string.Empty); + _status = TdsEnums.SNI_ERROR; + SqlClientEventSource.Log.SNITrace(" Pipe stream is not connected or cannot write or read to/from it."); + return; } - } - catch (TimeoutException te) - { - SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, te); - _status = TdsEnums.SNI_ERROR; - return; - } - catch (IOException ioe) - { - SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, ioe); - _status = TdsEnums.SNI_ERROR; - return; - } - if (!_pipeStream.IsConnected || !_pipeStream.CanWrite || !_pipeStream.CanRead) + _sslOverTdsStream = new SslOverTdsStream(_pipeStream); + _sslStream = new SslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate), null); + + _stream = _pipeStream; + _status = TdsEnums.SNI_SUCCESS; + } + finally { - SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.ConnOpenFailedError, string.Empty); - _status = TdsEnums.SNI_ERROR; - return; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - _sslOverTdsStream = new SslOverTdsStream(_pipeStream); - _sslStream = new SslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate), null); - - _stream = _pipeStream; - _status = TdsEnums.SNI_SUCCESS; } public override Guid ConnectionId @@ -110,13 +122,22 @@ public override uint Status public override uint CheckConnection() { - if (!_stream.CanWrite || !_stream.CanRead) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - return TdsEnums.SNI_ERROR; + if (!_stream.CanWrite || !_stream.CanRead) + { + SqlClientEventSource.Log.SNITrace(" cannot wite or read to/from the stream"); + return TdsEnums.SNI_ERROR; + } + else + { + return TdsEnums.SNI_SUCCESS; + } } - else + finally { - return TdsEnums.SNI_SUCCESS; + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } @@ -149,117 +170,156 @@ public override void Dispose() public override uint Receive(out SNIPacket packet, int timeout) { - SNIPacket errorPacket; - lock (this) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - packet = null; - try + SNIPacket errorPacket; + lock (this) { - packet = new SNIPacket(headerSize: 0, dataSize: _bufferSize); - packet.ReadFromStream(_stream); - - if (packet.Length == 0) + packet = null; + try + { + packet = new SNIPacket(headerSize: 0, dataSize: _bufferSize); + packet.ReadFromStream(_stream); + + if (packet.Length == 0) + { + errorPacket = packet; + packet = null; + var e = new Win32Exception(); + SqlClientEventSource.Log.SNITrace(" packet length is 0."); + return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message); + } + } + catch (ObjectDisposedException ode) + { + errorPacket = packet; + packet = null; + SqlClientEventSource.Log.SNITrace(" ObjectDisposedException message = {0}.", ode.Message); + return ReportErrorAndReleasePacket(errorPacket, ode); + } + catch (IOException ioe) { errorPacket = packet; packet = null; - var e = new Win32Exception(); - return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message); + SqlClientEventSource.Log.SNITrace(" IOException message = {0}.", ioe.Message); + return ReportErrorAndReleasePacket(errorPacket, ioe); } + return TdsEnums.SNI_SUCCESS; + } + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } + } + + public override uint ReceiveAsync(ref SNIPacket packet) + { + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + SNIPacket errorPacket; + packet = new SNIPacket(headerSize: 0, dataSize: _bufferSize); + + try + { + packet.ReadFromStreamAsync(_stream, _receiveCallback); + return TdsEnums.SNI_SUCCESS_IO_PENDING; } catch (ObjectDisposedException ode) { errorPacket = packet; packet = null; + SqlClientEventSource.Log.SNITrace(" ObjectDisposedException message = {0}.", ode.Message); return ReportErrorAndReleasePacket(errorPacket, ode); } catch (IOException ioe) { errorPacket = packet; packet = null; + SqlClientEventSource.Log.SNITrace(" IOException message = {0}.", ioe.Message); return ReportErrorAndReleasePacket(errorPacket, ioe); } - - return TdsEnums.SNI_SUCCESS; } - } - - public override uint ReceiveAsync(ref SNIPacket packet) - { - SNIPacket errorPacket; - packet = new SNIPacket(headerSize: 0, dataSize: _bufferSize); - - try - { - packet.ReadFromStreamAsync(_stream, _receiveCallback); - return TdsEnums.SNI_SUCCESS_IO_PENDING; - } - catch (ObjectDisposedException ode) - { - errorPacket = packet; - packet = null; - return ReportErrorAndReleasePacket(errorPacket, ode); - } - catch (IOException ioe) + finally { - errorPacket = packet; - packet = null; - return ReportErrorAndReleasePacket(errorPacket, ioe); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } public override uint Send(SNIPacket packet) { - bool releaseLock = false; + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); try { - // is the packet is marked out out-of-band (attention packets only) it must be - // sent immediately even if a send of recieve operation is already in progress - // because out of band packets are used to cancel ongoing operations - // so try to take the lock if possible but continue even if it can't be taken - if (packet.IsOutOfBand) - { - Monitor.TryEnter(this, ref releaseLock); - } - else - { - Monitor.Enter(this); - releaseLock = true; - } - - // this lock ensures that two packets are not being written to the transport at the same time - // so that sending a standard and an out-of-band packet are both written atomically no data is - // interleaved - lock (_sendSync) + bool releaseLock = false; + try { - try + // is the packet is marked out out-of-band (attention packets only) it must be + // sent immediately even if a send of recieve operation is already in progress + // because out of band packets are used to cancel ongoing operations + // so try to take the lock if possible but continue even if it can't be taken + if (packet.IsOutOfBand) { - packet.WriteToStream(_stream); - return TdsEnums.SNI_SUCCESS; + Monitor.TryEnter(this, ref releaseLock); } - catch (ObjectDisposedException ode) + else { - return ReportErrorAndReleasePacket(packet, ode); + Monitor.Enter(this); + releaseLock = true; } - catch (IOException ioe) + + // this lock ensures that two packets are not being written to the transport at the same time + // so that sending a standard and an out-of-band packet are both written atomically no data is + // interleaved + lock (_sendSync) { - return ReportErrorAndReleasePacket(packet, ioe); + try + { + packet.WriteToStream(_stream); + return TdsEnums.SNI_SUCCESS; + } + catch (ObjectDisposedException ode) + { + SqlClientEventSource.Log.SNITrace(" ObjectDisposedException message = {0}.", ode.Message); + return ReportErrorAndReleasePacket(packet, ode); + } + catch (IOException ioe) + { + SqlClientEventSource.Log.SNITrace(" IOException message = {0}.", ioe.Message); + + return ReportErrorAndReleasePacket(packet, ioe); + } + } + } + finally + { + if (releaseLock) + { + Monitor.Exit(this); } } } finally { - if (releaseLock) - { - Monitor.Exit(this); - } + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } } public override uint SendAsync(SNIPacket packet, bool disposePacketAfterSendAsync, SNIAsyncCallback callback = null) { - SNIAsyncCallback cb = callback ?? _sendCallback; - packet.WriteToStreamAsync(_stream, cb, SNIProviders.NP_PROV, disposePacketAfterSendAsync); - return TdsEnums.SNI_SUCCESS_IO_PENDING; + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + SNIAsyncCallback cb = callback ?? _sendCallback; + packet.WriteToStreamAsync(_stream, cb, SNIProviders.NP_PROV, disposePacketAfterSendAsync); + return TdsEnums.SNI_SUCCESS_IO_PENDING; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyncCallback sendCallback) @@ -270,24 +330,34 @@ public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyn public override uint EnableSsl(uint options) { - _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); try { - _sslStream.AuthenticateAsClientAsync(_targetServer).GetAwaiter().GetResult(); - _sslOverTdsStream.FinishHandshake(); - } - catch (AuthenticationException aue) - { - return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, aue); + _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; + + try + { + _sslStream.AuthenticateAsClientAsync(_targetServer).GetAwaiter().GetResult(); + _sslOverTdsStream.FinishHandshake(); + } + catch (AuthenticationException aue) + { + SqlClientEventSource.Log.SNITrace(" AuthenticationException message = {0}.", aue.Message); + return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, aue); + } + catch (InvalidOperationException ioe) + { + SqlClientEventSource.Log.SNITrace("InvalidOperationExceptionn message = {0}.", ioe.Message); + return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, ioe); + } + + _stream = _sslStream; + return TdsEnums.SNI_SUCCESS; } - catch (InvalidOperationException ioe) + finally { - return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, ioe); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - _stream = _sslStream; - return TdsEnums.SNI_SUCCESS; } public override void DisableSsl() @@ -310,12 +380,20 @@ public override void DisableSsl() /// true if valid private bool ValidateServerCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors) { - if (!_validateCert) + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try { - return true; - } + if (!_validateCert) + { + return true; + } - return SNICommon.ValidateSslServerCertificate(_targetServer, sender, cert, chain, policyErrors); + return SNICommon.ValidateSslServerCertificate(_targetServer, sender, cert, chain, policyErrors); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs index 188f02472e..8c9a27a115 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs @@ -26,35 +26,43 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc { Debug.Assert(!string.IsNullOrWhiteSpace(browserHostName), "browserHostName should not be null, empty, or whitespace"); Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); - - byte[] instanceInfoRequest = CreateInstanceInfoRequest(instanceName); - byte[] responsePacket = null; + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); try { - responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest); - } - catch (SocketException se) - { - throw new Exception(SQLMessage.SqlServerBrowserNotAccessible(), se); - } + byte[] instanceInfoRequest = CreateInstanceInfoRequest(instanceName); + byte[] responsePacket = null; + try + { + responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest); + } + catch (SocketException se) + { + SqlClientEventSource.Log.SNITrace(" SocketException Message = {0}", se.Message); + throw new Exception(SQLMessage.SqlServerBrowserNotAccessible(), se); + } - const byte SvrResp = 0x05; - if (responsePacket == null || responsePacket.Length <= 3 || responsePacket[0] != SvrResp || - BitConverter.ToUInt16(responsePacket, 1) != responsePacket.Length - 3) - { - throw new SocketException(); - } + const byte SvrResp = 0x05; + if (responsePacket == null || responsePacket.Length <= 3 || responsePacket[0] != SvrResp || + BitConverter.ToUInt16(responsePacket, 1) != responsePacket.Length - 3) + { + throw new SocketException(); + } - string serverMessage = Encoding.ASCII.GetString(responsePacket, 3, responsePacket.Length - 3); + string serverMessage = Encoding.ASCII.GetString(responsePacket, 3, responsePacket.Length - 3); - string[] elements = serverMessage.Split(SemicolonSeparator); - int tcpIndex = Array.IndexOf(elements, "tcp"); - if (tcpIndex < 0 || tcpIndex == elements.Length - 1) + string[] elements = serverMessage.Split(SemicolonSeparator); + int tcpIndex = Array.IndexOf(elements, "tcp"); + if (tcpIndex < 0 || tcpIndex == elements.Length - 1) + { + throw new SocketException(); + } + + return ushort.Parse(elements[tcpIndex + 1]); + } + finally { - throw new SocketException(); + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - - return ushort.Parse(elements[tcpIndex + 1]); } /// @@ -65,16 +73,23 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc private static byte[] CreateInstanceInfoRequest(string instanceName) { Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); + long scopeID = SqlClientEventSource.Log.SNIScopeEnter(""); + try + { + const byte ClntUcastInst = 0x04; + instanceName += char.MinValue; + int byteCount = Encoding.ASCII.GetByteCount(instanceName); - const byte ClntUcastInst = 0x04; - instanceName += char.MinValue; - int byteCount = Encoding.ASCII.GetByteCount(instanceName); - - byte[] requestPacket = new byte[byteCount + 1]; - requestPacket[0] = ClntUcastInst; - Encoding.ASCII.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 1); + byte[] requestPacket = new byte[byteCount + 1]; + requestPacket[0] = ClntUcastInst; + Encoding.ASCII.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 1); - return requestPacket; + return requestPacket; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 5e90796ade..360bbe4c2f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -73,6 +73,8 @@ public class Keywords internal const EventKeywords Advanced = (EventKeywords)512; internal const EventKeywords StateDump = (EventKeywords)1024; + + internal const EventKeywords SNITrace = (EventKeywords)2048; } #endregion @@ -118,16 +120,55 @@ public class Keywords [NonEvent] internal bool IsSqlClientEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.SqlClient); + + [NonEvent] + internal bool IsSNITraceEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.SNITrace); #endregion #region overloads //Never use event writer directly as they are not checking for enabled/disabled situations. Always use overloads. + [NonEvent] + internal void SNITrace(string message) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message)); + } + } + + [NonEvent] + internal void SNITrace(string message, T0 args0) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message, args0)); + } + } + + [NonEvent] + internal void SNITrace(string message, T0 args0, T1 args1) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message, args0, args1)); + } + } + + [NonEvent] + internal void SNITrace(string message, T0 args0, T1 args1, T2 args2) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2)); + } + } + [NonEvent] internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - TraceEvent(string.Format(message, args0)); + Trace(string.Format(message, args0)); } } @@ -253,7 +294,7 @@ internal long ScopeEnterEvent(string message, T0 args0) { if (Log.IsScopeEnabled()) { - return ScopeEnter(string.Format(message, args0)); + return SNIScopeEnter(string.Format(message, args0)); } return 0; } @@ -263,7 +304,7 @@ internal long AdvanceScopeEnter(string message, T0 args0) { if (Log.IsAdvanceTraceOn()) { - return ScopeEnter(string.Format(message, args0)); + return SNIScopeEnter(string.Format(message, args0)); } return 0; } @@ -273,7 +314,7 @@ internal long ScopeEnterEvent(string message) { if (Log.IsScopeEnabled()) { - return ScopeEnter(message); + return SNIScopeEnter(message); } return 0; } @@ -283,7 +324,7 @@ internal long ScopeEnterEvent(string message, T0 args0, T1 args1) { if (Log.IsScopeEnabled()) { - return ScopeEnter(string.Format(message, args0, args1)); + return SNIScopeEnter(string.Format(message, args0, args1)); } return 0; } @@ -293,7 +334,7 @@ internal long ScopeEnterEvent(string message, T0 args0, T1 args1, T2 { if (Log.IsScopeEnabled()) { - return ScopeEnter(string.Format(message, args0, args1, args2)); + return SNIScopeEnter(string.Format(message, args0, args1, args2)); } return 0; } @@ -303,7 +344,7 @@ internal long ScopeEnterEvent(string message, T0 args0, T1 args1 { if (Log.IsScopeEnabled()) { - return ScopeEnter(string.Format(message, args0, args1, args2, args3)); + return SNIScopeEnter(string.Format(message, args0, args1, args2, args3)); } return 0; } @@ -529,7 +570,7 @@ internal void Trace(string message) } [Event(EnterScopeId, Level = EventLevel.Verbose, Keywords = Keywords.Scope)] - internal long ScopeEnter(string message) + internal long SNIScopeEnter(string message) { long scopeId = Interlocked.Increment(ref s_nextScopeId); WriteEvent(EnterScopeId, message); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 1dfb67f4ba..20df06c880 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1246,120 +1246,141 @@ internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool calle internal SqlError ProcessSNIError(TdsParserStateObject stateObj) { + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(""); + try + { #if DEBUG - // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error - Debug.Assert(SniContext.Undefined != stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None"); + // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error + Debug.Assert(SniContext.Undefined != stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None"); + SqlClientEventSource.Log.SNITrace(" SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state); + #endif - SNIErrorDetails details = GetSniErrorDetails(); + SNIErrorDetails details = GetSniErrorDetails(); - if (details.sniErrorNumber != 0) - { - // handle special SNI error codes that are converted into exception which is not a SqlException. - switch (details.sniErrorNumber) + if (details.sniErrorNumber != 0) { - case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithMoreThan64IPs: - // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported. - throw SQL.MultiSubnetFailoverWithMoreThan64IPs(); + // handle special SNI error codes that are converted into exception which is not a SqlException. + switch (details.sniErrorNumber) + { + case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithMoreThan64IPs: + // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported. + SqlClientEventSource.Log.AdvanceTrace(" Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported."); + throw SQL.MultiSubnetFailoverWithMoreThan64IPs(); - case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithInstanceSpecified: - // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported. - throw SQL.MultiSubnetFailoverWithInstanceSpecified(); + case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithInstanceSpecified: + // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported. + SqlClientEventSource.Log.AdvanceTrace(" Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported."); + throw SQL.MultiSubnetFailoverWithInstanceSpecified(); - case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithNonTcpProtocol: - // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol. - throw SQL.MultiSubnetFailoverWithNonTcpProtocol(); - // continue building SqlError instance + case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithNonTcpProtocol: + // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol. + SqlClientEventSource.Log.AdvanceTrace(" Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol."); + throw SQL.MultiSubnetFailoverWithNonTcpProtocol(); + // continue building SqlError instance + } } - } - // PInvoke code automatically sets the length of the string for us - // So no need to look for \0 - string errorMessage = details.errorMessage; - - // Format SNI errors and add Context Information - // - // General syntax is: - // - // (provider:, error: - ) - // - // errorMessage | sniError | - // ------------------------------------------- - // ==null | x | must never happen - // !=null | != 0 | retrieve corresponding errorMessage from resources - // !=null | == 0 | replace text left of errorMessage - // + // PInvoke code automatically sets the length of the string for us + // So no need to look for \0 + string errorMessage = details.errorMessage; + SqlClientEventSource.Log.AdvanceTrace("< sc.TdsParser.ProcessSNIError |ERR|ADV > Error message Detail: {0}", details.errorMessage); - if (TdsParserStateObjectFactory.UseManagedSNI) - Debug.Assert(!string.IsNullOrEmpty(details.errorMessage) || details.sniErrorNumber != 0, "Empty error message received from SNI"); - else - Debug.Assert(!string.IsNullOrEmpty(details.errorMessage), "Empty error message received from SNI"); - - string sniContextEnumName = TdsEnums.GetSniContextEnumName(stateObj.SniContext); - - string sqlContextInfo = SRHelper.GetResourceString(sniContextEnumName); - string providerRid = string.Format("SNI_PN{0}", details.provider); - string providerName = SRHelper.GetResourceString(providerRid); - Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'"); - uint win32ErrorCode = details.nativeError; - - if (details.sniErrorNumber == 0) - { - // Provider error. The message from provider is preceded with non-localizable info from SNI - // strip provider info from SNI + // Format SNI errors and add Context Information + // + // General syntax is: + // + // (provider:, error: - ) + // + // errorMessage | sniError | + // ------------------------------------------- + // ==null | x | must never happen + // !=null | != 0 | retrieve corresponding errorMessage from resources + // !=null | == 0 | replace text left of errorMessage // - int iColon = errorMessage.IndexOf(':'); - Debug.Assert(0 <= iColon, "':' character missing in sni errorMessage"); - Debug.Assert(errorMessage.Length > iColon + 1 && errorMessage[iColon + 1] == ' ', "Expecting a space after the ':' character"); - // extract the message excluding the colon and trailing cr/lf chars - if (0 <= iColon) + if (TdsParserStateObjectFactory.UseManagedSNI) { - int len = errorMessage.Length; - len -= Environment.NewLine.Length; // exclude newline sequence - iColon += 2; // skip over ": " sequence - len -= iColon; - /* - The error message should come back in the following format: "TCP Provider: MESSAGE TEXT" - If the message is received on a Win9x OS, the error message will not contain MESSAGE TEXT - If we get an error message with no message text, just return the entire message otherwise - return just the message text. - */ - if (len > 0) - { - errorMessage = errorMessage.Substring(iColon, len); - } + Debug.Assert(!string.IsNullOrEmpty(details.errorMessage) || details.sniErrorNumber != 0, "Empty error message received from SNI"); + SqlClientEventSource.Log.AdvanceTrace(" Empty error message received from SNI. Error Message = {0}, SNI Error Number ={1}", details.errorMessage, details.sniErrorNumber); + } + else + { + Debug.Assert(!string.IsNullOrEmpty(details.errorMessage), "Empty error message received from SNI"); + SqlClientEventSource.Log.AdvanceTrace(" Empty error message received from SNI. Error Message = {0}", details.errorMessage); } - } - else - { - if (TdsParserStateObjectFactory.UseManagedSNI) + string sniContextEnumName = TdsEnums.GetSniContextEnumName(stateObj.SniContext); + + string sqlContextInfo = SRHelper.GetResourceString(sniContextEnumName); + string providerRid = string.Format("SNI_PN{0}", details.provider); + string providerName = SRHelper.GetResourceString(providerRid); + Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'"); + uint win32ErrorCode = details.nativeError; + SqlClientEventSource.Log.AdvanceTrace(" SNI Native Error Code = {0}", win32ErrorCode); + if (details.sniErrorNumber == 0) { - // SNI error. Append additional error message info if available. + // Provider error. The message from provider is preceded with non-localizable info from SNI + // strip provider info from SNI // - string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); - errorMessage = (errorMessage != string.Empty) ? - (sniLookupMessage + ": " + errorMessage) : - sniLookupMessage; + int iColon = errorMessage.IndexOf(':'); + Debug.Assert(0 <= iColon, "':' character missing in sni errorMessage"); + SqlClientEventSource.Log.AdvanceTrace(" ':' character missing in sni errorMessage. Error Mesage index of ':' = {0}", iColon); + Debug.Assert(errorMessage.Length > iColon + 1 && errorMessage[iColon + 1] == ' ', "Expecting a space after the ':' character"); + SqlClientEventSource.Log.AdvanceTrace(" Expecting a space after the ':' character. Error Mesage Length = {0}", errorMessage.Length); + // extract the message excluding the colon and trailing cr/lf chars + if (0 <= iColon) + { + int len = errorMessage.Length; + len -= Environment.NewLine.Length; // exclude newline sequence + iColon += 2; // skip over ": " sequence + len -= iColon; + /* + The error message should come back in the following format: "TCP Provider: MESSAGE TEXT" + If the message is received on a Win9x OS, the error message will not contain MESSAGE TEXT + If we get an error message with no message text, just return the entire message otherwise + return just the message text. + */ + if (len > 0) + { + errorMessage = errorMessage.Substring(iColon, len); + } + } } else { - // SNI error. Replace the entire message. - // - errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); - // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code - if (details.sniErrorNumber == (int)SNINativeMethodWrapper.SniSpecialErrors.LocalDBErrorCode) + if (TdsParserStateObjectFactory.UseManagedSNI) { - errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError); - win32ErrorCode = 0; + // SNI error. Append additional error message info if available. + // + string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); + errorMessage = (errorMessage != string.Empty) ? + (sniLookupMessage + ": " + errorMessage) : + sniLookupMessage; + } + else + { + // SNI error. Replace the entire message. + // + errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); + + // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code + if (details.sniErrorNumber == (int)SNINativeMethodWrapper.SniSpecialErrors.LocalDBErrorCode) + { + errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError); + win32ErrorCode = 0; + } } } + errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})", + sqlContextInfo, providerName, (int)details.sniErrorNumber, errorMessage); + SqlClientEventSource.Log.AdvanceTrace(" SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}", (int)details.nativeError, (int)details.lineNumber, details.function, details.exception, _server); + return new SqlError((int)details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS, + _server, errorMessage, details.function, (int)details.lineNumber, details.nativeError, details.exception); + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); } - errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})", - sqlContextInfo, providerName, (int)details.sniErrorNumber, errorMessage); - - return new SqlError((int)details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS, - _server, errorMessage, details.function, (int)details.lineNumber, details.nativeError, details.exception); } internal void CheckResetConnection(TdsParserStateObject stateObj) From 52716d9ac000e775ddc71234b75389f227baf83f Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 9 Mar 2020 14:49:53 -0700 Subject: [PATCH 04/13] EventSource --- .../Data/SqlClient/SqlClientEventSource.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 360bbe4c2f..4795b7bf93 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -163,6 +163,24 @@ internal void SNITrace(string message, T0 args0, T1 args1, T2 args2) } } + [NonEvent] + internal void SNITrace(string message, T0 args0, T1 args1, T2 args2, T3 args3) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2, args3)); + } + } + + [NonEvent] + internal void SNITrace(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4) + { + if (Log.IsSNITraceEnabled()) + { + Trace(string.Format(message, args0, args1, args2, args3, args4)); + } + } + [NonEvent] internal void TraceEvent(string message, T0 args0) { @@ -262,6 +280,14 @@ internal void AdvanceTrace(string message, T0 args0, T1 args1, T } } + [NonEvent] + internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4) + { + if (Log.IsAdvanceTraceOn()) + { + Trace(string.Format(message, args0, args1, args2, args3, args4)); + } + } [NonEvent] internal void AdvanceTrace(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4, T5 args5) { From 46425730eee176dd165d88e1a77e6098c7cc631d Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Tue, 10 Mar 2020 15:24:59 -0700 Subject: [PATCH 05/13] Extra line removed. --- .../src/Microsoft/Data/SqlClient/ColumnEncryptionKeyInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ColumnEncryptionKeyInfo.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ColumnEncryptionKeyInfo.cs index eaecec4f9d..1b1dbceba0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ColumnEncryptionKeyInfo.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ColumnEncryptionKeyInfo.cs @@ -6,7 +6,6 @@ namespace Microsoft.Data.SqlClient { - /// /// Class encapsulating Column encryption key info /// From 80807e4313f29ce9c65953b970bd1a8ddfefb10e Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Sun, 15 Mar 2020 22:10:45 -0700 Subject: [PATCH 06/13] EventSource --- .../src/Microsoft/Data/Common/AdapterUtil.cs | 2 +- .../Data/Common/DbConnectionOptions.Common.cs | 2 +- .../Data/ProviderBase/DbConnectionFactory.cs | 3 +- .../ProviderBase/DbConnectionPoolGroup.cs | 12 ++---- .../src/Microsoft/Data/DataException.cs | 10 +---- .../EnclaveProviderBase.NetCoreApp.cs | 1 - .../Data/SqlClient/LocalDBAPI.Windows.cs | 19 ++++----- .../Data/SqlClient/SNI/SNIMarsConnection.cs | 7 +++- .../Data/SqlClient/SNI/SNIMarsHandle.cs | 2 +- .../Data/SqlClient/SqlClientEventSource.cs | 6 +-- .../Microsoft/Data/SqlClient/SqlCommand.cs | 38 +++++++++--------- .../Data/SqlClient/EnclaveProviderBase.cs | 1 - .../Data/SqlClient/SqlClientEventSource.cs | 9 ++--- .../Microsoft/Data/SqlClient/SqlCommand.cs | 40 +++++++++---------- .../Data/SqlClient/SqlDependencyListener.cs | 1 - 15 files changed, 66 insertions(+), 87 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs index 4318d3d1e2..8745166f91 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs @@ -36,7 +36,7 @@ static private void TraceException(string trace, Exception e) Debug.Assert(null != e, "TraceException: null Exception"); if (null != e) { - SqlClientEventSource.Log.TraceEvent(trace, e.ToString()); // will include callstack if permission is available + SqlClientEventSource.Log.TraceEvent(trace, e.ToString()); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs index d3f9919d94..3714f41ccf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs @@ -163,7 +163,7 @@ private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dict { if (SqlClientEventSource.Log.IsAdvanceTraceOn()) { - Debug.Assert(keyname == keyname.ToLower(CultureInfo.InvariantCulture), "missing ToLower"); + Debug.Assert(string.Equals(keyname, keyname?.ToLower(), StringComparison.InvariantCulture), "missing ToLower"); string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); if ((KEY.Password != realkeyname) && (SYNONYM.Pwd != realkeyname)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 2aa715bdd3..bb5215baf1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -23,7 +23,7 @@ internal abstract partial class DbConnectionFactory private const int PruningPeriod = 30 * 1000; // thirty seconds private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to // a maximum of Environment.ProcessorCount at a time. @@ -31,7 +31,6 @@ internal abstract partial class DbConnectionFactory private static Task[] s_pendingOpenNonPooled = new Task[Environment.ProcessorCount]; private static Task s_completedTask; - internal int ObjectID => _objectID; protected DbConnectionFactory() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index f0c63eca37..9866d571ad 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -8,6 +8,7 @@ using System.Collections.Concurrent; using System.Data.Common; using System.Diagnostics; +using System.Threading; namespace Microsoft.Data.ProviderBase { @@ -40,8 +41,7 @@ sealed internal class DbConnectionPoolGroup private DbConnectionPoolGroupProviderInfo _providerInfo; private DbMetaDataFactory _metaDataFactory; - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private static int s_objectTypeCount; // EventSource counter // always lock this before changing _state, we don't want to move out of the 'Disabled' state // PoolGroupStateUninitialized = 0; @@ -87,13 +87,7 @@ internal DbConnectionPoolGroupProviderInfo ProviderInfo internal bool IsDisabled => (PoolGroupStateDisabled == _state); - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID { get; } = Interlocked.Increment(ref s_objectTypeCount); internal DbConnectionPoolGroupOptions PoolGroupOptions => _poolGroupOptions; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs index 4a401b4c22..bb39ad4984 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs @@ -31,15 +31,7 @@ static private void TraceException( Debug.Assert(null != e, "TraceException: null Exception"); if (null != e) { - SqlClientEventSource.Log.AdvanceTrace(trace, e.Message); - try - { - SqlClientEventSource.Log.AdvanceTrace(" Environment StackTrace = '{0}'", Environment.StackTrace); - } - catch (System.Security.SecurityException) - { - // if you don't have permission - you don't get the stack trace - } + SqlClientEventSource.Log.AdvanceTrace(" Environment StackTrace = '{0}'", Environment.StackTrace); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs index 3b38ec607a..03a49f34f1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/EnclaveProviderBase.NetCoreApp.cs @@ -64,7 +64,6 @@ namespace Microsoft.Data.SqlClient { - // Base class for Enclave provider internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs index af8aec7a73..2c50c2ba3a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs @@ -22,18 +22,15 @@ private static IntPtr UserInstanceDLLHandle if (s_userInstanceDLLHandle == IntPtr.Zero) { SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle); - if (s_userInstanceDLLHandle == IntPtr.Zero) + if(s_userInstanceDLLHandle != IntPtr.Zero) { - if (s_userInstanceDLLHandle != IntPtr.Zero) - { - SqlClientEventSource.Log.TraceEvent(" LocalDB - handle obtained"); - } - else - { - SNINativeMethodWrapper.SNI_Error sniError; - SNINativeMethodWrapper.SNIGetLastError(out sniError); - throw CreateLocalDBException(errorMessage: SRHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); - } + SqlClientEventSource.Log.TraceEvent(" LocalDB - handle obtained"); + } + else + { + SNINativeMethodWrapper.SNI_Error sniError; + SNINativeMethodWrapper.SNIGetLastError(out sniError); + throw CreateLocalDBException(errorMessage: SRHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs index 01a7317c7d..e6774fcf8d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs @@ -177,7 +177,10 @@ public uint CheckConnection() public void HandleReceiveError(SNIPacket packet) { Debug.Assert(Monitor.IsEntered(this), "HandleReceiveError was called without being locked."); - SqlClientEventSource.Log.SNITrace(" HandleReceiveError was called without being locked."); + if (!Monitor.IsEntered(this)) + { + SqlClientEventSource.Log.SNITrace(" HandleReceiveError was called without being locked."); + } foreach (SNIMarsHandle handle in _sessions.Values) { if (packet.HasCompletionCallback) @@ -243,7 +246,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { - SqlClientEventSource.Log.SNITrace(" not successfull IO Pending."); + SqlClientEventSource.Log.SNITrace(" not successfull."); return; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs index 5510a80d26..d455100fa8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs @@ -58,7 +58,7 @@ public override void Dispose() } catch (Exception e) { - SqlClientEventSource.Log.SNITrace(" internal exception error = {0}", e.Message); + SqlClientEventSource.Log.SNITrace(" internal exception error = {0}, Member Name={1}", e.Message, e.GetType().Name); SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } finally diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 4795b7bf93..1d7d2d545c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -466,7 +466,7 @@ internal void CorrelationTraceEvent(string message, T0 args0) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0)); + CorrelationTrace(string.Format(message, args0.ToString())); } } @@ -475,7 +475,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 args1) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0, args1)); + CorrelationTrace(string.Format(message, args0, args1.ToString())); } } @@ -484,7 +484,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 arg { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0, args1, args2)); + CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 8cfba4ae0e..f685ae125b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -707,7 +707,7 @@ override public void Prepare() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -832,7 +832,7 @@ internal void Unprepare() override public void Cancel() { long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlStatistics statistics = null; try { @@ -948,7 +948,7 @@ override public object ExecuteScalar() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Exception e = null; bool success = false; @@ -1032,7 +1032,7 @@ override public int ExecuteNonQuery() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Exception e = null; try { @@ -1070,7 +1070,7 @@ public IAsyncResult BeginExecuteNonQuery() /// public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false); } @@ -1267,7 +1267,7 @@ public int EndExecuteNonQuery(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } @@ -1286,7 +1286,7 @@ private void ThrowIfReconnectionHasBeenCanceled() /// public int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -1521,7 +1521,7 @@ public XmlReader ExecuteXmlReader() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); int? sqlExceptionNumber = null; Exception e = null; @@ -1572,7 +1572,7 @@ public IAsyncResult BeginExecuteXmlReader() /// public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false); } @@ -1695,13 +1695,13 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -1808,14 +1808,14 @@ private XmlReader CompleteXmlReader(SqlDataReader ds, bool isAsync = false) /// public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); } /// override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); return ExecuteReader(behavior); } @@ -1824,7 +1824,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1891,14 +1891,14 @@ public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -2247,7 +2247,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Guid operationId = _diagnosticListener.WriteCommandBefore(this); TaskCompletionSource source = new TaskCompletionSource(); @@ -2334,7 +2334,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b /// new public Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); Guid operationId = default(Guid); if (!_parentOperationStarted) operationId = _diagnosticListener.WriteCommandBefore(this); @@ -2485,7 +2485,7 @@ public Task ExecuteXmlReaderAsync() /// public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Guid operationId = _diagnosticListener.WriteCommandBefore(this); TaskCompletionSource source = new TaskCompletionSource(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs index 3b38ec607a..03a49f34f1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/EnclaveProviderBase.cs @@ -64,7 +64,6 @@ namespace Microsoft.Data.SqlClient { - // Base class for Enclave provider internal abstract class EnclaveProviderBase : SqlColumnEncryptionEnclaveProvider { #region Constants diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 4f02f43dd7..c65e7f5720 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -12,9 +12,6 @@ namespace Microsoft.Data.SqlClient internal class SqlClientEventSource : EventSource { // Defines the singleton instance for the Resources ETW provider - /// - /// - /// internal static readonly SqlClientEventSource Log = new SqlClientEventSource(); private static long s_nextScopeId = 0; @@ -402,7 +399,7 @@ internal void CorrelationTraceEvent(string message, T0 args0) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0)); + CorrelationTrace(string.Format(message, args0.ToString())); } } @@ -411,7 +408,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 args1) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0, args1)); + CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString())); } } @@ -420,7 +417,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 arg { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0, args1, args2)); + CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index e2ffc12caf..5cbb93dd02 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -991,7 +991,7 @@ override public void Prepare() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); statistics = SqlStatistics.StartTimer(Statistics); @@ -1168,7 +1168,7 @@ internal void Unprepare() override public void Cancel() { long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlStatistics statistics = null; try @@ -1331,7 +1331,7 @@ override public object ExecuteScalar() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); bool success = false; int? sqlExceptionNumber = null; @@ -1401,7 +1401,7 @@ override public int ExecuteNonQuery() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); bool success = false; int? sqlExceptionNumber = null; @@ -1464,7 +1464,7 @@ public IAsyncResult BeginExecuteNonQuery() [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)] public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false); } @@ -1690,7 +1690,7 @@ public int EndExecuteNonQuery(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } @@ -1708,7 +1708,7 @@ private void ThrowIfReconnectionHasBeenCanceled() private int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -2025,7 +2025,7 @@ public XmlReader ExecuteXmlReader() SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); bool success = false; int? sqlExceptionNumber = null; @@ -2066,7 +2066,7 @@ public IAsyncResult BeginExecuteXmlReader() [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)] public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false); } @@ -2219,13 +2219,13 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -2344,7 +2344,7 @@ public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObjec /// override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); return ExecuteReader(behavior, ADP.ExecuteReader); } @@ -2353,7 +2353,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#", ObjectID); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -2370,7 +2370,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) new public SqlDataReader ExecuteReader(CommandBehavior behavior) { long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}#, behavior={1}", ObjectID, (int)behavior); - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); try { @@ -2393,7 +2393,7 @@ public IAsyncResult BeginExecuteReader(CommandBehavior behavior) [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)] public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); } @@ -2474,13 +2474,13 @@ public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); } } private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; @@ -2881,7 +2881,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); TaskCompletionSource source = new TaskCompletionSource(); @@ -2966,7 +2966,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b /// new public Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); TaskCompletionSource source = new TaskCompletionSource(); @@ -3100,7 +3100,7 @@ public Task ExecuteXmlReaderAsync() /// public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current.ToString()); + SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}#, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlConnection.ExecutePermission.Demand(); TaskCompletionSource source = new TaskCompletionSource(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index 99a9d4fcda..21be33e976 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -583,7 +583,6 @@ private void ProcessNotificationResults(SqlDataReader reader) string msgType = reader.GetString(0); SqlClientEventSource.Log.NotificationsTraceEvent(" msgType: '{0}'", msgType); handle = reader.GetGuid(1); - //SqlClientEventSource.Log.NotificationsTrace("SqlConnectionContainer.ProcessNotificationResults(SqlDataReader)|DEP> conversationHandle: '%p(GUID)'", conversationHandle); // Only process QueryNotification messages. if (0 == String.Compare(msgType, "http://schemas.microsoft.com/SQL/Notifications/QueryNotification", StringComparison.OrdinalIgnoreCase)) From 488705546de11a439f25fc3b0b463c92af2af0ba Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 16 Mar 2020 12:03:22 -0700 Subject: [PATCH 07/13] EventSource NetCore. --- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 5cbb93dd02..6a9e121e41 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -26,7 +26,6 @@ namespace Microsoft.Data.SqlClient { - /// [ DefaultEvent("RecordsAffected"), From 7aa6228b5b8a90635ba9bca0744437793424831c Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 18 Mar 2020 16:30:02 -0700 Subject: [PATCH 08/13] EventSource --- .../src/Microsoft/Data/Common/AdapterUtil.cs | 2 +- .../src/Microsoft/Data/DataException.cs | 1 + .../Data/ProviderBase/DbConnectionInternal.cs | 6 +- .../Data/SqlClient/SqlClientEventSource.cs | 6 +- .../Data/SqlClient/SqlConnectionHelper.cs | 2 +- .../Data/SqlClient/TdsParserStateObject.cs | 2 +- .../Data/SqlTypes/SqlFileStream.Windows.cs | 151 ++++++++++-------- .../Data/ProviderBase/DbConnectionInternal.cs | 6 +- .../Microsoft/Data/SqlTypes/SqlFileStream.cs | 2 +- 9 files changed, 98 insertions(+), 80 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs index 8745166f91..7f5335c7d3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs @@ -36,7 +36,7 @@ static private void TraceException(string trace, Exception e) Debug.Assert(null != e, "TraceException: null Exception"); if (null != e) { - SqlClientEventSource.Log.TraceEvent(trace, e.ToString()); + SqlClientEventSource.Log.TraceEvent(trace, e); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs index bb39ad4984..66a8533e4b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/DataException.cs @@ -31,6 +31,7 @@ static private void TraceException( Debug.Assert(null != e, "TraceException: null Exception"); if (null != e) { + SqlClientEventSource.Log.AdvanceTrace(trace, e.Message); SqlClientEventSource.Log.AdvanceTrace(" Environment StackTrace = '{0}'", Environment.StackTrace); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 8350c765ee..9cfbee338d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -334,7 +334,7 @@ virtual internal void DelegatedTransactionEnded() // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - SqlClientEventSource.Log.TraceEvent(" {0}#, Delegated Transaction Completed.", ObjectID); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Delegated Transaction Completed.", ObjectID); if (1 == _pooledCount) { @@ -426,7 +426,7 @@ internal void DetachCurrentTransactionIfEnded() // Detach transaction from connection. internal void DetachTransaction(Transaction transaction, bool isExplicitlyReleasing) { - SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount={1})", ObjectID, _pooledCount); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction Completed. (pooledCount={1})", ObjectID, _pooledCount); // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should @@ -466,7 +466,7 @@ internal void CleanupConnectionOnTransactionCompletion(Transaction transaction) void TransactionCompletedEvent(object sender, TransactionEventArgs e) { Transaction transaction = e.Transaction; - SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount = {1})", ObjectID, _pooledCount); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction Completed. (pooledCount = {1})", ObjectID, _pooledCount); CleanupTransactionOnCompletion(transaction); CleanupConnectionOnTransactionCompletion(transaction); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 1d7d2d545c..415bf493f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -186,7 +186,7 @@ internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0)); + Trace(string.Format(message, args0.ToString())); } } @@ -204,7 +204,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0, args1)); + Trace(string.Format(message, args0.ToString(), args1.ToString())); } } @@ -213,7 +213,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1, T2 args { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0, args1, args2)); + Trace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index f342e313cf..dd7e862792 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -139,7 +139,7 @@ internal void Abort(Exception e) } else { - SqlClientEventSource.Log.TraceEvent(" {0}#, Aborting operation due to asynchronous exception: {1}", ObjectID, e.ToString()); + SqlClientEventSource.Log.TraceEvent(" {0}#, Aborting operation due to asynchronous exception: {1}", ObjectID, e); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 32f96f3205..690b28c65e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -22,7 +22,7 @@ sealed internal class LastIOTimer internal abstract class TdsParserStateObject { - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource counter internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); [Flags] diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs index 71c622860e..f6f716c399 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs @@ -9,7 +9,9 @@ using System.IO; using System.Runtime.InteropServices; using System.Security.Permissions; +using System.Threading; using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; using Microsoft.Win32.SafeHandles; namespace Microsoft.Data.SqlTypes @@ -21,6 +23,9 @@ public sealed partial class SqlFileStream : System.IO.Stream // TransactionContext accessors as virtual methods. Doing so now on a sealed class // generates a compiler error (CS0549) + private static int _objectTypeCount; // Bid counter + internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + // from System.IO.FileStream implementation // DefaultBufferSize = 4096; // SQLBUVSTS# 193123 - disable lazy flushing of written data in order to prevent @@ -56,25 +61,33 @@ public SqlFileStream(string path, byte[] transactionContext, FileAccess access) /// public SqlFileStream(string path, byte[] transactionContext, FileAccess access, FileOptions options, long allocationSize) { - //----------------------------------------------------------------- - // precondition validation + long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}# access={1} options={2} path='{3}'", ObjectID, (int)access, (int)options, path); + try + { + //----------------------------------------------------------------- + // precondition validation - if (transactionContext == null) - throw ADP.ArgumentNull("transactionContext"); + if (transactionContext == null) + throw ADP.ArgumentNull("transactionContext"); - if (path == null) - throw ADP.ArgumentNull("path"); + if (path == null) + throw ADP.ArgumentNull("path"); - //----------------------------------------------------------------- + //----------------------------------------------------------------- - _m_disposed = false; - _m_fs = null; + _m_disposed = false; + _m_fs = null; - OpenSqlFileStream(path, transactionContext, access, options, allocationSize); + OpenSqlFileStream(path, transactionContext, access, options, allocationSize); - // only set internal state once the file has actually been successfully opened - Name = path; - TransactionContext = transactionContext; + // only set internal state once the file has actually been successfully opened + Name = path; + TransactionContext = transactionContext; + } + finally + { + SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); + } } #region destructor/dispose code @@ -585,6 +598,10 @@ long allocationSize createOptions: dwCreateOptions, eaBuffer: b, eaLength: (uint)fullSize); + + SqlClientEventSource.Log.AdvanceTrace(" {0}#, desiredAccess=0x{1}, allocationSize={2}, " + + "fileAttributes=0x{3}, shareAccess=0x{4}, dwCreateDisposition=0x{5}, createOptions=0x{ dwCreateOptions}", ObjectID, (int)nDesiredAccess, allocationSize, 0, (int)nShareAccess, dwCreateDisposition); + retval = status; hFile = new SafeFileHandle(handle, true); } @@ -597,71 +614,71 @@ long allocationSize } switch (retval) - { - case 0: - break; + { + case 0: + break; - case Interop.Errors.ERROR_SHARING_VIOLATION: - throw ADP.InvalidOperation(System.SRHelper.GetString(SR.SqlFileStream_FileAlreadyInTransaction)); + case Interop.Errors.ERROR_SHARING_VIOLATION: + throw ADP.InvalidOperation(System.SRHelper.GetString(SR.SqlFileStream_FileAlreadyInTransaction)); - case Interop.Errors.ERROR_INVALID_PARAMETER: - throw ADP.Argument(System.SRHelper.GetString(SR.SqlFileStream_InvalidParameter)); + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw ADP.Argument(System.SRHelper.GetString(SR.SqlFileStream_InvalidParameter)); - case Interop.Errors.ERROR_FILE_NOT_FOUND: - { - System.IO.DirectoryNotFoundException e = new System.IO.DirectoryNotFoundException(); - ADP.TraceExceptionAsReturnValue(e); - throw e; - } - default: + case Interop.Errors.ERROR_FILE_NOT_FOUND: + { + System.IO.DirectoryNotFoundException e = new System.IO.DirectoryNotFoundException(); + ADP.TraceExceptionAsReturnValue(e); + throw e; + } + default: + { + uint error = Interop.NtDll.RtlNtStatusToDosError(retval); + if (error == ERROR_MR_MID_NOT_FOUND) { - uint error = Interop.NtDll.RtlNtStatusToDosError(retval); - if (error == ERROR_MR_MID_NOT_FOUND) - { - // status code could not be mapped to a Win32 error code - error = (uint)retval; - } - - System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(unchecked((int)error)); - ADP.TraceExceptionAsReturnValue(e); - throw e; + // status code could not be mapped to a Win32 error code + error = (uint)retval; } - } - - if (hFile.IsInvalid) - { - System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Interop.Errors.ERROR_INVALID_HANDLE); - ADP.TraceExceptionAsReturnValue(e); - throw e; - } - - if (Interop.Kernel32.GetFileType(hFile) != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) - { - hFile.Dispose(); - throw ADP.Argument(System.SRHelper.GetString(SR.SqlFileStream_PathNotValidDiskResource)); - } - // if the user is opening the SQL FileStream in read/write mode, we assume that they want to scan - // through current data and then append new data to the end, so we need to tell SQL Server to preserve - // the existing file contents. - if (access == System.IO.FileAccess.ReadWrite) - { - uint ioControlCode = Interop.Kernel32.CTL_CODE(FILE_DEVICE_FILE_SYSTEM, - IoControlCodeFunctionCode, (byte)Interop.Kernel32.IoControlTransferType.METHOD_BUFFERED, - (byte)Interop.Kernel32.IoControlCodeAccess.FILE_ANY_ACCESS); - - if (!Interop.Kernel32.DeviceIoControl(hFile, ioControlCode, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint cbBytesReturned, IntPtr.Zero)) - { - System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(unchecked((int)error)); ADP.TraceExceptionAsReturnValue(e); throw e; } + } + + if (hFile.IsInvalid) + { + System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Interop.Errors.ERROR_INVALID_HANDLE); + ADP.TraceExceptionAsReturnValue(e); + throw e; + } + + if (Interop.Kernel32.GetFileType(hFile) != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) + { + hFile.Dispose(); + throw ADP.Argument(System.SRHelper.GetString(SR.SqlFileStream_PathNotValidDiskResource)); + } + + // if the user is opening the SQL FileStream in read/write mode, we assume that they want to scan + // through current data and then append new data to the end, so we need to tell SQL Server to preserve + // the existing file contents. + if (access == System.IO.FileAccess.ReadWrite) + { + uint ioControlCode = Interop.Kernel32.CTL_CODE(FILE_DEVICE_FILE_SYSTEM, + IoControlCodeFunctionCode, (byte)Interop.Kernel32.IoControlTransferType.METHOD_BUFFERED, + (byte)Interop.Kernel32.IoControlCodeAccess.FILE_ANY_ACCESS); + + if (!Interop.Kernel32.DeviceIoControl(hFile, ioControlCode, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint cbBytesReturned, IntPtr.Zero)) + { + System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + ADP.TraceExceptionAsReturnValue(e); + throw e; } + } - // now that we've successfully opened a handle on the path and verified that it is a file, - // use the SafeFileHandle to initialize our internal System.IO.FileStream instance - System.Diagnostics.Debug.Assert(_m_fs == null); - _m_fs = new System.IO.FileStream(hFile, access, DefaultBufferSize, ((options & System.IO.FileOptions.Asynchronous) != 0)); + // now that we've successfully opened a handle on the path and verified that it is a file, + // use the SafeFileHandle to initialize our internal System.IO.FileStream instance + System.Diagnostics.Debug.Assert(_m_fs == null); + _m_fs = new System.IO.FileStream(hFile, access, DefaultBufferSize, ((options & System.IO.FileOptions.Asynchronous) != 0)); } catch { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 446d22b560..095b105a0b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -598,7 +598,7 @@ virtual internal void DelegatedTransactionEnded() // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - SqlClientEventSource.Log.TraceEvent(" {0}#, Delegated Transaction Completed.", ObjectID); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Delegated Transaction Completed.", ObjectID); if (1 == _pooledCount) { @@ -892,7 +892,7 @@ internal void DetachCurrentTransactionIfEnded() // Detach transaction from connection. internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) { - SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount={1})", ObjectID, _pooledCount); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction Completed. (pooledCount={1})", ObjectID, _pooledCount); // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should @@ -933,7 +933,7 @@ internal void CleanupConnectionOnTransactionCompletion(SysTx.Transaction transac void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) { SysTx.Transaction transaction = e.Transaction; - SqlClientEventSource.Log.TraceEvent(" {0}#, Transaction Completed. (pooledCount = {1})", ObjectID, _pooledCount); + SqlClientEventSource.Log.PoolerTraceEvent(" {0}#, Transaction Completed. (pooledCount = {1})", ObjectID, _pooledCount); CleanupTransactionOnCompletion(transaction); CleanupConnectionOnTransactionCompletion(transaction); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs index 785f22ae31..bb6a836b74 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs @@ -23,7 +23,7 @@ sealed public class SqlFileStream : System.IO.Stream // TransactionContext accessors as virtual methods. Doing so now on a sealed class // generates a compiler error (CS0549) - // For BID tracing output + // For EventTrace output private static int _objectTypeCount; // Bid counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); From fb8f8995633fe11e181e4d46841c03977c8ff3a9 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Wed, 18 Mar 2020 18:20:40 -0700 Subject: [PATCH 09/13] EventSource --- .../netfx/src/Microsoft/Data/Common/AdapterUtil.cs | 4 ++-- .../src/Microsoft/Data/SqlClient/SqlClientEventSource.cs | 4 ++-- .../netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs index 0947158b5f..a5cfebd16e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/AdapterUtil.cs @@ -101,13 +101,13 @@ static private void TraceException(string trace, Exception e) Debug.Assert(null != e, "TraceException: null Exception"); if (null != e) { - SqlClientEventSource.Log.TraceEvent(trace, e.ToString()); // will include callstack if permission is available + SqlClientEventSource.Log.TraceEvent(trace, e); } } static internal void TraceExceptionAsReturnValue(Exception e) { - TraceException(" '%ls'\n", e); + TraceException(" {0}", e); } static internal void TraceExceptionForCapture(Exception e) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index c65e7f5720..ec49c08a30 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -127,7 +127,7 @@ internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - TraceEvent(string.Format(message, args0)); + TraceEvent(string.Format(message, args0.ToString())); } } @@ -145,7 +145,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0, args1)); + Trace(string.Format(message, args0.ToString(), args1.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs index bb6a836b74..a66e9f186a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs @@ -24,7 +24,7 @@ sealed public class SqlFileStream : System.IO.Stream // generates a compiler error (CS0549) // For EventTrace output - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // from System.IO.FileStream implementation From 2aac5ff2c373a3f970f6b3913078a4a0e7011bb8 Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 20 Mar 2020 09:03:47 -0700 Subject: [PATCH 10/13] EventSource --- .../src/Microsoft/Data/ProviderBase/DbConnectionPool.cs | 2 +- .../netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlClientEventSource.cs | 6 +++--- .../netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs | 2 +- .../netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs | 2 +- .../netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs | 2 +- .../src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs | 2 +- .../src/Microsoft/Data/ProviderBase/DbConnectionPool.cs | 2 +- .../src/Microsoft/Data/ProviderBase/SqlConnectionHelper.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlClientEventSource.cs | 4 ++-- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlCommandSet.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlDependencyListener.cs | 4 ++-- .../src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs | 2 +- .../src/Microsoft/Data/SqlClient/TdsParserStateObject.cs | 2 +- .../src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs | 2 +- 26 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 3ad60ca40e..67492f5694 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -65,7 +65,7 @@ sealed private class TransactedConnectionPool DbConnectionPool _pool; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal TransactedConnectionPool(DbConnectionPool pool) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index d5c114615c..f1fa9af23f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -236,7 +236,7 @@ private int RowNumber private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); // Newly added member variables for Async modification, m = member variable to bcp. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 415bf493f8..b15841e0f3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -186,7 +186,7 @@ internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0.ToString())); + Trace(string.Format(message, args0?.ToString())); } } @@ -204,7 +204,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0.ToString(), args1.ToString())); + Trace(string.Format(message, args0?.ToString(), args1?.ToString())); } } @@ -213,7 +213,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1, T2 args { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); + Trace(string.Format(message, args0?.ToString(), args1?.ToString(), args2?.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index f685ae125b..b74d3999fe 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -25,7 +25,7 @@ namespace Microsoft.Data.SqlClient /// public sealed partial class SqlCommand : DbCommand, ICloneable { - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText; private CommandType _commandType; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs index 2d649f194c..df1d0b706e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommandSet.cs @@ -22,7 +22,7 @@ internal sealed class SqlCommandSet private SqlCommand _batchCommand; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private sealed class LocalCommand diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index dd7e862792..2fa1a8481a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -23,7 +23,7 @@ public sealed partial class SqlConnection : DbConnection private DbConnectionInternal _innerConnection; private int _closeCount; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs index 0fe94fd229..42bd34b435 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs @@ -22,7 +22,7 @@ public sealed class SqlDataAdapter : DbDataAdapter, IDbDataAdapter, ICloneable private SqlCommandSet _commandSet; private int _updateBatchSize = 1; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal int ObjectID => _objectID; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index b7e8e83bba..09ac390078 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -16,7 +16,7 @@ public sealed class SqlTransaction : DbTransaction { private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs index 8717267d61..b25b9eaef1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs @@ -21,7 +21,7 @@ internal class TdsParserSessionPool private const int MaxInactiveCount = 10; // pick something, preferably small... - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private readonly TdsParser _parser; // parser that owns us diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs index f6f716c399..8723e77cda 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlFileStream.Windows.cs @@ -23,7 +23,7 @@ public sealed partial class SqlFileStream : System.IO.Stream // TransactionContext accessors as virtual methods. Doing so now on a sealed class // generates a compiler error (CS0549) - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); // from System.IO.FileStream implementation diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 74522ea5ff..19d00a3805 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -73,7 +73,7 @@ sealed private class TransactedConnectionPool DbConnectionPool _pool; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal TransactedConnectionPool(DbConnectionPool pool) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/SqlConnectionHelper.cs index c90c78e665..e1847db521 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/SqlConnectionHelper.cs @@ -25,7 +25,7 @@ public sealed partial class SqlConnection : DbConnection private DbConnectionInternal _innerConnection; private int _closeCount; // used to distinguish between different uses of this object, so we don't have to maintain a list of it's children - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 0168ace7d1..c369f64f1b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -294,7 +294,7 @@ private int RowNumber private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); //newly added member variables for Async modification, m = member variable to bcp diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index ec49c08a30..5998a22254 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -127,7 +127,7 @@ internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - TraceEvent(string.Format(message, args0.ToString())); + TraceEvent(string.Format(message, args0?.ToString())); } } @@ -145,7 +145,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0.ToString(), args1.ToString())); + Trace(string.Format(message, args0?.ToString(), args1?.ToString())); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 6a9e121e41..951353e7ae 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -34,7 +34,7 @@ namespace Microsoft.Data.SqlClient ] public sealed class SqlCommand : DbCommand, ICloneable { - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private string _commandText; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandSet.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandSet.cs index 81acaf4c38..66a92cf6bd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandSet.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommandSet.cs @@ -23,7 +23,7 @@ internal sealed class SqlCommandSet private SqlCommand _batchCommand; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private sealed class LocalCommand diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs index aa9f9f5781..d41c3823ae 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs @@ -28,7 +28,7 @@ public sealed class SqlDataAdapter : DbDataAdapter, IDbDataAdapter, ICloneable private SqlCommandSet _commandSet; private int _updateBatchSize = 1; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal int ObjectID diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index dc2f496ff7..9b7dc1ee75 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -72,7 +72,7 @@ internal class SharedState private FieldNameLookup _fieldNameLookup; private CommandBehavior _commandBehavior; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); // context diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs index 3698926c8d..7264a88d1c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -288,7 +288,7 @@ private static void InvokeCallback(object eventContextPair) private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal int ObjectID { get diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index 21be33e976..91a98920d2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -64,7 +64,7 @@ private class SqlConnectionContainer // ----------- private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal int ObjectID { get @@ -1455,7 +1455,7 @@ override public int GetHashCode() // ----------- private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal int ObjectID { get diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index bba7960aa8..2febbeed39 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -81,7 +81,7 @@ internal DependencyList(string commandHash) // ----------- private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal int ObjectID { get diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 69ce8dec81..6ce35ec499 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -15,7 +15,7 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlTransaction : DbTransaction { - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 54e707380b..c4b835c2e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -29,7 +29,7 @@ namespace Microsoft.Data.SqlClient // and surfacing objects to the user. sealed internal class TdsParser { - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); static Task completedTask; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs index 0063243702..a0f55550cc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs @@ -21,7 +21,7 @@ internal class TdsParserSessionPool private const int MaxInactiveCount = 10; // pick something, preferably small... - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private readonly TdsParser _parser; // parser that owns us diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 4c7ad34547..bb0a5e542f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -33,7 +33,7 @@ sealed internal class TdsParserStateObject // of very small open, query, close loops. private const long CheckConnectionWindow = 50000; - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal int ObjectID diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs index 5a107878b8..6e1c1beac9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs @@ -41,7 +41,7 @@ sealed internal class SqlInternalTransaction private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. - private static int _objectTypeCount; // Bid counter + private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal bool RestoreBrokenConnection { get; set; } From 1d8bb35b73f7aec55671db87106c1c4b1b7ee86f Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 20 Mar 2020 09:13:16 -0700 Subject: [PATCH 11/13] EventSource --- .../Microsoft/Data/SqlClient/SqlClientEventSource.cs | 10 +++++----- .../Microsoft/Data/SqlClient/SqlClientEventSource.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index b15841e0f3..bef2b2eb39 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -204,7 +204,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0?.ToString(), args1?.ToString())); + Trace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null")); } } @@ -213,7 +213,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1, T2 args { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0?.ToString(), args1?.ToString(), args2?.ToString())); + Trace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null", args2?.ToString() ?? "Null")); } } @@ -466,7 +466,7 @@ internal void CorrelationTraceEvent(string message, T0 args0) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0.ToString())); + CorrelationTrace(string.Format(message, args0?.ToString() ?? "Null")); } } @@ -475,7 +475,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 args1) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0, args1.ToString())); + CorrelationTrace(string.Format(message, args0, args1?.ToString() ?? "Null")); } } @@ -484,7 +484,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 arg { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); + CorrelationTrace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null", args2?.ToString() ?? "Null")); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index 5998a22254..9ed6d0b7bf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -127,7 +127,7 @@ internal void TraceEvent(string message, T0 args0) { if (Log.IsTraceEnabled()) { - TraceEvent(string.Format(message, args0?.ToString())); + TraceEvent(string.Format(message, args0?.ToString() ?? "Null")); } } @@ -145,7 +145,7 @@ internal void TraceEvent(string message, T0 args0, T1 args1) { if (Log.IsTraceEnabled()) { - Trace(string.Format(message, args0?.ToString(), args1?.ToString())); + Trace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null")); } } @@ -399,7 +399,7 @@ internal void CorrelationTraceEvent(string message, T0 args0) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0.ToString())); + CorrelationTrace(string.Format(message, args0?.ToString() ?? "Null")); } } @@ -408,7 +408,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 args1) { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString())); + CorrelationTrace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null")); } } @@ -417,7 +417,7 @@ internal void CorrelationTraceEvent(string message, T0 args0, T1 arg { if (Log.IsCorrelationEnabled()) { - CorrelationTrace(string.Format(message, args0.ToString(), args1.ToString(), args2.ToString())); + CorrelationTrace(string.Format(message, args0?.ToString() ?? "Null", args1?.ToString() ?? "Null", args2?.ToString() ?? "Null")); } } From 5e9accbd53bbb901a1f66f70287d91b600764a35 Mon Sep 17 00:00:00 2001 From: Javad Date: Fri, 20 Mar 2020 09:22:01 -0700 Subject: [PATCH 12/13] Update SNINpHandle.cs --- .../netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs index 99f9bf0654..17cc6c1932 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs @@ -337,7 +337,7 @@ public override uint EnableSsl(uint options) try { - _sslStream.AuthenticateAsClientAsync(_targetServer).GetAwaiter().GetResult(); + _sslStream.AuthenticateAsClient(_targetServer); _sslOverTdsStream.FinishHandshake(); } catch (AuthenticationException aue) From d3e2060de2b38f6f0a154e57731caaf144ab003e Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 20 Mar 2020 09:25:34 -0700 Subject: [PATCH 13/13] EventSource --- .../netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs index 99f9bf0654..1b86490fe5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs @@ -334,10 +334,9 @@ public override uint EnableSsl(uint options) try { _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - try { - _sslStream.AuthenticateAsClientAsync(_targetServer).GetAwaiter().GetResult(); + _sslStream.AuthenticateAsClient(_targetServer); _sslOverTdsStream.FinishHandshake(); } catch (AuthenticationException aue)