Search code examples
c#azure-functionsdotnet-httpclientpollyretry-logic

Polly retry delay based on Service Bus Trigger value


I have an Azure function with a servicebustrigger as below

[FunctionName("ServiceBusQueueTriggerCSharp")]                    
public static void Run(
    [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnection")] 
    string myQueueItem,
    Int32 deliveryCount,
    ILogger log)
{
    // Do some stuff with httpclient
}

We are using polly to retry the HTTP call in case of a failure. Polly is configured as part of the DI setup as follows

services.AddHttpClient(ClientNames.HttpClient).SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddPolicyHandler(GetRetryPolicy());

private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
            .WaitAndRetryAsync(1, retryAttempt => TimeSpan.FromSeconds(5 * retryAttempt));
}

I would like the retry delay to be based on "deliveryCount" (the ServiceBus trigger parameter) value instead of retryAttempt which is currently being used.

I'm not able to access deliveryCount during the DI setup phase.

If anyone could provide some guidance on how this can be done, please let me know?


Solution

  • You can create dynamically the policy to use the deliveryCount parameter:

    [FunctionName("ServiceBusQueueTriggerCSharp")]                    
    public static void Run(
        [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnection")] 
        string myQueueItem,
        Int32 deliveryCount,
        ILogger log)
    {
        var policy = GetRetryPolicy(deliveryCount);
        var pollyHandler = new PolicyHttpMessageHandler(policy);
        pollyHandler.InnerHandler = new HttpClientHandler();
    
        var httpClient = new HttpClient(pollyHandler);
        //usage of the `httpClient`
    }
    
    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(int deliveryCount)
    {
        return HttpPolicyExtensions
                .HandleTransientHttpError()
                .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
                .WaitAndRetryAsync(1, _ => TimeSpan.FromSeconds(5 * deliveryCount)); 
    }
    
    1. Create a new retry policy by utilizing the deliveryCount parameter
    2. Create a PolicyHttpMessageHandler by hand
      2.1 The AddPolicyHandler registers a PolicyHttpMessageHandler under the hood
    3. Set the InnerHandler of the previously created handler to an HttpClientHandler
      3.1 For more information check this topic
    4. Pass the policy handler to a new HttpClient
    5. Use the HttpClient instance without the need to explicitly wrap each and every SendAsync or GetAsync calls into an ExecuteAsync