Skip to content

3.0: sender/receiver split, per-client builder, hosted processor - #42

Merged
jasonshave merged 6 commits into
masterfrom
v3
Oct 16, 2026
Merged

jasonshave merged 6 commits into
masterfrom
v3

Conversation

@jasonshave

@jasonshave jasonshave commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Implements the 3.0 design (issue #39, docs/v3-design.md). Breaking release; requires .NET 10. Existing .NET 8 customers stay on the 2.x line.

What changes for users

  • Send / receive / process are separate. Inject IQueueSender, IQueueReceiver or IStorageQueue; named clients use [FromKeyedServices("name")].
  • Hosted processor (recommended). AddClient(...).AddProcessor<TMessage, THandler>() runs the polling loop for you: backoff, lease renewal while the handler runs, retries with a delay, a poison queue, cancellation and graceful shutdown.
  • Messages settle themselves. QueueMessage<T> has CompleteAsync, AbandonAsync, RenewLockAsync and DeadLetterAsync. A body that can't be read comes back with Error set instead of failing the whole receive.
  • Per-client builder. Each client can have its own serializer and processor. ValidateOnStart() fails startup for a misconfigured client.
  • The 2.x client API is removed; the README has the 2.x to 3.0 mapping.

Fixes from the pre-release reviews

  • Sends with Timeout.InfiniteTimeSpan ("never expire") failed with a 400. They now work.
  • A slow shutdown no longer makes the app exit with an error when the host's own 30-second limit arrives first.
  • ValidateOnStart() is no longer silently switched off when the app registers its own settings validator.
  • A blank PoisonQueueName in config falls back to <queue>-poison.
  • The processor's VisibilityTimeout must be whole seconds, so lease renewal can't arrive after the lease has lapsed.
  • Logs and metrics no longer report a handler-settled message as a failure, or an unsettled message as removed.
  • The poison queue is created once instead of on every dead-letter.
  • Public API trimmed: LoggerMessageExtensions is internal; unused TagListExtensions and the queue_depth metric are removed.

Verification

  • 106 unit tests pass, with no real-time waits (fake clock).
  • 8 integration tests pass against Azurite.

After merge: tag v3.0.0 to publish to nuget.org as AzureStorage.QueueService.

🤖 Generated with Claude Code

Jason Shave and others added 6 commits October 15, 2026 09:12
… processor

Public API spelled out for review before implementation. Tracks #39.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mended, not required

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…enew/complete race, lease holding, dead-letter replay)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Implements the design in docs/v3-design.md (issue #39) and folds in the
pre-release review fixes:

- "Never expire" sends: Timeout.InfiniteTimeSpan is mapped to the -1 second
  the service expects, instead of failing every send with a 400.
- Shutdown: when the host's deadline cancels StopAsync, the processor
  cancels handlers and returns instead of throwing; tokens are captured up
  front so a late worker never reads a disposed source.
- ValidateOnStart registers its validator with TryAddEnumerable, so an
  app's own validator for QueueClientSettings no longer switches it off.
- An empty PoisonQueueName falls back to "<queue>-poison".
- Processor VisibilityTimeout must be whole seconds, so renewal at half the
  timeout stays inside the lease the service actually grants.
- A handler that settles and then throws keeps its settlement and is no
  longer logged as a failure to be redelivered; a message left unsettled
  with AutoComplete off is no longer logged as removed.
- The poison queue is created once rather than on every dead-letter, and
  recreated if it is deleted.
- Public surface trimmed: LoggerMessageExtensions is internal;
  TagListExtensions, the queue_depth metric and an unused event name are
  removed; duplicate InternalsVisibleTo dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jasonshave jasonshave changed the title 3.0: design for review (sender/receiver, per-client builder, hosted processor) 3.0: sender/receiver split, per-client builder, hosted processor Oct 16, 2026
@jasonshave
jasonshave marked this pull request as ready for review October 16, 2026 18:47
@jasonshave
jasonshave enabled auto-merge (squash) October 16, 2026 18:48
@jasonshave
jasonshave merged commit d18e9f8 into master Oct 16, 2026
1 check passed
@jasonshave
jasonshave deleted the v3 branch October 16, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant