Skip to content

Run the outbox relay cycle in the engine: stores own only the transaction, claim, record and commit, and drop the options parameter - #428

Merged
Vulthil merged 1 commit into
mainfrom
refactor/outbox-relay-cycle
Oct 1, 2026
Merged

Vulthil merged 1 commit into
mainfrom
refactor/outbox-relay-cycle

Conversation

@Vulthil

@Vulthil Vulthil commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Summary

The outbox relay cycle now lives in one engine module. The store keeps only the transactional boundary.

  • IOutboxStore.ProcessBatchAsync(dispatch) is replaced by RunRelayUnitAsync(unit). The store opens the transaction (inside its execution strategy), passes the engine a new IOutboxRelayUnit (ClaimAsync, RecordAsync), and commits when the unit returns.
  • The engine's OutboxRelayCycle (was OutboxProcessor) owns the dispatch rules: batch size and retry limit, dispatch in sequence or in parallel, the MaxDegreeOfParallelism throttle, and the cancel rule. Before, the store had its own copy of the parallel fan-out and throttle. The back-off rule moves to OutboxRelayBackoff, so it can be tested without the hosted service.
  • The scope rule does not change. It is now stated in one place: in sequence, a message is dispatched in the relay's own scope, so handler writes and transactional publishes join the relay transaction. In parallel, each message gets its own scope.
  • EntityFrameworkOutboxStore<TContext> and its 4 subclasses drop the IOptions<OutboxProcessingOptions> constructor parameter, because the store no longer reads the options.
  • Docs: the custom-store section explains the relay-unit seam. A new "Dispatch scope" section states the sequence/parallel rule, including that in sequence a failed handler's unsaved changes can be saved with the batch. Package pages and READMEs are updated.
  • Tests: OutboxRelayCycleTests run the real cycle against an in-memory store (claim arguments, empty claim, scope per mode, deterministic throttle check, failures, cancel rule). OutboxRelayBackoffTests cover the back-off rule. The store tests drive one relay unit through a shared test helper (tests/Shared/OutboxRelayUnitTestExtensions.cs). The duplicate throttle test on the EF store is deleted.

Breaking changes (2.0 line)

Reason: a leaked implementation seam. The store interface carried the engine's batch logic, so EnableParallelPublishing and BatchSize were read in two places.

  • IOutboxStore.ProcessBatchAsync is removed, and IOutboxStore.RunRelayUnitAsync<TResult> is added.
  • EntityFrameworkOutboxStore<TContext>.ProcessBatchAsync is removed.
  • The IOptions<OutboxProcessingOptions> constructor parameter is removed from EntityFrameworkOutboxStore, RelationalOutboxStore, NpgsqlOutboxStore, MySqlOutboxStore and CosmosOutboxStore.

Each break has a CompatibilitySuppressions entry (IsBaselineSuppression) and a *REMOVED* PublicAPI line.

One small runtime difference: an idle cycle (empty claim) now commits its empty transaction. Before, it rolled the transaction back on dispose. No data changes, and the commit does not wake the relay.

Verification

  • dotnet build Vulthil.SharedKernel.slnx: 0 warnings, 0 errors.
  • All unit test projects pass on net10.0 and net9.0 (Outbox.Tests 64, Outbox.EntityFrameworkCore.Tests 19, Infrastructure.Relational.Tests 17, and the rest).
  • Vulthil.IntegrationTests (Postgres, MySQL and Cosmos with Testcontainers): 68/68 on net10.0 and on net9.0.
  • Aspire MessagingIntegrationTests: 13/13 on net10.0.
  • dotnet pack package validation against 1.2.0 passes for the 6 changed packages.
  • Break checks. I made each change, saw tests fail, then reverted it:
    • parallel dispatch without the throttle: 1 test fails;
    • dispatch in sequence with a scope per message: 8 tests fail;
    • the loop ignores the back-off: 1 test fails;
    • the back-off counts claimed messages instead of relayed messages: 2 tests fail;
    • the store rolls back instead of committing: 4 tests fail.

Backport to v1.0: no (breaking change on the 2.0 line).

…tion, claim, record and commit, and drop the options parameter
@Vulthil
Vulthil merged commit 0564f03 into main Oct 1, 2026
7 checks passed
@Vulthil
Vulthil deleted the refactor/outbox-relay-cycle branch October 1, 2026 10:48
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