#4666 Phase B — TelehealthComposite 4+2+2 + rebuild subcommand#4675
Merged
Conversation
What ships:
* Stage-2 projections (Domain/Stage2Projections.cs) — lifted from
src/DaemonTests/TeleHealth/:
- AppointmentDetailsProjection (multi-stream + enrichment) +
AppointmentDetails aggregate.
- BoardSummaryProjection (multi-stream + ReferencePeerView<Board>) +
BoardSummary + AssignedAppointment.
- AppointmentMetricsProjection (custom IProjection) +
AppointmentMetrics.
Snapshot init guard added inside AppointmentDetailsProjection.Evolve:
enrichment-added References<T> events can arrive before the snapshot-
creating Updated<Appointment>, which made the lifted "snapshot!.X = ..."
pattern NRE on rebuild. Default-init at the top of Evolve.
* Stage-3 projections (Domain/Stage3Projections.cs) — NEW for the
scaletesting harness:
- ProviderUtilizationProjection: per-provider rollup keyed by
ProviderId. Reads Updated<AppointmentDetails> (stage-2 emission),
enriches with Provider reference doc, increments
AssignedCount/CompletedCount/CancelledCount.
- TenantDailyRollupProjection: per-day rollup keyed by ISO date
string. Reads Updated<AppointmentDetails>. Under conjoined tenancy
the date-only key naturally scopes per-tenant. Exercises the
cross-stage chaining + per-tenant aggregation surface.
* CompositeProjection wiring (Program.cs) — registers TelehealthComposite
via opts.Projections.CompositeProjectionFor(...) with the issue's 4+2+2
stage layout:
Stage 1: AppointmentProjection, ProviderShiftProjection,
Snapshot<Board>, AppointmentMetricsProjection.
Stage 2: AppointmentDetailsProjection, BoardSummaryProjection.
Stage 3: ProviderUtilizationProjection, TenantDailyRollupProjection.
* Source-gen dispatcher dependency: csproj now references
JasperFx.Events.SourceGenerator as Analyzer (matches every Marten
test project shape) so the conventional Apply/Create/ShouldDelete
methods on Board / Appointment / ProviderShift get compiled
dispatchers — without it the rebuild fails at startup with
"No source-generated dispatcher found for SingleStreamProjection<Board, Guid>".
* `rebuild` CLI subcommand (Commands/Rebuild{Command,Input}.cs):
marten-scaletest rebuild [--projection TelehealthComposite]
[--shard-timeout-seconds 600]
Builds the daemon, calls RebuildProjectionAsync against the supplied
projection, prints throughput + a one-row Spectre table summary.
Fails fast (returns false from Execute) on any exception so a stress
chain can stop early.
Smoke (Postgres on docker-compose's localhost:5432):
* 2 tenants × 500 events seed = 1,136 events / 150 streams.
* rebuild TelehealthComposite — clean, 0.5s (~2.2K events/sec).
* Projection row counts after rebuild (via direct psql):
mt_doc_appointment 100
mt_doc_board 6
mt_doc_providershift 36
mt_doc_appointmentmetrics 16
mt_doc_appointmentdetails 108
mt_doc_boardsummary 6
mt_doc_providerutilization 93
mt_doc_tenantdailyrollup 2
All 8 projections populated end-to-end through the composite's 3 stages.
Phase C (validate subcommand + JSON metrics + stress chain) follows in a
separate PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Jun 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase B of #4666. Wires the
composite projection topology and the
rebuildsubcommand so the harness canactually exercise the daemon's projection paths under load — the regression
bed for the #4667 race fixes.
What ships
Domain/Stage2Projections.cs)src/DaemonTests/TeleHealth/:AppointmentDetailsProjection(multi-stream + enrichment),BoardSummaryProjection(multi-stream +ReferencePeerView<Board>),AppointmentMetricsProjection(customIProjection).Domain/Stage3Projections.cs) — NEWProviderUtilizationProjection(per-provider rollup keyed byProviderId, reads stage-2Updated<AppointmentDetails>, enriches withProvider) +TenantDailyRollupProjection(per-day rollup keyed by ISO date string; under conjoined tenancy the date-only key naturally scopes per-tenant, exercising cross-stage chaining + per-tenant aggregation).Program.cs)opts.Projections.CompositeProjectionFor("TelehealthComposite", ...)with the issue's 4+2+2 stage layout.csproj)JasperFx.Events.SourceGeneratorasAnalyzer(matches every Marten test project shape). Without it the rebuild fails withNo source-generated dispatcher found for SingleStreamProjection<Board, Guid>.rebuildCLI subcommandmarten-scaletest rebuild [--projection TelehealthComposite] [--shard-timeout-seconds 600]. Builds the daemon, callsRebuildProjectionAsync, prints a Spectre summary. Returns false on any exception so a stress chain can stop early.Snapshot init guard
AppointmentDetailsProjection.Evolvegot asnapshot ??= new ...(id)at thetop: enrichment-added
References<T>synthetic events can arrive before thesnapshot-creating
Updated<Appointment>, which made the liftedsnapshot!.X = ...pattern NRE on rebuild (smoke caught this immediately —~16GB memory blow-up before I killed the process, root cause was the
unguarded NRE adding to the daemon's exception/retry queue).
Smoke (local Postgres on docker-compose's localhost:5432)
seed --tenants 2 --events-per-tenant 500rebuild --projection TelehealthCompositeProjection row counts after rebuild (direct psql):
mt_doc_appointmentmt_doc_boardmt_doc_providershiftmt_doc_appointmentmetricsmt_doc_appointmentdetailsmt_doc_boardsummarymt_doc_providerutilizationmt_doc_tenantdailyrollupAll 8 projections populated end-to-end through the composite's 3 stages.
Phase C / D follow-ups
validatesubcommand (single-shard baseline diff) + JSON metrics sink +stresschain.stressat scale against the dev box; if crash-free, close #4667.🤖 Generated with Claude Code