14

I'm using Microsoft.Azure.ServiceBus. (doc)

I was getting an exception of:

The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue.

By the help of these questions:

1, 2, 3,

I am able to avoid the Exception by setting the AutoComplete to false and by increment the Azure's queue lock duration to its max (from 30 seconds to 5 minutes).

_queueClient.RegisterMessageHandler(ProcessMessagesAsync, new 
                         MessageHandlerOptions(ExceptionReceivedHandler)
                         {
                             MaxConcurrentCalls = 1,
                             MaxAutoRenewDuration = TimeSpan.FromSeconds(10),
                             AutoComplete = false
                         }
);

private async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
    await ProccesMessage(message);
}

private async Task ProccesMessage(Message message)
{
    //The complete should be closed before long-timed process
    await _queueClient.CompleteAsync(message.SystemProperties.LockToken);
    await DoFoo(message.Body); //some long running process
}

My questions are:

    1. This answer suggested that the exception was raised because the lock was being expired before the long time process, but in my case I was marking the message as complete immediately (before the long run process), so I'm not sure why changing the locking duration from azure made any difference? when I change it back to 30 seconds I can see the exception again.
    1. Not sure if it related to the question but what is the purpose MaxAutoRenewDuration, the official docs is The maximum duration during which locks are automatically renewed.. If in my case I have only one app receiver that en-queue from this queue, so is it not needed because I do not need to lock the message from another app to capture it? and why this value should be greater than the longest message lock duration?
Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91

1 Answers1

28

There are a few things you need to consider.

  1. Lock duration
  2. Total time since a message acquired from the broker

The lock duration is simple - for how long a single competing consumer can lease a message w/o having that message leased to any other competing consumer.

The total time is a bit tricker. Your callback ProcessMessagesAsync registered with to receive the message is not the only thing that is involved. In the code sample, you've provided, you're setting the concurrency to 1. If there's a prefetch configured (queue gets more than one message with every request for a message or several), the lock duration clock on the server starts ticking for all those messages. So if your processing is done slightly under MaxLockDuration but for the same of example, the last prefetched message was waiting to get processed too long, even if it's done within less than lock duration time, it might lose its lock and the exception will be thrown when attempting completion of that message.

This is where MaxAutoRenewDuration comes into the game. What it does is extends the message lease with the broker, "re-locking" it for the competing consumer that is currently handling the message. MaxAutoRenewDuration should be set to the "possibly maximum processing time a lease will be required". In your sample, it's set to TimeSpan.FromSeconds(10) which is extremely low. It needs to be set to be at least longer than the MaxLockDuration and adjusted to the longest period of time ProccesMessage will need to run. Taking prefetching into consideration.

To help to visualize it, think of the client-side having an in-memory queue where the messages can be stored while you perform the serial processing of the messages one by one in your handler. Lease starts the moment a message arrives from the broker to that in-memory queue. If the total time in the in-memory queue plus the processing exceeds the lock duration, the lease is lost. Your options are:

  1. Enable concurrent processing by setting MaxConcurrentCalls > 1
  2. Increase MaxLockDuration
  3. Reduce message prefetch (if you use it)
  4. Configure MaxAutoRenewDuration to renew the lock and overcome the MaxLockDuration constraint

Note about #4 - it's not a guaranteed operation. Therefore there's a chance a call to the broker will fail and message lock will not be extended. I recommend designing your solutions to work within the lock duration limit. Alternatively, persist message information so that your processing doesn't have to be constrained by the messaging.

Sean Feldman
  • 23,443
  • 7
  • 55
  • 80
  • Hey @sean great answer, about adjusted to the longest period of time ProccesMessage will need to run, why is it relevant in my case? I'm marking it as complete before. – Shahar Shokrani Feb 25 '20 at 05:31
  • 1
    Thank you. In your case, `MaxAutoRenewDuration = TimeSpan.FromSeconds(10),` should be omitted and not configured at all. That way the SDK will not try to extend the lock. Also, I've seen side effects when `MaxAutoRenewDuration` would be set to a shorter period of time than the `MaxLockDuraitons`. – Sean Feldman Feb 25 '20 at 06:48
  • @SeanFeldman Any idea upon https://stackoverflow.com/questions/63171548/control-azure-service-bus-queue-message-reception – Darey Jul 30 '20 at 11:15
  • 1
    Added an answer. Your requirement of two messages only is the key. It cannot be concurrent, rather batched. – Sean Feldman Jul 30 '20 at 15:17
  • 1
    `It needs to be set to be at least longer than the MaxLockDuration` This implies that you would want the lock to expire while its running, which seems backwards from how the documents specify its use – Captain Prinny Feb 03 '22 at 14:50
  • `MaxAutoRenewDuration` has to be longer than `MaxLockDuration` otherwise what to extend there? I suggest trying it and seeing for yourself. Documentation can be confusing at times. – Sean Feldman Feb 03 '22 at 15:18
  • Also, the lock won't expire. It will be renewed *automatically*. That's the whole point of this `MaxAutoRenewDuration` setting. – Sean Feldman Feb 03 '22 at 15:19
  • 1
    This is so weird. What's the point of setting `MaxAutoRenewDuration` to greater than `MaxLockDuration`. Doesn't that guarantee that the lock will expire before the renew happens? – Martin Wickman Mar 22 '22 at 15:10
  • That was addressed in the answer. – Sean Feldman Mar 22 '22 at 15:55
  • MaxLockDuration is 1 minute and MaxAutoRenewDDuration is 5 minutes. But still I am getting "The session lock has expired on the MessageSession. Accept a new MessageSession." some times. Any Idea how to handle that case. Do we need to catch with catch (SessionLockLostException). Any suggestions – laxman Apr 08 '22 at 11:35
  • Sessions have their own timeout, `SessionIdleTimeout`. – Sean Feldman Apr 08 '22 at 14:02
  • I am using ServiceBusTrigger class. I did not find SessionIdleTimeout. – laxman Apr 11 '22 at 12:43
  • I created azure ServiceBusTrigger function app to process message. I am unable to find SessionIdleTimeout in that class. Tried to reproduce the issue "The session lock has expired on the MessageSession.Accept a new MessageSession" in ServiceBusTrigger. Only it is coming MaxLockDuration > MaxAutoRenewDuration and process not completed with in MaxLockDuration. But this error should not appear when MaxLockDuration < MaxAutoRenewDuration. There is open git hub issue on it https://github.com/Azure/azure-sdk-for-net/issues/26755. Any suggestions – laxman Apr 11 '22 at 12:49
  • @laxman, ask a separate question. It's clearly not the same as the original question. – Sean Feldman Apr 11 '22 at 13:40