From c4879f0962875f0824edd1aac903f4e2c4d6215d Mon Sep 17 00:00:00 2001 From: David Engel Date: Thu, 26 May 2022 10:15:42 -0700 Subject: [PATCH] Automatically adjust the default ConnectRetryCount if targeting OnDemand endpoint --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 10 +++++-- .../Microsoft/Data/SqlClient/SqlConnection.cs | 10 +++++-- .../src/Microsoft/Data/Common/AdapterUtil.cs | 19 ++++++++++-- .../FunctionalTests/SqlConnectionTest.cs | 30 +++++++++++++++++++ 4 files changed, 62 insertions(+), 7 deletions(-) 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 bcb9378643..08f32ef0bf 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 @@ -412,9 +412,15 @@ private void CacheConnectionStringProperties() if (connString != null) { _connectRetryCount = connString.ConnectRetryCount; + // For Azure Synapse ondemand connections, set _connectRetryCount to 5 instead of 1 to greatly improve recovery + // success rate. Note: Synapse should be detected first as it could be detected as a regular Azure SQL DB endpoint. + if (_connectRetryCount == 1 && ADP.IsAzureSynapseOnDemandEndpoint(connString.DataSource)) + { + _connectRetryCount = 5; + } // For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery - // success rate - if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) + // success rate + else if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) { _connectRetryCount = 2; } 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 2172fa6151..ae1c8d50db 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 @@ -432,9 +432,15 @@ private void CacheConnectionStringProperties() if (connString != null) { _connectRetryCount = connString.ConnectRetryCount; + // For Azure Synapse ondemand connections, set _connectRetryCount to 5 instead of 1 to greatly improve recovery + // success rate. Note: Synapse should be detected first as it could be detected as a regular Azure SQL DB endpoint. + if (_connectRetryCount == 1 && ADP.IsAzureSynapseOnDemandEndpoint(connString.DataSource)) + { + _connectRetryCount = 5; + } // For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery - // success rate - if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) + // success rate + else if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) { _connectRetryCount = 2; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index d7f6ce7cf0..290bed05c5 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -686,13 +686,26 @@ internal static Version GetAssemblyVersion() } + private const string ONDEMAND_PREFIX = "-ondemand"; + private const string AZURE_SYNAPSE = "-ondemand.sql.azuresynapse."; + + internal static bool IsAzureSynapseOnDemandEndpoint(string dataSource) + { + return IsEndpoint(dataSource, ONDEMAND_PREFIX) || dataSource.Contains(AZURE_SYNAPSE); + } + internal static readonly string[] s_azureSqlServerEndpoints = { StringsHelper.GetString(Strings.AZURESQL_GenericEndpoint), StringsHelper.GetString(Strings.AZURESQL_GermanEndpoint), StringsHelper.GetString(Strings.AZURESQL_UsGovEndpoint), StringsHelper.GetString(Strings.AZURESQL_ChinaEndpoint)}; - // This method assumes dataSource parameter is in TCP connection string format. internal static bool IsAzureSqlServerEndpoint(string dataSource) + { + return IsEndpoint(dataSource, null); + } + + // This method assumes dataSource parameter is in TCP connection string format. + private static bool IsEndpoint(string dataSource, string prefix) { int length = dataSource.Length; // remove server port @@ -715,10 +728,10 @@ internal static bool IsAzureSqlServerEndpoint(string dataSource) length -= 1; } - // check if servername end with any azure endpoints + // check if servername ends with any endpoints for (int index = 0; index < s_azureSqlServerEndpoints.Length; index++) { - string endpoint = s_azureSqlServerEndpoints[index]; + string endpoint = string.IsNullOrEmpty(prefix) ? s_azureSqlServerEndpoints[index] : prefix + s_azureSqlServerEndpoints[index]; if (length > endpoint.Length) { if (string.Compare(dataSource, length - endpoint.Length, endpoint, 0, endpoint.Length, StringComparison.OrdinalIgnoreCase) == 0) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs index 888abca555..af8c76878a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs @@ -6,6 +6,7 @@ using System.Data; using System.Collections.Generic; using Xunit; +using System.Reflection; namespace Microsoft.Data.SqlClient.Tests { @@ -1031,5 +1032,34 @@ public void ConnectionString_IPAddressPreference_Invalid(string value) Assert.Contains("'ip address preference'", ex.Message, StringComparison.OrdinalIgnoreCase); Assert.Null(ex.ParamName); } + + [Fact] + public void ConnectionRetryForNonAzureEndpoints() + { + SqlConnection cn = new SqlConnection("Data Source = someserver"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(1, (int)field.GetValue(cn)); + } + + [Fact] + public void ConnectionRetryForAzureDbEndpoints() + { + SqlConnection cn = new SqlConnection("Data Source = someserver.database.windows.net"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(2, (int)field.GetValue(cn)); + } + + [Theory] + [InlineData("myserver-ondemand.sql.azuresynapse.net")] + [InlineData("someserver-ondemand.database.windows.net")] + public void ConnectionRetryForAzureOnDemandEndpoints(string serverName) + { + SqlConnection cn = new SqlConnection($"Data Source = {serverName}"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(5, (int)field.GetValue(cn)); + } } }