diff --git a/src/Polly.Core/Registry/ResiliencePipelineRegistry.cs b/src/Polly.Core/Registry/ResiliencePipelineRegistry.cs index f8a8bff85ae..9fbd0bd3503 100644 --- a/src/Polly.Core/Registry/ResiliencePipelineRegistry.cs +++ b/src/Polly.Core/Registry/ResiliencePipelineRegistry.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using Polly.Telemetry; +using Polly.Utils.Pipeline; namespace Polly.Registry; @@ -288,7 +289,7 @@ private static PipelineComponent CreatePipelineComponent( return pipeline; } - return PipelineComponent.CreateReloadable(pipeline, context.ReloadTokenProducer(), () => factory().BuildPipelineComponent(), telemetry); + return PipelineComponentFactory.CreateReloadable(pipeline, context.ReloadTokenProducer(), () => factory().BuildPipelineComponent(), telemetry); } private GenericRegistry GetGenericRegistry() diff --git a/src/Polly.Core/ResiliencePipeline.Async.cs b/src/Polly.Core/ResiliencePipeline.Async.cs index 6e078a44b4c..b556446d4d7 100644 --- a/src/Polly.Core/ResiliencePipeline.Async.cs +++ b/src/Polly.Core/ResiliencePipeline.Async.cs @@ -24,7 +24,7 @@ public async ValueTask ExecuteAsync( InitializeAsyncContext(context); - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -59,7 +59,7 @@ public async ValueTask ExecuteAsync( InitializeAsyncContext(context); - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -98,7 +98,7 @@ public async ValueTask ExecuteAsync( try { - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -139,7 +139,7 @@ public async ValueTask ExecuteAsync( try { - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try diff --git a/src/Polly.Core/ResiliencePipeline.AsyncT.cs b/src/Polly.Core/ResiliencePipeline.AsyncT.cs index 2fda3b36e2d..f12a7521b6c 100644 --- a/src/Polly.Core/ResiliencePipeline.AsyncT.cs +++ b/src/Polly.Core/ResiliencePipeline.AsyncT.cs @@ -29,7 +29,7 @@ public ValueTask> ExecuteOutcomeAsync( InitializeAsyncContext(context); - return ExecuteCore(callback, context, state); + return Component.ExecuteCore(callback, context, state); } /// @@ -52,7 +52,7 @@ public async ValueTask ExecuteAsync( InitializeAsyncContext(context); - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -87,7 +87,7 @@ public async ValueTask ExecuteAsync( InitializeAsyncContext(context); - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -126,7 +126,7 @@ public async ValueTask ExecuteAsync( try { - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try @@ -167,7 +167,7 @@ public async ValueTask ExecuteAsync( try { - var outcome = await ExecuteCore( + var outcome = await Component.ExecuteCore( static async (context, state) => { try diff --git a/src/Polly.Core/ResiliencePipeline.Sync.cs b/src/Polly.Core/ResiliencePipeline.Sync.cs index 909570f218b..32cb19ad501 100644 --- a/src/Polly.Core/ResiliencePipeline.Sync.cs +++ b/src/Polly.Core/ResiliencePipeline.Sync.cs @@ -23,7 +23,7 @@ public void Execute( InitializeSyncContext(context); - ExecuteCoreSync( + Component.ExecuteCoreSync( static (context, state) => { try @@ -55,7 +55,7 @@ public void Execute( InitializeSyncContext(context); - ExecuteCoreSync( + Component.ExecuteCoreSync( static (context, state) => { try @@ -91,7 +91,7 @@ public void Execute( try { - ExecuteCoreSync( + Component.ExecuteCoreSync( static (context, state) => { try @@ -129,7 +129,7 @@ public void Execute( try { - ExecuteCoreSync( + Component.ExecuteCoreSync( static (context, state) => { try @@ -168,7 +168,7 @@ public void Execute( try { - ExecuteCoreSync( + Component.ExecuteCoreSync( static (_, state) => { try @@ -203,7 +203,7 @@ public void Execute(Action callback) try { - ExecuteCoreSync( + Component.ExecuteCoreSync( static (_, state) => { try diff --git a/src/Polly.Core/ResiliencePipeline.SyncT.cs b/src/Polly.Core/ResiliencePipeline.SyncT.cs index 7c197e921e5..844f42d802e 100644 --- a/src/Polly.Core/ResiliencePipeline.SyncT.cs +++ b/src/Polly.Core/ResiliencePipeline.SyncT.cs @@ -25,7 +25,7 @@ public TResult Execute( InitializeSyncContext(context); - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (context, state) => { try @@ -59,7 +59,7 @@ public TResult Execute( InitializeSyncContext(context); - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (context, state) => { try @@ -94,7 +94,7 @@ public TResult Execute( try { - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (context, state) => { try @@ -130,7 +130,7 @@ public TResult Execute(Func callback) try { - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (_, state) => { try @@ -168,7 +168,7 @@ public TResult Execute(Func callback, TState s try { - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (_, state) => { try @@ -210,7 +210,7 @@ public TResult Execute( try { - return ExecuteCoreSync( + return Component.ExecuteCoreSync( static (context, state) => { try diff --git a/src/Polly.Core/ResiliencePipeline.cs b/src/Polly.Core/ResiliencePipeline.cs index 2384014c8c0..b6535fbf63b 100644 --- a/src/Polly.Core/ResiliencePipeline.cs +++ b/src/Polly.Core/ResiliencePipeline.cs @@ -1,3 +1,5 @@ +using Polly.Utils.Pipeline; + namespace Polly; /// @@ -25,25 +27,4 @@ internal ResiliencePipeline(PipelineComponent component, DisposeBehavior dispose internal PipelineComponent Component { get; } internal ComponentDisposeHelper DisposeHelper { get; } - - internal ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) => Component.ExecuteCore(callback, context, state); - - private Outcome ExecuteCoreSync( - Func> callback, - ResilienceContext context, - TState state) - { - return ExecuteCore( - static (context, state) => - { - var result = state.callback(context, state.state); - - return new ValueTask>(result); - }, - context, - (callback, state)).GetResult(); - } } diff --git a/src/Polly.Core/ResiliencePipelineBuilderBase.cs b/src/Polly.Core/ResiliencePipelineBuilderBase.cs index d6e94f8e2f7..738e6df5485 100644 --- a/src/Polly.Core/ResiliencePipelineBuilderBase.cs +++ b/src/Polly.Core/ResiliencePipelineBuilderBase.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using Polly.Telemetry; +using Polly.Utils.Pipeline; namespace Polly; @@ -123,7 +124,7 @@ internal PipelineComponent BuildPipelineComponent() throw new InvalidOperationException("The resilience pipeline must contain unique resilience strategies."); } - return PipelineComponent.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider); + return PipelineComponentFactory.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider); } private PipelineComponent CreateComponent(Entry entry) diff --git a/src/Polly.Core/ResiliencePipelineBuilderExtensions.cs b/src/Polly.Core/ResiliencePipelineBuilderExtensions.cs index 93ffaf5a946..513d7a03d76 100644 --- a/src/Polly.Core/ResiliencePipelineBuilderExtensions.cs +++ b/src/Polly.Core/ResiliencePipelineBuilderExtensions.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using Polly.Utils.Pipeline; namespace Polly; @@ -27,7 +28,7 @@ public static TBuilder AddPipeline(this TBuilder builder, ResiliencePi Guard.NotNull(builder); Guard.NotNull(pipeline); - builder.AddPipelineComponent(_ => PipelineComponent.FromPipeline(pipeline), EmptyOptions.Instance); + builder.AddPipelineComponent(_ => PipelineComponentFactory.FromPipeline(pipeline), EmptyOptions.Instance); return builder; } @@ -49,7 +50,7 @@ public static ResiliencePipelineBuilder AddPipeline(this Resil Guard.NotNull(builder); Guard.NotNull(pipeline); - builder.AddPipelineComponent(_ => PipelineComponent.FromPipeline(pipeline), EmptyOptions.Instance); + builder.AddPipelineComponent(_ => PipelineComponentFactory.FromPipeline(pipeline), EmptyOptions.Instance); return builder; } @@ -72,7 +73,7 @@ public static TBuilder AddStrategy(this TBuilder builder, Func PipelineComponent.FromStrategy(factory(context)), options); + builder.AddPipelineComponent(context => PipelineComponentFactory.FromStrategy(factory(context)), options); return builder; } @@ -95,7 +96,7 @@ public static ResiliencePipelineBuilder AddStrategy( Guard.NotNull(factory); Guard.NotNull(options); - builder.AddPipelineComponent(context => PipelineComponent.FromStrategy(factory(context)), options); + builder.AddPipelineComponent(context => PipelineComponentFactory.FromStrategy(factory(context)), options); return builder; } @@ -119,7 +120,7 @@ public static ResiliencePipelineBuilder AddStrategy( Guard.NotNull(factory); Guard.NotNull(options); - builder.AddPipelineComponent(context => PipelineComponent.FromStrategy(factory(context)), options); + builder.AddPipelineComponent(context => PipelineComponentFactory.FromStrategy(factory(context)), options); return builder; } diff --git a/src/Polly.Core/ResiliencePipelineT.cs b/src/Polly.Core/ResiliencePipelineT.cs index d17030a8718..5b96106ec64 100644 --- a/src/Polly.Core/ResiliencePipelineT.cs +++ b/src/Polly.Core/ResiliencePipelineT.cs @@ -1,3 +1,5 @@ +using Polly.Utils.Pipeline; + namespace Polly; /// diff --git a/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs new file mode 100644 index 00000000000..0c4b7b3ce44 --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs @@ -0,0 +1,37 @@ +namespace Polly.Utils.Pipeline; + +[DebuggerDisplay("{Strategy}")] +internal sealed class BridgeComponent : BridgeComponentBase +{ + public BridgeComponent(ResilienceStrategy strategy) + : base(strategy) => Strategy = strategy; + + public ResilienceStrategy Strategy { get; } + + internal override ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) + { + // Check if we can cast directly, thus saving some cycles and improving the performance + if (callback is Func>> casted) + { + return TaskHelper.ConvertValueTask( + Strategy.ExecuteCore(casted, context, state), + context); + } + else + { + var valueTask = Strategy.ExecuteCore( + static async (context, state) => + { + var outcome = await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext); + return outcome.AsOutcome(); + }, + context, + (callback, state)); + + return TaskHelper.ConvertValueTask(valueTask, context); + } + } +} diff --git a/src/Polly.Core/Utils/Pipeline/BridgeComponent.cs b/src/Polly.Core/Utils/Pipeline/BridgeComponent.cs new file mode 100644 index 00000000000..80fc5ef8f4f --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/BridgeComponent.cs @@ -0,0 +1,15 @@ +namespace Polly.Utils.Pipeline; + +[DebuggerDisplay("{Strategy}")] +internal sealed class BridgeComponent : BridgeComponentBase +{ + public BridgeComponent(ResilienceStrategy strategy) + : base(strategy) => Strategy = strategy; + + public ResilienceStrategy Strategy { get; } + + internal override ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) => Strategy.ExecuteCore(callback, context, state); +} diff --git a/src/Polly.Core/Utils/Pipeline/BridgeComponentBase.cs b/src/Polly.Core/Utils/Pipeline/BridgeComponentBase.cs new file mode 100644 index 00000000000..85548f1f70a --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/BridgeComponentBase.cs @@ -0,0 +1,33 @@ +namespace Polly.Utils.Pipeline; + +internal abstract class BridgeComponentBase : PipelineComponent +{ + private readonly object _strategy; + + protected BridgeComponentBase(object strategy) => _strategy = strategy; + + public override void Dispose() + { + if (_strategy is IDisposable disposable) + { + disposable.Dispose(); + } + else if (_strategy is IAsyncDisposable asyncDisposable) + { + asyncDisposable.DisposeAsync().AsTask().GetAwaiter().GetResult(); + } + } + + public override ValueTask DisposeAsync() + { + if (_strategy is IAsyncDisposable asyncDisposable) + { + return asyncDisposable.DisposeAsync(); + } + else + { + Dispose(); + return default; + } + } +} diff --git a/src/Polly.Core/Utils/ComponentDisposeHelper.cs b/src/Polly.Core/Utils/Pipeline/ComponentDisposeHelper.cs similarity index 98% rename from src/Polly.Core/Utils/ComponentDisposeHelper.cs rename to src/Polly.Core/Utils/Pipeline/ComponentDisposeHelper.cs index b4789963096..e32a71b4e67 100644 --- a/src/Polly.Core/Utils/ComponentDisposeHelper.cs +++ b/src/Polly.Core/Utils/Pipeline/ComponentDisposeHelper.cs @@ -1,4 +1,4 @@ -namespace Polly.Utils; +namespace Polly.Utils.Pipeline; internal sealed class ComponentDisposeHelper : IDisposable, IAsyncDisposable { diff --git a/src/Polly.Core/Utils/Pipeline/CompositeComponent.cs b/src/Polly.Core/Utils/Pipeline/CompositeComponent.cs new file mode 100644 index 00000000000..99fefff4d50 --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/CompositeComponent.cs @@ -0,0 +1,142 @@ +using Polly.Telemetry; + +namespace Polly.Utils.Pipeline; + +/// +/// A combination of multiple components. +/// +[DebuggerDisplay("Pipeline, Strategies = {Strategies.Count}")] +[DebuggerTypeProxy(typeof(CompositeComponentDebuggerProxy))] +internal sealed class CompositeComponent : PipelineComponent +{ + private readonly ResilienceStrategyTelemetry _telemetry; + private readonly TimeProvider _timeProvider; + + private CompositeComponent( + PipelineComponent first, + IReadOnlyList components, + ResilienceStrategyTelemetry telemetry, + TimeProvider timeProvider) + { + Components = components; + + _telemetry = telemetry; + _timeProvider = timeProvider; + FirstComponent = first; + } + + internal PipelineComponent FirstComponent { get; } + + public static PipelineComponent Create( + IReadOnlyList components, + ResilienceStrategyTelemetry telemetry, + TimeProvider timeProvider) + { + if (components.Count == 1) + { + return new CompositeComponent(components[0], components, telemetry, timeProvider); + } + + // convert all components to delegating ones (except the last one as it's not required) + var delegatingComponents = components + .Take(components.Count - 1) + .Select(strategy => new DelegatingComponent(strategy)) + .ToList(); + +#if NET6_0_OR_GREATER + // link the last one + delegatingComponents[^1].Next = components[^1]; +#else + delegatingComponents[delegatingComponents.Count - 1].Next = components[components.Count - 1]; +#endif + + // link the remaining ones + for (var i = 0; i < delegatingComponents.Count - 1; i++) + { + delegatingComponents[i].Next = delegatingComponents[i + 1]; + } + + return new CompositeComponent(delegatingComponents[0], components, telemetry, timeProvider); + } + + public IReadOnlyList Components { get; } + + public override void Dispose() + { + foreach (var component in Components) + { + component.Dispose(); + } + } + + public override async ValueTask DisposeAsync() + { + foreach (var component in Components) + { + await component.DisposeAsync().ConfigureAwait(false); + } + } + + internal override async ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) + { + var timeStamp = _timeProvider.GetTimestamp(); + _telemetry.Report(new ResilienceEvent(ResilienceEventSeverity.Debug, TelemetryUtil.PipelineExecuting), context, default(PipelineExecutingArguments)); + + Outcome outcome; + + if (context.CancellationToken.IsCancellationRequested) + { + outcome = Outcome.FromException(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); + } + else + { + outcome = await FirstComponent.ExecuteCore(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext); + } + + _telemetry.Report( + new ResilienceEvent(ResilienceEventSeverity.Information, TelemetryUtil.PipelineExecuted), + new OutcomeArguments(context, outcome, new PipelineExecutedArguments(_timeProvider.GetElapsedTime(timeStamp)))); + + return outcome; + } + + /// + /// A component that delegates the execution to the next component in the chain. + /// + private sealed class DelegatingComponent : PipelineComponent + { + private readonly PipelineComponent _component; + + public DelegatingComponent(PipelineComponent component) => _component = component; + + public PipelineComponent? Next { get; set; } + + internal override ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) + { + return _component.ExecuteCore( + static (context, state) => + { + if (context.CancellationToken.IsCancellationRequested) + { + return Outcome.FromExceptionAsTask(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); + } + + return state.Next!.ExecuteCore(state.callback, context, state.state); + }, + context, + (Next, callback, state)); + } + + public override void Dispose() + { + } + + public override ValueTask DisposeAsync() => default; + } +} diff --git a/src/Polly.Core/Utils/Pipeline/CompositeComponentDebuggerProxy.cs b/src/Polly.Core/Utils/Pipeline/CompositeComponentDebuggerProxy.cs new file mode 100644 index 00000000000..821fe452b34 --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/CompositeComponentDebuggerProxy.cs @@ -0,0 +1,11 @@ +namespace Polly.Utils.Pipeline; + +internal sealed class CompositeComponentDebuggerProxy +{ + private readonly CompositeComponent _pipeline; + + public CompositeComponentDebuggerProxy(CompositeComponent pipeline) => _pipeline = pipeline; + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public IEnumerable Strategies => _pipeline.Components; +} diff --git a/src/Polly.Core/Utils/Pipeline/PipelineComponent.cs b/src/Polly.Core/Utils/Pipeline/PipelineComponent.cs new file mode 100644 index 00000000000..f19236fe22f --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/PipelineComponent.cs @@ -0,0 +1,51 @@ +namespace Polly.Utils.Pipeline; + +/// +/// Represents a single component of a resilience pipeline. +/// +/// +/// The component of the pipeline can be either a strategy, a generic strategy or a whole pipeline. +/// +internal abstract class PipelineComponent : IDisposable, IAsyncDisposable +{ + public static PipelineComponent Null { get; } = new NullComponent(); + + internal ResilienceStrategyOptions? Options { get; set; } + + internal abstract ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state); + + internal Outcome ExecuteCoreSync( + Func> callback, + ResilienceContext context, + TState state) + { + return ExecuteCore( + static (context, state) => + { + var result = state.callback(context, state.state); + + return new ValueTask>(result); + }, + context, + (callback, state)).GetResult(); + } + + public abstract void Dispose(); + + public abstract ValueTask DisposeAsync(); + + private class NullComponent : PipelineComponent + { + internal override ValueTask> ExecuteCore(Func>> callback, ResilienceContext context, TState state) + => callback(context, state); + + public override void Dispose() + { + } + + public override ValueTask DisposeAsync() => default; + } +} diff --git a/src/Polly.Core/Utils/Pipeline/PipelineComponentFactory.cs b/src/Polly.Core/Utils/Pipeline/PipelineComponentFactory.cs new file mode 100644 index 00000000000..d5473d442ad --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/PipelineComponentFactory.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Polly.Telemetry; +namespace Polly.Utils.Pipeline; + +internal static class PipelineComponentFactory +{ + public static PipelineComponent FromPipeline(ResiliencePipeline pipeline) => pipeline.Component; + + public static PipelineComponent FromPipeline(ResiliencePipeline pipeline) => pipeline.Component; + + public static PipelineComponent FromStrategy(ResilienceStrategy strategy) => new BridgeComponent(strategy); + + public static PipelineComponent FromStrategy(ResilienceStrategy strategy) => new BridgeComponent(strategy); + + public static PipelineComponent CreateComposite( + IReadOnlyList components, + ResilienceStrategyTelemetry telemetry, + TimeProvider timeProvider) => CompositeComponent.Create(components, telemetry, timeProvider); + + public static PipelineComponent CreateReloadable( + PipelineComponent initialComponent, + Func onReload, + Func factory, + ResilienceStrategyTelemetry telemetry) => new ReloadableComponent(initialComponent, onReload, factory, telemetry); +} diff --git a/src/Polly.Core/Utils/Pipeline/ReloadableComponent.cs b/src/Polly.Core/Utils/Pipeline/ReloadableComponent.cs new file mode 100644 index 00000000000..98f5cfde2e7 --- /dev/null +++ b/src/Polly.Core/Utils/Pipeline/ReloadableComponent.cs @@ -0,0 +1,94 @@ +using Polly.Telemetry; + +namespace Polly.Utils.Pipeline; + +#pragma warning disable CA1031 // Do not catch general exception types + +internal sealed class ReloadableComponent : PipelineComponent +{ + public const string ReloadFailedEvent = "ReloadFailed"; + + public const string OnReloadEvent = "OnReload"; + + private readonly Func _onReload; + private readonly Func _factory; + private readonly ResilienceStrategyTelemetry _telemetry; + private CancellationTokenRegistration _registration; + + public ReloadableComponent( + PipelineComponent initialComponent, + Func onReload, + Func factory, + ResilienceStrategyTelemetry telemetry) + { + Component = initialComponent; + + _onReload = onReload; + _factory = factory; + _telemetry = telemetry; + + RegisterOnReload(default); + } + + public PipelineComponent Component { get; private set; } + + internal override ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) + { + return Component.ExecuteCore(callback, context, state); + } + + public override void Dispose() + { + DisposeRegistration(); + Component.Dispose(); + } + + public override ValueTask DisposeAsync() + { + DisposeRegistration(); + return Component.DisposeAsync(); + } + + private void RegisterOnReload(CancellationToken previousToken) + { + var token = _onReload(); + if (token == previousToken) + { + return; + } + + _registration = token.Register(() => + { + var context = ResilienceContextPool.Shared.Get().Initialize(isSynchronous: true); + PipelineComponent previousComponent = Component; + + try + { + _telemetry.Report(new(ResilienceEventSeverity.Information, OnReloadEvent), context, new OnReloadArguments()); + Component = _factory(); + + previousComponent.Dispose(); + } + catch (Exception e) + { + var args = new OutcomeArguments(context, Outcome.FromException(e), new ReloadFailedArguments(e)); + _telemetry.Report(new(ResilienceEventSeverity.Error, ReloadFailedEvent), args); + ResilienceContextPool.Shared.Return(context); + } + + DisposeRegistration(); + RegisterOnReload(token); + }); + } + +#pragma warning disable S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods + private void DisposeRegistration() => _registration.Dispose(); +#pragma warning restore S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods + + internal readonly record struct ReloadFailedArguments(Exception Exception); + + internal readonly record struct OnReloadArguments(); +} diff --git a/src/Polly.Core/Utils/PipelineComponent.Bridge.cs b/src/Polly.Core/Utils/PipelineComponent.Bridge.cs deleted file mode 100644 index e0c3a50103d..00000000000 --- a/src/Polly.Core/Utils/PipelineComponent.Bridge.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Polly.Utils; - -internal abstract partial class PipelineComponent -{ - [DebuggerDisplay("{Strategy}")] - internal sealed class BridgeComponent : BridgeComponentBase - { - public BridgeComponent(ResilienceStrategy strategy) - : base(strategy) => Strategy = strategy; - - public ResilienceStrategy Strategy { get; } - - internal override ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) - { - // Check if we can cast directly, thus saving some cycles and improving the performance - if (callback is Func>> casted) - { - return TaskHelper.ConvertValueTask( - Strategy.ExecuteCore(casted, context, state), - context); - } - else - { - var valueTask = Strategy.ExecuteCore( - static async (context, state) => - { - var outcome = await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext); - return outcome.AsOutcome(); - }, - context, - (callback, state)); - - return TaskHelper.ConvertValueTask(valueTask, context); - } - } - } - - [DebuggerDisplay("{Strategy}")] - internal sealed class BridgeComponent : BridgeComponentBase - { - public BridgeComponent(ResilienceStrategy strategy) - : base(strategy) => Strategy = strategy; - - public ResilienceStrategy Strategy { get; } - - internal override ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) => Strategy.ExecuteCore(callback, context, state); - } - - internal abstract class BridgeComponentBase : PipelineComponent - { - private readonly object _strategy; - - protected BridgeComponentBase(object strategy) => _strategy = strategy; - - public override void Dispose() - { - if (_strategy is IDisposable disposable) - { - disposable.Dispose(); - } - else if (_strategy is IAsyncDisposable asyncDisposable) - { - asyncDisposable.DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - } - - public override ValueTask DisposeAsync() - { - if (_strategy is IAsyncDisposable asyncDisposable) - { - return asyncDisposable.DisposeAsync(); - } - else - { - Dispose(); - return default; - } - } - } - -} diff --git a/src/Polly.Core/Utils/PipelineComponent.Composite.cs b/src/Polly.Core/Utils/PipelineComponent.Composite.cs deleted file mode 100644 index a4401839bda..00000000000 --- a/src/Polly.Core/Utils/PipelineComponent.Composite.cs +++ /dev/null @@ -1,155 +0,0 @@ -using Polly.Telemetry; - -namespace Polly.Utils; - -internal abstract partial class PipelineComponent -{ - /// - /// A combination of multiple components. - /// - [DebuggerDisplay("Pipeline, Strategies = {Strategies.Count}")] - [DebuggerTypeProxy(typeof(CompositeDebuggerProxy))] - internal sealed class CompositeComponent : PipelineComponent - { - private readonly ResilienceStrategyTelemetry _telemetry; - private readonly TimeProvider _timeProvider; - - private CompositeComponent( - PipelineComponent first, - IReadOnlyList components, - ResilienceStrategyTelemetry telemetry, - TimeProvider timeProvider) - { - Components = components; - - _telemetry = telemetry; - _timeProvider = timeProvider; - FirstComponent = first; - } - - internal PipelineComponent FirstComponent { get; } - - public static PipelineComponent Create( - IReadOnlyList components, - ResilienceStrategyTelemetry telemetry, - TimeProvider timeProvider) - { - if (components.Count == 1) - { - return new CompositeComponent(components[0], components, telemetry, timeProvider); - } - - // convert all components to delegating ones (except the last one as it's not required) - var delegatingComponents = components - .Take(components.Count - 1) - .Select(strategy => new DelegatingComponent(strategy)) - .ToList(); - -#if NET6_0_OR_GREATER - // link the last one - delegatingComponents[^1].Next = components[^1]; -#else - delegatingComponents[delegatingComponents.Count - 1].Next = components[components.Count - 1]; -#endif - - // link the remaining ones - for (var i = 0; i < delegatingComponents.Count - 1; i++) - { - delegatingComponents[i].Next = delegatingComponents[i + 1]; - } - - return new CompositeComponent(delegatingComponents[0], components, telemetry, timeProvider); - } - - public IReadOnlyList Components { get; } - - public override void Dispose() - { - foreach (var component in Components) - { - component.Dispose(); - } - } - - public override async ValueTask DisposeAsync() - { - foreach (var component in Components) - { - await component.DisposeAsync().ConfigureAwait(false); - } - } - - internal override async ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) - { - var timeStamp = _timeProvider.GetTimestamp(); - _telemetry.Report(new ResilienceEvent(ResilienceEventSeverity.Debug, TelemetryUtil.PipelineExecuting), context, default(PipelineExecutingArguments)); - - Outcome outcome; - - if (context.CancellationToken.IsCancellationRequested) - { - outcome = Outcome.FromException(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); - } - else - { - outcome = await FirstComponent.ExecuteCore(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext); - } - - _telemetry.Report( - new ResilienceEvent(ResilienceEventSeverity.Information, TelemetryUtil.PipelineExecuted), - new OutcomeArguments(context, outcome, new PipelineExecutedArguments(_timeProvider.GetElapsedTime(timeStamp)))); - - return outcome; - } - } - - /// - /// A component that delegates the execution to the next component in the chain. - /// - private sealed class DelegatingComponent : PipelineComponent - { - private readonly PipelineComponent _component; - - public DelegatingComponent(PipelineComponent component) => _component = component; - - public PipelineComponent? Next { get; set; } - - internal override ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) - { - return _component.ExecuteCore( - static (context, state) => - { - if (context.CancellationToken.IsCancellationRequested) - { - return Outcome.FromExceptionAsTask(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); - } - - return state.Next!.ExecuteCore(state.callback, context, state.state); - }, - context, - (Next, callback, state)); - } - - public override void Dispose() - { - } - - public override ValueTask DisposeAsync() => default; - } - - internal sealed class CompositeDebuggerProxy - { - private readonly CompositeComponent _pipeline; - - public CompositeDebuggerProxy(CompositeComponent pipeline) => _pipeline = pipeline; - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public IEnumerable Strategies => _pipeline.Components; - } -} diff --git a/src/Polly.Core/Utils/PipelineComponent.Reloadale.cs b/src/Polly.Core/Utils/PipelineComponent.Reloadale.cs deleted file mode 100644 index 96422482465..00000000000 --- a/src/Polly.Core/Utils/PipelineComponent.Reloadale.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Polly.Telemetry; - -namespace Polly.Utils; - -#pragma warning disable CA1031 // Do not catch general exception types - -internal abstract partial class PipelineComponent -{ - internal sealed class ReloadableComponent : PipelineComponent - { - public const string ReloadFailedEvent = "ReloadFailed"; - - public const string OnReloadEvent = "OnReload"; - - private readonly Func _onReload; - private readonly Func _factory; - private readonly ResilienceStrategyTelemetry _telemetry; - private CancellationTokenRegistration _registration; - - public ReloadableComponent( - PipelineComponent initialComponent, - Func onReload, - Func factory, - ResilienceStrategyTelemetry telemetry) - { - Component = initialComponent; - - _onReload = onReload; - _factory = factory; - _telemetry = telemetry; - - RegisterOnReload(default); - } - - public PipelineComponent Component { get; private set; } - - internal override ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state) - { - return Component.ExecuteCore(callback, context, state); - } - - public override void Dispose() - { - DisposeRegistration(); - Component.Dispose(); - } - - public override ValueTask DisposeAsync() - { - DisposeRegistration(); - return Component.DisposeAsync(); - } - - private void RegisterOnReload(CancellationToken previousToken) - { - var token = _onReload(); - if (token == previousToken) - { - return; - } - - _registration = token.Register(() => - { - var context = ResilienceContextPool.Shared.Get().Initialize(isSynchronous: true); - PipelineComponent previousComponent = Component; - - try - { - _telemetry.Report(new(ResilienceEventSeverity.Information, OnReloadEvent), context, new OnReloadArguments()); - Component = _factory(); - - previousComponent.Dispose(); - } - catch (Exception e) - { - var args = new OutcomeArguments(context, Outcome.FromException(e), new ReloadFailedArguments(e)); - _telemetry.Report(new(ResilienceEventSeverity.Error, ReloadFailedEvent), args); - ResilienceContextPool.Shared.Return(context); - } - - DisposeRegistration(); - RegisterOnReload(token); - }); - } - -#pragma warning disable S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods - private void DisposeRegistration() => _registration.Dispose(); -#pragma warning restore S2952 // Classes should "Dispose" of members from the classes' own "Dispose" methods - - internal readonly record struct ReloadFailedArguments(Exception Exception); - - internal readonly record struct OnReloadArguments(); - } -} diff --git a/src/Polly.Core/Utils/PipelineComponent.cs b/src/Polly.Core/Utils/PipelineComponent.cs deleted file mode 100644 index b9ae69bed75..00000000000 --- a/src/Polly.Core/Utils/PipelineComponent.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Polly.Telemetry; - -namespace Polly.Utils; - -/// -/// Represents a single component of a resilience pipeline. -/// -/// -/// The component of the pipeline can be either a strategy, a generic strategy or a whole pipeline. -/// -internal abstract partial class PipelineComponent : IDisposable, IAsyncDisposable -{ - public static PipelineComponent Null { get; } = new NullComponent(); - - internal ResilienceStrategyOptions? Options { get; set; } - - public static PipelineComponent FromPipeline(ResiliencePipeline pipeline) => pipeline.Component; - - public static PipelineComponent FromPipeline(ResiliencePipeline pipeline) => pipeline.Component; - - public static PipelineComponent FromStrategy(ResilienceStrategy strategy) => new BridgeComponent(strategy); - - public static PipelineComponent FromStrategy(ResilienceStrategy strategy) => new BridgeComponent(strategy); - - public static PipelineComponent CreateComposite( - IReadOnlyList components, - ResilienceStrategyTelemetry telemetry, - TimeProvider timeProvider) => CompositeComponent.Create(components, telemetry, timeProvider); - - public static PipelineComponent CreateReloadable( - PipelineComponent initialComponent, - Func onReload, - Func factory, - ResilienceStrategyTelemetry telemetry) => new ReloadableComponent(initialComponent, onReload, factory, telemetry); - - internal abstract ValueTask> ExecuteCore( - Func>> callback, - ResilienceContext context, - TState state); - - public abstract void Dispose(); - - public abstract ValueTask DisposeAsync(); - - private class NullComponent : PipelineComponent - { - internal override ValueTask> ExecuteCore(Func>> callback, ResilienceContext context, TState state) - => callback(context, state); - - public override void Dispose() - { - } - - public override ValueTask DisposeAsync() => default; - } -} diff --git a/src/Polly.Testing/ResiliencePipelineExtensions.cs b/src/Polly.Testing/ResiliencePipelineExtensions.cs index f3a625ab17e..db5d03589b8 100644 --- a/src/Polly.Testing/ResiliencePipelineExtensions.cs +++ b/src/Polly.Testing/ResiliencePipelineExtensions.cs @@ -1,4 +1,5 @@ using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Testing; @@ -43,17 +44,17 @@ private static ResiliencePipelineDescriptor GetPipelineDescriptorCore(Pipelin return new ResiliencePipelineDescriptor( descriptors.Where(s => !ShouldSkip(s.StrategyInstance)).ToList().AsReadOnly(), - isReloadable: components.Exists(s => s is PipelineComponent.ReloadableComponent)); + isReloadable: components.Exists(s => s is ReloadableComponent)); } private static object GetStrategyInstance(PipelineComponent component) { - if (component is PipelineComponent.BridgeComponent reactiveBridge) + if (component is BridgeComponent reactiveBridge) { return reactiveBridge.Strategy; } - if (component is PipelineComponent.BridgeComponent nonReactiveBridge) + if (component is BridgeComponent nonReactiveBridge) { return nonReactiveBridge.Strategy; } @@ -61,18 +62,18 @@ private static object GetStrategyInstance(PipelineComponent component) return component; } - private static bool ShouldSkip(object instance) => instance is PipelineComponent.ReloadableComponent; + private static bool ShouldSkip(object instance) => instance is ReloadableComponent; private static void ExpandComponents(this PipelineComponent component, List components) { - if (component is PipelineComponent.CompositeComponent pipeline) + if (component is CompositeComponent pipeline) { foreach (var inner in pipeline.Components) { inner.ExpandComponents(components); } } - else if (component is PipelineComponent.ReloadableComponent reloadable) + else if (component is ReloadableComponent reloadable) { components.Add(reloadable); ExpandComponents(reloadable.Component, components); diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderTests.cs index 05f6eb62633..0cdd794f01c 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderTests.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Time.Testing; using Polly.CircuitBreaker; using Polly.Testing; -using Polly.Utils; namespace Polly.Core.Tests.CircuitBreaker; diff --git a/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs b/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs index 79928ad1cbb..909dbfc7f2a 100644 --- a/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs +++ b/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Time.Testing; using NSubstitute; -using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Core.Tests; @@ -44,7 +44,7 @@ public void Build_Ok() // assert strategy.Should().NotBeNull(); - strategy.Component.Should().BeOfType().Subject.Components.Should().HaveCount(2); + strategy.Component.Should().BeOfType().Subject.Components.Should().HaveCount(2); } [Fact] @@ -59,6 +59,6 @@ public void AddGenericStrategy_Ok() // assert strategy.Should().NotBeNull(); - ((PipelineComponent.CompositeComponent)strategy.Component).Components[0].Should().Be(testStrategy.Component); + ((CompositeComponent)strategy.Component).Components[0].Should().Be(testStrategy.Component); } } diff --git a/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs b/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs index 1fbdf987220..f6fd84728e0 100644 --- a/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs +++ b/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs @@ -4,7 +4,7 @@ using Polly.Retry; using Polly.Telemetry; using Polly.Testing; -using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Core.Tests; @@ -91,7 +91,7 @@ public void AddPipeline_Multiple_Ok() strategy .Component .Should() - .BeOfType() + .BeOfType() .Subject .Components.Should().HaveCount(3); diff --git a/test/Polly.Core.Tests/ResiliencePipelineTTests.Async.cs b/test/Polly.Core.Tests/ResiliencePipelineTTests.Async.cs index da7d9c9e638..57b172c9848 100644 --- a/test/Polly.Core.Tests/ResiliencePipelineTTests.Async.cs +++ b/test/Polly.Core.Tests/ResiliencePipelineTTests.Async.cs @@ -1,4 +1,5 @@ using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Core.Tests; @@ -65,7 +66,7 @@ public partial class ResiliencePipelineTests [Theory] public async Task ExecuteAsync_GenericStrategy_Ok(Func, ValueTask> execute) { - var pipeline = new ResiliencePipeline(PipelineComponent.FromStrategy(new TestResilienceStrategy + var pipeline = new ResiliencePipeline(PipelineComponentFactory.FromStrategy(new TestResilienceStrategy { Before = (c, _) => { diff --git a/test/Polly.Core.Tests/ResiliencePipelineTTests.Sync.cs b/test/Polly.Core.Tests/ResiliencePipelineTTests.Sync.cs index 9f4717c5b53..61378e7bcb2 100644 --- a/test/Polly.Core.Tests/ResiliencePipelineTTests.Sync.cs +++ b/test/Polly.Core.Tests/ResiliencePipelineTTests.Sync.cs @@ -1,4 +1,5 @@ using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Core.Tests; @@ -79,7 +80,7 @@ public partial class ResiliencePipelineTests [Theory] public void Execute_GenericStrategy_Ok(Action> execute) { - var pipeline = new ResiliencePipeline(PipelineComponent.FromStrategy(new TestResilienceStrategy + var pipeline = new ResiliencePipeline(PipelineComponentFactory.FromStrategy(new TestResilienceStrategy { Before = (c, _) => { diff --git a/test/Polly.Core.Tests/ResiliencePipelineTests.cs b/test/Polly.Core.Tests/ResiliencePipelineTests.cs index 0fda45e2b05..2cb06a360b3 100644 --- a/test/Polly.Core.Tests/ResiliencePipelineTests.cs +++ b/test/Polly.Core.Tests/ResiliencePipelineTests.cs @@ -1,5 +1,6 @@ using NSubstitute; using Polly.Utils; +using Polly.Utils.Pipeline; namespace Polly.Core.Tests; @@ -84,13 +85,13 @@ public void Null_Ok() [Fact] public void DebuggerProxy_Ok() { - using var pipeline = (PipelineComponent.CompositeComponent)PipelineComponent.CreateComposite(new[] + using var pipeline = (CompositeComponent)PipelineComponentFactory.CreateComposite(new[] { Substitute.For(), Substitute.For(), }, null!, null!); - new PipelineComponent.CompositeDebuggerProxy(pipeline).Strategies.Should().HaveCount(2); + new CompositeComponentDebuggerProxy(pipeline).Strategies.Should().HaveCount(2); } public class ExecuteParameters : ExecuteParameters diff --git a/test/Polly.Core.Tests/Telemetry/ExecutionAttemptArgumentsTests.cs b/test/Polly.Core.Tests/Telemetry/ExecutionAttemptArgumentsTests.cs index aac43591052..c30994634f3 100644 --- a/test/Polly.Core.Tests/Telemetry/ExecutionAttemptArgumentsTests.cs +++ b/test/Polly.Core.Tests/Telemetry/ExecutionAttemptArgumentsTests.cs @@ -1,6 +1,6 @@ using Polly.Telemetry; -namespace Polly.Extensions.Tests.Telemetry; +namespace Polly.Core.Tests.Telemetry; public class ExecutionAttemptArgumentsTests { diff --git a/test/Polly.Core.Tests/Telemetry/PipelineExecutedArgumentsTests.cs b/test/Polly.Core.Tests/Telemetry/PipelineExecutedArgumentsTests.cs index 2c436449d44..8edf8635511 100644 --- a/test/Polly.Core.Tests/Telemetry/PipelineExecutedArgumentsTests.cs +++ b/test/Polly.Core.Tests/Telemetry/PipelineExecutedArgumentsTests.cs @@ -1,6 +1,6 @@ using Polly.Telemetry; -namespace Polly.Extensions.Tests.Telemetry; +namespace Polly.Core.Tests.Telemetry; public class PipelineExecutedArgumentsTests { diff --git a/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs b/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs index ec6a68bf1b9..a73a2d6efca 100644 --- a/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs +++ b/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs @@ -1,6 +1,6 @@ using Polly.Telemetry; -namespace Polly.Extensions.Tests.Telemetry; +namespace Polly.Core.Tests.Telemetry; public class TelemetryEventArgumentsTests { @@ -10,7 +10,7 @@ public class TelemetryEventArgumentsTests public void Ctor_Ok() { var context = ResilienceContextPool.Shared.Get(); - var args = new TelemetryEventArguments(_source, new ResilienceEvent(ResilienceEventSeverity.Warning, "ev"), context, "arg", Outcome.FromResult("dummy")); + var args = new TelemetryEventArguments(_source, new ResilienceEvent(ResilienceEventSeverity.Warning, "ev"), context, "arg", Outcome.FromResult("dummy")); args.Outcome!.Value.Result.Should().Be("dummy"); args.Context.Should().Be(context); diff --git a/test/Polly.Core.Tests/Utils/PipelineComponents/BridgePipelineComponentTests.cs b/test/Polly.Core.Tests/Utils/Pipeline/BridgePipelineComponentTests.cs similarity index 80% rename from test/Polly.Core.Tests/Utils/PipelineComponents/BridgePipelineComponentTests.cs rename to test/Polly.Core.Tests/Utils/Pipeline/BridgePipelineComponentTests.cs index e4f3e73fb41..7769c6ff0ba 100644 --- a/test/Polly.Core.Tests/Utils/PipelineComponents/BridgePipelineComponentTests.cs +++ b/test/Polly.Core.Tests/Utils/Pipeline/BridgePipelineComponentTests.cs @@ -1,7 +1,8 @@ using NSubstitute; using Polly.Utils; +using Polly.Utils.Pipeline; -namespace Polly.Core.Tests.Utils.PipelineComponents; +namespace Polly.Core.Tests.Utils.Pipeline; public class BridgePipelineComponentTests { @@ -16,7 +17,7 @@ public void Execute_NonGeneric_Ok() { var values = new List(); - var pipeline = new ResiliencePipeline(PipelineComponent.FromStrategy(new Strategy(outcome => + var pipeline = new ResiliencePipeline(PipelineComponentFactory.FromStrategy(new Strategy(outcome => { values.Add(outcome.Result); })), DisposeBehavior.Allow); @@ -37,7 +38,7 @@ public void Execute_Generic_Ok() { var values = new List(); - var pipeline = new ResiliencePipeline(PipelineComponent.FromStrategy(new Strategy(outcome => + var pipeline = new ResiliencePipeline(PipelineComponentFactory.FromStrategy(new Strategy(outcome => { values.Add(outcome.Result); })), DisposeBehavior.Allow); @@ -53,7 +54,7 @@ public void Pipeline_TypeCheck_Ok() { var called = false; - var pipeline = new ResiliencePipeline(PipelineComponent.FromStrategy(new Strategy(outcome => + var pipeline = new ResiliencePipeline(PipelineComponentFactory.FromStrategy(new Strategy(outcome => { outcome.Result.Should().Be(-1); called = true; @@ -71,11 +72,11 @@ public void Pipeline_TypeCheck_Ok() public async Task Dispose_EnsureStrategyDisposed(bool isAsync) { var strategy = Substitute.For(); - await Dispose(PipelineComponent.FromStrategy(strategy), isAsync); + await Dispose(PipelineComponentFactory.FromStrategy(strategy), isAsync); ((IDisposable)strategy).Received(1).Dispose(); strategy = Substitute.For(); - await Dispose(PipelineComponent.FromStrategy(strategy), isAsync); + await Dispose(PipelineComponentFactory.FromStrategy(strategy), isAsync); await ((IAsyncDisposable)strategy).Received(1).DisposeAsync(); } @@ -85,11 +86,11 @@ public async Task Dispose_EnsureStrategyDisposed(bool isAsync) public async Task Dispose_Generic_EnsureStrategyDisposed(bool isAsync) { var strategy = Substitute.For, IDisposable>(); - await Dispose(PipelineComponent.FromStrategy(strategy), isAsync); + await Dispose(PipelineComponentFactory.FromStrategy(strategy), isAsync); ((IDisposable)strategy).Received(1).Dispose(); strategy = Substitute.For, IAsyncDisposable>(); - await Dispose(PipelineComponent.FromStrategy(strategy), isAsync); + await Dispose(PipelineComponentFactory.FromStrategy(strategy), isAsync); await ((IAsyncDisposable)strategy).Received(1).DisposeAsync(); } #pragma warning restore S1944 // Invalid casts should be avoided diff --git a/test/Polly.Core.Tests/Utils/PipelineComponents/CompositePipelineComponentTests.cs b/test/Polly.Core.Tests/Utils/Pipeline/CompositePipelineComponentTests.cs similarity index 80% rename from test/Polly.Core.Tests/Utils/PipelineComponents/CompositePipelineComponentTests.cs rename to test/Polly.Core.Tests/Utils/Pipeline/CompositePipelineComponentTests.cs index 4121854dbbe..89b54a7c30b 100644 --- a/test/Polly.Core.Tests/Utils/PipelineComponents/CompositePipelineComponentTests.cs +++ b/test/Polly.Core.Tests/Utils/Pipeline/CompositePipelineComponentTests.cs @@ -2,8 +2,9 @@ using NSubstitute; using Polly.Telemetry; using Polly.Utils; +using Polly.Utils.Pipeline; -namespace Polly.Core.Tests.Utils.PipelineComponents; +namespace Polly.Core.Tests.Utils.Pipeline; #pragma warning disable CA2000 // Dispose objects before losing scope @@ -43,8 +44,8 @@ public async Task Create_EnsureExceptionsNotWrapped() { var components = new[] { - PipelineComponent.FromStrategy(new TestResilienceStrategy { Before = (_, _) => throw new NotSupportedException() }), - PipelineComponent.FromStrategy(new TestResilienceStrategy { Before = (_, _) => throw new NotSupportedException() }), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy { Before = (_, _) => throw new NotSupportedException() }), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy { Before = (_, _) => throw new NotSupportedException() }), }; var pipeline = CreateSut(components); @@ -59,9 +60,9 @@ public void Create_EnsurePipelineReusableAcrossDifferentPipelines() { var components = new[] { - PipelineComponent.FromStrategy(new TestResilienceStrategy()), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy()), Substitute.For(), - PipelineComponent.FromStrategy(new TestResilienceStrategy()), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy()), }; @@ -81,8 +82,8 @@ public async Task Create_Cancelled_EnsureNoExecution() cancellation.Cancel(); var strategies = new[] { - PipelineComponent.FromStrategy(new TestResilienceStrategy()), - PipelineComponent.FromStrategy(new TestResilienceStrategy()), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy()), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy()), }; var pipeline = new ResiliencePipeline(CreateSut(strategies, new FakeTimeProvider()), DisposeBehavior.Allow); @@ -100,8 +101,8 @@ public async Task Create_CancelledLater_EnsureNoExecution() using var cancellation = new CancellationTokenSource(); var strategies = new[] { - PipelineComponent.FromStrategy(new TestResilienceStrategy { Before = (_, _) => { executed = true; cancellation.Cancel(); } }), - PipelineComponent.FromStrategy(new TestResilienceStrategy()), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy { Before = (_, _) => { executed = true; cancellation.Cancel(); } }), + PipelineComponentFactory.FromStrategy(new TestResilienceStrategy()), }; var pipeline = new ResiliencePipeline(CreateSut(strategies, new FakeTimeProvider()), DisposeBehavior.Allow); var context = ResilienceContextPool.Shared.Get(); @@ -154,8 +155,8 @@ public async Task DisposeAsync_EnsureInnerComponentsDisposed() await b.Received(1).DisposeAsync(); } - private PipelineComponent.CompositeComponent CreateSut(PipelineComponent[] components, TimeProvider? timeProvider = null) + private CompositeComponent CreateSut(PipelineComponent[] components, TimeProvider? timeProvider = null) { - return (PipelineComponent.CompositeComponent)PipelineComponent.CreateComposite(components, _telemetry, timeProvider ?? Substitute.For()); + return (CompositeComponent)PipelineComponentFactory.CreateComposite(components, _telemetry, timeProvider ?? Substitute.For()); } } diff --git a/test/Polly.Core.Tests/Utils/PipelineComponents/PipelineComponentTests.cs b/test/Polly.Core.Tests/Utils/Pipeline/PipelineComponentTests.cs similarity index 79% rename from test/Polly.Core.Tests/Utils/PipelineComponents/PipelineComponentTests.cs rename to test/Polly.Core.Tests/Utils/Pipeline/PipelineComponentTests.cs index f90c065f744..e7b79aecbcb 100644 --- a/test/Polly.Core.Tests/Utils/PipelineComponents/PipelineComponentTests.cs +++ b/test/Polly.Core.Tests/Utils/Pipeline/PipelineComponentTests.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; -using Polly.Utils; +using Polly.Utils.Pipeline; -namespace Polly.Core.Tests.Utils.PipelineComponents; +namespace Polly.Core.Tests.Utils.Pipeline; public class PipelineComponentTests { diff --git a/test/Polly.Core.Tests/Utils/PipelineComponents/ReloadablePipelineComponentTests.cs b/test/Polly.Core.Tests/Utils/Pipeline/ReloadablePipelineComponentTests.cs similarity index 84% rename from test/Polly.Core.Tests/Utils/PipelineComponents/ReloadablePipelineComponentTests.cs rename to test/Polly.Core.Tests/Utils/Pipeline/ReloadablePipelineComponentTests.cs index ebf5771e302..59f3eafec9f 100644 --- a/test/Polly.Core.Tests/Utils/PipelineComponents/ReloadablePipelineComponentTests.cs +++ b/test/Polly.Core.Tests/Utils/Pipeline/ReloadablePipelineComponentTests.cs @@ -1,8 +1,8 @@ using NSubstitute; using Polly.Telemetry; -using Polly.Utils; +using Polly.Utils.Pipeline; -namespace Polly.Core.Tests.Utils.PipelineComponents; +namespace Polly.Core.Tests.Utils.Pipeline; public class ReloadablePipelineComponentTests : IDisposable { @@ -30,7 +30,7 @@ public void Ctor_Ok() sut.Component.Should().Be(component); - PipelineComponent.ReloadableComponent.ReloadFailedEvent.Should().Be("ReloadFailed"); + ReloadableComponent.ReloadFailedEvent.Should().Be("ReloadFailed"); } [Fact] @@ -98,22 +98,22 @@ public void ChangeTriggered_FactoryError_LastStrategyUsedAndErrorReported() _events[0] .Arguments .Should() - .BeOfType(); + .BeOfType(); var args = _events[1] .Arguments .Should() - .BeOfType() + .BeOfType() .Subject; args.Exception.Should().BeOfType(); } - private PipelineComponent.ReloadableComponent CreateSut(PipelineComponent? initial = null, Func? factory = null) + private ReloadableComponent CreateSut(PipelineComponent? initial = null, Func? factory = null) { factory ??= () => PipelineComponent.Null; - return (PipelineComponent.ReloadableComponent)PipelineComponent.CreateReloadable(initial ?? PipelineComponent.Null, + return (ReloadableComponent)PipelineComponentFactory.CreateReloadable(initial ?? PipelineComponent.Null, () => _cancellationTokenSource.Token, factory, _telemetry);