'Delete messages from MSMQ queue without iterating on all messages

I have a cleanup method triggered by a timer and each time I verify if I reach or not the max size defined in a configuration file.

The cleanup method will delete some messages from the queue when I reach the extra 5% of the max size, in a result, the method will delete hundreds of messages at once and I experience a huge percent of CPU usage at that time, I had an idea to make a Task.Delay between message deletion but I cannot use async / await inside a ReaderWriterLockSlim block, calling the timer more frequently is not a good solution as well since I'll iterate again on some 80 thousand messages, cannot filter the queue to delete them.

Another point is that each time I want to delete the oldest five minutes in the queue.

So, my question is, is there any deferred execution or some other optimization you suggest?

The code below illustrates the problem:

    private void CleanupQueue(object state)
    {
        _lock.EnterWriteLock();

        try
        {
            if (_localQueue != null)
            {
                var filter = new MessagePropertyFilter();         // configure props to read
                filter.ClearAll();                                // don't read any property
                filter.ArrivedTime = true;                        // enable arrived time
                _localQueue.MessageReadPropertyFilter = filter;

                var messages = _localQueue.GetAllMessages();

                if (!messages.Any())
                {
                    return;
                }

                long size = _localQueue.GetSize();

                long maxSize = _localSettings.QueueMaxSize * 1024 * 1024;

                if (size <= maxSize * 1.05)
                {
                    return;
                }

                _localQueue.MessageReadPropertyFilter.Body = true;

                MessageEnumerator enumerator = _localQueue.GetMessageEnumerator2();

                var oldestMessageDate = messages.Min(m => m.ArrivedTime);
                var staleDate = oldestMessageDate.AddMinutes(5); // take oldest 5 minutes  

                while (enumerator.MoveNext(TimeSpan.Zero))
                {
                    if (enumerator.Current is null || enumerator.Current.ArrivedTime > staleDate)
                    {
                        continue;
                    }

                    enumerator.RemoveCurrent();
                    enumerator.Reset();
                }
            }
        }
        catch (Exception e)
        {
            //error handling
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source