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;