Skip to content

Per-tenant-partitioned-events Phase 1 (cont.): required-write isolation, optimistic version, cascading tenant inheritance (#3021)#3026

Merged
jeremydmiller merged 1 commit into
mainfrom
feat-3021-phase1-2-tenant-partitioning-matrix
Jun 4, 2026
Merged

Per-tenant-partitioned-events Phase 1 (cont.): required-write isolation, optimistic version, cascading tenant inheritance (#3021)#3026
jeremydmiller merged 1 commit into
mainfrom
feat-3021-phase1-2-tenant-partitioning-matrix

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Advances #3021. Test-only (no shippable change). Builds on the merged Phase-1 slice (#3022) with three more single-store, tenant-scoped aggregate-handler scenarios against a Conjoined + Quick + UseTenantPartitionedEvents store:

  • [WriteAggregate(Required=true, OnMissing=ThrowException)] isolation — aggregate present in tenant1 but routed to tenant2 → RequiredDataMissingException (not a silently-started tenant2 stream).
  • Optimistic version check ([AggregateHandler(VersionSource=...)]) scoped to the tenant partition — correct version appends in the routed tenant only; stale version → ConcurrencyException; sibling tenant's version unaffected.
  • Cascading-message tenant inheritance — a message cascaded from an aggregate handler carries the handler's tenant (outbox session is tenant-scoped).

Bug found + filed: #3025

While building these I found that MartenOps.StartStream returned from a handler is silently dropped under UseTenantPartitionedEvents (0 events persisted), whereas a direct session.Events.StartStream persists. The earlier tests masked this because aggregate handlers start-if-missing — these version/required scenarios are the first that need a genuinely pre-existing stream. The seed therefore uses a direct tenant session, with a comment pointing at #3025.

Verification

Full MartenTests/AggregateHandlerWorkflow folder green (80, +3).

Still on #3021 (deferred)

Natural-key aggregate, MartenOps document tenant overloads, event-as-message, ancillary stores, the HTTP [Aggregate]/[WriteAggregate]/[ReadAggregate] cluster (needs an Alba host), and all of Phase 2 (multi-node projection/subscription distribution) + Phase 3 (sharded DBs). The single-PR goal hit two practical limits: the #3025 StartStream bug (worked around), and the HTTP/multi-node clusters needing separate harnesses — so those are tracked rather than forced in.

🤖 Generated with Claude Code

…on, optimistic version, cascading tenant inheritance (#3021)

Extends the single-store partitioned-events aggregate matrix with three more tenant-scoped
scenarios against a Conjoined + Quick + UseTenantPartitionedEvents store:

- [WriteAggregate(Required=true, OnMissing=ThrowException)]: an aggregate present in tenant1 but
  routed to tenant2 raises RequiredDataMissingException (partition isolation) rather than starting a
  tenant2 stream.
- Optimistic version check ([AggregateHandler(VersionSource=...)]) is scoped to the tenant's
  partition: correct version appends in the routed tenant only; a stale version raises
  ConcurrencyException; the sibling tenant's own version is unaffected.
- A message cascaded from an aggregate handler inherits the handler's tenant (outbox session is
  tenant-scoped).

Finding (filed as #3025): MartenOps.StartStream returned from a handler is silently dropped under
UseTenantPartitionedEvents (0 events persisted), while a direct session.Events.StartStream persists.
Earlier tests masked this because the aggregate handlers start-if-missing; these version/required
scenarios are the first that need a genuinely pre-existing stream, so the seed now uses a direct
tenant session.

Full AggregateHandlerWorkflow folder green (80). Remaining Phase-1 clusters (natural-key, MartenOps
document tenant overloads, event-as-message, ancillary stores, HTTP) and Phases 2-3 (distribution,
sharded) remain on #3021.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit 96ae95e into main Jun 4, 2026
24 checks passed
This was referenced Jun 8, 2026
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