closing the default connections that are opned just for validating the connections. Added the feature to application name in the connection (#483)

This commit is contained in:
Leila Lali
2017-10-09 10:49:12 -07:00
committed by GitHub
parent a8f3503c00
commit fecf56bce2
12 changed files with 96 additions and 42 deletions

View File

@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
{ {
get get
{ {
return this.ownerToConnectionMap; return this.ownerToConnectionMap;
} }
} }
@@ -264,6 +264,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
return validationResults; return validationResults;
} }
TrySetConnectionType(connectionParams);
connectionParams.Connection.ApplicationName = GetApplicationNameWithFeature(connectionParams.Connection.ApplicationName, connectionParams.Type);
// If there is no ConnectionInfo in the map, create a new ConnectionInfo, // If there is no ConnectionInfo in the map, create a new ConnectionInfo,
// but wait until later when we are connected to add it to the map. // but wait until later when we are connected to add it to the map.
ConnectionInfo connectionInfo; ConnectionInfo connectionInfo;
@@ -305,16 +308,63 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
// Invoke callback notifications // Invoke callback notifications
InvokeOnConnectionActivities(connectionInfo, connectionParams); InvokeOnConnectionActivities(connectionInfo, connectionParams);
if(connectionParams.Type == ConnectionType.ObjectExplorer) TryCloseConnectionTemporaryConnection(connectionParams, connectionInfo);
return completeParams;
}
private void TryCloseConnectionTemporaryConnection(ConnectParams connectionParams, ConnectionInfo connectionInfo)
{
try
{ {
DbConnection connection; if (connectionParams.Type == ConnectionType.ObjectExplorer || connectionParams.Type == ConnectionType.Dashboard || connectionParams.Type == ConnectionType.ConnectionValidation)
if (connectionInfo.TryGetConnection(ConnectionType.ObjectExplorer, out connection))
{ {
// OE doesn't need to keep the connection open DbConnection connection;
connection.Close(); string type = connectionParams.Type;
if (connectionInfo.TryGetConnection(type, out connection))
{
// OE doesn't need to keep the connection open
connection.Close();
}
}
}
catch (Exception ex)
{
Logger.Write(LogLevel.Normal, "Failed to close temporary connections. error: " + ex.Message);
}
}
private static string GetApplicationNameWithFeature(string applicationName, string featureName)
{
string appNameWithFeature = applicationName;
if (!string.IsNullOrWhiteSpace(applicationName) && !string.IsNullOrWhiteSpace(featureName))
{
int index = applicationName.IndexOf('-');
string appName = applicationName;
if (index > 0)
{
appName = applicationName.Substring(0, index - 1);
}
appNameWithFeature = $"{appName}-{featureName}";
}
return appNameWithFeature;
}
private void TrySetConnectionType(ConnectParams connectionParams)
{
if (connectionParams != null && connectionParams.Type == ConnectionType.Default && !string.IsNullOrWhiteSpace(connectionParams.OwnerUri))
{
if (connectionParams.OwnerUri.ToLowerInvariant().StartsWith("dashboard://"))
{
connectionParams.Type = ConnectionType.Dashboard;
}
else if (connectionParams.OwnerUri.ToLowerInvariant().StartsWith("connection://"))
{
connectionParams.Type = ConnectionType.ConnectionValidation;
} }
} }
return completeParams;
} }
private bool IsConnectionChanged(ConnectParams connectionParams, ConnectionInfo connectionInfo) private bool IsConnectionChanged(ConnectParams connectionParams, ConnectionInfo connectionInfo)
@@ -1159,7 +1209,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
/// </summary> /// </summary>
private void InvokeOnConnectionActivities(ConnectionInfo connectionInfo, ConnectParams connectParams) private void InvokeOnConnectionActivities(ConnectionInfo connectionInfo, ConnectParams connectParams)
{ {
if (connectParams.Type != ConnectionType.Default) if (connectParams.Type != ConnectionType.Default && connectParams.Type != ConnectionType.ConnectionValidation)
{ {
return; return;
} }
@@ -1219,7 +1269,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
/// Note: we need to audit all uses of this method to determine why we're /// Note: we need to audit all uses of this method to determine why we're
/// bypassing normal ConnectionService connection management /// bypassing normal ConnectionService connection management
/// </summary> /// </summary>
internal static SqlConnection OpenSqlConnection(ConnectionInfo connInfo) internal static SqlConnection OpenSqlConnection(ConnectionInfo connInfo, string featureName = null)
{ {
try try
{ {
@@ -1234,6 +1284,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
connInfo.ConnectionDetails.PersistSecurityInfo = true; connInfo.ConnectionDetails.PersistSecurityInfo = true;
// turn off connection pool to avoid hold locks on server resources after calling SqlConnection Close method // turn off connection pool to avoid hold locks on server resources after calling SqlConnection Close method
connInfo.ConnectionDetails.Pooling = false; connInfo.ConnectionDetails.Pooling = false;
connInfo.ConnectionDetails.ApplicationName = GetApplicationNameWithFeature(connInfo.ConnectionDetails.ApplicationName, featureName);
// generate connection string // generate connection string
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails); string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);

View File

@@ -17,5 +17,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
public const string Query = "Query"; public const string Query = "Query";
public const string Edit = "Edit"; public const string Edit = "Edit";
public const string ObjectExplorer = "ObjectExplorer"; public const string ObjectExplorer = "ObjectExplorer";
public const string Dashboard = "Dashboard";
public const string ConnectionValidation = "ConnectionValidation";
} }
} }

View File

@@ -31,7 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
private Dictionary<string, ManualResetEvent> databaseAccessEvents = new Dictionary<string, ManualResetEvent>(); private Dictionary<string, ManualResetEvent> databaseAccessEvents = new Dictionary<string, ManualResetEvent>();
private object databaseAccessLock = new object(); private object databaseAccessLock = new object();
public const int DefaultWaitToGetFullAccess = 60000; public const int DefaultWaitToGetFullAccess = 10000;
public int waitToGetFullAccess = DefaultWaitToGetFullAccess; public int waitToGetFullAccess = DefaultWaitToGetFullAccess;
private ManualResetEvent GetResetEvent(string serverName, string databaseName) private ManualResetEvent GetResetEvent(string serverName, string databaseName)
@@ -53,6 +53,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
public bool GainFullAccessToDatabase(string serverName, string databaseName) public bool GainFullAccessToDatabase(string serverName, string databaseName)
{ {
/* /*
* TODO: add the lock so not two process can get full access at the same time
ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName); ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
if (resetEvent.WaitOne(this.waitToGetFullAccess)) if (resetEvent.WaitOne(this.waitToGetFullAccess))
{ {
@@ -71,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
*/ */
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues) foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
{ {
item.CloseConnections(serverName, databaseName); item.CloseConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
} }
return true; return true;
@@ -91,7 +92,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
*/ */
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues) foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
{ {
item.OpenConnections(serverName, databaseName); item.OpenConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
} }
return true; return true;

View File

