简体   繁体   中英

Azure Function V3 .NET Core and Microsoft.Azure.ServiceBus: The lock supplied is invalid

I am using Azure Function Service Bus Queue Trigger binding to retry my service if there is any exception been thrown.

I have the following in the host.json

 "extensions": {
      "serviceBus": {
        "messageHandlerOptions": {
          "autoComplete": false
        }
      }
    }

My package reference

<ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.15.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="2.5.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.3.0" />
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="3.1.20" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
</ItemGroup>

But keep getting

Microsoft.Azure.ServiceBus: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue, or was received by a different receiver instance.

My Azure Function method

 public async Task Run([ServiceBusTrigger("subprocess", Connection = "sb")] Message message,
                               MessageReceiver messageReceiver,
                               [ServiceBus("subprocess", Connection = "sb")] MessageSender sender,
                               ILogger log)

Here is the complete code

  public class BozaSubProcessServiceBusQueueTrigger
{
    private const string RetryCountUserProperty = "retry-count";
    private const string SubProcessException = "SubProcessException";
    private const string OriginalSequenceNumber = "original-SequenceNumber";
    private static readonly int RetryCount = 5;
    private readonly BozaSubProcessFactory accountTransferProcessFactory;

    public BozaSubProcessServiceBusQueueTrigger(BozaSubProcessFactory bozaProcessFactory)
    {
        this.BozaProcessFactory = bozaProcessFactory;
    }

    [FunctionName("BozaSubProcessServiceBusQueueTrigger")]
    public async Task Run([ServiceBusTrigger("bozasubprocess", Connection = "sb")] Message message,
                           MessageReceiver messageReceiver,
                           [ServiceBus("bozasubprocess", Connection = "sb")] MessageSender sender,
                           ILogger log)
    {
        try
        {
            string body = Encoding.UTF8.GetString(message.Body);
           
            //log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
            if (this.accountTransferProcessFactory.GetBozaProcessService(body) != null)
            {
                log.LogInformation($"Boza Sub Process Invoked");
                await this.accountTransferProcessFactory.GetBozaProcessService(body).ExecuteAsync(body);
                await messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
            }
            else
            {
                log.LogInformation("No any Boza Sub Process Invoked");
            }
        }
        catch (Exception ex)
        {
            if (!message.UserProperties.ContainsKey(RetryCountUserProperty))
            {
                message.UserProperties[RetryCountUserProperty] = 0;
                message.UserProperties[OriginalSequenceNumber] = message.SystemProperties.SequenceNumber;

            }

            if ((int)message.UserProperties[RetryCountUserProperty] < RetryCount)
            {
                Message retryMessage = message.Clone();
                int retryCount = (int)message.UserProperties[RetryCountUserProperty] + 1;
                int interval = 5 * retryCount;
                DateTimeOffset scheduleTime = DateTimeOffset.Now.AddSeconds(interval);
                retryMessage.UserProperties[RetryCountUserProperty] = retryCount;

                retryMessage.UserProperties[SubProcessException] = ex.Message;
                await sender.ScheduleMessageAsync(retryMessage, scheduleTime);
                await messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
                log.LogInformation($"Scheduling message retry {retryCount} to wait {interval} seconds and arrive at {scheduleTime.UtcDateTime}");
            }
            else
            {
                log.LogWarning(message.SystemProperties.LockToken);
                log.LogCritical($"Exhausted all retries for message sequence  {message.UserProperties["original-SequenceNumber"]}");
                await messageReceiver.DeadLetterAsync(message.SystemProperties.LockToken, "Exhausted all retries");
            }
        }

    }
}

Since this exception is raised as part of the service bus extension, you can't directly handle the error (like a try/catch), but can setup retry policies (on top of the default service bus retry). This official doc covers more of this in detail.

source: https://docs.microsoft.com/en-us/answers/questions/274925/service-bus-triggere-azure-function-app-throwing-3.html

Rather than implementing the retry in the catch statement as you're doing, try using the ones provided by the SDK which I believe renew the lock:

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-error-pages?tabs=csharp#fixed-delay-retry

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM