From 997bf383e9ed801d2d0ecff9cca6aa201ef57119 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Wed, 3 May 2023 12:58:29 -0700 Subject: [PATCH] Add | Use Minimum Login Timeout as 1 sec in .NET Core and enable behavior by default (#2012) --- .../SqlClient/SqlInternalConnectionTds.cs | 12 ++- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 +- .../LocalAppContextSwitches.netfx.cs | 34 -------- .../SqlClient/SqlInternalConnectionTds.cs | 3 +- .../Data/SqlClient/LocalAppContextSwitches.cs | 79 +++++++++++++------ .../SQL/DataReaderTest/DataReaderTest.cs | 2 +- 6 files changed, 71 insertions(+), 62 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.netfx.cs 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 49c0883e3d..66631151c9 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 @@ -1263,7 +1263,14 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, if (!timeout.IsInfinite) { long t = timeout.MillisecondsRemaining / 1000; - if ((long)int.MaxValue > t) + if (t == 0 && LocalAppContextSwitches.UseMinimumLoginTimeout) + { + // Take 1 as the minimum value, since 0 is treated as an infinite timeout + // to allow 1 second more for login to complete, since it should take only a few milliseconds. + t = 1; + } + + if (int.MaxValue > t) { timeoutInSeconds = (int)t; } @@ -1279,7 +1286,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, login.language = _currentLanguage; if (!login.userInstance) - { // Do not send attachdbfilename or database to SSE primary instance + { + // Do not send attachdbfilename or database to SSE primary instance login.database = CurrentDatabase; login.attachDBFilename = ConnectionOptions.AttachDBFilename; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index bd8d67b6e2..4492274116 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -621,7 +621,6 @@ - @@ -735,4 +734,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.netfx.cs deleted file mode 100644 index 1ca55ab848..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.netfx.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Data.SqlClient -{ - internal static partial class LocalAppContextSwitches - { - internal const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin"; - private static bool _useMinimumLoginTimeout; - public static bool UseMinimumLoginTimeout - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out _useMinimumLoginTimeout) ? _useMinimumLoginTimeout : false; - } - } - - internal const string DisableTNIRByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString"; - private static bool _disableTNIRByDefault; - public static bool DisableTNIRByDefault - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return AppContext.TryGetSwitch(DisableTNIRByDefaultString, out _disableTNIRByDefault) ? _disableTNIRByDefault : false; - } - } - } -} 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 748483b49d..da623f874e 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 @@ -1530,10 +1530,11 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, if (t == 0 && LocalAppContextSwitches.UseMinimumLoginTimeout) { // Take 1 as the minimum value, since 0 is treated as an infinite timeout + // to allow 1 second more for login to complete, since it should take only a few milliseconds. t = 1; } - if ((long)Int32.MaxValue > t) + if (int.MaxValue > t) { timeoutInSeconds = (int)t; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs index ca96328361..3872b215ae 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Reflection; -using System.Runtime.CompilerServices; namespace Microsoft.Data.SqlClient { @@ -13,10 +11,12 @@ internal static partial class LocalAppContextSwitches internal const string MakeReadAsyncBlockingString = @"Switch.Microsoft.Data.SqlClient.MakeReadAsyncBlocking"; internal const string LegacyRowVersionNullString = @"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"; internal const string SuppressInsecureTLSWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning"; + internal const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin"; + private static bool? s_legacyRowVersionNullBehavior; + private static bool? s_suppressInsecureTLSWarning; private static bool s_makeReadAsyncBlocking; - private static bool? s_LegacyRowVersionNullBehavior; - private static bool? s_SuppressInsecureTLSWarning; + private static bool s_useMinimumLoginTimeout; #if !NETFRAMEWORK static LocalAppContextSwitches() @@ -34,46 +34,81 @@ static LocalAppContextSwitches() } #endif +#if NETFRAMEWORK + internal const string DisableTNIRByDefaultString = @"Switch.Microsoft.Data.SqlClient.DisableTNIRByDefaultInConnectionString"; + private static bool s_disableTNIRByDefault; + + /// + /// Transparent Network IP Resolution (TNIR) is a revision of the existing MultiSubnetFailover feature. + /// TNIR affects the connection sequence of the driver in the case where the first resolved IP of the hostname + /// doesn't respond and there are multiple IPs associated with the hostname. + /// + /// TNIR interacts with MultiSubnetFailover to provide the following three connection sequences: + /// 0: One IP is attempted, followed by all IPs in parallel + /// 1: All IPs are attempted in parallel + /// 2: All IPs are attempted one after another + /// + /// TransparentNetworkIPResolution is enabled by default. MultiSubnetFailover is disabled by default. + /// To disable TNIR, you can enable the app context switch. + /// + /// This app context switch defaults to 'false'. + /// + public static bool DisableTNIRByDefault + => AppContext.TryGetSwitch(DisableTNIRByDefaultString, out s_disableTNIRByDefault) && s_disableTNIRByDefault; +#endif + + /// + /// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower. + /// This warning can be suppressed by enabling this AppContext switch. + /// This app context switch defaults to 'false'. + /// public static bool SuppressInsecureTLSWarning { get { - if (s_SuppressInsecureTLSWarning is null) + if (s_suppressInsecureTLSWarning is null) { bool result; - result = AppContext.TryGetSwitch(SuppressInsecureTLSWarningString, out result) ? result : false; - s_SuppressInsecureTLSWarning = result; + result = AppContext.TryGetSwitch(SuppressInsecureTLSWarningString, out result) && result; + s_suppressInsecureTLSWarning = result; } - return s_SuppressInsecureTLSWarning.Value; - } - } - - public static bool MakeReadAsyncBlocking - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out s_makeReadAsyncBlocking) ? s_makeReadAsyncBlocking : false; + return s_suppressInsecureTLSWarning.Value; } } /// /// In System.Data.SqlClient and Microsoft.Data.SqlClient prior to 3.0.0 a field with type Timestamp/RowVersion /// would return an empty byte array. This switch contols whether to preserve that behaviour on newer versions - /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned + /// of Microsoft.Data.SqlClient, if this switch returns false an appropriate null value will be returned. + /// This app context switch defaults to 'false'. /// public static bool LegacyRowVersionNullBehavior { get { - if (s_LegacyRowVersionNullBehavior is null) + if (s_legacyRowVersionNullBehavior is null) { bool result; - result = AppContext.TryGetSwitch(LegacyRowVersionNullString, out result) ? result : false; - s_LegacyRowVersionNullBehavior = result; + result = AppContext.TryGetSwitch(LegacyRowVersionNullString, out result) && result; + s_legacyRowVersionNullBehavior = result; } - return s_LegacyRowVersionNullBehavior.Value; + return s_legacyRowVersionNullBehavior.Value; } } + + /// + /// When enabled, ReadAsync runs asynchronously and does not block the calling thread. + /// This app context switch defaults to 'false'. + /// + public static bool MakeReadAsyncBlocking + => AppContext.TryGetSwitch(MakeReadAsyncBlockingString, out s_makeReadAsyncBlocking) && s_makeReadAsyncBlocking; + + /// + /// Specifies minimum login timeout to be set to 1 second instead of 0 seconds, + /// to prevent a login attempt from waiting indefinitely. + /// This app context switch defaults to 'true'. + /// + public static bool UseMinimumLoginTimeout + => !AppContext.TryGetSwitch(UseMinimumLoginTimeoutString, out s_useMinimumLoginTimeout) || s_useMinimumLoginTimeout; } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index b8731ea672..f3bf37d2d4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -332,7 +332,7 @@ public static void CheckLegacyNullRowVersionIsEmptyArray() private static bool? SetLegacyRowVersionNullBehavior(bool? value) { Type switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); - FieldInfo switchField = switchesType.GetField("s_LegacyRowVersionNullBehavior", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + FieldInfo switchField = switchesType.GetField("s_legacyRowVersionNullBehavior", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); bool? originalValue = (bool?)switchField.GetValue(null); switchField.SetValue(null, value); return originalValue;