Skip to content

#4751: regression test for async composite catch-up under sharded + tenant-partitioned - #4767

Merged
jeremydmiller merged 1 commit into
masterfrom
fix-4751-composite-catchup-test
Jun 18, 2026
Merged

jeremydmiller merged 1 commit into
masterfrom
fix-4751-composite-catchup-test

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Locks in the fix for #4751 (the functional fix already shipped in #4764) and lets the issue be closed.

Background

Under MultiTenantedWithShardedDatabases + UseTenantPartitionedEvents, an async CompositeProjection was not driven to a non-stale state by the normal daemon catch-up path (StartAllAsync + WaitForNonStaleData / IDocumentStore.WaitForNonStaleProjectionDataAsync) — the composite's member read models stayed null after catch-up returned, even though the same composite caught up fine via RebuildProjectionAsync and in long-running deployed apps.

Root cause (shared with #4761, fixed in #4764)

WaitForNonStaleData's "caught up" check could be satisfied by the two HighWaterMark rows alone (count >= projectionsCount + all >= initial) without ever requiring the composite's per-tenant projection-progression row — so it reported non-stale before the composite had processed, leaving the member read models empty. #4764 changed the wait to require each registered projection shard's per-tenant progression row to reach its tenant's high-water, which forces it to actually wait for the composite (its member read models commit in the same ProjectionBatch as the composite shard's progression).

This PR

Adds the sharded integration regression test: a tenant-partitioned composite under MultiTenantedWithShardedDatabases must materialize both stages via the async daemon (not just via RebuildProjectionAsync). Verified green on the current JasperFx 2.13.0 master pin — i.e. the merged #4764 fix is sufficient on its own.

A separate defensive guard in JasperFx.Events (ExecutionStage.ExecuteDownstreamAsync, #457 — an invariant assertion that a composite member writes into the shared ProjectionBatch, not a behavior change) is merged to JasperFx main and will ride the next routine JasperFx version bump; it is not required for this fix.

Closes #4751.

🤖 Generated with Claude Code

…ant-partitioned

Locks in the fix from #4764. Under MultiTenantedWithShardedDatabases +
UseTenantPartitionedEvents, an async CompositeProjection was not driven to a
non-stale state by the normal daemon catch-up path (StartAllAsync +
WaitForNonStaleData) — the member read models stayed empty after catch-up
returned.

Root cause was shared with #4761: WaitForNonStaleData's "caught up" check could
be satisfied by the HighWaterMark rows alone, without requiring the composite's
per-tenant projection-progression row. #4764 fixed that. This test pins the
behavior: a sharded, tenant-partitioned composite must materialize BOTH stages
via the async daemon (not just via RebuildProjectionAsync).

Passes on JasperFx 2.13.0 (the merged #4764 fix is sufficient; the defensive
JasperFx ExecutionStage guard from #457 is a separate invariant
assertion that rides the next routine JasperFx bump).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit 3a2eb07 into master Jun 18, 2026
9 checks passed
@jeremydmiller
jeremydmiller deleted the fix-4751-composite-catchup-test branch June 18, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant