microsoft / azuredatastudio

Azure Data Studio is a data management and development tool with connectivity to popular cloud and on-premises databases. Azure Data Studio supports Windows, macOS, and Linux, with immediate capability to connect to Azure SQL and SQL Server. Browse the extension library for more database support options including MySQL, PostgreSQL, and MongoDB.
https://learn.microsoft.com/sql/azure-data-studio
MIT License
7.58k stars 906 forks source link

Connection errors in drop database initialize result in null ref exception #24199

Closed Charles-Gagnon closed 1 year ago

Charles-Gagnon commented 1 year ago

I saw this when opening the "Drop database" dialog, but I imagine it's a more general issue.

  1. Open dialog (in this case by selecting "Drop" on a database
  2. Have some connection error occur. In my case it was a "Connection Timeout Expired"
  3. User sees an error similar to this
An error occurred while opening the drop database dialog. Object reference not set to an instance of an object.

In the STS logs I see this.

23-08-22 13:34:25.8060333 pid:45888 tid:12 sqltools Error: 0 : Failed opening a SqlConnection: error:Connection Timeout Expired.  The timeout period elapsed during the post-login phase.  The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections.  The duration spent while attempting to connect to this server was - [Pre-Login] initialization=267; handshake=511; [Login] initialization=0; authentication=0; [Post-Login] complete=28635;  inner:The wait operation timed out. stacktrace:   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
   at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte& value)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   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)
   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.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlRetryLogicProvider.Execute[TResult](Object sender, Func`1 function)
   at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
   at Microsoft.SqlTools.ServiceLayer.Connection.ConnectionService.OpenSqlConnection(ConnectionInfo connInfo, String featureName) in /_/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs:line 1921
23-08-22 13:34:25.8219420 pid:45888 tid:12 sqltools Error: 0 : objectManagement/initializeView : Object reference not set to an instance of an object.
   at Microsoft.SqlServer.Management.Common.ConnectionSettings.InitFromSqlConnection(SqlConnection sc)
   at Microsoft.SqlServer.Management.Common.ConnectionManager..ctor(SqlConnection sqlConnectionObject, IRenewableToken accessToken)
   at Microsoft.SqlServer.Management.Common.ServerConnection..ctor(SqlConnection sqlConnection)
   at Microsoft.SqlTools.ServiceLayer.Connection.ConnectionService.OpenServerConnection(ConnectionInfo connInfo, String featureName) in /_/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs:line 1953
   at Microsoft.SqlTools.ServiceLayer.Management.CDataContainer.CreateDataContainer(ConnectionInfo connInfo, Boolean databaseExists, XmlDocument containerDoc) in /_/src/Microsoft.SqlTools.ServiceLayer/Management/Common/DataContainer.cs:line 1139
   at Microsoft.SqlTools.ServiceLayer.ObjectManagement.DatabaseHandler.CreateDatabaseDataContainer(String connectionUri, String objectURN, Boolean isNewDatabase, String databaseName) in /_/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs:line 492
   at Microsoft.SqlTools.ServiceLayer.ObjectManagement.DatabaseHandler.InitializeObjectView(InitializeViewRequestParams requestParams) in /_/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs:line 134
   at Microsoft.SqlTools.ServiceLayer.ObjectManagement.ObjectManagementService.HandleInitializeViewRequest(InitializeViewRequestParams requestParams, RequestContext`1 requestContext) in /_/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectManagementService.cs:line 92
   at Microsoft.SqlTools.Hosting.Protocol.MessageDispatcher.<>c__DisplayClass31_0`2.<<SetRequestHandler>b__0>d.MoveNext() in /_/src/Microsoft.SqlTools.Hosting/Hosting/Protocol/MessageDispatcher.cs:line 164

Two things of note:

  1. Earlier I see this error

23-08-22 13:34:12.4076024 pid:45888 tid:12 sqltools Verbose: 0 : eventTID:9468 SqlError.ctor | ERR | Info Number 40613, Error State 1, Error Class 20, Error Message 'Database 'new_db' on server 'chgagnon.database.windows.net' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of '{AEF25BF9-3295-413A-9D42-9178F6AB0E7F}'.', Procedure '', Line Number 0

It's not clear to me why this wasn't the error surfaced in the actual connection error. Maybe @cheenamalhotra can help investigate that.

  1. But regardless, the fact that we threw a null ref exception here should be fixed. Ideally the user should get the error saying what the connection error was back instead.
cheenamalhotra commented 1 year ago

It seems ConnectionService.OpenSqlConnection doesn't throw exception when it occurs, but simply logs it and returns null. This certainly looks like a big miss in capturing connection failures and sounds like a good opportunity to address unknown failures.

We can fix this to throw exception, but is going to be a huge change to ensure callers catch exception and send response accordingly. And we also would need to see how these responses are handled by ADS, if we send error/send failed result.

Charles-Gagnon commented 1 year ago

Agreed, would probably suggest that we start by making an opt-in parameter for whether it throws or not and then going through the calls one-by-one as time permits and convert them to handle that if so.

cheenamalhotra commented 1 year ago

It wasn't too bad once I started working on it, was able to handle exceptions just fine. Please review: https://github.com/microsoft/sqltoolsservice/pull/2190

cheenamalhotra commented 1 year ago

Closing as resolved in the latest insiders build.