// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Microsoft.SqlTools.ResourceProvider.Core { /// /// Utils class supporting parallel querying of Azure resources /// public static class AzureUtil { /// /// 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. /// Note: Will not throw if errors occur. Instead the caller should check for errors and either notify /// or rethrow as needed. /// /// Resource management session to use to call the resource manager /// List of inputs /// optional lookup key to filter the result /// Cancellation token /// Async action /// ServiceResponse including the list of data and errors public static async Task> ExecuteGetAzureResourceAsParallel( TConfig config, IEnumerable inputs, string lookupKey, CancellationToken cancellationToken, Func>> asyncAction ) { List mergedResult = new List(); List mergedErrors = new List(); try { if (inputs == null) { return new ServiceResponse(mergedResult); } List inputList = inputs.ToList(); ServiceResponse[] resultList = new ServiceResponse[inputList.Count]; CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); var tasks = Enumerable.Range(0, inputList.Count()) .Select(async i => { ServiceResponse 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(lookupKey) && result.Found) { cancellationTokenSource.Cancel(); } resultList[i] = result; return result; } ); await Task.WhenAll(tasks); if (!cancellationToken.IsCancellationRequested) { foreach (ServiceResponse resultForEachInput in resultList) { mergedResult.AddRange(resultForEachInput.Data); mergedErrors.AddRange(resultForEachInput.Errors); } } } catch (Exception ex) { mergedErrors.Add(ex); return new ServiceResponse(mergedResult, mergedErrors); } return new ServiceResponse(mergedResult, mergedErrors); } private static async Task> GetResult( TConfig config, TInput input, string lookupKey, CancellationToken cancellationToken, CancellationToken internalCancellationToken, Func>> asyncAction ) { if (cancellationToken.IsCancellationRequested || internalCancellationToken.IsCancellationRequested) { return new ServiceResponse(); } try { return await asyncAction(config, input, lookupKey, cancellationToken, internalCancellationToken); } catch (Exception ex) { return new ServiceResponse(ex); } } } }