Skip to content

[Bug]: ResiliencePipelineBuilder AddCircuitBreaker grows stacktrace uncontrollably #1877

@Doviskaune

Description

@Doviskaune

Describe the bug

Using ResiliencePipelineBuilder from v8, after Circuit Breaker is open, stacktrace in exception grows by three lines after each ExecuteAsync/Execute call.

Insights:

  • Manually closing circuit breaker "resets" stacktrace but does not prevent it from growing
  • Manually closing and then opening (isolating) circuit breaker prevents stacktrace from growing
  • AdvancedCircuitBreakerAsync from v7 does not have this problem

Expected behavior

Stacktraces should be 3 lines long in all BrokenCircuitException exceptions

Actual behavior

Stacktraces at indices 3 and 8 have 6 lines, stacktraces at indices 4 and 9 have 9 lines. Incorrect stacktraces repeat following lines 2 or 3 times:

   at Polly.Outcome`1.GetResultOrRethrow()
   at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Steps to reproduce

Create new dotnet project "Console Application", add nuget package Polly and put following code into Program.cs file. Entire program output is provided in Exceptions part. To test sync version or v7 version, comment / uncomment lines 40-42.

using Polly;
using Polly.CircuitBreaker;

var manualControl = new CircuitBreakerManualControl();
var resiliencePipeline =
    new ResiliencePipelineBuilder()
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            Name = "CircuitBreaker",
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromMinutes(2),
            MinimumThroughput = 2,
            BreakDuration = TimeSpan.FromMinutes(5),
            ShouldHandle = new PredicateBuilder().Handle<Exception>(),
            ManualControl = manualControl,
        })
        .Build();

var policy = Policy<bool>
    .Handle<Exception>()
    .AdvancedCircuitBreakerAsync(0.5, TimeSpan.FromMinutes(2), 2, TimeSpan.FromMinutes(5));

for (int i = 0; i < 15; i++)
{
    if (i == 5)
    {
        Console.WriteLine("Manually closing circuit breaker");
        await manualControl.CloseAsync();
    }
    else if (i == 10)
    {
        Console.WriteLine("Manually closing and then opening circuit breaker");
        await manualControl.CloseAsync();
        await manualControl.IsolateAsync();
    }

    try
    {
        Console.WriteLine("Index: {0}", i);
        await resiliencePipeline.ExecuteAsync<bool>(static _ => { throw new Exception(); });
        // resiliencePipeline.Execute<bool>(static _ => { throw new Exception(); });
        // await policy.ExecuteAsync(static () => { throw new Exception(); });
    }
    catch (BrokenCircuitException ex)
    {
        Console.BackgroundColor = ConsoleColor.Red;
        Console.ForegroundColor = ConsoleColor.Black;
        Console.Write(ex.Message);
        Console.ResetColor();
        Console.WriteLine();
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine();
    }
    catch (Exception ex)
    {
        Console.BackgroundColor = ConsoleColor.DarkYellow;
        Console.ForegroundColor = ConsoleColor.White;
        Console.Write(ex.Message);
        Console.ResetColor();
        Console.WriteLine();
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine();
    }
    finally
    {
        await Task.Delay(100);
    }
}

Exception(s) (if any)

Index: 0
Exception of type 'System.Exception' was thrown.
at Program.<>c.<<Main>$>b__0_0(CancellationToken _) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.ResiliencePipeline.<>c__10`1.<<ExecuteAsync>b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 1
Exception of type 'System.Exception' was thrown.
at Program.<>c.<<Main>$>b__0_0(CancellationToken _) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.ResiliencePipeline.<>c__10`1.<<ExecuteAsync>b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 2
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 3
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 4
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Manually closing circuit breaker
Index: 5
Exception of type 'System.Exception' was thrown.
at Program.<>c.<<Main>$>b__0_0(CancellationToken _) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.ResiliencePipeline.<>c__10`1.<<ExecuteAsync>b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 6
Exception of type 'System.Exception' was thrown.
at Program.<>c.<<Main>$>b__0_0(CancellationToken _) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.ResiliencePipeline.<>c__10`1.<<ExecuteAsync>b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 7
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 8
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 9
The circuit is now open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Manually closing and then opening circuit breaker
Index: 10
The circuit is manually held open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 11
The circuit is manually held open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 12
The circuit is manually held open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 13
The circuit is manually held open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Index: 14
The circuit is manually held open and is not allowing calls.
at Polly.Outcome`1.GetResultOrRethrow()
at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in C:\Users\xxx\RiderProjects\PollyTest\PollyTest\Program.cs:line 40

Polly version

8.2.0

.NET Version

8.0.100 (target framework - net8.0)

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions