mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-04 01:25:43 -05:00
3278 Kusto Unit Test Refactor (#1053)
* 3278 Moved functions related to Metadata from DataSourceFactory to MetadataFactory.cs * 3278 Refactored DataSourceFactory to not be static. Added IDataSourceFactory interface. * 3278 Refactored IKustoIntellisenseHelper.cs to not be static. Added IKustoIntellisenseHelper.cs interface. * 3278 Removed unused functions from Scripter and deleted ScripterCore.cs because it was unused. Refactored Scripter.cs methods to not be static and created IScripter.cs * 3278 Refactored datsasourceConnectionFactory in ConnectionService to use MEF through the InitializeService function * 3278 Refactored IAutoCompleteHelper to not be static and added IAutoCompleteHelper interface. * 3278 Removed unused classes DatabaseFullAccessException and FeatureWithFullDbAccess. Refactored ObjectExplorerService to use ImportingConstructor attribute. Removed commented out in KustoResultsReader. Removed unused functions from DatabaseLocksManager. Removed unused IDataSourceConnectionFactory from ConnectionService * 3278 Moved SqlConnectionOpener class to new file. Added new interfaces IConnectedBindingQueue.cs and ISqlConnectionOpener.cs Added sqlConnectionOpener to dependency in ConnectedBindingQueue. * 3278 Removed needsMetadata param from ConnectedBindingQueue. Added param to AddConnectionContext where it's used * 3278 Refactored ConnectedBindingQueue to use MEF. Refactored usages to run against interface. * 3278 Corrected ServiceHost namespace. Removed unused dependency to sqlToolsContext in LanguageService. Updated dependency in MetadataService and LanguageService to IProtocolEndpoint instead of ServiceHost * 3278 Added back NextResult function and summary to KustoResultsReader. Renamed instance to _instance in ConnectionService and DatabaseLocksManager to stay consistent. Changed OpenDataSourceConnection to private in ConnectionService * 3278 Converted helper methods back to static and removed backing interfaces * 3278 Reverted AdminService > InitializeService to use ServiceHost as param
This commit is contained in:
@@ -38,18 +38,13 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Singleton service instance
|
||||
/// </summary>
|
||||
private static readonly Lazy<ConnectionService> instance
|
||||
private static readonly Lazy<ConnectionService> _instance
|
||||
= new Lazy<ConnectionService>(() => new ConnectionService());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton service instance
|
||||
/// </summary>
|
||||
public static ConnectionService Instance => instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// The SQL connection factory object
|
||||
/// </summary>
|
||||
private IDataSourceConnectionFactory connectionFactory;
|
||||
public static ConnectionService Instance => _instance.Value;
|
||||
|
||||
private DatabaseLocksManager lockedDatabaseManager;
|
||||
|
||||
@@ -113,9 +108,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// </summary>
|
||||
public ConnectionService()
|
||||
{
|
||||
var defaultQueue = new ConnectedBindingQueue(needsMetadata: false);
|
||||
connectedQueues.AddOrUpdate("Default", defaultQueue, (key, old) => defaultQueue);
|
||||
this.LockedDatabaseManager.ConnectionService = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -181,25 +173,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Gets the SQL connection factory instance
|
||||
/// </summary>
|
||||
public IDataSourceConnectionFactory ConnectionFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.connectionFactory == null)
|
||||
{
|
||||
this.connectionFactory = new DataSourceConnectionFactory();
|
||||
}
|
||||
return this.connectionFactory;
|
||||
}
|
||||
|
||||
internal set { this.connectionFactory = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test constructor that injects dependency interfaces
|
||||
/// </summary>
|
||||
/// <param name="testFactory"></param>
|
||||
public ConnectionService(IDataSourceConnectionFactory testFactory) => this.connectionFactory = testFactory;
|
||||
private IDataSourceConnectionFactory _dataSourceConnectionFactory;
|
||||
|
||||
// Attempts to link a URI to an actively used connection for this URI
|
||||
public virtual bool TryFindConnection(string ownerUri, out ConnectionInfo connectionInfo) => this.OwnerToConnectionMap.TryGetValue(ownerUri, out connectionInfo);
|
||||
@@ -254,7 +228,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
bool connectionChanged = false;
|
||||
if (!OwnerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo))
|
||||
{
|
||||
connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||
connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||
}
|
||||
else if (IsConnectionChanged(connectionParams, connectionInfo))
|
||||
{
|
||||
@@ -267,7 +241,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
|
||||
if (connectionChanged)
|
||||
{
|
||||
connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||
connectionInfo = new ConnectionInfo(_dataSourceConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||
}
|
||||
|
||||
// Try to open a connection with the given ConnectParams
|
||||
@@ -362,11 +336,6 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsDefaultConnectionType(string connectionType)
|
||||
{
|
||||
return string.IsNullOrEmpty(connectionType) || ConnectionType.Default.Equals(connectionType, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private void DisconnectExistingConnectionIfNeeded(ConnectParams connectionParams, ConnectionInfo connectionInfo, bool disconnectAll)
|
||||
{
|
||||
// Resolve if it is an existing connection
|
||||
@@ -425,7 +394,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
|
||||
var reliableConnection = connection as ReliableDataSourceConnection;
|
||||
IDataSource dataSource = reliableConnection.GetUnderlyingConnection();
|
||||
DataSourceObjectMetadata clusterMetadata = DataSourceFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
|
||||
DataSourceObjectMetadata clusterMetadata = MetadataFactory.CreateClusterMetadata(connectionInfo.ConnectionDetails.ServerName);
|
||||
|
||||
DiagnosticsInfo clusterDiagnostics = dataSource.GetDiagnostics(clusterMetadata);
|
||||
ReliableConnectionHelper.ServerInfo serverInfo = DataSourceFactory.ConvertToServerinfoFormat(DataSourceType.Kusto, clusterDiagnostics);
|
||||
@@ -801,14 +770,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
ConnectionDetails connectionDetails = info.ConnectionDetails.Clone();
|
||||
|
||||
IDataSource dataSource = OpenDataSourceConnection(info);
|
||||
DataSourceObjectMetadata objectMetadata = DataSourceFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
|
||||
DataSourceObjectMetadata objectMetadata = MetadataFactory.CreateClusterMetadata(info.ConnectionDetails.ServerName);
|
||||
|
||||
ListDatabasesResponse response = new ListDatabasesResponse();
|
||||
|
||||
// Mainly used by "manage" dashboard
|
||||
if(listDatabasesParams.IncludeDetails.HasTrue()){
|
||||
IEnumerable<DataSourceObjectMetadata> databaseMetadataInfo = dataSource.GetChildObjects(objectMetadata, true);
|
||||
List<DatabaseInfo> metadata = DataSourceFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
|
||||
List<DatabaseInfo> metadata = MetadataFactory.ConvertToDatabaseInfo(databaseMetadataInfo);
|
||||
response.Databases = metadata.ToArray();
|
||||
|
||||
return response;
|
||||
@@ -819,9 +788,14 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
return response;
|
||||
}
|
||||
|
||||
public void InitializeService(IProtocolEndpoint serviceHost)
|
||||
public void InitializeService(IProtocolEndpoint serviceHost, IDataSourceConnectionFactory dataSourceConnectionFactory,
|
||||
IConnectedBindingQueue connectedBindingQueue)
|
||||
{
|
||||
this.ServiceHost = serviceHost;
|
||||
ServiceHost = serviceHost;
|
||||
_dataSourceConnectionFactory = dataSourceConnectionFactory;
|
||||
|
||||
connectedQueues.AddOrUpdate("Default", connectedBindingQueue, (key, old) => connectedBindingQueue);
|
||||
LockedDatabaseManager.ConnectionService = this;
|
||||
|
||||
// Register request and event handlers with the Service Host
|
||||
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
|
||||
@@ -1429,12 +1403,12 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// <param name="connInfo">The connection info to connect with</param>
|
||||
/// <param name="featureName">A plaintext string that will be included in the application name for the connection</param>
|
||||
/// <returns>A SqlConnection created with the given connection info</returns>
|
||||
internal static IDataSource OpenDataSourceConnection(ConnectionInfo connInfo, string featureName = null)
|
||||
private IDataSource OpenDataSourceConnection(ConnectionInfo connInfo, string featureName = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// generate connection string
|
||||
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
|
||||
string connectionString = BuildConnectionString(connInfo.ConnectionDetails);
|
||||
|
||||
// TODOKusto: Pass in type of DataSource needed to make this generic. Hard coded to Kusto right now.
|
||||
return DataSourceFactory.Create(DataSourceType.Kusto, connectionString, connInfo.ConnectionDetails.AzureAccountToken);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Data.Common;
|
||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using System.Composition;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
@@ -14,6 +14,7 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
/// The purpose of the factory is to make it easier to mock out the database
|
||||
/// in 'offline' unit test scenarios.
|
||||
/// </summary>
|
||||
[Export(typeof(IDataSourceConnectionFactory))]
|
||||
public class DataSourceConnectionFactory : IDataSourceConnectionFactory
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
{
|
||||
public class DatabaseFullAccessException: Exception
|
||||
{
|
||||
public DatabaseFullAccessException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public DatabaseFullAccessException(string message, Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public DatabaseFullAccessException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@@ -12,100 +11,23 @@ namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
{
|
||||
public class DatabaseLocksManager: IDisposable
|
||||
{
|
||||
internal DatabaseLocksManager(int waitToGetFullAccess)
|
||||
{
|
||||
this.waitToGetFullAccess = waitToGetFullAccess;
|
||||
}
|
||||
|
||||
private static DatabaseLocksManager instance = new DatabaseLocksManager(DefaultWaitToGetFullAccess);
|
||||
private static readonly DatabaseLocksManager _instance = new DatabaseLocksManager();
|
||||
|
||||
public static DatabaseLocksManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance;
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionService ConnectionService { get; set; }
|
||||
|
||||
private Dictionary<string, ManualResetEvent> databaseAccessEvents = new Dictionary<string, ManualResetEvent>();
|
||||
private object databaseAccessLock = new object();
|
||||
public const int DefaultWaitToGetFullAccess = 10000;
|
||||
public int waitToGetFullAccess = DefaultWaitToGetFullAccess;
|
||||
|
||||
private ManualResetEvent GetResetEvent(string serverName, string databaseName)
|
||||
{
|
||||
string key = GenerateKey(serverName, databaseName);
|
||||
ManualResetEvent resetEvent = null;
|
||||
lock (databaseAccessLock)
|
||||
{
|
||||
if (!databaseAccessEvents.TryGetValue(key, out resetEvent))
|
||||
{
|
||||
resetEvent = new ManualResetEvent(true);
|
||||
databaseAccessEvents.Add(key, resetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return resetEvent;
|
||||
}
|
||||
|
||||
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);
|
||||
if (resetEvent.WaitOne(this.waitToGetFullAccess))
|
||||
{
|
||||
resetEvent.Reset();
|
||||
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.CloseConnections(serverName, databaseName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DatabaseFullAccessException($"Waited more than {waitToGetFullAccess} milli seconds for others to release the lock");
|
||||
}
|
||||
*/
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.CloseConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public bool ReleaseAccess(string serverName, string databaseName)
|
||||
{
|
||||
/*
|
||||
ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
|
||||
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.OpenConnections(serverName, databaseName);
|
||||
}
|
||||
|
||||
resetEvent.Set();
|
||||
*/
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.OpenConnections(serverName, databaseName, DefaultWaitToGetFullAccess);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private string GenerateKey(string serverName, string databaseName)
|
||||
{
|
||||
return $"{serverName.ToLowerInvariant()}-{databaseName.ToLowerInvariant()}";
|
||||
}
|
||||
private readonly Dictionary<string, ManualResetEvent> _databaseAccessEvents = new Dictionary<string, ManualResetEvent>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var resetEvent in databaseAccessEvents)
|
||||
foreach (var resetEvent in _databaseAccessEvents)
|
||||
{
|
||||
resetEvent.Value.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Connection
|
||||
{
|
||||
/// <summary>
|
||||
/// Any operation that needs full access to databas should implement this interface.
|
||||
/// Make sure to call GainAccessToDatabase before the operation and ReleaseAccessToDatabase after
|
||||
/// </summary>
|
||||
public interface IFeatureWithFullDbAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Database Lock Manager
|
||||
/// </summary>
|
||||
DatabaseLocksManager LockedDatabaseManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure the feature has fill access to the database
|
||||
/// </summary>
|
||||
bool GainAccessToDatabase();
|
||||
|
||||
/// <summary>
|
||||
/// Release the access to db
|
||||
/// </summary>
|
||||
bool ReleaseAccessToDatabase();
|
||||
|
||||
/// <summary>
|
||||
/// Server name
|
||||
/// </summary>
|
||||
string ServerName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Database name
|
||||
/// </summary>
|
||||
string DatabaseName { get; }
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user