question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] SNAT Port Exhaustion with Azure Service Bus

See original GitHub issue

Describe the bug I’m using a Linux Web App that connects to an Azure Service Bus to send messages. The web app is causing SNAT port exhaustion connecting to servicebus.windows.net. It seems connections to servicebus.windows.net are not being reused or closed correctly. Messages are received in another app service. We send under a few hundred messages per hour.

Expected behavior Service Bus connections are reused or closed after use.

Actual behavior (include Exception or Stack Trace) The app is having issues with SNAT port exhaustion causing crashing.

To Reproduce In startup.cs, registration:

services.AddAzureClients(clientsBuilder =>
                {
                    clientsBuilder.AddServiceBusClient(Configuration.GetValue<string>("Queue:ServiceBusConnectionString"))
                      // (Optional) Provide name for instance to retrieve by with DI
                      //.WithName("Client1Name")
                      // (Optional) Override ServiceBusClientOptions (e.g. change retry settings)
                      .ConfigureOptions(options =>
                      {
                          //options.RetryOptions.Delay = TimeSpan.FromMilliseconds(50);
                          options.RetryOptions.MaxDelay = TimeSpan.FromSeconds(5);
                          options.RetryOptions.MaxRetries = 3;
                      });
                });

All queue message creation goes through this class:

    public class QueueService : IQueueService
    {
        private readonly IOptions<AppSettings> options;
        private readonly ILogger<QueueService> logger;
        private readonly ServiceBusClient serviceBusClient;

        public QueueService(IOptions<AppSettings> options, ILogger<QueueService> logger, ServiceBusClient serviceBusClient)
        {
            this.options = options;
            this.logger = logger;
            this.serviceBusClient = serviceBusClient;
        }

        public async Task Enqueue(string queue, string label, string message, bool throwErrors = false)
        {
            if ((options.Value?.Queue?.ServiceBusConnectionString).IsNullOrEmpty())
            {
                logger.LogError($"No service bus connection set for {label}: {message}");
                if (throwErrors)
                {
                    throw new Exception($"No service bus connection set for {label}: {message}");
                }
                return;
            }
            try
            {
                //https://docs.microsoft.com/en-us/dotnet/api/overview/azure/messaging.servicebus-readme-pre
                //Sender should not be disposed
                var sender = serviceBusClient.CreateSender(queue);
                
                var queueMessage = new ServiceBusMessage(Encoding.UTF8.GetBytes(message));
                queueMessage.Subject = label;
                await sender.SendMessageAsync(queueMessage);
            }
            catch (Exception e)
            {
                logger.LogError(e, $"Queue Error for {label}: {message}");
                if (throwErrors)
                {
                    throw;
                }
            }
        }

Environment: Assembly Azure.Messaging.ServiceBus, Version=7.4.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8 Azure Web App on Linux App Service .NET 5.0

image image

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:29 (14 by maintainers)

github_iconTop GitHub Comments

2reactions
mjkentcommented, Oct 21, 2021

This seems to be working better. We’re deploying to test and will deploy to prod later today. Thank you again for your help!

services.AddSingleton<IQueueSenderCache, QueueSenderCache>();

    public class QueueService : IQueueService
    {
        private readonly IOptions<AppSettings> options;
        private readonly ILogger<QueueService> logger;
        private readonly IQueueSenderCache queueSenderCache;

        public QueueService(IOptions<AppSettings> options, ILogger<QueueService> logger, IQueueSenderCache queueSenderCache)
        {
            this.options = options;
            this.logger = logger;
            this.queueSenderCache = queueSenderCache;
        }

        public async Task Enqueue(string queue, string label, string message, bool throwErrors = false)
        {
            if ((options.Value?.Queue?.ServiceBusConnectionString).IsNullOrEmpty())
            {
                logger.LogError($"No service bus connection set for {label}: {message}");
                if (throwErrors)
                {
                    throw new Exception($"No service bus connection set for {label}: {message}");
                }
                return;
            }
            try
            {
                var sender = queueSenderCache.GetSender(queue);
                
                var queueMessage = new ServiceBusMessage(Encoding.UTF8.GetBytes(message));
                queueMessage.Subject = label;
                await sender.SendMessageAsync(queueMessage);
            }
            catch (Exception e)
            {
                logger.LogError(e, $"Queue Error for {label}: {message}");
                if (throwErrors)
                {
                    throw;
                }
            }
        }


    }
public class QueueSenderCache : IQueueSenderCache
    {
        private readonly ServiceBusClient serviceBusClient;
        public ConcurrentDictionary<string, ServiceBusSender> serviceBuses = new ConcurrentDictionary<string, ServiceBusSender>();

        //WARNING:SINGLETON
        public QueueSenderCache(ServiceBusClient serviceBusClient)
        {
            this.serviceBusClient = serviceBusClient;
        }

        public ServiceBusSender GetSender(string queueOrTopicName)
        {
            //https://docs.microsoft.com/en-us/dotnet/api/overview/azure/messaging.servicebus-readme-pre
            //Sender should not be disposed
            return serviceBuses.GetOrAdd(queueOrTopicName, (queueOrTopicName) =>  serviceBusClient.CreateSender(queueOrTopicName) );
        }
    }
1reaction
mjkentcommented, Feb 27, 2022

If someone else stumbles upon this, we found a few ways to fix this:

  1. Switch to Windows App Service
  2. Switch to NAT Gateway
  3. Upgrade to Net 6.0

🤷

Read more comments on GitHub >

github_iconTop Results From Across the Web

Troubleshooting intermittent outbound connection errors in ...
The Azure Network load balancer reclaims SNAT port from closed connections only after waiting for 4 minutes. When applications or functions ...
Read more >
How To Easily Exhaust SNAT Sockets in Your Azure Function
This is done via "Diagnose and solve problems" section in your function app. Choose "Availability and Performance" and then "SNAT Port ...
Read more >
Azure load balancer SNAT behavior explained
When SNAT port exhaustion occurs, application from backend instance won't be able to establish outbound connection to destination, this is ...
Read more >
What is Azure NAT Gateway | SNAT Port Exhaustion - YouTube
Azure NAT Gateway is a Network Address Translation Service. It provides outbound connectivity for one or more subnets of a virtual network.
Read more >
SNAT with App Service
SNAT ports exhaustion. SNAT load balancer is a shared resource between all of App Service sites in the same stamp. Including the web...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found