@@ -144,7 +144,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{ {
using (DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true)) using (DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true))
{ {
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo)) using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Backup"))
{ {
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure) if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
{ {
@@ -307,7 +307,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
if (supported && connInfo != null) if (supported && connInfo != null)
{ {
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true);
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo); SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Backup");
// Connection gets discounnected when backup is done // Connection gets discounnected when backup is done
BackupOperation backupOperation = CreateBackupOperation(helper.DataContainer, sqlConn, backupParams.BackupInfo); BackupOperation backupOperation = CreateBackupOperation(helper.DataContainer, sqlConn, backupParams.BackupInfo);
@@ -344,7 +344,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
if (connInfo != null) if (connInfo != null)
{ {
using (sqlConn = ConnectionService.OpenSqlConnection(connInfo)) using (sqlConn = ConnectionService.OpenSqlConnection(connInfo, "DisasterRecovery"))
{ {
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure) if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
{ {

View File

@@ -19,9 +19,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
public interface IConnectedBindingQueue public interface IConnectedBindingQueue
{ {
void CloseConnections(string serverName, string databaseName); void CloseConnections(string serverName, string databaseName, int millisecondsTimeout);
void OpenConnections(string serverName, string databaseName); void OpenConnections(string serverName, string databaseName, int millisecondsTimeout);
string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false); string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false);
void Dispose(); void Dispose();
QueueItem QueueBindingOperation( QueueItem QueueBindingOperation(
string key, string key,
@@ -91,28 +91,28 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
public void CloseConnections(string serverName, string databaseName) public void CloseConnections(string serverName, string databaseName, int millisecondsTimeout)
{ {
string connectionKey = GetConnectionContextKey(serverName, databaseName); string connectionKey = GetConnectionContextKey(serverName, databaseName);
var contexts = GetBindingContexts(connectionKey); var contexts = GetBindingContexts(connectionKey);
foreach (var bindingContext in contexts) foreach (var bindingContext in contexts)
{ {
if (bindingContext.BindingLock.WaitOne(2000)) if (bindingContext.BindingLock.WaitOne(millisecondsTimeout))
{ {
bindingContext.ServerConnection.Disconnect(); bindingContext.ServerConnection.Disconnect();
} }
} }
} }
public void OpenConnections(string serverName, string databaseName) public void OpenConnections(string serverName, string databaseName, int millisecondsTimeout)
{ {
string connectionKey = GetConnectionContextKey(serverName, databaseName); string connectionKey = GetConnectionContextKey(serverName, databaseName);
var contexts = GetBindingContexts(connectionKey); var contexts = GetBindingContexts(connectionKey);
foreach (var bindingContext in contexts) foreach (var bindingContext in contexts)
{ {
if (bindingContext.BindingLock.WaitOne(2000)) if (bindingContext.BindingLock.WaitOne(millisecondsTimeout))
{ {
//bindingContext.ServerConnection.Connect(); bindingContext.ServerConnection.Connect();
} }
} }
} }
@@ -122,7 +122,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
/// <param name="connInfo">Connection info used to create binding context</param> /// <param name="connInfo">Connection info used to create binding context</param>
/// <param name="overwrite">Overwrite existing context</param> /// <param name="overwrite">Overwrite existing context</param>
public virtual string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false) public virtual string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false)
{ {
if (connInfo == null) if (connInfo == null)
{ {
@@ -150,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
try try
{ {
bindingContext.BindingLock.Reset(); bindingContext.BindingLock.Reset();
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo); SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, featureName);
// populate the binding context to work with the SMO metadata provider // populate the binding context to work with the SMO metadata provider
bindingContext.ServerConnection = new ServerConnection(sqlConn); bindingContext.ServerConnection = new ServerConnection(sqlConn);
@@ -166,7 +166,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout; bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
bindingContext.IsConnected = true; bindingContext.IsConnected = true;
} }
catch (Exception) catch (Exception)
{ {

View File

@@ -602,7 +602,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
try try
{ {
this.BindingQueue.AddConnectionContext(connInfo, overwrite: true); this.BindingQueue.AddConnectionContext(connInfo, featureName: "LanguageService", overwrite: true);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -849,7 +849,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
try try
{ {
scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(info); scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(info, "languageService");
scriptInfo.IsConnected = true; scriptInfo.IsConnected = true;
} }

View File

@@ -74,7 +74,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Metadata
var metadata = new List<ObjectMetadata>(); var metadata = new List<ObjectMetadata>();
if (connInfo != null) if (connInfo != null)
{ {
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo)) using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Metadata"))
{ {
ReadMetadata(sqlConn, metadata); ReadMetadata(sqlConn, metadata);
} }
@@ -129,7 +129,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Metadata
ColumnMetadata[] metadata = null; ColumnMetadata[] metadata = null;
if (connInfo != null) if (connInfo != null)
{ {
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo)) using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Metadata"))
{ {
TableMetadata table = new SmoMetadataFactory().GetObjectMetadata( TableMetadata table = new SmoMetadataFactory().GetObjectMetadata(
sqlConn, metadataParams.Schema, sqlConn, metadataParams.Schema,

View File

@@ -392,7 +392,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
try try
{ {
QueueItem queueItem = bindingQueue.QueueBindingOperation( QueueItem queueItem = bindingQueue.QueueBindingOperation(
key: bindingQueue.AddConnectionContext(session.ConnectionInfo), key: bindingQueue.AddConnectionContext(session.ConnectionInfo, "OE"),
bindingTimeout: PrepopulateBindTimeout, bindingTimeout: PrepopulateBindTimeout,
waitForLockTimeout: PrepopulateBindTimeout, waitForLockTimeout: PrepopulateBindTimeout,
bindOperation: (bindingContext, cancelToken) => bindOperation: (bindingContext, cancelToken) =>

View File

@@ -231,7 +231,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
private void RunSelectTask(ConnectionInfo connInfo, ScriptingParams parameters, RequestContext<ScriptingResult> requestContext) private void RunSelectTask(ConnectionInfo connInfo, ScriptingParams parameters, RequestContext<ScriptingResult> requestContext)
{ {
ConnectionServiceInstance.ConnectionQueue.QueueBindingOperation( ConnectionServiceInstance.ConnectionQueue.QueueBindingOperation(
key: ConnectionServiceInstance.ConnectionQueue.AddConnectionContext(connInfo), key: ConnectionServiceInstance.ConnectionQueue.AddConnectionContext(connInfo, "Scripting"),
bindingTimeout: ScriptingOperationTimeout, bindingTimeout: ScriptingOperationTimeout,
bindOperation: (bindingContext, cancelToken) => bindOperation: (bindingContext, cancelToken) =>
{ {

View File

@@ -188,7 +188,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.ObjectExplorer); connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.ObjectExplorer);
//Opening a connection to db to lock the db //Opening a connection to db to lock the db
connectionService.ConnectionQueue.AddConnectionContext(connectionResult.ConnectionInfo, true); connectionService.ConnectionQueue.AddConnectionContext(connectionResult.ConnectionInfo, "", true);
try try
{ {

View File

@@ -19,14 +19,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
public void GainFullAccessShouldDisconnectTheConnections() public void GainFullAccessShouldDisconnectTheConnections()
{ {
var connectionLock = new Mock<IConnectedBindingQueue>(); var connectionLock = new Mock<IConnectedBindingQueue>();
connectionLock.Setup(x => x.CloseConnections(server1, database1)); connectionLock.Setup(x => x.CloseConnections(server1, database1, DatabaseLocksManager.DefaultWaitToGetFullAccess));
using (DatabaseLocksManager databaseLocksManager = CreateManager()) using (DatabaseLocksManager databaseLocksManager = CreateManager())
{ {
databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object); databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object);
databaseLocksManager.GainFullAccessToDatabase(server1, database1); databaseLocksManager.GainFullAccessToDatabase(server1, database1);
connectionLock.Verify(x => x.CloseConnections(server1, database1)); connectionLock.Verify(x => x.CloseConnections(server1, database1, DatabaseLocksManager.DefaultWaitToGetFullAccess));
} }
} }
@@ -34,14 +34,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
public void ReleaseAccessShouldConnectTheConnections() public void ReleaseAccessShouldConnectTheConnections()
{ {
var connectionLock = new Mock<IConnectedBindingQueue>(); var connectionLock = new Mock<IConnectedBindingQueue>();
connectionLock.Setup(x => x.OpenConnections(server1, database1)); connectionLock.Setup(x => x.OpenConnections(server1, database1, DatabaseLocksManager.DefaultWaitToGetFullAccess));
using (DatabaseLocksManager databaseLocksManager = CreateManager()) using (DatabaseLocksManager databaseLocksManager = CreateManager())
{ {
databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object); databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object);
databaseLocksManager.ReleaseAccess(server1, database1); databaseLocksManager.ReleaseAccess(server1, database1);
connectionLock.Verify(x => x.OpenConnections(server1, database1)); connectionLock.Verify(x => x.OpenConnections(server1, database1, DatabaseLocksManager.DefaultWaitToGetFullAccess));
} }
} }
@@ -74,10 +74,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
DatabaseLocksManager databaseLocksManager = new DatabaseLocksManager(2000); DatabaseLocksManager databaseLocksManager = new DatabaseLocksManager(2000);
var connectionLock1 = new Mock<IConnectedBindingQueue>(); var connectionLock1 = new Mock<IConnectedBindingQueue>();
var connectionLock2 = new Mock<IConnectedBindingQueue>(); var connectionLock2 = new Mock<IConnectedBindingQueue>();
connectionLock1.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>())); connectionLock1.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()));
connectionLock2.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>())); connectionLock2.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()));
connectionLock1.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>())); connectionLock1.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()));
connectionLock2.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>())); connectionLock2.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()));
ConnectionService connectionService = new ConnectionService(); ConnectionService connectionService = new ConnectionService();
databaseLocksManager.ConnectionService = connectionService; databaseLocksManager.ConnectionService = connectionService;

View File

@@ -78,7 +78,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
// setup binding queue mock // setup binding queue mock
bindingQueue = new Mock<ConnectedBindingQueue>(); bindingQueue = new Mock<ConnectedBindingQueue>();
bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny<ConnectionInfo>(), It.IsAny<bool>())) bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny<ConnectionInfo>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(this.testConnectionKey); .Returns(this.testConnectionKey);
langService = new LanguageService(); langService = new LanguageService();