Skip to content

Investigate & fix SemaphoreFullException #1819

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions projects/RabbitMQ.Client/Impl/AsyncRpcContinuations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
//---------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -280,10 +279,20 @@ protected override async Task DoHandleCommandAsync(IncomingCommand cmd)
{
if (cmd.CommandId == ProtocolCommandId.BasicCancelOk)
{
Debug.Assert(_consumerTag == new BasicCancelOk(cmd.MethodSpan)._consumerTag);
await _consumerDispatcher.HandleBasicCancelOkAsync(_consumerTag, CancellationToken)
.ConfigureAwait(false);
_tcs.SetResult(true);
var result = new BasicCancelOk(cmd.MethodSpan);
if (_consumerTag == result._consumerTag)
{
await _consumerDispatcher.HandleBasicCancelOkAsync(_consumerTag, CancellationToken)
.ConfigureAwait(false);
_tcs.SetResult(true);
}
else
{
string msg = string.Format("Consumer tag '{0}' does not match expected consumer tag for basic.cancel operation {1}",
result._consumerTag, _consumerTag);
var ex = new InvalidOperationException(msg);
_tcs.SetException(ex);
}
}
else
{
Expand Down
8 changes: 4 additions & 4 deletions projects/RabbitMQ.Client/Impl/Channel.BasicPublish.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async ValueTask BasicPublishAsync<TProperties>(string exchange, string ro
try
{
publisherConfirmationInfo =
await MaybeStartPublisherConfirmationTracking(cancellationToken)
await MaybeStartPublisherConfirmationTrackingAsync(cancellationToken)
.ConfigureAwait(false);

await MaybeEnforceFlowControlAsync(cancellationToken)
Expand Down Expand Up @@ -93,7 +93,7 @@ await ModelSendAsync(in cmd, in props, body, cancellationToken)
}
finally
{
await MaybeEndPublisherConfirmationTracking(publisherConfirmationInfo, cancellationToken)
await MaybeEndPublisherConfirmationTrackingAsync(publisherConfirmationInfo, cancellationToken)
.ConfigureAwait(false);
}
}
Expand All @@ -107,7 +107,7 @@ public async ValueTask BasicPublishAsync<TProperties>(CachedString exchange, Cac
try
{
publisherConfirmationInfo =
await MaybeStartPublisherConfirmationTracking(cancellationToken)
await MaybeStartPublisherConfirmationTrackingAsync(cancellationToken)
.ConfigureAwait(false);

await MaybeEnforceFlowControlAsync(cancellationToken)
Expand Down Expand Up @@ -148,7 +148,7 @@ await ModelSendAsync(in cmd, in props, body, cancellationToken)
}
finally
{
await MaybeEndPublisherConfirmationTracking(publisherConfirmationInfo, cancellationToken)
await MaybeEndPublisherConfirmationTrackingAsync(publisherConfirmationInfo, cancellationToken)
.ConfigureAwait(false);
}
}
Expand Down
43 changes: 26 additions & 17 deletions projects/RabbitMQ.Client/Impl/Channel.PublisherConfirms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
Expand Down Expand Up @@ -126,7 +125,7 @@ private void ConfigurePublisherConfirmations(bool publisherConfirmationsEnabled,
_outstandingPublisherConfirmationsRateLimiter = outstandingPublisherConfirmationsRateLimiter;
}

private async Task MaybeConfirmSelect(CancellationToken cancellationToken)
private async Task MaybeConfirmSelectAsync(CancellationToken cancellationToken)
{
if (_publisherConfirmationsEnabled)
{
Expand All @@ -148,13 +147,14 @@ private async Task MaybeConfirmSelect(CancellationToken cancellationToken)
enqueued = Enqueue(k);

var method = new ConfirmSelect(false);

await ModelSendAsync(in method, k.CancellationToken)
.ConfigureAwait(false);

bool result = await k;
Debug.Assert(result);

return;
if (false == await k)
{
throw new InvalidOperationException(InternalConstants.BugFound);
}
}
finally
{
Expand All @@ -180,12 +180,14 @@ private void HandleAck(ulong deliveryTag, bool multiple)
{
if (multiple)
{
foreach (KeyValuePair<ulong, TaskCompletionSource<bool>> pair in _confirmsTaskCompletionSources)
foreach (KeyValuePair<ulong, TaskCompletionSource<bool>> pair in _confirmsTaskCompletionSources.ToArray())
{
if (pair.Key <= deliveryTag)
{
pair.Value.SetResult(true);
_confirmsTaskCompletionSources.Remove(pair.Key, out _);
if (_confirmsTaskCompletionSources.TryRemove(pair.Key, out TaskCompletionSource<bool>? tcs))
{
tcs.SetResult(true);
}
}
}
}
Expand All @@ -208,20 +210,22 @@ private void HandleNack(ulong deliveryTag, bool multiple, bool isReturn,
{
if (multiple)
{
foreach (KeyValuePair<ulong, TaskCompletionSource<bool>> pair in _confirmsTaskCompletionSources)
foreach (KeyValuePair<ulong, TaskCompletionSource<bool>> pair in _confirmsTaskCompletionSources.ToArray())
{
if (pair.Key <= deliveryTag)
{
PublishException ex = PublishExceptionFactory.Create(isReturn, pair.Key,
exchange, routingKey, replyCode, replyText);
pair.Value.SetException(ex);
_confirmsTaskCompletionSources.Remove(pair.Key, out _);
if (_confirmsTaskCompletionSources.TryRemove(pair.Key, out TaskCompletionSource<bool>? tcs))
{
PublishException ex = PublishExceptionFactory.Create(isReturn, pair.Key,
exchange, routingKey, replyCode, replyText);
tcs.SetException(ex);
}
}
}
}
else
{
if (_confirmsTaskCompletionSources.Remove(deliveryTag, out TaskCompletionSource<bool>? tcs))
if (_confirmsTaskCompletionSources.TryRemove(deliveryTag, out TaskCompletionSource<bool>? tcs))
{
PublishException ex = PublishExceptionFactory.Create(isReturn, deliveryTag,
exchange, routingKey, replyCode, replyText);
Expand Down Expand Up @@ -289,7 +293,7 @@ await _confirmSemaphore.WaitAsync(reason.CancellationToken)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private async Task<PublisherConfirmationInfo?> MaybeStartPublisherConfirmationTracking(CancellationToken cancellationToken)
private async Task<PublisherConfirmationInfo?> MaybeStartPublisherConfirmationTrackingAsync(CancellationToken cancellationToken)
{
if (_publisherConfirmationsEnabled)
{
Expand Down Expand Up @@ -357,7 +361,7 @@ private bool MaybeHandleExceptionWithEnabledPublisherConfirmations(PublisherConf
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private async Task MaybeEndPublisherConfirmationTracking(PublisherConfirmationInfo? publisherConfirmationInfo,
private async Task MaybeEndPublisherConfirmationTrackingAsync(PublisherConfirmationInfo? publisherConfirmationInfo,
CancellationToken cancellationToken)
{
if (_publisherConfirmationsEnabled)
Expand Down Expand Up @@ -388,6 +392,11 @@ private async Task MaybeEndPublisherConfirmationTracking(PublisherConfirmationIn
await publisherConfirmationInfo.MaybeWaitForConfirmationAsync(cancellationToken)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
_confirmsTaskCompletionSources.TryRemove(publisherConfirmationInfo.PublishSequenceNumber, out _);
throw;
}
finally
{
publisherConfirmationInfo.Dispose();
Expand Down
60 changes: 23 additions & 37 deletions projects/RabbitMQ.Client/Impl/Channel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,7 @@ await ModelSendAsync(in method, k.CancellationToken)
.ConfigureAwait(false);
}

bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);

await ConsumerDispatcher.WaitForShutdownAsync()
.ConfigureAwait(false);
Expand Down Expand Up @@ -387,10 +386,9 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);

await MaybeConfirmSelect(cancellationToken)
await MaybeConfirmSelectAsync(cancellationToken)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
Expand Down Expand Up @@ -465,6 +463,14 @@ await c.HandleCommandAsync(cmd)
}
}

private static void AssertResultIsTrue(bool result)
{
if (false == result)
{
throw new InvalidOperationException(InternalConstants.BugFound);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected ValueTask ModelSendAsync<T>(in T method, CancellationToken cancellationToken) where T : struct, IOutgoingAmqpMethod
{
Expand Down Expand Up @@ -978,8 +984,7 @@ await ModelSendAsync(in method, k.CancellationToken)
await ModelSendAsync(in method, k.CancellationToken)
.ConfigureAwait(false);

bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);
}
catch
{
Expand Down Expand Up @@ -1108,9 +1113,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1143,9 +1146,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1187,9 +1188,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1240,9 +1239,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1286,8 +1283,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1332,8 +1328,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1456,8 +1451,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1587,9 +1581,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1621,9 +1613,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1655,9 +1645,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -1689,9 +1677,7 @@ await ModelSendAsync(in method, k.CancellationToken)

try
{
bool result = await k;
Debug.Assert(result);
return;
AssertResultIsTrue(await k);
}
catch (OperationCanceledException)
{
Expand Down
Loading