Skip to content

Commit dbbf038

Browse files
committed
Add a test that closes a connection with Toxiproxy
Follow-up / replacement to #1519 * Begin addressing `CA2007` errors (missing `ConfigureAwait`) * Finish addressing `CA2007` errors (missing `ConfigureAwait`) * Increase wait time in test for CI. * Ensure toxiproxy proxy is deleted before trying to create it. * Add debugging of `rabbitmqctl list_connections` * Use correct file for `hashFiles` * Misc other changes from #1519 * Add `.ConfigureAwait(false)` * Add `TestCloseConnection` * Fix `Makefile` * Add `ToxiproxyManager` to allow multiple proxies to be set up in `TestToxiproxy` * Ensure correct port ranges are published in Ubuntu tests * Add re-tries to running external processes * Fix very subtle bug when connection is closed before `basic.ack` is received. * Add debugging to see if `list_connections` is hanging on Windows GHA runner * Init some static vars differently, remove debugging * Add an acceptable inner exception case to the test
1 parent e0367ad commit dbbf038

30 files changed

+548
-316
lines changed

.ci/ubuntu/gha-setup.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ function start_toxiproxy
4343
# sudo ss -4nlp
4444
echo "[INFO] starting Toxiproxy server docker container"
4545
docker rm --force "$toxiproxy_docker_name" 2>/dev/null || echo "[INFO] $toxiproxy_docker_name was not running"
46-
docker run --pull always --detach \
46+
docker run --detach \
4747
--name "$toxiproxy_docker_name" \
4848
--hostname "$toxiproxy_docker_name" \
4949
--publish 8474:8474 \
50-
--publish 55672:55672 \
50+
--publish 55670-55680:55670-55680 \
5151
--network "$docker_network_name" \
52-
'ghcr.io/shopify/toxiproxy:2.7.0'
52+
'ghcr.io/shopify/toxiproxy:latest'
5353
fi
5454
}
5555

@@ -58,7 +58,7 @@ function start_rabbitmq
5858
echo "[INFO] starting RabbitMQ server docker container"
5959
chmod 0777 "$GITHUB_WORKSPACE/.ci/ubuntu/log"
6060
docker rm --force "$rabbitmq_docker_name" 2>/dev/null || echo "[INFO] $rabbitmq_docker_name was not running"
61-
docker run --pull always --detach \
61+
docker run --detach \
6262
--name "$rabbitmq_docker_name" \
6363
--hostname "$rabbitmq_docker_name" \
6464
--publish 5671:5671 \

.editorconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ dotnet_diagnostic.RS0036.severity = none
169169
dotnet_diagnostic.RS0041.severity = none
170170
dotnet_diagnostic.RS0051.severity = error
171171

172+
dotnet_diagnostic.CA2007.severity = error
173+
172174
# C++ Files
173175
[*.{cpp,h,in}]
174176
curly_bracket_next_line = true

.github/workflows/build-test.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
# Note: the cache path is relative to the workspace directory
5858
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#using-the-cache-action
5959
path: ~/installers
60-
key: ${{ runner.os }}-v0-${{ hashFiles('.ci/versions.json') }}
60+
key: ${{ runner.os }}-v0-${{ hashFiles('.ci/windows/versions.json') }}
6161
- name: Download Build (Debug)
6262
uses: actions/download-artifact@v4
6363
with:
@@ -104,7 +104,7 @@ jobs:
104104
# Note: the cache path is relative to the workspace directory
105105
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#using-the-cache-action
106106
path: ~/installers
107-
key: ${{ runner.os }}-v0-${{ hashFiles('.ci/versions.json') }}
107+
key: ${{ runner.os }}-v0-${{ hashFiles('.ci/windows/versions.json') }}
108108
- name: Download Build (Debug)
109109
uses: actions/download-artifact@v4
110110
with:
@@ -190,6 +190,9 @@ jobs:
190190
"${{ github.workspace }}/projects/Test/Integration/Integration.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
191191
- name: Check for errors in RabbitMQ logs
192192
run: ${{ github.workspace}}/.ci/ubuntu/gha-log-check.sh
193+
- name: Maybe collect toxiproxy logs
194+
if: failure()
195+
run: docker logs rabbitmq-dotnet-client-toxiproxy > ${{ github.workspace }}/.ci/ubuntu/log/toxiproxy.log
193196
- name: Maybe upload RabbitMQ logs
194197
if: failure()
195198
uses: actions/upload-artifact@v4

