Skip to content

Commit deb8c31

Browse files
Introduce app context switch for setting MSF=true by default (#3841)
This commit Introduces app context switch for setting MSF=true in connection string by default. The app context switch is called `Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault` and it defaults to false.
1 parent 45a4937 commit deb8c31

File tree

5 files changed

+106
-11
lines changed

5 files changed

+106
-11
lines changed

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: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ private enum Tristate : byte
2727
private const string TruncateScaledDecimalString = @"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal";
2828
private const string IgnoreServerProvidedFailoverPartnerString = @"Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner";
2929
private const string EnableUserAgentString = @"Switch.Microsoft.Data.SqlClient.EnableUserAgent";
30+
private const string EnableMultiSubnetFailoverByDefaultString = @"Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault";
3031

3132
#if NET
3233
private const string GlobalizationInvariantModeString = @"System.Globalization.Invariant";
@@ -52,6 +53,7 @@ private enum Tristate : byte
5253
private static Tristate s_truncateScaledDecimal;
5354
private static Tristate s_ignoreServerProvidedFailoverPartner;
5455
private static Tristate s_enableUserAgent;
56+
private static Tristate s_multiSubnetFailoverByDefault;
5557

5658
#if NET
5759
private static Tristate s_globalizationInvariantMode;
@@ -511,6 +513,31 @@ public static bool DisableTnirByDefault
511513
return s_disableTnirByDefault == Tristate.True;
512514
}
513515
}
514-
#endif
516+
#endif
517+
518+
/// <summary>
519+
/// When set to true, the default value for MultiSubnetFailover connection string property
520+
/// will be true instead of false. This enables parallel IP connection attempts for
521+
/// improved connection times in multi-subnet environments.
522+
/// This app context switch defaults to 'false'.
523+
/// </summary>
524+
public static bool EnableMultiSubnetFailoverByDefault
525+
{
526+
get
527+
{
528+
if (s_multiSubnetFailoverByDefault == Tristate.NotInitialized)
529+
{
530+
if (AppContext.TryGetSwitch(EnableMultiSubnetFailoverByDefaultString, out bool returnedValue) && returnedValue)
531+
{
532+
s_multiSubnetFailoverByDefault = Tristate.True;
533+
}
534+
else
535+
{
536+
s_multiSubnetFailoverByDefault = Tristate.False;
537+
}
538+
}
539+
return s_multiSubnetFailoverByDefault == Tristate.True;
540+
}
541+
}
515542
}
516543
}

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

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
3333
private readonly PropertyInfo _truncateScaledDecimalProperty;
3434
private readonly PropertyInfo _ignoreServerProvidedFailoverPartner;
3535
private readonly PropertyInfo _enableUserAgent;
36-
37-
#if NET
36+
private readonly PropertyInfo _enableMultiSubnetFailoverByDefaultProperty;
37+
#if NET
3838
private readonly PropertyInfo _globalizationInvariantModeProperty;
3939
#endif
4040

@@ -69,8 +69,9 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable
6969
private readonly Tristate _ignoreServerProvidedFailoverPartnerOriginal;
7070
private readonly FieldInfo _enableUserAgentField;
7171
private readonly Tristate _enableUserAgentOriginal;
72-
73-
#if NET
72+
private readonly FieldInfo _multiSubnetFailoverByDefaultField;
73+
private readonly Tristate _multiSubnetFailoverByDefaultOriginal;
74+
#if NET
7475
private readonly FieldInfo _globalizationInvariantModeField;
7576
private readonly Tristate _globalizationInvariantModeOriginal;
7677
#endif
@@ -181,7 +182,11 @@ void InitProperty(string name, out PropertyInfo property)
181182
"EnableUserAgent",
182183
out _enableUserAgent);
183184

184-
#if NET
185+
InitProperty(
186+
"EnableMultiSubnetFailoverByDefault",
187+
out _enableMultiSubnetFailoverByDefaultProperty);
188+
189+
#if NET
185190
InitProperty(
186191
"GlobalizationInvariantMode",
187192
out _globalizationInvariantModeProperty);
@@ -269,7 +274,12 @@ void InitField(string name, out FieldInfo field, out Tristate value)
269274
out _enableUserAgentField,
270275
out _enableUserAgentOriginal);
271276

