Skip to content

Commit 008b952

Browse files
martintmkmartincostello
authored andcommitted
Fix stack trace growing for opened circuit breaker (#1878)
1 parent 87819ce commit 008b952

File tree

3 files changed

+49
-12
lines changed

3 files changed

+49
-12
lines changed

src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal sealed class CircuitStateController<T> : IDisposable
2020
private DateTimeOffset _blockedUntil;
2121
private CircuitState _circuitState = CircuitState.Closed;
2222
private Outcome<T>? _lastOutcome;
23-
private BrokenCircuitException _breakingException = new();
23+
private Exception? _breakingException;
2424
private bool _disposed;
2525

2626
#pragma warning disable S107
@@ -139,8 +139,8 @@ public ValueTask CloseCircuitAsync(ResilienceContext context)
139139

140140
exception = _circuitState switch
141141
{
142-
CircuitState.Open => _breakingException,
143-
CircuitState.HalfOpen when !isHalfOpen => _breakingException,
142+
CircuitState.Open => CreateBrokenCircuitException(),
143+
CircuitState.HalfOpen when !isHalfOpen => CreateBrokenCircuitException(),
144144
CircuitState.Isolated => new IsolatedCircuitException(),
145145
_ => null
146146
};
@@ -302,17 +302,15 @@ private bool PermitHalfOpenCircuitTest_NeedsLock()
302302
private void SetLastHandledOutcome_NeedsLock(Outcome<T> outcome)
303303
{
304304
_lastOutcome = outcome;
305-
306-
if (outcome.Exception is Exception exception)
307-
{
308-
_breakingException = new BrokenCircuitException(BrokenCircuitException.DefaultMessage, exception);
309-
}
310-
else
311-
{
312-
_breakingException = new BrokenCircuitException(BrokenCircuitException.DefaultMessage);
313-
}
305+
_breakingException = outcome.Exception;
314306
}
315307

308+
private BrokenCircuitException CreateBrokenCircuitException() => _breakingException switch
309+
{
310+
Exception exception => new BrokenCircuitException(BrokenCircuitException.DefaultMessage, exception),
311+
_ => new BrokenCircuitException(BrokenCircuitException.DefaultMessage)
312+
};
313+
316314
private void OpenCircuit_NeedsLock(Outcome<T> outcome, bool manual, ResilienceContext context, out Task? scheduledTask)
317315
{
318316
OpenCircuitFor_NeedsLock(outcome, _breakDuration, manual, context, out scheduledTask);

test/Polly.Core.Tests/CircuitBreaker/BrokenCircuitExceptionTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class BrokenCircuitExceptionTests
77
[Fact]
88
public void Ctor_Ok()
99
{
10+
new BrokenCircuitException().Message.Should().Be("The circuit is now open and is not allowing calls.");
1011
new BrokenCircuitException("Dummy.").Message.Should().Be("Dummy.");
1112
new BrokenCircuitException("Dummy.", new InvalidOperationException()).Message.Should().Be("Dummy.");
1213
new BrokenCircuitException("Dummy.", new InvalidOperationException()).InnerException.Should().BeOfType<InvalidOperationException>();

test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,44 @@ public async Task OnActionPreExecute_CircuitOpenedByValue()
124124
GetBlockedTill(controller).Should().Be(_timeProvider.GetUtcNow() + _options.BreakDuration);
125125
}
126126

127+
[InlineData(true)]
128+
[InlineData(false)]
129+
[Theory]
130+
public async Task OnActionPreExecute_CircuitOpened_EnsureExceptionStackTraceDoesNotGrow(bool innerException)
131+
{
132+
var stacks = new List<string>();
133+
var context = ResilienceContextPool.Shared.Get();
134+
using var controller = CreateController();
135+
136+
await OpenCircuit(
137+
controller,
138+
innerException ? Outcome.FromException<int>(new InvalidOperationException()) : Outcome.FromResult(99));
139+
140+
for (int i = 0; i < 100; i++)
141+
{
142+
try
143+
{
144+
(await controller.OnActionPreExecuteAsync(context)).Value.ThrowIfException();
145+
}
146+
catch (BrokenCircuitException e)
147+
{
148+
stacks.Add(e.StackTrace!);
149+
e.Message.Should().Be("The circuit is now open and is not allowing calls.");
150+
151+
if (innerException)
152+
{
153+
e.InnerException.Should().BeOfType<InvalidOperationException>();
154+
}
155+
else
156+
{
157+
e.InnerException.Should().BeNull();
158+
}
159+
}
160+
}
161+
162+
stacks.Distinct().Should().HaveCount(1);
163+
}
164+
127165
[Fact]
128166
public async Task HalfOpen_EnsureBreakDuration()
129167
{

0 commit comments

Comments
 (0)