Timeout exception when peeking a session enabled topic/subscription with no active messages

1.1k Views Asked by At

I am writing some code to monitor our azure servicebus queues and topics, I want to notify if we have old messages. My code works except that, if a subscription does not have any active messages in it, then it will hang for one minute and then throw an exception that says:

Unhandled exception. Azure.Messaging.ServiceBus.ServiceBusException: The operation did not complete within the allocated time 00:01:00 for object receiver12. (ServiceTimeout)
---> System.TimeoutException: The operation did not complete within the allocated time 00:01:00 for object receiver12.

Looks like it only happens on session enabled subscriptions. I have tried to first look if there are any messages in the subscription and if so, peek them, but often the messages will be received between I check messages count and the peek so the exception occurs. My monitoring solution does not handle that long timeout. I would just like to retrieve and empty list if the subscription is empty. Have also tried the python implementation and it is the same so there must be something fundamental that I don't get.

Some code:

using Azure.Messaging.ServiceBus;

string connectionString = "Endpoint=sb://xxxxxxxx";
string topicName = "topic";
string subscriptionName = "subscription";


await using var client = new ServiceBusClient(connectionString);

// Exception if subscription does not have any active messages...
ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync(topicName, subscriptionName);

// Peek messages
IReadOnlyList<ServiceBusReceivedMessage> peekedMessages = await receiver.PeekMessagesAsync(10);

foreach (ServiceBusReceivedMessage peekedMessage in peekedMessagesFiltered)
{
    Console.WriteLine(peekedMessage.EnqueuedTime.ToString());

}
2

There are 2 best solutions below

5
On
  • The idea for TimeoutExceptions is to include retries, which should already be happening. These exceptions from log entries should be transient in nature, and your Function should recover and continue to run.
  • The server failed to respond to the requested operation within the time period specified, which is controlled by OperationTimeout. The requested operation may have been completed by the server. Delays in the network or other infrastructure might cause this.
  • To avoid such behaviour, you should implement the retry logic.
    Reference doc: Service Bus messaging exceptions
  • Please see the following link for further information on exception types and how to handle them properly http://go.microsoft.com/fwlink/?LinkId=761101
  • Check the value of the ServicePointManager.DefaultConnectionLimit property, as exceeding it limit may result in a TimeoutException.
  • References - link1, link2, link3
0
On

** DISCLAIMER ** Not best practice to catch an exception this way and not use it.

Wrapping it in a try catch block, gives us the opportunity to retry.

I would also wrap it in a loop, so it would return and pick up a new session of messages. If not anyone else can make a better answer for this, I hope this helps some.

the closeAsync() is for disposing the receiver.

    using Azure.Messaging.ServiceBus;
    
    string connectionString = "Endpoint=sb://xxxxxxxx";
    string topicName = "topic";
    string subscriptionName = "subscription";
    
    
    await using var client = new ServiceBusClient(connectionString);
    do
    {
      // Exception if subscription does not have any active messages...
      ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync(topicName, subscriptionName);
      try
      {

          // Peek messages
          IReadOnlyList<ServiceBusReceivedMessage> peekedMessages = await receiver.PeekMessagesAsync(10);
    
          foreach (ServiceBusReceivedMessage peekedMessage in peekedMessagesFiltered)
          {
            Console.WriteLine(peekedMessage.EnqueuedTime.ToString());
    
          }
            await reciver.CloseAsync();
        }
        catch (ServiceBusException ex)
        {
          Log.Logger.Warning($"Timeout exception caught: {ex.Message}", ex);
        }
     } while (true)