Skip to content

#407 Phase 0: additive per-tenant surface for JasperFx.Events#408

Merged
jeremydmiller merged 1 commit into
mainfrom
feat/407-phase0-tenant-surface
Jun 2, 2026
Merged

#407 Phase 0: additive per-tenant surface for JasperFx.Events#408
jeremydmiller merged 1 commit into
mainfrom
feat/407-phase0-tenant-surface

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Closes the Phase 0 — Surface (additive) portion of #407 (the standalone first PR / "first round of JasperFx work"). Part of the per-tenant partitioning master plan CritterWatch#209.

This is purely additive with zero behavior change — a null tenant everywhere is store-global, i.e. today's exact behavior. It unblocks Marten's signature-level work on marten#4596. Phases A (composite replay executor) and 2 (daemon abstractions) are intentionally not in this PR — per the issue they co-evolve with marten#4596 afterward.

Changes

ShardName grammar

  • New nullable TenantId — a distinct trailing slot, never folded into ShardKey.
  • Compose(name, shardKey, tenantId, version) factory + TryParse(...) handling all forms: Name:ShardKey, Name:ShardKey:Tenant, Name:V{n}:ShardKey, Name:V{n}:ShardKey:Tenant.
  • ForTenant(...) helper. Null/empty tenant keeps Identity/RelativeUrl byte-for-byte identical to today.

State types

  • Nullable TenantId on ShardState (copied from ShardName) and HighWaterStatistics. Defaults null; serializes null without breaking existing consumers.

Admin-API tenant overloads (default interface implementations: delegate when tenant is null, throw NotSupportedException otherwise — so existing implementers compile untouched and override later)

  • IEventDatabase: AllProjectionProgress, FindEventStoreFloorAtTimeAsync
  • IEventStore / IEventStore<,>: GetProjectionStatusesAsync, DeleteProjectionProgressAsync
  • IProjectionDaemon: RebuildProjectionAsync (×2), RewindSubscriptionAsync
  • APIs already taking a ShardName (ProjectionProgressFor, StartAgentAsync, StopAgentAsync) thread tenant through it — no new methods, per the issue's principle.

Tests

  • ShardNameTests: Compose/TryParse round-trip for 2-/3-/4-segment forms, null-tenant unchanged, empty→global, HighWaterMark, ForTenant.
  • New TenantIdSurfaceSerializationTests: ShardState/HighWaterStatistics serialize TenantId as null and round-trip a set value.

Verification: full EventTests suite green — 323/323 on net9.0 and net10.0.

🤖 Generated with Claude Code

First round of the per-tenant partitioning work (CritterWatch#209). Pure
additive surface with zero behavior change -- a null tenant everywhere is
store-global, i.e. today's exact behavior. Unblocks Marten's signature-level
work on marten#4596.

- ShardName: nullable TenantId as a distinct trailing slot (never folded into
  ShardKey), Compose(...) factory, TryParse(...) for the 2/3/4-segment forms,
  and a ForTenant(...) helper. Null tenant keeps Identity/RelativeUrl
  byte-for-byte identical to before.
- ShardState + HighWaterStatistics: nullable TenantId, defaults null,
  serializes null without breaking existing consumers.
- Admin-API tenant overloads with default interface implementations that
  delegate when tenant is null and throw NotSupportedException otherwise, so
  existing implementers (Marten/Polecat) compile untouched:
  IEventDatabase (AllProjectionProgress, FindEventStoreFloorAtTimeAsync),
  IEventStore (GetProjectionStatusesAsync, DeleteProjectionProgressAsync),
  IProjectionDaemon (RebuildProjectionAsync x2, RewindSubscriptionAsync).
  APIs that already take a ShardName thread tenant through it.
- Tests: ShardName Compose/TryParse round-trip (all segment forms) and
  ShardState/HighWaterStatistics null tenant serialization.

Full EventTests suite green (323/323) on net9.0 and net10.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit 262ace2 into main Jun 2, 2026
1 check passed
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