272-
#if NET
277+
InitField(
278+
"s_multiSubnetFailoverByDefault",
279+
out _multiSubnetFailoverByDefaultField,
280+
out _multiSubnetFailoverByDefaultOriginal);
281+
282+
#if NET
273283
InitField(
274284
"s_globalizationInvariantMode",
275285
out _globalizationInvariantModeField,
@@ -281,8 +291,8 @@ void InitField(string name, out FieldInfo field, out Tristate value)
281291
"s_useManagedNetworking",
282292
out _useManagedNetworkingField,
283293
out _useManagedNetworkingOriginal);
284-
#endif
285-
294+
#endif
295+
286296
#if NETFRAMEWORK
287297
InitField(
288298
"s_disableTnirByDefault",
@@ -359,6 +369,10 @@ void RestoreField(FieldInfo field, Tristate value)
359369
_enableUserAgentField,
360370
_enableUserAgentOriginal);
361371

372+
RestoreField(
373+
_multiSubnetFailoverByDefaultField,
374+
_multiSubnetFailoverByDefaultOriginal);
375+
362376
#if NET
363377
RestoreField(
364378
_globalizationInvariantModeField,
@@ -474,6 +488,11 @@ public bool EnableUserAgent
474488
get => (bool)_enableUserAgent.GetValue(null);
475489
}
476490

491+
public bool EnableMultiSubnetFailoverByDefault
492+
{
493+
get => (bool)_enableMultiSubnetFailoverByDefaultProperty.GetValue(null);
494+
}
495+
477496
#if NET
478497
/// <summary>
479498
/// Access the LocalAppContextSwitches.GlobalizationInvariantMode property.
@@ -608,7 +627,13 @@ public Tristate EnableUserAgentField
608627
set => SetValue(_enableUserAgentField, value);
609628
}
610629

611-
#if NET
630+
public Tristate EnableMultiSubnetFailoverByDefaultField
631+
{
632+
get => GetValue(_multiSubnetFailoverByDefaultField);
633+
set => SetValue(_multiSubnetFailoverByDefaultField, value);
634+
}
635+
636+
#if NET
612637
/// <summary>
613638
/// Get or set the LocalAppContextSwitches.GlobalizationInvariantMode switch value.
614639
/// </summary>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public void TestDefaultAppContextSwitchValues()
2727
Assert.True(LocalAppContextSwitches.UseCompatibilityAsyncBehaviour);
2828
Assert.False(LocalAppContextSwitches.UseConnectionPoolV2);
2929
Assert.False(LocalAppContextSwitches.TruncateScaledDecimal);
30+
Assert.False(LocalAppContextSwitches.EnableMultiSubnetFailoverByDefault);
3031
#if NETFRAMEWORK
3132
Assert.False(LocalAppContextSwitches.DisableTnirByDefault);
3233
Assert.False(LocalAppContextSwitches.UseManagedNetworking);

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,48 @@ public void TestDefaultTnir(string dataSource, bool? tnirEnabledInConnString, Tr
5858
Assert.Equal(expectedValue, connectionString.TransparentNetworkIPResolution);
5959
}
6060
#endif
61+
/// <summary>
62+
/// Test MSF values when set through connection string and through app context switch.
63+
/// </summary>
64+
[Theory]
65+
[InlineData(true, Tristate.True, true)]
66+
[InlineData(false, Tristate.True, false)]
67+
[InlineData(null, Tristate.True, true)]
68+
[InlineData(true, Tristate.False, true)]
69+
[InlineData(false, Tristate.False, false)]
70+
[InlineData(null, Tristate.False, false)]
71+
[InlineData(null, Tristate.NotInitialized, false)]
72+
public void TestDefaultMultiSubnetFailover(bool? msfInConnString, Tristate msfEnabledAppContext, bool expectedValue)
73+
{
74+
_appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = msfEnabledAppContext;
75+
76+
SqlConnectionStringBuilder builder = new();
77+
if (msfInConnString.HasValue)
78+
{
79+
builder.MultiSubnetFailover = msfInConnString.Value;
80+
}
81+
SqlConnectionString connectionString = new(builder.ConnectionString);
82+
83+
Assert.Equal(expectedValue, connectionString.MultiSubnetFailover);
84+
}
85+
86+
/// <summary>
87+
/// Tests that MultiSubnetFailover=true cannot be used with FailoverPartner.
88+
/// </summary>
89+
[Fact]
90+
public void TestMultiSubnetFailoverWithFailoverPartnerThrows()
91+
{
92+
_appContextSwitchHelper.EnableMultiSubnetFailoverByDefaultField = Tristate.True;
93+
94+
SqlConnectionStringBuilder builder = new()
95+
{
96+
DataSource = "server",
97+
FailoverPartner = "partner",
98+
InitialCatalog = "database"
99+
};
100+
101+
Assert.Throws<ArgumentException>(() => new SqlConnectionString(builder.ConnectionString));
102+
}
61103

62104
public void Dispose()
63105
{

0 commit comments

Comments
 (0)