Repository navigation
2.1.0: resilient message receive, factory fixes, dependency bumps - #40
Merged
Merged
Conversation
…dependencies (2.1.0) ReceiveMessagesAsync no longer throws for a failed read, a throwing handler, a failed delete, or a throwing exception handler; everything is logged and routed to the caller's exception handler and the rest of the batch is still processed. Fixes the customer-reported host crash. Also: per-service-collection settings via named options (was a process-wide static), thread-safe factory with async queue creation on first use, keyed service injection, IAzureStorageQueueClient, per-client VisibilityTimeout / SerializerOptions / ClientOptions, serializer options honoured on send, trace event timestamps, process_success tag, richer exceptions and logs, README samples that compile, Azure.Identity dropped, dependencies bumped, AutoFixture/FakeItEasy removed from tests (vulnerable transitive deps). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaces the AI-generated infinity JPEG with the Unifinity mark from the brand kit: icon-256.png as the NuGet package icon, and the dark/light SVG masters in the README via a <picture> element so GitHub shows the right colourway for the viewer's theme. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Azure.Storage.Queues 12.28 sends API version 2026-10-06, which Azurite rejects with a 400 unless the version check is skipped. A service container cannot take command-line arguments, so Azurite is now started with docker run instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Customers reported that a throwing message handler could take down their host.
ReceiveMessagesAsynchad three separate ways to throw out into the caller's polling loop — a failed queue read, a failed metadata read, and the customer's own exception handler throwing — and none of them were logged. In .NET 6+ an unhandled exception in aBackgroundServicestops the host, so one transient storage error was enough.This PR makes the receive path resilient, fixes the other bugs a full sweep turned up, adds the additive
IHttpClientFactory-style improvements to the factory, and bumps dependencies. No existing call site needs to change. Ships as 2.1.0. The breaking redesign is tracked separately in #39.Stability (the customer-reported crash)
ReceiveMessagesAsyncno longer throws for a failed read, a failed deserialization, a throwing handler, a failed delete, or a throwing exception handler. Each is logged (queue name, message id, dequeue count) and passed to the customer's exception handler; the rest of the batch is still processed. Only the caller's ownOperationCanceledExceptionescapes.nullmetadata instead of throwing.Bugs fixed
JsonSerializerOptionswere ignored when sending (only applied on receive).AddClient("name")was silently dropped. Settings now use named options per service collection and compose.QueueClientFactorywas not thread-safe and made a blockingCreateIfNotExists()network call during DI resolution. NowConcurrentDictionary+Lazy; queue creation happens once, asynchronously, on first use.ActivityEvents were static instances, so every event carried the process start time.queue.process_successtag was added to a struct copy and never recorded.DeserializationExceptiondropped the inner exception; Base64 sniffing mangled short valid JSON (true,null,1234). Now tries JSON first, then falls back to Base64.e.Messageonly; receive-side failures weren't logged at all.queue_message_size_byteswas declared but never recorded.Additive API
IAzureStorageQueueClientinterface (the class implements it; the factory still returns the class) so consumers can mock the client.[FromKeyedServices("orders")] AzureStorageQueueClientworks without the factory.QueueClientSettings:VisibilityTimeout,SerializerOptions(per client),ClientOptions(SDK retry policy /MessageEncoding).AddClient(name, IConfiguration)andAddDefaultClient(IConfiguration)overloads.LoggerMessageExtensionsmethods are kept but[Obsolete]for removal in 3.0.Dependencies
Azure.Identityremoved from the package — it was never used by the library (only by tests).Azure.Storage.Queues12.23.0 → 12.28.1,OpenTelemetry.Api1.15.3 → 1.19.1.Microsoft.Extensions.*9.0.7 → 10.0.13. This floor is set byAzure.Core1.60 (pulled by the new storage SDK), not by us; the 10.x packages still target .NET 8. Documented in the README upgrade notes.AutoFixtureandFakeItEasyremoved (FakeItEasywas unused;AutoFixturewas the source of two High-severity transitive vulnerabilities viaNETStandard.Library 1.6.1). Everything else bumped to latest;Microsoft.AspNetCore.Mvc.Testingstays on 8.0.x to match the target framework.dotnet list package --vulnerable --include-transitiveis clean across all projects.actions/checkoutandactions/setup-dotnetv3 → v4 (Node 16 runtime retirement).Docs
The logo is now the Unifinity brand mark from the brand kit (
icon-256.pngas the NuGet icon, dark/light SVG masters in the README via<picture>), replacing the AI-generated infinity JPEG.README samples rewritten: they didn't compile against the current API (wrong handler arity and argument order) and ran a forever-loop inside
IHostedService.StartAsync, which blocks host startup. NowBackgroundService.ExecuteAsyncwith cancellation. Added per-client settings, direct injection, and an "Upgrading to 2.1" section listing every behaviour change.Behaviour changes to be aware of
CreateIfNotExistsruns on first send/receive/peek, not on DI resolution.Test plan
🤖 Generated with Claude Code