Closed danielmarbach closed 3 years ago
By default the size based link credit mode is off. Right now there is no known usage of it. So the queue behaves just as a normal Queue
I did a timeline analysis of a regular ServiceBus SDK receiver some some concurrency
// FYI @JoshLove-msft @jsquire
I did a CPU usage profiling and this is the data I got (the queue is preloaded with enough messages). The test is a console app for net452.
Test code
protected async Task ReceiveOneAsync()
{
while (true)
{
AmqpMessage message = await this.link.ReceiveMessageAsync(this.link.DefaultOpenTimeout);
if (message != null)
{
this.link.AcceptMessage(message, true);
if (!this.Attempt())
{
break;
}
}
}
}
I then started different number of tasks to control concurrency. The CPU% is inclusive and it is 100% for the process.
Concurrency | CPU% of BeginReceiveMessages | CPU% of lock(this.SyncRoot) |
---|---|---|
1 | 8 | 0.06 |
2 | 8.5 | 1.33 |
3 | 9.89 | 3.97 |
5 | 14 | 7 |
10 | 15.29 | 8.38 |
It is as expected. Lock contention increases when concurrency level (# of receive tasks) increases but it is not as high as shown in your timeline profiling. Use ConcurrentQueue may help lock contention but I don't have the data for that yet.
I used a concurrency of 100 in the example that created the screenshot
What is exactly that 100 for? Concurrent tasks, or threads? What is the thread pool size if tasks are used?
I think the current issue is that in non-prefetch mode, the credits are sent very frequently and that is done inside the lock (IssueCredit calls). I moved it out and with 10 tasks, lock contention is reduced to 3%. I am not sure if using ConcurrentQueue for messages will help further here because we still need a lock for accesses to the waiter list. Even if we could make the waiter list lock free , then we would need something else to synchronize the message queue and waiter list.
Yeah good call. Did some further spiking last night too and saw the IssueCredit being the culprit once I swapped to a concurrent queue since that did not help with the lock contention.
I used the Azure Service Bus SDK Processor type. That one has a setting called Max Concurrent alls which I set to 100. Example https://github.com/danielmarbach/AzureServiceBus.DeepDive/blob/master/Receive/Program.cs#L39
I'm not sure if I have set the prefetch settings there since I used a modified version of the above code that I haven't pushed yet. I can double-check later.
Assuming a scenario where prefetch is almost always used, such as with the Event Hubs SDK, would the ConcurrentDictionary
be helpful?
The juggling in a good key on that dictionary might be nontrivial. I need to have a deeper look at the code at some point. Maybe it triggers some good ideas
@xinchen10 I just saw https://github.com/Azure/azure-amqp/commit/31dc27ca8eda003d2a5bcdda77d51f43e67c934b. This is awesome!
Would it be possible to inherit
SizeBasedFlowQueue
from ConcurrentQueue and then reduce the scope ofSyncRoot
to the waiterlist andSetLinkCreditUsingTotalCacheSize
access only to reduce lock contention when the queue has data?