Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback Policy with dynamic result #292

Closed
udlose opened this issue Aug 9, 2017 · 8 comments
Closed

Fallback Policy with dynamic result #292

udlose opened this issue Aug 9, 2017 · 8 comments

Comments

@udlose
Copy link

udlose commented Aug 9, 2017

I was wondering if you have any direction on adding request-based data to a Fallback policy for an API. I have to add some info from the HTTP Headers (specific to each request) to the "default" value returned from the Fallback Policy covering that API. TIA.

@reisenberger
Copy link
Member

@udlose . Do you have (iiuc) that HTTP header information in hand in the code before you execute through the policy? If so, you can achieve what I think you are after by using a Context which travels with each call, to pass information between Execute and the fallbackAction .

All the Execute/Async/AndCapture() execute variants have overloads where you can pass in a Context or dictionary-like data that will get placed on the Context.

FallbackPolicy has configuration overloads where the Context can be an input parameter to a func that returns the fallback value.

If that doesn't cover it (I've had to make some assumptions), please do elaborate and we can help further!

Thanks

@udlose
Copy link
Author

udlose commented Aug 10, 2017

Thank you for your reply! You understood correctly. I do have the information available and will take a look at using the Context. The problem I was trying to solve was avoiding the overhead of creating the policies every time they are used and instead create them upfront and cache them. The problem came when I needed to create a Fallback response that was specific to each request. I wasn't sure if using the context was the right way to go - or using a lambda for the creation of the Fallback response. The latter seemed to hang me up though. I took a look at the Polly samples and didn't see anything using the overload with the lambda using anything other than static data.

@reisenberger
Copy link
Member

👍 @udlose . There are some code examples also in this blog post (they are based around retry, not fallback, but the concepts are the same).

@udlose
Copy link
Author

udlose commented Aug 11, 2017

@reisenberger - so I looked at using the Context to build the Fallback. The problem is that I don't have access to the Context for the fallbackValue parameter - Context (from what I've seen) is only available in the event lambdas. Maybe I'm doing something wrong?

On a different note, it would be great if the construction of the Fallback didn't actually occur until/unless the FallbackPolicy is actually executed. Why allocate memory and CPU to construct something that in most cases isn't used.

var fallbackPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .FallbackAsync(BuildFallback(/*need Context here*/), onFallbackAsync: async (result, context) =>
    {
        await Task.Run(() => _log.Error($"{context.PolicyKey} at {context.ExecutionKey}: fallback value substituted, due to: {result.Exception}."));
    });

//wrap the circuit breaker with a wait/retry
var policyWrap = Policy.WrapAsync(waitAndRetryPolicy, circuitBreakerPolicy, fallbackPolicy)
                        .WithPolicyKey(Key);

@reisenberger
Copy link
Member

Hi. Which version of Polly are you using? The ability to use context in the fallback delegates was added in v5.3.0.

@udlose
Copy link
Author

udlose commented Aug 14, 2017

5.3.1

@reisenberger
Copy link
Member

reisenberger commented Aug 14, 2017

Hi @udlose ! What you're after is covered by Fallback configuration overloads which take a Func to return the fallback value, rather than a pre-computed value.

The principle is shown in the second example here or here.

I've just seen the edit here. Great q. The Func isn't executed unless needed (see this test for confirmation), so this meets:

the construction of the Fallback [doesn't] actually occur until/unless the FallbackPolicy is actually executed

A specific async-TResult overload taking Context as an input parameter for computing the fallback is here.

Using this, you can re-express the policy configuration you quoted as, eg:

var fallbackPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .FallbackAsync(fallbackAction: async (context, token) => await BuildFallbackAsync(context), onFallbackAsync: async (result, context) =>
    {
        await Task.Run(() => _log.Error($"{context.PolicyKey} at {context.ExecutionKey}: fallback value substituted, due to: {result.Exception}."));
    });

EDIT: This earlier comment shows the ExecuteAsync(...) overloads to use to pass in context; also examples in the blog post.

Let us know if that gives you what you need, or if you need anything else!

@udlose
Copy link
Author

udlose commented Aug 15, 2017

Thank you. That seems to work!

@udlose udlose closed this as completed Aug 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants