mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-22 01:25:44 -05:00
Improve performance of Create Firewall Rule code (#489)
* Multi-thread server lookup to increase perf for multiple subscriptions * Use parallel execution for listsubscriptions - Also refactored parallel execution code to be a bit more generic
This commit is contained in:
@@ -121,7 +121,7 @@ namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
IEnumerable<IAzureUserAccountSubscriptionContext> subscriptions = await GetSubscriptionsAsync(string.IsNullOrEmpty(serverName));
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
result = await AzureUtil.ExecuteGetAzureResourceAsParallel(null, subscriptions, serverName, cancellationToken,
|
||||
result = await AzureUtil.ExecuteGetAzureResourceAsParallel((object)null, subscriptions, serverName, cancellationToken,
|
||||
GetDatabaseForSubscriptionAsync);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
/// <summary>
|
||||
/// Returns a list of Azure sql databases for given subscription
|
||||
/// </summary>
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseForSubscriptionAsync(IAzureResourceManagementSession parentSession,
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseForSubscriptionAsync(object notRequired,
|
||||
IAzureUserAccountSubscriptionContext input, string serverName,
|
||||
CancellationToken cancellationToken, CancellationToken internalCancellationToken)
|
||||
{
|
||||
|
||||
@@ -10,25 +10,30 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
internal static class AzureUtil
|
||||
/// <summary>
|
||||
/// Utils class supporting parallel querying of Azure resources
|
||||
/// </summary>
|
||||
public static class AzureUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Execute an async action for each input in the a list of input in parallel.
|
||||
/// If any task fails, adds the exeption message to the response errors
|
||||
/// If cancellation token is set to cancel, returns empty response
|
||||
/// </summary>
|
||||
/// If cancellation token is set to cancel, returns empty response.
|
||||
/// Note: Will not throw if errors occur. Instead the caller should check for errors and either notify
|
||||
/// or rethrow as needed.
|
||||
/// </summary>
|
||||
/// <param name="session">Resource management session to use to call the resource manager</param>
|
||||
/// <param name="inputs">List of inputs</param>
|
||||
/// <param name="serverName">server name to filter the result</param>
|
||||
/// <param name="lookupKey">optional lookup key to filter the result</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <param name="asyncAction">Async action</param>
|
||||
/// <returns>ServiceResponse including the list of data and errors</returns>
|
||||
public static async Task<ServiceResponse<TResult>> ExecuteGetAzureResourceAsParallel<TInput, TResult>(
|
||||
IAzureResourceManagementSession session,
|
||||
public static async Task<ServiceResponse<TResult>> ExecuteGetAzureResourceAsParallel<TConfig, TInput, TResult>(
|
||||
TConfig config,
|
||||
IEnumerable<TInput> inputs,
|
||||
string serverName,
|
||||
string lookupKey,
|
||||
CancellationToken cancellationToken,
|
||||
Func<IAzureResourceManagementSession,
|
||||
Func<TConfig,
|
||||
TInput,
|
||||
string,
|
||||
CancellationToken,
|
||||
@@ -51,10 +56,10 @@ namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
var tasks = Enumerable.Range(0, inputList.Count())
|
||||
.Select(async i =>
|
||||
{
|
||||
ServiceResponse<TResult> result = await GetResult(session, inputList[i], serverName, cancellationToken,
|
||||
ServiceResponse<TResult> result = await GetResult(config, inputList[i], lookupKey, cancellationToken,
|
||||
cancellationTokenSource.Token, asyncAction);
|
||||
//server name is used to filter the result and if the data is already found not, we need to cancel the other tasks
|
||||
if (!string.IsNullOrEmpty(serverName) && result.Found)
|
||||
if (!string.IsNullOrEmpty(lookupKey) && result.Found)
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
@@ -82,13 +87,13 @@ namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
return new ServiceResponse<TResult>(mergedResult, mergedErrors);
|
||||
}
|
||||
|
||||
private static async Task<ServiceResponse<TResult>> GetResult<TInput, TResult>(
|
||||
IAzureResourceManagementSession session,
|
||||
private static async Task<ServiceResponse<TResult>> GetResult<TConfig, TInput, TResult>(
|
||||
TConfig config,
|
||||
TInput input,
|
||||
string serverName,
|
||||
string lookupKey,
|
||||
CancellationToken cancellationToken,
|
||||
CancellationToken internalCancellationToken,
|
||||
Func<IAzureResourceManagementSession,
|
||||
Func<TConfig,
|
||||
TInput,
|
||||
string,
|
||||
CancellationToken,
|
||||
@@ -102,7 +107,7 @@ namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
}
|
||||
try
|
||||
{
|
||||
return await asyncAction(session, input, serverName, cancellationToken, internalCancellationToken);
|
||||
return await asyncAction(config, input, lookupKey, cancellationToken, internalCancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Firewall
|
||||
@@ -73,7 +76,7 @@ namespace Microsoft.SqlTools.ResourceProvider.Core.Firewall
|
||||
IAzureAuthenticationManager authenticationManager = AuthenticationManager;
|
||||
|
||||
if (authenticationManager != null && !await authenticationManager.GetUserNeedsReauthenticationAsync())
|
||||
{
|
||||
{
|
||||
FirewallRuleResource firewallRuleResource = await FindAzureResourceAsync(serverName);
|
||||
firewallRuleResponse = await CreateFirewallRule(firewallRuleResource, startIpAddress, endIpAddress);
|
||||
}
|
||||
@@ -158,22 +161,22 @@ namespace Microsoft.SqlTools.ResourceProvider.Core.Firewall
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed);
|
||||
}
|
||||
|
||||
foreach (IAzureUserAccountSubscriptionContext subscription in subscriptions)
|
||||
ServiceResponse<FirewallRuleResource> response = await AzureUtil.ExecuteGetAzureResourceAsParallel((object)null,
|
||||
subscriptions, serverName, new CancellationToken(), TryFindAzureResourceForSubscriptionAsync);
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
using (IAzureResourceManagementSession session = await ResourceManager.CreateSessionAsync(subscription))
|
||||
if (response.Data != null && response.Data.Any())
|
||||
{
|
||||
IAzureSqlServerResource azureSqlServer = await FindAzureResourceForSubscriptionAsync(serverName, session);
|
||||
|
||||
if (azureSqlServer != null)
|
||||
{
|
||||
return new FirewallRuleResource()
|
||||
{
|
||||
SubscriptionContext = subscription,
|
||||
AzureResource = azureSqlServer
|
||||
};
|
||||
}
|
||||
return response.Data.First();
|
||||
}
|
||||
if (response.HasError)
|
||||
{
|
||||
var error = response.Errors.FirstOrDefault();
|
||||
throw new FirewallRuleException(error.Message, error);
|
||||
}
|
||||
}
|
||||
// Else throw as we couldn't find the resource as expected
|
||||
var currentUser = await AuthenticationManager.GetCurrentAccountAsync();
|
||||
|
||||
throw new FirewallRuleException(string.Format(CultureInfo.CurrentCulture, SR.AzureServerNotFound, serverName, currentUser != null ? currentUser.UniqueId : string.Empty));
|
||||
@@ -187,6 +190,34 @@ namespace Microsoft.SqlTools.ResourceProvider.Core.Firewall
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of Azure sql databases for given subscription
|
||||
/// </summary>
|
||||
private async Task<ServiceResponse<FirewallRuleResource>> TryFindAzureResourceForSubscriptionAsync(object notRequired,
|
||||
IAzureUserAccountSubscriptionContext input, string serverName,
|
||||
CancellationToken cancellationToken, CancellationToken internalCancellationToken)
|
||||
{
|
||||
ServiceResponse<FirewallRuleResource> result = null;
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
using (IAzureResourceManagementSession session = await ResourceManager.CreateSessionAsync(input))
|
||||
{
|
||||
IAzureSqlServerResource azureSqlServer = await FindAzureResourceForSubscriptionAsync(serverName, session);
|
||||
if (azureSqlServer != null)
|
||||
{
|
||||
result = new ServiceResponse<FirewallRuleResource>(new FirewallRuleResource()
|
||||
{
|
||||
SubscriptionContext = input,
|
||||
AzureResource = azureSqlServer
|
||||
}.SingleItemAsEnumerable());
|
||||
result.Found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result ?? new ServiceResponse<FirewallRuleResource>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a firewallRule exception based on give status code
|
||||
|
||||
Reference in New Issue
Block a user