Azure AppService unable to connect to SQL database - sql-server

I have an Azure AppService written in C# that connects to a SQL Server database hosted outside of Azure using NHibernate. The connection string looks like this:
Data Source=tcp:SQL1234.3rdpartyserver.net;MultipleActiveResultSets=true;Initial Catalog=DB_SQL1234;User Id=****;Password=****;
Most of the time everything works fine, but occasionally my AppService loses the connection, and I am getting the following exception:
System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - A connection
attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.) ---> System.ComponentModel.Win32Exception: A connection
attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond --- End of inner exception stack trace
---
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at NHibernate.Connection.DriverConnectionProvider.GetConnection()
at NHibernate.Tool.hbm2ddl.SuppliedConnectionProviderConnectionHelper.Prepare()
at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.GetReservedWords(Dialect dialect, IConnectionHelper connectionHelper)
at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.Update(ISessionFactoryImplementor sessionFactory)
at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
at NHibernate.Cfg.Configuration.BuildSessionFactory()
at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() --- End of inner exception stack trace
This starts happening out of nowhere: I am not updating any connection string, not restarting my AppService, etc. The application only fails to connect to the database from Azure. If I launch the application locally, everything works as expected using the same connection string. Additionally, I can connect to the DB fine from SSMS.
Sometimes restarting my AppService helps, and the connectivity is restored after a restart. But sometimes it doesn't help.
I am suspecting the connection may be blocked by Azure's firewall, but I don't know how to check this. My application is using a B1 App Service plan, and I haven't created any custom firewalls, or load balancers in my Azure Portal. In fact, this AppService is the only resource that I currently have.
Any ideas what might be causing this, and hw to fix it?

You're most likely hitting SNAT exhaustion. Under Diagnose and Solve Problems Blade search for "TCP Connections" which will show you how many TCP connections your application is making. If there is a high number of connections to SQL (~128+) you're application is in a state that will most likely run into timeout exceptions.
App Services run in the 201-400 range for the multi-tenant app services so once you application makes 128 individual TCP connections to a specific destination IP/port you'll likely see these issues.
https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-connections
My recommendations would be in the following order:
Make sure to use connection pooling to limit the individual number of tcp connections. I've worked with customers who had 1000s of tcp connections and after using connection pooling for all their connections it dropped down to sub 100. The plan size does not make a difference for this particular issue. https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling
Use Regional VNET integration - SNAT ports do not come into play with VNET integration. You can then utilize service endpoints to route traffic to Azure SQL.
https://learn.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#regional-vnet-integration
Scale out the application to multiple instances - This helps spread the requests and outbound SQL connection across multiple VMs
Use an ASE - This is a much more expensive option but just wanted to
add it for answer completeness sake. The SNAT ports depend on the
number of instances you have as seen in the doc above

This is a very common and frequent problem and it happens due network instability. The way to solve is just wrap the code block using a retry pattern.
https://learn.microsoft.com/en-us/azure/architecture/patterns/retry

In your SQL Server firewall you can configure/allow the outbound IP addresses for your App Service. You can get these IPs from property section of your App Service or by using CLI.
Inbound and outbound IP addresses in Azure App Service

Related

Unable to connect SQL Server from some computer

I am using Microsoft SQL Server 2019 (OS: Windows Server 2019). It is running fine till yesterday. But suddenly I am not able to connect the database. I did not change anything, but still I check service status, ping that ip, port number, firewall, as well as firewall of local computer. Everything is ok, but unable to connect database (but also connect to server using remote desktop, no firewall issue). Interestingly some other computer can connect database normally (using SQL Server Management Studio 2018).
From yesterday, I try to find out the issue, but not getting any hints. If there is problem on server then no one will connect. If firewall block me (from both side), then I can not connect server.
Can any one please give some idea? Please...
Update:
I try to check port using: Test-NetConnection xxx.xxx.xxx.xxx -Port 1433 (using PowerShell). But result is fail (But it successfully connect IP).
Attaching Error (SQL Server Management Studio 2018)
Cannot connect to xxx.xxx.xxx.xxx.
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) (.Net SqlClient Data Provider)
For help, click: https://learn.microsoft.com/sql/relational-databases/errors-events/mssqlserver-53-database-engine-error
Error Number: 53
Severity: 20
State: 0
Program Location:
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at Microsoft.SqlServer.Management.SqlStudio.Explorer.ObjectExplorerService.ValidateConnection(UIConnectionInfo ci, IServerType server)
at Microsoft.SqlServer.Management.UI.ConnectionDlg.Connector.ConnectionThreadUser()
===================================
The network path was not found

Connection to SQL server fails after evry update of IIS hosted .net core app

We've deployed a .net core 3.1 app to IIS server. The app connects to a SQL server on another VM. After the initial deployment the app had no problems connecting to a database. However, every time we update the app database connections fail with the following exception:
Microsoft.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired. The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. This could be because the pre-login handshake failed or the server was unable to respond back in time. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=21010; handshake=2;
---> System.ComponentModel.Win32Exception (258): The wait operation timed out.
at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool, SqlAuthenticationProviderManager sqlAuthProviderManager)
at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at Microsoft.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()
Restarting the Web site or restarting/resetting IIS does not help.
The weird thing is that the connection starts working again an hour or longer after each update. This is very consistent.
Connecting to the database using SSMS on the same box is never affected by app updates - i.e. it always works.
When we update the app we first stop the site, copy new files into the app folder, and then start the site.
Deployment environment: Windows Server 2019 Standard, SQL Server 12.0.5223.6.
Has anyone run into a similar issue? Any suggestions on how to troubleshoot it?

