/// <summary> /// Builds a mutable bulkhead isolation <see cref="Policy{TResult}" />, which limits the maximum concurrency of actions executed through the policy. Imposing a maximum concurrency limits the potential of governed actions, when faulting, to bring down the system. /// <para>When an execution would cause the number of actions executing concurrently through the policy to exceed <paramref name="maxParallelization" />, the policy allows a further <paramref name="maxQueuingActions" /> executions to queue, waiting for a concurrent execution slot. When an execution would cause the number of queuing actions to exceed <paramref name="maxQueuingActions" />, a <see cref="BulkheadRejectedException" /> is thrown.</para> /// </summary> /// <param name="maxParallelization">The maximum number of concurrent actions that may be executing through the policy.</param> /// <param name="maxQueuingActions">The maxmimum number of actions that may be queuing, waiting for an execution slot.</param> /// <param name="onBulkheadRejected">An action to call, if the MutableBulkhead rejects execution due to oversubscription.</param> /// <returns>The policy instance.</returns> /// <exception cref="System.ArgumentOutOfRangeException">maxParallelization;Value must be greater than zero.</exception> /// <exception cref="System.ArgumentOutOfRangeException">maxQueuingActions;Value must be greater than or equal to zero.</exception> /// <exception cref="System.ArgumentNullException">onBulkheadRejected</exception> public static MutableBulkheadPolicy <TResult> Create <TResult>(int maxParallelization, int maxQueuingActions, Action <Context> onBulkheadRejected) { if (maxParallelization <= 0) { throw new ArgumentOutOfRangeException(nameof(maxParallelization), "Value must be greater than zero."); } if (maxQueuingActions < 0) { throw new ArgumentOutOfRangeException(nameof(maxQueuingActions), "Value must be greater than or equal to zero."); } if (onBulkheadRejected == null) { throw new ArgumentNullException(nameof(onBulkheadRejected)); } SemaphoreSlimDynamic maxParallelizationSemaphore = new SemaphoreSlimDynamic(0, maxParallelization, int.MaxValue); var maxQueuingCompounded = maxQueuingActions <= int.MaxValue - maxParallelization ? maxQueuingActions + maxParallelization : int.MaxValue; SemaphoreSlimDynamic maxQueuedActionsSemaphore = new SemaphoreSlimDynamic(0, maxQueuingCompounded, int.MaxValue); return(new MutableBulkheadPolicy <TResult>( maxParallelization, maxQueuingActions, maxParallelizationSemaphore, maxQueuedActionsSemaphore, onBulkheadRejected )); }
internal static async Task <TResult> ImplementationAsync <TResult>( Func <Context, CancellationToken, Task <TResult> > action, Context context, Func <Context, Task> onBulkheadRejectedAsync, SemaphoreSlimDynamic maxParallelizationSemaphore, SemaphoreSlimDynamic maxQueuedActionsSemaphore, CancellationToken cancellationToken, bool continueOnCapturedContext) { if (!await maxQueuedActionsSemaphore.WaitAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(continueOnCapturedContext)) { await onBulkheadRejectedAsync(context).ConfigureAwait(continueOnCapturedContext); throw new BulkheadRejectedException(); } try { await maxParallelizationSemaphore.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext); try { return(await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)); } finally { maxParallelizationSemaphore.Release(); } } finally { maxQueuedActionsSemaphore.Release(); } }
internal static TResult Implementation <TResult>( Func <Context, CancellationToken, TResult> action, Context context, Action <Context> onBulkheadRejected, SemaphoreSlimDynamic maxParallelizationSemaphore, SemaphoreSlimDynamic maxQueuedActionsSemaphore, CancellationToken cancellationToken) { if (!maxQueuedActionsSemaphore.Wait(TimeSpan.Zero, cancellationToken)) { onBulkheadRejected(context); throw new BulkheadRejectedException(); } try { maxParallelizationSemaphore.Wait(cancellationToken); try { return(action(context, cancellationToken)); } finally { maxParallelizationSemaphore.Release(); } } finally { maxQueuedActionsSemaphore.Release(); } }
internal MutableBulkheadPolicy( int maxParallelization, int maxQueueingActions, SemaphoreSlimDynamic maxParallelizationSemaphore, SemaphoreSlimDynamic maxQueuedActionsSemaphore, Action <Context> onBulkheadRejected) { _maxParallelization = maxParallelization; _maxQueuedActions = maxQueueingActions; _maxParallelizationSemaphore = maxParallelizationSemaphore ?? throw new ArgumentNullException(nameof(maxParallelizationSemaphore)); _maxQueuedActionsSemaphore = maxQueuedActionsSemaphore ?? throw new ArgumentNullException(nameof(maxQueuedActionsSemaphore)); _onBulkheadRejected = onBulkheadRejected ?? throw new ArgumentNullException(nameof(onBulkheadRejected)); }
internal AsyncMutableBulkheadPolicy( int maxParallelization, int maxQueueingActions, SemaphoreSlimDynamic maxParallelizationSemaphore, SemaphoreSlimDynamic maxQueuedActionsSemaphore, Func <Context, Task> onBulkheadRejectedAsync) { _maxParallelization = maxParallelization; _maxQueuedActions = maxQueueingActions; _maxParallelizationSemaphore = maxParallelizationSemaphore; _maxQueuedActionsSemaphore = maxQueuedActionsSemaphore; _onBulkheadRejectedAsync = onBulkheadRejectedAsync ?? throw new ArgumentNullException(nameof(onBulkheadRejectedAsync)); }