Skip to content
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
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 5.7.3
- Fixed
- Corrected the shutdown `OperationCanceledException` guard introduced in 5.7.2. The previous guard checked `oce.CancellationToken.IsCancellationRequested`, which was always `false` because the Azure ServiceBus SDK raises `ProcessErrorAsync` with `CancellationToken.None` during processor shutdown. The guard now matches any `OperationCanceledException`, which is safe since genuine transport errors surface as `ServiceBusException`.

## 5.7.2
- Fixed
- Suppressed spurious `OperationCanceledException` / `TaskCanceledException` APM error entries during pod graceful shutdown. When the Service Bus receive loop is cancelled with a requested cancellation token, the exception is now logged at `Warning` level instead of `Error`.
Expand Down
2 changes: 1 addition & 1 deletion src/Ev.ServiceBus/Management/Wrappers/ReceiverWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private void TrySetReceptionRegistrationOnContext(MessageContext context, IServi
/// <returns></returns>
protected async Task OnExceptionOccured(ProcessErrorEventArgs exceptionEvent)
{
if (exceptionEvent.Exception is OperationCanceledException oce && oce.CancellationToken.IsCancellationRequested)
if (exceptionEvent.Exception is OperationCanceledException)
{
_messageProcessingLogger.LogWarning(
"[Ev.ServiceBus] Receive loop cancelled for {ClientType} '{ResourceId}' during shutdown.",
Expand Down
34 changes: 29 additions & 5 deletions tests/Ev.ServiceBus.UnitTests/ReceiverWrapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,44 @@ private static TestableReceiverWrapper CreateWrapper(ILogger<LoggingExtensions.M
}

[Fact]
public async Task OnExceptionOccured_WithCancelledToken_DoesNotLogError()
public async Task OnExceptionOccured_WithOperationCanceledException_DoesNotLogError()
{
var mockLogger = new Mock<ILogger<LoggingExtensions.MessageProcessing>>();
var wrapper = CreateWrapper(mockLogger.Object);

using var cts = new CancellationTokenSource();
cts.Cancel();
// Azure SDK raises ProcessErrorAsync with CancellationToken.None during shutdown —
// the token on the exception is not the shutdown token, so IsCancellationRequested is false.
var args = new ProcessErrorEventArgs(
new OperationCanceledException("shutdown", CancellationToken.None),
ServiceBusErrorSource.Receive,
"test-namespace",
"test-queue",
CancellationToken.None);

await wrapper.InvokeOnExceptionOccuredAsync(args);

mockLogger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception?>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
Times.Never());
}

[Fact]
public async Task OnExceptionOccured_WithTaskCanceledException_DoesNotLogError()
{
var mockLogger = new Mock<ILogger<LoggingExtensions.MessageProcessing>>();
var wrapper = CreateWrapper(mockLogger.Object);

var args = new ProcessErrorEventArgs(
new OperationCanceledException("shutdown", cts.Token),
new TaskCanceledException(),
ServiceBusErrorSource.Receive,
"test-namespace",
"test-queue",
cts.Token);
CancellationToken.None);

await wrapper.InvokeOnExceptionOccuredAsync(args);

Expand Down
Loading