Makefile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,23 @@ RABBITMQ_DOCKER_NAME ?= rabbitmq-dotnet-client-rabbitmq
66
build:
77
dotnet build $(CURDIR)/Build.csproj
88

9+
# Note:
10+
#
11+
# --environment 'GITHUB_ACTIONS=true'
12+
#
13+
# The above argument is passed to `dotnet test` because it's assumed you
14+
# use this command to set up your local environment on Linux:
15+
#
16+
# ./.ci/ubuntu/gha-setup.sh toxiproxy
17+
#
18+
# The gha-setup.sh command has been tested on Ubuntu 22 and Arch Linux
19+
# and should work on any Linux system with a recent docker.
20+
#
921
test:
1022
dotnet test $(CURDIR)/projects/Test/Unit/Unit.csproj --logger 'console;verbosity=detailed'
11-
dotnet test --environment "RABBITMQ_RABBITMQCTL_PATH=DOCKER:$$(docker inspect --format='{{.Id}}' $(RABBITMQ_DOCKER_NAME))" \
12-
--environment 'RABBITMQ_LONG_RUNNING_TESTS=true' $(CURDIR)/projects/Test/Integration/Integration.csproj --logger 'console;verbosity=detailed'
23+
dotnet test --environment 'GITHUB_ACTIONS=true' \
24+
--environment "RABBITMQ_RABBITMQCTL_PATH=DOCKER:$$(docker inspect --format='{{.Id}}' $(RABBITMQ_DOCKER_NAME))" \
25+
--environment 'RABBITMQ_LONG_RUNNING_TESTS=true' \
1326
--environment 'RABBITMQ_TOXIPROXY_TESTS=true' \
1427
--environment 'PASSWORD=grapefruit' \
1528
--environment SSL_CERTS_DIR="$(CURDIR)/.ci/certs" \

projects/Benchmarks/Benchmarks.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
44
<TargetFrameworks>net6.0;net472</TargetFrameworks>
5+
<NoWarn>$(NoWarn);CA2007</NoWarn>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
57
</PropertyGroup>
68

79
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
810
<TargetFramework>net6.0</TargetFramework>
11+
<NoWarn>$(NoWarn);CA2007</NoWarn>
12+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
913
</PropertyGroup>
1014

1115
<PropertyGroup>

projects/RabbitMQ.Client/client/api/InternalConstants.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,5 @@ internal static class InternalConstants
3737
{
3838
internal static readonly TimeSpan DefaultConnectionAbortTimeout = TimeSpan.FromSeconds(5);
3939
internal static readonly TimeSpan DefaultConnectionCloseTimeout = TimeSpan.FromSeconds(30);
40-
41-
internal static string Now => DateTime.UtcNow.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
4240
}
4341
}

projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,8 @@ public Task<uint> ConsumerCountAsync(string queue)
396396

397397
public async Task<uint> QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty, bool noWait)
398398
{
399-
uint result = await InnerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty, noWait);
399+
uint result = await InnerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty, noWait)
400+
.ConfigureAwait(false);
400401
await _connection.DeleteRecordedQueueAsync(queue, recordedEntitiesSemaphoreHeld: false)
401402
.ConfigureAwait(false);
402403
return result;

projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.Recovery.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ await RecoverChannelsAndItsConsumersAsync(recordedEntitiesSemaphoreHeld: true, c
214214
*/
215215
if (_innerConnection?.IsOpen == true)
216216
{
217-
await _innerConnection.AbortAsync(Constants.InternalError, "FailedAutoRecovery", _config.RequestedConnectionTimeout);
217+
await _innerConnection.AbortAsync(Constants.InternalError, "FailedAutoRecovery", _config.RequestedConnectionTimeout)
218+
.ConfigureAwait(false);
218219
}
219220
}
220221
catch (Exception e2)
@@ -286,7 +287,8 @@ await ch.CloseAsync()
286287
try
287288
{
288289
_recordedEntitiesSemaphore.Release();
289-
await _config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync(recordedExchange, ex, this);
290+
await _config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync(recordedExchange, ex, this)
291+
.ConfigureAwait(false);
290292
}
291293
finally
292294
{
@@ -373,7 +375,8 @@ await _recordedEntitiesSemaphore.WaitAsync()
373375
try
374376
{
375377
_recordedEntitiesSemaphore.Release();
376-
await _config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync(recordedQueue, ex, this);
378+
await _config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync(recordedQueue, ex, this)
379+
.ConfigureAwait(false);
377380
}
378381
finally
379382
{
@@ -445,7 +448,8 @@ await ch.CloseAsync()
445448
try
446449
{
447450
_recordedEntitiesSemaphore.Release();
448-
await _config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync(binding, ex, this);
451+
await _config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync(binding, ex, this)
452+
.ConfigureAwait(false);
449453
}
450454
finally
451455
{
@@ -521,7 +525,8 @@ await _recordedEntitiesSemaphore.WaitAsync()
521525
try
522526
{
523527
_recordedEntitiesSemaphore.Release();
524-
await _config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync(consumer, ex, this);
528+
await _config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync(consumer, ex, this)
529+
.ConfigureAwait(false);
525530
}
526531
finally
527532
{

projects/RabbitMQ.Client/client/impl/ChannelBase.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,8 @@ protected async Task<bool> HandleConnectionStartAsync(IncomingCommand cmd, Cance
846846
var reason = new ShutdownEventArgs(ShutdownInitiator.Library, Constants.CommandInvalid, "Unexpected Connection.Start");
847847
await Session.Connection.CloseAsync(reason, false,
848848
InternalConstants.DefaultConnectionCloseTimeout,
849-
cancellationToken);
849+
cancellationToken)
850+
.ConfigureAwait(false);
850851
}
851852
else
852853
{
@@ -1045,12 +1046,14 @@ public async ValueTask BasicPublishAsync<TProperties>(string exchange, string ro
10451046
{
10461047
BasicProperties props = PopulateActivityAndPropagateTraceId(basicProperties, sendActivity);
10471048
// TODO cancellation token
1048-
await ModelSendAsync(in cmd, in props, body, CancellationToken.None);
1049+
await ModelSendAsync(in cmd, in props, body, CancellationToken.None)
1050+
.ConfigureAwait(false);
10491051
}
10501052
else
10511053
{
10521054
// TODO cancellation token
1053-
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None);
1055+
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None)
1056+
.ConfigureAwait(false);
10541057
}
10551058
}
10561059
catch
@@ -1107,12 +1110,14 @@ public async void BasicPublish<TProperties>(CachedString exchange, CachedString
11071110
{
11081111
BasicProperties props = PopulateActivityAndPropagateTraceId(basicProperties, sendActivity);
11091112
// TODO cancellation token
1110-
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None);
1113+
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None)
1114+
.ConfigureAwait(false);
11111115
}
11121116
else
11131117
{
11141118
// TODO cancellation token
1115-
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None);
1119+
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None)
1120+
.ConfigureAwait(false);
11161121
}
11171122
}
11181123
catch
@@ -1155,12 +1160,14 @@ public async ValueTask BasicPublishAsync<TProperties>(CachedString exchange, Cac
11551160
{
11561161
BasicProperties props = PopulateActivityAndPropagateTraceId(basicProperties, sendActivity);
11571162
// TODO cancellation token
1158-
await ModelSendAsync(in cmd, in props, body, CancellationToken.None);
1163+
await ModelSendAsync(in cmd, in props, body, CancellationToken.None)
1164+
.ConfigureAwait(false);
11591165
}
11601166
else
11611167
{
11621168
// TODO cancellation token
1163-
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None);
1169+
await ModelSendAsync(in cmd, in basicProperties, body, CancellationToken.None)
1170+
.ConfigureAwait(false);
11641171
}
11651172
}
11661173
catch
@@ -1564,13 +1571,15 @@ await ModelSendAsync(method, k.CancellationToken)
15641571

15651572
public async Task<uint> MessageCountAsync(string queue)
15661573
{
1567-
QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue);
1574+
QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue)
1575+
.ConfigureAwait(false);
15681576
return ok.MessageCount;
15691577
}
15701578

15711579
public async Task<uint> ConsumerCountAsync(string queue)
15721580
{
1573-
QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue);
1581+
QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue)
1582+
.ConfigureAwait(false);
15741583
return ok.ConsumerCount;
15751584
}
15761585

projects/RabbitMQ.Client/client/impl/Connection.Commands.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ await _frameHandler.SendProtocolHeaderAsync(cancellationToken)
117117
* FinishCloseAsync will cancel the main loop
118118
*/
119119
MaybeTerminateMainloopAndStopHeartbeatTimers();
120-
await FinishCloseAsync(cancellationToken);
120+
await FinishCloseAsync(cancellationToken)
121+
.ConfigureAwait(false);
121122
throw new ProtocolVersionMismatchException(Protocol.MajorVersion, Protocol.MinorVersion, serverVersion.Major, serverVersion.Minor);
122123
}
123124

