Skip to content

Preserve original session tenant on IEvent for AddGlobalProjection (#4270)#4280

Merged
jeremydmiller merged 1 commit intomasterfrom
fix-4270-global-projection-tenant
Apr 22, 2026
Merged

Preserve original session tenant on IEvent for AddGlobalProjection (#4270)#4280
jeremydmiller merged 1 commit intomasterfrom
fix-4270-global-projection-tenant

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Summary

  • StreamAction.TenantId's setter propagates the new tenant to every IEvent on the stream.
  • GlobalEventAppenderDecorator sets action.TenantId = *DEFAULT* so AddGlobalProjection stores single-tenanted — but that side-effect also clobbered each event's TenantId, so inline SingleStreamProjection Create(IEvent<T>) / Apply(IEvent<T>, TDoc) convention methods read *DEFAULT* instead of the session tenant the events were appended under.
  • After forcing the default on the action, restore the original tenant on each IEvent. Storage is unchanged (mt_events / mt_streams still route to the default tenant via action.TenantId); the only behavior change is that in-flight event objects preserve the appending session's tenant.

Fixes #4270.

Test plan

  • New regression test Bug_4270_global_projection_preserves_event_tenant — two facts, both now pass:
    • event_tenant_id_is_preserved_in_create_apply_convention_methods asserts @event.TenantId == "acme" (not *DEFAULT*) inside Create/Apply.
    • projection_storage_is_still_single_tenanted_after_fix asserts the projected document is reachable from the default-tenant QuerySession (proves the global-within-conjoined storage guarantee still holds).
  • Existing global_tenanted_streams_within_conjoined_tenancy suite (34 tests, inline + async + live + quick + rich append modes) still passes.

🤖 Generated with Claude Code

StreamAction.TenantId's setter propagates to every IEvent on the
stream. GlobalEventAppenderDecorator sets action.TenantId to the
default tenant so AddGlobalProjection stores single-tenanted, but that
also clobbered each event's TenantId to "*DEFAULT*". Inline
SingleStreamProjection convention methods that read
IEvent<T>.TenantId inside Create/Apply saw the default tenant instead
of the session tenant the events were appended under.

Capture action.TenantId before forcing default, then restore the
original tenant on each IEvent. Event-row storage still routes to the
default tenant (via action.TenantId), so the documented
"global within conjoined tenancy" guarantee is unchanged.

Fixes #4270.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

AddGlobalProjection Changes Event Tenancy to Default if using Create/Apply pattern

1 participant