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] EventHubBufferedProducerClient: high CPU usage when no events are being sent

See original GitHub issue

Library name and version

Azure.Messaging.EventHubs 5.7.0

Describe the bug

We have an application with high event producing rate (at least hundreads per second) and after a load test (peak of 25K events in 1 minute) with duration of 3 minutes, we observed that CPU usage keeps almost the same as the event ‘processing’.

We compared the very same approach/code/test exchanging to an EventHubProducerClient (on same version) and CPU drops seconds after the ‘injection’ of events finishes. In both versions, each queue name has a unique instance of this class/Producer being (re)used to send all events.

Expected behavior

After the load test has stopped - as the the production of events has stopped too - we expect the CPU usage drops to same levels before the test, after a few seconds.

Actual behavior

CPU usage keeps at the almost same level after several minutes of the ending of load test. Only after a process restart the CPU usage has got back to normal levels.

Reproduction Steps

The code is very straighforward (I have edited to remove some internal abstraction details, but nothing relevant):

  • Using EventHubBufferedProducerClient:
    public class SenderQueue<T> : IAsyncDisposable
    {
        private readonly ISerializer<T> _serializer;
        private readonly EventHubBufferedProducerClient _eventHubProducerClient;
        private readonly ILogger _logger;

        public SenderQueue(
            IConfiguration configuration,
            string queueName,
            ISerializer<T> serializer,
            ILogger logger)
        {
            _serializer = serializer;
            _logger = logger;
            _eventHubProducerClient = GetProducerClient(configuration, queueName);
        }

        public virtual async Task EnqueueAsync(T item, CancellationToken cancellationToken = default)
        {
            if (item == null)
                throw new ArgumentNullException(nameof(item));
           var serializedItem = _serializer.Serialize(item);
           await _eventHubProducerClient.EnqueueEventAsync(new EventData(serializedItem), cancellationToken);
        }

        public async ValueTask DisposeAsync()
        {
            _eventHubProducerClient.SendEventBatchSucceededAsync -= SendSuccessfulHandler;
            _eventHubProducerClient.SendEventBatchFailedAsync -= SendFailedHandler;
            await _eventHubProducerClient.DisposeAsync();
        }

        protected EventHubBufferedProducerClient GetProducerClient(IConfiguration configuration, string queueName)
        {
            var eventHubBufferedProducerClient = new EventHubBufferedProducerClient(
                            configuration.AzureEventHubConnectionString,
                            queueName);
            eventHubBufferedProducerClient.SendEventBatchSucceededAsync += SendSuccessfulHandler;
            eventHubBufferedProducerClient.SendEventBatchFailedAsync += SendFailedHandler;

            return eventHubBufferedProducerClient;
        }

        private Task SendSuccessfulHandler(SendEventBatchSucceededEventArgs args)
        {
            _logger.Verbose("Successfully sent {EventBatchCount} events of {ItemType} to EventHub", args.EventBatch.Count, typeof(T).Name);
            return Task.CompletedTask;
        }

        private Task SendFailedHandler(SendEventBatchFailedEventArgs args) 
        {
            _logger.Warning(args.Exception, "Failed to send {EventBatchCount} events of {ItemType} to EventHub", args.EventBatch.Count, typeof(T).Name);
            return Task.CompletedTask;
        }
    }
  • Using EventHubProducerClient:
    public class SenderQueue<T> : IAsyncDisposable
    {
        private readonly ISerializer<T> _serializer;
        private readonly EventHubProducerClient _eventHubProducerClient;
        private readonly ILogger _logger;

        public SenderQueue(
            IConfiguration configuration,
            string queueName,
            ISerializer<T> serializer,
            ILogger logger)
        {
            _serializer = serializer;
            _logger = logger;
            _eventHubProducerClient = GetProducerClient(configuration, queueName);
        }

        public virtual async Task EnqueueAsync(T item, CancellationToken cancellationToken = default)
        {
            if (item == null)
                throw new ArgumentNullException(nameof(item));
            var eventArray = BuildEventData(item);
            await _eventHubProducerClient.SendAsync(eventArray, cancellationToken);
        }

        public async ValueTask DisposeAsync() => await _eventHubProducerClient.DisposeAsync();

        protected EventHubProducerClient GetProducerClient(IConfiguration configuration, string queueName)
        {
            return new EventHubProducerClient(
                configuration.AzureEventHubConnectionString,
                queueName);
        }

        private EventData[] BuildEventData(T item)
        {
            var serializedItem = _serializer.Serialize(item);
            return new[] { new EventData(serializedItem) };
        }
    }

Environment

  • Windows Server 2016 (OS Version: 10.0.14393)
  • .Net Core 3.1 (runtime Microsoft.NETCore.App 3.1.1)
  • Visual Studio 2022

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:3
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
jsquirecommented, Jun 11, 2022

Confirming that we’ve traced this back to a bug in which the task responsible for inspecting the buffers is not backing off during idle periods due to an error in the condition. A fix is in progress.

0reactions
andreminellicommented, Jun 17, 2022

Awesome, thank you!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Troubleshoot Event Hubs issues
High CPU usage is usually because an event processor owns too many partitions. See: Too many partitions are owned.
Read more >
Find High CPU USage Process Names (ISSUE)
First of all, your mistake is that you have empty lines between the different parts of the query so the bottom part doesn't...
Read more >
Azure event hub can't make available all the events in a ...
We thought that Azure Eventhub is a fail-safe way to create events and retrieve them in a reliable way even for large-scale applications ......
Read more >
Azure asp.net core 2.2 getting high CPU usage
I did try to use Kudu profiler but it did not help, most of the time it shows that some methods using 40%...
Read more >
sitemap.xml - Brian Pedersen's Sitecore and .NET Blog
... -pass-access-control-check-no-access-control-allow-origin-header-net-api/ ... https://briancaos.wordpress.com/2021/11/25/sitecore-high-cpu-usage-is-the- ...
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