Skip to content

Commit 35314d9

Browse files
committed
Spike an exception based approach (misses removing the bool value return type)
1 parent a11e32d commit 35314d9

File tree

1 file changed

+54
-88
lines changed

1 file changed

+54
-88
lines changed

projects/RabbitMQ.Client/Impl/ChannelBase.cs

Lines changed: 54 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
using System;
3333
using System.Buffers.Binary;
34+
using System.Collections.Concurrent;
3435
using System.Collections.Generic;
3536
using System.Diagnostics;
3637
using System.Diagnostics.CodeAnalysis;
@@ -43,6 +44,7 @@
4344
using RabbitMQ.Client.Events;
4445
using RabbitMQ.Client.Exceptions;
4546
using RabbitMQ.Client.Framing;
47+
using RabbitMQ.Client.Util;
4648

4749
namespace RabbitMQ.Client.Impl
4850
{
@@ -62,10 +64,7 @@ internal abstract class ChannelBase : IChannel, IRecoverable
6264
private bool _publisherConfirmationTrackingEnabled = false;
6365
private ulong _nextPublishSeqNo = 0;
6466
private readonly SemaphoreSlim _confirmSemaphore = new(1, 1);
65-
private readonly LinkedList<ulong> _pendingDeliveryTags = new();
66-
private readonly Dictionary<ulong, TaskCompletionSource<bool>> _confirmsTaskCompletionSources = new();
67-
68-
private bool _onlyAcksReceived = true;
67+
private readonly ConcurrentDictionary<ulong, TaskCompletionSource<bool>> _confirmsTaskCompletionSources = new();
6968

7069
private ShutdownEventArgs? _closeReason;
7170
public ShutdownEventArgs? CloseReason => Volatile.Read(ref _closeReason);
@@ -505,7 +504,7 @@ await _confirmSemaphore.WaitAsync(reason.CancellationToken)
505504
.ConfigureAwait(false);
506505
try
507506
{
508-
if (_confirmsTaskCompletionSources?.Count > 0)
507+
if (!_confirmsTaskCompletionSources.IsEmpty)
509508
{
510509
var exception = new AlreadyClosedException(reason);
511510
foreach (TaskCompletionSource<bool> confirmsTaskCompletionSource in _confirmsTaskCompletionSources.Values)
@@ -615,7 +614,7 @@ await _basicAcksAsyncWrapper.InvokeAsync(this, args)
615614
.ConfigureAwait(false);
616615
}
617616

618-
await HandleAckNack(ack._deliveryTag, ack._multiple, false, cancellationToken)
617+
await HandleAck(ack._deliveryTag, ack._multiple, cancellationToken)
619618
.ConfigureAwait(false);
620619

621620
return true;
@@ -633,7 +632,7 @@ await _basicNacksAsyncWrapper.InvokeAsync(this, args)
633632
.ConfigureAwait(false);
634633
}
635634

636-
await HandleAckNack(nack._deliveryTag, nack._multiple, true, cancellationToken)
635+
await HandleNack(nack._deliveryTag, nack._multiple, cancellationToken)
637636
.ConfigureAwait(false);
638637

639638
return true;
@@ -657,20 +656,14 @@ await _basicReturnAsyncWrapper.InvokeAsync(this, e)
657656
{
658657
ulong publishSequenceNumber = 0;
659658
IReadOnlyBasicProperties props = e.BasicProperties;
660-
if (props.Headers is not null)
659+
object? maybeSeqNum = props.Headers?[Constants.PublishSequenceNumberHeader];
660+
if (maybeSeqNum != null)
661661
{
662-
object? maybeSeqNum = props.Headers[Constants.PublishSequenceNumberHeader];
663-
if (maybeSeqNum is not null)
664-
{
665-
publishSequenceNumber = BinaryPrimitives.ReadUInt64BigEndian((byte[])maybeSeqNum);
666-
}
662+
publishSequenceNumber = BinaryPrimitives.ReadUInt64BigEndian((byte[])maybeSeqNum);
667663
}
668664

669-
if (publishSequenceNumber != 0 && _publisherConfirmationTrackingEnabled)
670-
{
671-
await HandleAckNack(publishSequenceNumber, false, true, cancellationToken)
672-
.ConfigureAwait(false);
673-
}
665+
await HandleNack(publishSequenceNumber, false, cancellationToken)
666+
.ConfigureAwait(false);
674667
}
675668

676669
return true;
@@ -1022,7 +1015,6 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
10221015

10231016
if (_publisherConfirmationTrackingEnabled)
10241017
{
1025-
_pendingDeliveryTags.AddLast(publishSequenceNumber);
10261018
publisherConfirmationTcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
10271019
_confirmsTaskCompletionSources[publishSequenceNumber] = publisherConfirmationTcs;
10281020
}
@@ -1068,9 +1060,9 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
10681060
try
10691061
{
10701062
_nextPublishSeqNo--;
1071-
if (_publisherConfirmationTrackingEnabled && _pendingDeliveryTags is not null)
1063+
if (_publisherConfirmationTrackingEnabled)
10721064
{
1073-
_pendingDeliveryTags.RemoveLast();
1065+
_confirmsTaskCompletionSources.TryRemove(publishSequenceNumber, out _);
10741066
}
10751067
}
10761068
finally
@@ -1119,7 +1111,6 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
11191111

11201112
if (_publisherConfirmationTrackingEnabled)
11211113
{
1122-
_pendingDeliveryTags.AddLast(publishSequenceNumber);
11231114
publisherConfirmationTcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
11241115
_confirmsTaskCompletionSources[publishSequenceNumber] = publisherConfirmationTcs;
11251116
}
@@ -1164,9 +1155,9 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
11641155
try
11651156
{
11661157
_nextPublishSeqNo--;
1167-
if (_publisherConfirmationTrackingEnabled && _pendingDeliveryTags is not null)
1158+
if (_publisherConfirmationTrackingEnabled)
11681159
{
1169-
_pendingDeliveryTags.RemoveLast();
1160+
_confirmsTaskCompletionSources.TryRemove(publishSequenceNumber, out _);
11701161
}
11711162
}
11721163
finally
@@ -1189,6 +1180,7 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
11891180
{
11901181
await publisherConfirmationTcs.Task.WaitAsync(cancellationToken)
11911182
.ConfigureAwait(false);
1183+
11921184
return await publisherConfirmationTcs.Task
11931185
.ConfigureAwait(false);
11941186
}
@@ -1770,7 +1762,6 @@ private async Task ConfirmSelectAsync(bool publisherConfirmationTrackingEnablefd
17701762
{
17711763
if (_publisherConfirmationTrackingEnabled)
17721764
{
1773-
_pendingDeliveryTags.Clear();
17741765
_confirmsTaskCompletionSources.Clear();
17751766
}
17761767
_nextPublishSeqNo = 1;
@@ -1796,84 +1787,57 @@ await ModelSendAsync(in method, k.CancellationToken)
17961787
}
17971788
}
17981789

1799-
// TODO NOTE: this method used to be internal for its use in this test:
1800-
// TestWaitForConfirmsWithTimeoutAsync_MessageNacked_WaitingHasTimedout_ReturnFalse
1801-
private async Task HandleAckNack(ulong deliveryTag, bool multiple, bool isNack, CancellationToken cancellationToken = default)
1790+
private Task HandleAck(ulong deliveryTag, bool multiple, CancellationToken cancellationToken = default)
18021791
{
1803-
bool isAck = false == isNack;
1804-
1805-
// Only do this if confirms are enabled *and* the library is tracking confirmations
1806-
if (_publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled)
1792+
if (_publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled && deliveryTag > 0 && !_confirmsTaskCompletionSources.IsEmpty)
18071793
{
1808-
// let's take a lock so we can assume that deliveryTags are unique, never duplicated and always sorted
1809-
await _confirmSemaphore.WaitAsync(cancellationToken)
1810-
.ConfigureAwait(false);
1811-
try
1794+
if (multiple)
18121795
{
1813-
// No need to do anything if there are no delivery tags in the list
1814-
if (_pendingDeliveryTags.Count > 0)
1796+
foreach (var pair in _confirmsTaskCompletionSources)
18151797
{
1816-
if (multiple)
1817-
{
1818-
do
1819-
{
1820-
if (_pendingDeliveryTags.First is null)
1821-
{
1822-
break;
1823-
}
1824-
else
1825-
{
1826-
ulong pendingDeliveryTag = _pendingDeliveryTags.First.Value;
1827-
if (pendingDeliveryTag > deliveryTag)
1828-
{
1829-
break;
1830-
}
1831-
else
1832-
{
1833-
TaskCompletionSource<bool> tcs = _confirmsTaskCompletionSources[pendingDeliveryTag];
1834-
tcs.SetResult(isAck);
1835-
_confirmsTaskCompletionSources.Remove(pendingDeliveryTag);
1836-
_pendingDeliveryTags.RemoveFirst();
1837-
}
1838-
}
1839-
}
1840-
while (true);
1841-
}
1842-
else
1798+
if (pair.Key <= deliveryTag)
18431799
{
1844-
/*
1845-
* Note:
1846-
* In the case of `basic.return`, the TCS will have been handled and removed by HandleBasicReturn()
1847-
* RabbitMQ still sends `basic.ack`, so the TCS will not be in the dict, hence, TryGetValue here
1848-
*/
1849-
if (_confirmsTaskCompletionSources.TryGetValue(deliveryTag, out TaskCompletionSource<bool>? tcs))
1850-
{
1851-
tcs.SetResult(isAck);
1852-
_confirmsTaskCompletionSources.Remove(deliveryTag);
1853-
_pendingDeliveryTags.Remove(deliveryTag);
1854-
}
1800+
pair.Value.SetResult(true);
1801+
_confirmsTaskCompletionSources.Remove(pair.Key, out _);
18551802
}
18561803
}
1804+
}
1805+
else
1806+
{
1807+
if (_confirmsTaskCompletionSources.TryRemove(deliveryTag, out TaskCompletionSource<bool>? tcs))
1808+
{
1809+
tcs.SetResult(true);
1810+
}
1811+
}
1812+
}
1813+
return Task.CompletedTask;
1814+
}
18571815

1858-
_onlyAcksReceived = _onlyAcksReceived && isAck;
1859-
1860-
if (_pendingDeliveryTags.Count == 0 && _confirmsTaskCompletionSources.Count > 0)
1816+
private Task HandleNack(ulong deliveryTag, bool multiple, CancellationToken cancellationToken = default)
1817+
{
1818+
if (_publisherConfirmationsEnabled && _publisherConfirmationTrackingEnabled && deliveryTag > 0 && !_confirmsTaskCompletionSources.IsEmpty)
1819+
{
1820+
if (multiple)
1821+
{
1822+
foreach (var pair in _confirmsTaskCompletionSources)
18611823
{
1862-
// Done, mark tasks
1863-
foreach (TaskCompletionSource<bool> tcs in _confirmsTaskCompletionSources.Values)
1824+
if (pair.Key <= deliveryTag)
18641825
{
1865-
tcs.TrySetResult(_onlyAcksReceived);
1826+
pair.Value.SetException(new Exception("TBD"));
1827+
_confirmsTaskCompletionSources.Remove(pair.Key, out _);
18661828
}
1867-
1868-
_confirmsTaskCompletionSources.Clear();
1869-
_onlyAcksReceived = true;
18701829
}
18711830
}
1872-
finally
1831+
else
18731832
{
1874-
_confirmSemaphore.Release();
1833+
if (_confirmsTaskCompletionSources.Remove(deliveryTag, out TaskCompletionSource<bool>? tcs))
1834+
{
1835+
tcs.SetException(new Exception("TBD"));
1836+
}
18751837
}
18761838
}
1839+
1840+
return Task.CompletedTask;
18771841
}
18781842

18791843
private BasicProperties? PopulateBasicPropertiesHeaders<TProperties>(TProperties basicProperties,
@@ -1948,7 +1912,9 @@ void MaybeAddPublishSequenceNumberToHeaders(IDictionary<string, object?> headers
19481912
publishSequenceNumberBytes = BitConverter.GetBytes(publishSequenceNumber);
19491913
}
19501914

1951-
headers[Constants.PublishSequenceNumberHeader] = publishSequenceNumberBytes;
1915+
var ulongByte = new byte[8];
1916+
NetworkOrderSerializer.WriteUInt64(ref ulongByte.AsSpan().GetStart(), publishSequenceNumber);
1917+
headers[Constants.PublishSequenceNumberHeader] = ulongByte;
19521918
}
19531919
}
19541920
}

0 commit comments

Comments
 (0)