Comments (9)
Unfortunately, this won't work 100%. For example this piece of code fails:
var outcome = ResiliencePipeline.Empty.ExecuteOutcomeAsync<string, string>(
(_, _) => throw new InvalidOperationException(),
ResilienceContextPool.Shared.Get(),
"dummy");
To fix this, try catch needs to be added here :
Basically, we need to define a static lambda and call callback
inside it with try/catch block. The reason why I haven't done this is tht async machinery adds constant overhead (around 40ns) on my machine and since this was intended to be high-performance API I thought it's not necessary there.
@peter-csala
Your example works by luck because the retry strategy does exception handling inside. if you added other strategy (custom for example) the test would fail.
from polly.
As the author of this question, I like that you are putting yourselves in the user's shoes. Thanks for thinking this through. IMO, I think it should work the same for all strategies if possible. Adding some user documentation around opt-in and opt-out is a great idea to add clarity to the situation as well.
from polly.
I've put together two simple unit tests.
[Fact]
public async Task ThrowNotHandledException()
{
//Arrange
const int expectedRetryCount = 0;
int actualRetryCount = 0;
var context = ResilienceContextPool.Shared.Get();
var retry = new ResiliencePipelineBuilder<int>()
.AddRetry(new () {
ShouldHandle = new PredicateBuilder<int>().Handle<MyException>(),
MaxRetryAttempts = 3,
OnRetry = _ =>
{
actualRetryCount++;
return default;
}
}).Build();
//Act
Outcome<int> actual = await retry.ExecuteOutcomeAsync<int, string>((ctx, stt) => throw new Exception("blah"), context, "a");
ResilienceContextPool.Shared.Return(context);
//Assert
Assert.Equal(expectedRetryCount, actualRetryCount);
Assert.IsType<Exception>(actual.Exception);
}
[Fact]
public async Task ThrowHandledException()
{
//Arrange
const int expectedRetryCount = 3;
int actualRetryCount = 0;
var context = ResilienceContextPool.Shared.Get();
var retry = new ResiliencePipelineBuilder<int>()
.AddRetry(new () {
ShouldHandle = new PredicateBuilder<int>().Handle<MyException>(),
MaxRetryAttempts = 3,
OnRetry = _ =>
{
actualRetryCount++;
return default;
}
}).Build();
//Act
Outcome<int> actual = await retry.ExecuteOutcomeAsync<int, string>((ctx, stt) => throw new MyException(), context, "a");
ResilienceContextPool.Shared.Return(context);
//Assert
Assert.Equal(expectedRetryCount, actualRetryCount);
Assert.IsType<MyException>(actual.Exception);
}
Yes, it seems like we don't need to wrap the Exception explicitly into an Outcome
.
But let's double-check this with @martintmk
from polly.
@peter-csala Your example works by luck because the retry strategy does exception handling inside. if you added other strategy (custom for example) the test would fail.
I did not know that. Thanks for calling it out!
from polly.
Ok, while it works by luck, we don't want to depend on the fact that its works by luck. Someone else might look at this, and do the same with a different strategy, and it doesn't work the same.
Based on the discussion, it feels like it's not a great idea to try to catch exceptions inside of the callback, and throw them as our own custom exception. Keep it simple inside the Polly pipeline? Maybe this is just a personal preference, and not really an anti-pattern.
Not so good?
var outcome = await pipeline.ExecuteOutcomeAsync(static async (_, state) =>
{
try
{
var httpResponseMessage = await state.HttpClient.PostAsJsonAsync(state.Path, state.Content);
return Outcome.FromResult(httpResponseMessage);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(new MyApplicationCustomException("something bad happened", e));
}
}, context, new { HttpClient = httpClient, Content = content, Path = purgePath });
This will interfere with the ShouldHandle
method without looking at the InnerException
of Outcome.Exception
.
Better?
var outcome = await pipeline.ExecuteOutcomeAsync(static async (_, state) =>
{
try
{
var httpResponseMessage = await state.HttpClient.PostAsJsonAsync(state.Path, state.Content);
return Outcome.FromResult(httpResponseMessage);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(e);
}
}, context, new { HttpClient = httpClient, Content = content, Path = purgePath });
// Deal with returning a custom application exception here instead.
from polly.
We can handle this on Polly level, the question is whether we want to take a perf hit @martincostello, @peter-csala ?
from polly.
Is there a way to make this opt-in somehow?
I think we'd only want to "spend" some of our theoretical performance budget on making the change for all usage if this is proving a common stumbling block for people.
from polly.
I think we can agree on that the current situation is sub-optimal. Asking the caller via a documentation comment to wrap the exceptions into Outcome
objects is not ideal.
Even though the use of ExecuteOutcomeAsync
is considered as advanced scenario I still think that every public API should be easy to use, safe and then performant. Because C# allows you to simply throw
the exception rather than requiring to wrap it into an Outcome
object I think it is the library's responsibility to handle this case gracefully as well.
opt-in vs opt-out
I would personally vote for opt-out instead of opt-in. The default behavior should work with throw
statements as well. But when all nanoseconds and bytes matter then you can turn off the "safe guard" and let it run without the try-catch
.
from polly.
This issue is stale because it has been open for 60 days with no activity. It will be automatically closed in 14 days if no further updates are made.
from polly.
Related Issues (20)
- [Question]: Primary Hedging Resiliency Context not being propagated to Action Context? HOT 1
- [Feature request]: StrategyHelper, DisposeHelper, TimeProviderExtensions, TaskHelper should be public not internal HOT 6
- [Question]: Mock Policy Timing behaviour HOT 3
- [Question]: How to use ExecuteAsync In VB.Net? HOT 8
- [Question]: How to respect 429 errors and Retry-After headers in Polly v8? HOT 14
- Remove polyfill packages HOT 7
- Deprecate all packages which are no longer maintained HOT 5
- Consider making Polly easier to consume from F# and Visual Basic HOT 1
- Fix CI for macos-latest HOT 1
- [Feature request]: Conditionally set log levels for specific resilience policy HOT 1
- [Bug]: README has improper grammar, which makes the meaning unclear. HOT 2
- [Question]: Polly v8 AddPolicyHandler HOT 3
- Is there a way to execute an action AFTER a retry in Polly v8? HOT 7
- [Bug]: System.InvalidOperationException thrown when registering with AddResiliencePipeline in .NET 8 Isolated Function App HOT 1
- [Feature request]: Simplified and faster alternative to ExecuteOutcomeAsync HOT 5
- [Docs]: The main category pages are only available through the breadcrumbs HOT 5
- What's the origin of the name Polly? HOT 4
- [Bug]: DelayBackoffType Exponential Broken Sequence HOT 3
- [Bug]: Undocumented breaking changes from Policy.HandleInner to PredicateBuilder.HandleInner HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from polly.