1

I have the following requirement that I can't really figure out how to solve.

Using Polly in C#/.NET: Given an user defined timeout and an internally/system defined retry strategy. How can I get the result from the latest retry attempt when the timeout is expired?

I have come up with this solution, but it seems odd that I have to capture the result like in the following code. If I do not have the timeout + fallback strategy ExecuteAsyc will return the latest attempt by itself.

var pipelineBuilder = new ResiliencePipelineBuilder<(Orders.Contracts.OrderView?, RequestResult)>();

(Orders.Contracts.OrderView?, RequestResult) outerTempResult = new(null, new()); // A non-empty temp result for fallbacks.

var pipeline = pipelineBuilder
    .AddFallback(new()
    {
        ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>().Handle<TimeoutRejectedException>(),
        FallbackAction = args => Outcome.FromResultAsValueTask(outerTempResult),
    })
    .AddTimeout(TimeSpan.FromMilliseconds(options.MaxPollingTimeout)) // Global timeout for the entire pipeline
    .AddRetry(new RetryStrategyOptions<(Orders.Contracts.OrderView?, RequestResult)>
    {
        ShouldHandle = new PredicateBuilder<(Orders.Contracts.OrderView?, RequestResult)>()
                .HandleResult(newOrder =>  newOrder.Item1 == null || newOrder.Item1.Status == S4.Orders.Contracts.OrderStatus.InProgress),
        Delay = TimeSpan.FromMilliseconds(OrderSettings.RetryInterval),
        MaxRetryAttempts = OrderSettings.RetryCount,
        BackoffType = DelayBackoffType.Linear,
        UseJitter = true,
        OnRetry = (args) =>
        {
            outerTempResult = args.Outcome.Result; // Assign this to an outer variable to use in the fallback handling callback.
            return ValueTask.CompletedTask;
        }
    })
.Build();

And the request execution looks like this:

var orderView = await pipeline.ExecuteAsync(async token =>
{
    return await this.orderServiceClient.GetOrderAsync(id);
}, CancellationToken.None);

return orderView.Item2.ToApiResult();

There is probably something obvious I am missing here...

0

1 Answer 1

1

Gladly you don't need outerTempResult. You can use ResilienceContext to pass information between strategies, like this:

var context_key = new ResiliencePropertyKey<(Orders.Contracts.OrderView?, RequestResult)>("LastResult");

var pipeline = new ResiliencePipelineBuilder<(Orders.Contracts.OrderView?, RequestResult)>()
    .AddFallback(new()
    {
        ShouldHandle = ...,
        FallbackAction = args => 
        {
            var lastResult = args.Context.Properties.GetValue(context_key, fallback_value);
            return Outcome.FromResultAsValueTask(lastResult);
        },
    })
    .AddTimeout(...)
    .AddRetry(new RetryStrategyOptions<(Orders.Contracts.OrderView?, RequestResult)>
    {
        ShouldHandle = ...,
        OnRetry = args =>
        {
            args.Context.Properties.Set(context_key, args.Outcome.Result);
            return ValueTask.CompletedTask;
        }
    })
    .Build();

You can find here further information about the context.

Sign up to request clarification or add additional context in comments.

2 Comments

I had to modify the above just a tiny bit, so that instead of Context.SetValue and Context.GetValue it becomes: For setting: var key = new ResiliencePropertyKey<(Orders.Contracts.OrderView?, RequestResult)>("LastResult"); args.Context.Properties.Set(key, args.Outcome.Result); For getting: var key = new ResiliencePropertyKey<(Orders.Contracts.OrderView?, RequestResult)>("LastResult"); var lastResult = args.Context.Properties.GetValue(key, (null, new RequestResult())); Other than that it works :) Thanks.
@EsbenBach Yes, you should use ResiliencePropertyKey not just a simple string. Sorry for the inconvience, I was putting together the example without probing it... I've updated the example code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.