@@ -183,7 +184,8 @@ await _frameHandler.SendProtocolHeaderAsync(cancellationToken)
183184
uint heartbeatInSeconds = NegotiatedMaxValue((uint)_config.HeartbeatInterval.TotalSeconds, (uint)connectionTune.m_heartbeatInSeconds);
184185
Heartbeat = TimeSpan.FromSeconds(heartbeatInSeconds);
185186

186-
await _channel0.ConnectionTuneOkAsync(channelMax, frameMax, (ushort)Heartbeat.TotalSeconds, cancellationToken);
187+
await _channel0.ConnectionTuneOkAsync(channelMax, frameMax, (ushort)Heartbeat.TotalSeconds, cancellationToken)
188+
.ConfigureAwait(false);
187189

188190
// TODO check for cancellation
189191
MaybeStartCredentialRefresher();

projects/RabbitMQ.Client/client/impl/Connection.Receive.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ await HardProtocolExceptionHandlerAsync(hpe, mainLoopToken)
8787
}
8888

8989
using var cts = new CancellationTokenSource(InternalConstants.DefaultConnectionCloseTimeout);
90-
await FinishCloseAsync(cts.Token);
90+
await FinishCloseAsync(cts.Token)
91+
.ConfigureAwait(false);
9192
}
9293

9394
private async Task ReceiveLoopAsync(CancellationToken mainLoopCancelllationToken)
@@ -194,7 +195,8 @@ private async Task HardProtocolExceptionHandlerAsync(HardProtocolException hpe,
194195
if (SetCloseReason(hpe.ShutdownReason))
195196
{
196197
OnShutdown(hpe.ShutdownReason);
197-
await _session0.SetSessionClosingAsync(false);
198+
await _session0.SetSessionClosingAsync(false)
199+
.ConfigureAwait(false);
198200
try
199201
{
200202
var cmd = new ConnectionClose(hpe.ShutdownReason.ReplyCode, hpe.ShutdownReason.ReplyText, 0, 0);

projects/RabbitMQ.Client/client/impl/Connection.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ internal async Task CloseAsync(ShutdownEventArgs reason, bool abort, TimeSpan ti
322322
cancellationToken.ThrowIfCancellationRequested();
323323

324324
OnShutdown(reason);
325-
await _session0.SetSessionClosingAsync(false);
325+
await _session0.SetSessionClosingAsync(false)
326+
.ConfigureAwait(false);
326327

327328
try
328329
{

projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcher.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ protected override async Task ProcessChannelAsync(CancellationToken token)
3131
case WorkType.Deliver:
3232
await consumer.HandleBasicDeliverAsync(
3333
consumerTag, work.DeliveryTag, work.Redelivered,
34-
work.Exchange, work.RoutingKey, work.BasicProperties, work.Body.Memory);
34+
work.Exchange, work.RoutingKey, work.BasicProperties, work.Body.Memory)
35+
.ConfigureAwait(false);
3536
break;
3637
case WorkType.Cancel:
3738
consumer.HandleBasicCancel(consumerTag);

projects/Test/Applications/CreateChannel/CreateChannel.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
44
<TargetFrameworks>net6.0;net472</TargetFrameworks>
5+
<NoWarn>$(NoWarn);CA2007</NoWarn>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
57
</PropertyGroup>
68

79
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
810
<TargetFramework>net6.0</TargetFramework>
11+
<NoWarn>$(NoWarn);CA2007</NoWarn>
12+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
913
</PropertyGroup>
1014

1115
<PropertyGroup>

projects/Test/Applications/MassPublish/MassPublish.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
44
<TargetFrameworks>net6.0;net472</TargetFrameworks>
5+
<NoWarn>$(NoWarn);CA2007</NoWarn>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
57
</PropertyGroup>
68

79
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
810
<TargetFramework>net6.0</TargetFramework>
11+
<NoWarn>$(NoWarn);CA2007</NoWarn>
12+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
913
</PropertyGroup>
1014

1115
<PropertyGroup>

projects/Test/Common/Common.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
44
<TargetFrameworks>net6.0;net472</TargetFrameworks>
5+
<NoWarn>$(NoWarn);CA2007</NoWarn>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
57
</PropertyGroup>
68

79
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
810
<TargetFramework>net6.0</TargetFramework>
11+
<NoWarn>$(NoWarn);CA2007</NoWarn>
12+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
913
</PropertyGroup>
1014

1115
<PropertyGroup>

0 commit comments

Comments
 (0)