After move to dotnet core 2.2 on Linux, connecting to our Azure SQL database often throws pre-login handshake error

Last week we migrated our production environment from .NET Framework 4.6.2 to .NET Core 2.2. Everything works as expected except we now often get the following error when we connect to the database:
A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: SSL Provider, error: 31 - Encryption(ssl/tls) handshake failed)
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
Our production environment used to run on a Windows 32 bit App Service plan in Azure. In this environment we almost never had any issues with pre-login handshakes.
Now suddenly after we've moved to Linux 64 bit containers, these errors start to pop up a few times a day.
I've searched the internet for a solution, but I cannot find it. Does anyone have an idea of what we should do?
This is our connection string:
Server=tcp:{server_url},1433;Data Source={server_url};Initial Catalog={database};Persist Security Info=False;User ID={your_username};Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Max Pool Size=500;
EDIT: Just to be clear: most of the connections to the SQL database succeed. We have many users on our server. However, the amount of unsuccessful connections has increased significantly.
Finally I found out what was going on.
We were leaking connections in another piece of code (and to another server). The responses were not disposed correctly. Since the allowed amount of open connections was smaller in Linux, we ran into this problem more quickly.
It's too bad the exception message is so ambiguous, but I can understand that it's difficult to raise a less ambiguous exception.
Anyway, for people looking for an answer to the same problem: do a "netstat -an" on your server when you run into this issue and check whether you're not looking connections.

Unable to connect to AWS database from SSMS

I have created a Microsoft SQL server database with Amazon RDS. I have created a security group with an inbound rule which includes my IP. I modified the DB instance to include that security group. I Copy and pasted the connection endpoint and pasted that into the server name field of Microsoft SQL Server connect window followed by a comma and port number 1433. I use SQL server authentication with the correct credentials. This is the Error I get when I try to connect, What am I missing?
Cannot connect to "copied name of endpoint...."us-east-1.rds.amazonaws.com,1433.
===================================
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - The wait operation timed out.) (.Net SqlClient Data Provider)
For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&EvtSrc=MSSQLServer&EvtID=258&LinkId=20476
Error Number: 258
Severity: 20
State: 0
Program Location:
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at Microsoft.SqlServer.Management.SqlStudio.Explorer.ObjectExplorerService.ValidateConnection(UIConnectionInfo ci, IServerType server)
at Microsoft.SqlServer.Management.UI.ConnectionDlg.Connector.ConnectionThreadUser()
===================================
The wait operation timed out
Solved,
I modified the DB instance via AWS portal to enable public access, then I added "TrustServerCertificate=True" to the connection string via SSMS connection options. I am able to connect now.

Connecting to SqlServer from .Net Core on Ubuntu/Visual Studio Code

I've got a console application that connects to a SQL Server DB (hosted on a dedicated win machine within the same network), written in .Net core (using Visual Studio Code) and it's working perfectly fine on Windows.
The very same code base running on Visual Studio Code on Ubuntu can't connect to the SQL Server instance saying it cannot find the server.
I can ping the SQL Server IP and get response from it.
Also I can connect to the SQLServer instance using telnet on the 1433 port.
Also, I have pulled the System.Data.Common and System.Data.SqlClient from nuget as recommended and set MultipleActiveResultSets=False in the connection string as recommended as well.
Just for completeness, this is the code that tries to connect:
var connection = new SqlConnection(myConnectionString);
connection.Open();
And this is the stacktrace:
Unhandled Exception: System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 35 - An internal exception was caught) ---> System.AggregateException: One or more errors occurred. (No such device or address) ---> System.Net.Internals.SocketExceptionFactory+ExtendedSocketException: No such device or address
at System.Net.Dns.HostResolutionEndHelper(IAsyncResult asyncResult)
at System.Net.Dns.EndGetHostAddresses(IAsyncResult asyncResult)
at System.Net.Dns.<>c.<GetHostAddressesAsync>b__14_1(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Data.SqlClient.SNI.SNITCPHandle.<ConnectAsync>d__22.MoveNext()
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait(TimeSpan timeout)
at System.Data.SqlClient.SNI.SNITCPHandle..ctor(String serverName, Int32 port, Int64 timerExpire, Object callbackObject, Boolean parallel)
--- End of inner exception stack trace ---
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
Any ideas?
Thanks
I found the problem and it was already raised as an issue in the coreclr repo. The problem is the SQLServer instance version I'm connecting to. It's 2008r2 SP1 and the .net core sql client just can't connect to it. Upgrade to SP3 fixes it.
More details here
Obviously, as the SQL server was present on your windows machine and it is not there on your Ubuntu machine. And it can't be installed on Ubuntu machine. You have to host it on a db server even for testing purposes, and point your connection string to that.
Edit
Try to make sure that your db server's firewall is not blocking access. Even if you have dual boot in same pc, they are still 2 different machines for computers.

Resources