Skip to content

Commit 5572da6

Browse files
Port #3841 to 6.1: Introduce app context switch for setting MSF=true by default
1 parent 8291391 commit 5572da6

6 files changed

Lines changed: 105 additions & 6 deletions

File tree

src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionStringDefaults.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal static class DbConnectionStringDefaults
3939
internal const int MaxPoolSize = 100;
4040
internal const int MinPoolSize = 0;
4141
internal const bool MultipleActiveResultSets = false;
42-
internal const bool MultiSubnetFailover = false;
42+
internal static bool MultiSubnetFailover => LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault;
4343
internal const int PacketSize = 8000;
4444
internal const string Password = "";
4545
internal const bool PersistSecurityInfo = false;

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ private enum Tristate : byte
2222
internal const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
2323
internal const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
2424
private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
25+
private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
2526

2627
// this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
2728
private static Tristate s_legacyRowVersionNullBehavior;
@@ -32,6 +33,7 @@ private enum Tristate : byte
3233
private static Tristate s_legacyVarTimeZeroScaleBehaviour;
3334
private static Tristate s_useConnectionPoolV2;
3435
private static Tristate s_ignoreServerProvidedFailoverPartner;
36+
private static Tristate s_multiSubnetFailoverByDefault;
3537

3638

3739
#if NET
@@ -263,5 +265,30 @@ public static bool IgnoreServerProvidedFailoverPartner
263265
return s_ignoreServerProvidedFailoverPartner == Tristate.True;
264266
}
265267
}
268+
269+
/// <summary>
270+
/// When set to true, the default value for MultiSubnetFailover connection string property
271+
/// will be true instead of false. This enables parallel IP connection attempts for
272+
/// improved connection times in multi-subnet environments.
273+
/// This app context switch defaults to 'false'.
274+
/// </summary>
275+
public static bool EnableMultiSubnetFailoverByDefault
276+
{
277+
get
278+
{
279+
if (s_multiSubnetFailoverByDefault == Tristate.NotInitialized)
280+
{
281+
if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
282+
{
283+
s_multiSubnetFailoverByDefault = Tristate.True;
284+
}
285+
else
286+
{
287+
s_multiSubnetFailoverByDefault = Tristate.False;
288+
}
289+
}
290+
return s_multiSubnetFailoverByDefault == Tristate.True;
291+
}
292+
}
266293
}
267294
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal static class DEFAULT
4545
internal const bool MARS = DbConnectionStringDefaults.MultipleActiveResultSets;
4646
internal const int Max_Pool_Size = DbConnectionStringDefaults.MaxPoolSize;
4747
internal const int Min_Pool_Size = DbConnectionStringDefaults.MinPoolSize;
48-
internal const bool MultiSubnetFailover = DbConnectionStringDefaults.MultiSubnetFailover;
48+
internal static bool MultiSubnetFailover => DbConnectionStringDefaults.MultiSubnetFailover;
4949
internal const int Packet_Size = DbConnectionStringDefaults.PacketSize;
5050
internal const string Password = DbConnectionStringDefaults.Password;
5151
internal const bool Persist_Security_Info = DbConnectionStringDefaults.PersistSecurityInfo;

src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
3232
#if NETFRAMEWORK
3333
private readonly PropertyInfo _disableTnirByDefaultProperty;
3434
#endif
35+
private readonly PropertyInfo _enableMultiSubnetFailoverByDefaultProperty;
3536

3637
// These fields are used to capture the original switch values.
3738
private readonly FieldInfo _legacyRowVersionNullBehaviorField;
@@ -48,10 +49,13 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
4849
private readonly Tristate _useConnectionPoolV2Original;
4950
private readonly FieldInfo _ignoreServerProvidedFailoverPartnerField;
5051
private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
51-
#if NETFRAMEWORK
52+
#if NETFRAMEWORK
5253
private readonly FieldInfo _disableTnirByDefaultField;
5354
private readonly Tristate _disableTnirByDefaultOriginal;
54-
#endif
55+
#endif
56+
private readonly FieldInfo _multiSubnetFailoverByDefaultField;
57+
private readonly Tristate _multiSubnetFailoverByDefaultOriginal;
58+
5559

5660
#endregion
5761

@@ -139,6 +143,10 @@ void InitProperty(string name, out PropertyInfo property)
139143
out _disableTnirByDefaultProperty);
140144
#endif
141145

146+
InitProperty(
147+
"EnableMultiSubnetFailoverByDefault",
148+
out _enableMultiSubnetFailoverByDefaultProperty);
149+
142150
// A local helper to capture the original value of a switch.
143151
void InitField(string name, out FieldInfo field, out Tristate value)
144152
{
@@ -195,6 +203,11 @@ void InitField(string name, out FieldInfo field, out Tristate value)
195203
out _disableTnirByDefaultField,
196204
out _disableTnirByDefaultOriginal);
197205
#endif
206+
207+
InitField(
208+
"s_multiSubnetFailoverByDefault",
209+
out _multiSubnetFailoverByDefaultField,
210+
out _multiSubnetFailoverByDefaultOriginal);
198211
}
199212

200213
/// <summary>
@@ -255,6 +268,10 @@ void RestoreField(FieldInfo field, Tristate value)
255268
_disableTnirByDefaultOriginal);
256269
#endif
257270

271+
RestoreField(
272+
_multiSubnetFailoverByDefaultField,
273+
_multiSubnetFailoverByDefaultOriginal);
274+
258275
if (failedFields.Count > 0)
259276
{
260277
throw new Exception(
@@ -327,6 +344,11 @@ public bool DisableTnirByDefault
327344
}
328345
#endif
329346

347+
public bool EnableMultiSubnetFailoverByDefault
348+
{
349+
get => (bool)_enableMultiSubnetFailoverByDefaultProperty.GetValue(null);
350+
}
351+
330352
// These properties get or set the like-named underlying switch field value.
331353
//
332354
// They all fail the test if the value cannot be retrieved or set.
@@ -408,6 +430,12 @@ public Tristate DisableTnirByDefaultField
408430
}
409431
#endif
410432

433+
public Tristate EnableMultiSubnetFailoverByDefaultField
434+
{
435+
get => GetValue(_multiSubnetFailoverByDefaultField);
436+
set => SetValue(_multiSubnetFailoverByDefaultField, value);
437+
}
438+
411439
#endregion
412440

413441
#region Private Helpers

src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class LocalAppContextSwitchesTests
2020
#if NETFRAMEWORK
2121
[InlineData("DisableTnirByDefault", false)]
2222
#endif
23+
[InlineData("EnableMultiSubnetFailoverByDefault", false)]
2324
public void DefaultSwitchValue(string property, bool expectedDefaultValue)
2425
{
2526
var switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches");

src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionStringTest.cs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public SqlConnectionStringTest()
1717
_appContextSwitchHelper = new LocalAppContextSwitchesHelper();
1818
}
1919

20-
#if NETFRAMEWORK
20+
#if NETFRAMEWORK
2121
[Theory]
2222
[InlineData("test.database.windows.net", true, Tristate.True, true)]
2323
[InlineData("test.database.windows.net", false, Tristate.True, false)]
@@ -60,7 +60,50 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
6060
// Assert
6161
Assert.Equal(expectedValue, connectionString.TransparentNetworkIPResolution);
6262
}
63-
#endif
63+
#endif
64+
65+
/// <summary>
66+
/// Test MSF values when set through connection string and through app context switch.
67+
/// </summary>
68+
[Theory]
69+
[InlineData(true, Tristate.True, true)]
70+
[InlineData(false, Tristate.True, false)]
71+
[InlineData(null, Tristate.True, true)]
72+
[InlineData(true, Tristate.False, true)]
73+
[InlineData(false, Tristate.False, false)]
74+
[InlineData(null, Tristate.False, false)]
75+
[InlineData(null, Tristate.NotInitialized, false)]
76+
public void TestDefaultMultiSubnetFailover(bool? msfInConnString, Tristate msfEnabledAppContext, bool expectedValue)
77+
{
78+
_appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = msfEnabledAppContext;
79+
80+
SqlConnectionStringBuilder builder = new();
81+
if (msfInConnString.HasValue)
82+
{
83+
builder.MultiSubnetFailover = msfInConnString.Value;
84+
}
85+
SqlConnectionString connectionString = new(builder.ConnectionString);
86+
87+
Assert.Equal(expectedValue, connectionString.MultiSubnetFailover);
88+
}
89+
90+
/// <summary>
91+
/// Tests that MultiSubnetFailover=true cannot be used with FailoverPartner.
92+
/// </summary>
93+
//[Fact]
94+
//public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
95+
//{
96+
// _appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = Tristate.True;
97+
98+
// SqlConnectionStringBuilder builder = new()
99+
// {
100+
// DataSource = "server",
101+
// FailoverPartner = "partner",
102+
// InitialCatalog = "database"
103+
// };
104+
105+
// Assert.Throws<ArgumentException>(() => new SqlConnectionString(builder.ConnectionString));
106+
//}
64107

65108
public void Dispose()
66109
{

0 commit comments

Comments
 (0)