Skip to content

Marten 9.40.0

Choose a tag to compare

@jeremydmiller jeremydmiller released this 27 Sep 22:23
· 59 commits to master since this release

Minor rather than patch for one reason: the source-generated DetermineAction now decides one action per batch rather than per event. See the behaviour change below before upgrading if you archive streams from a projection.

⚠️ Behaviour changes

Archiving projections: one action per batch, not per event (JasperFx/jasperfx#889, via the JasperFx 2.75.2 bump). The source-generated DetermineAction used to decide per event; it now decides once per batch. This changes what an archiving projection does when a single batch mixes an archive with later events for the same stream. StreamArchivingCompliance gained facts for it and Marten passes them, but if you depend on the old per-event evaluation, test before upgrading.

TenantIdStyle is now applied where the tenant id is stamped and keyed, not only where a tenant is resolved (#5516). Under ForceLowerCase, these three previously wrote or matched the raw id:

  • BulkInsertEventsAsync / BulkInsertEventStreamAsync resolved the right database or partition and then stamped the raw id on every mt_streams / mt_events row — rows invisible to any normalised read under conjoined tenancy, and the wrong key inside the right partition under UseTenantPartitionedEvents.
  • TenantIsOneOf(...) filtered on the raw values, so a mixed-case id matched nothing.
  • DeleteAllTenantDataAsync used the raw id as its DELETE ... WHERE tenant_id = :p parameter, so it matched no rows and reported success — the one to look at hardest, since callers are usually honouring an erasure request.

If you have been calling these with mixed-case tenant ids, data already written under the raw casing stays where it is; it will not be found by a normalised read. ForTenant(string) also now normalises its nested-session cache key, which makes ForTenant("RED") and ForTenant("red") one session rather than two.

Event store

  • IEventStore.OpenReadOnlyEventStore(tenantId) is implemented (#5513). With Advanced.DefaultTenantUsageEnabled = false — the automatic state once database-per-tenant tenancy is configured — the entire IReadOnlyEventStore surface was unreachable, because the tenant-less overload opens the store's default session and is refused before any tenant scope applies. That is why QueryStreamStates(tenantId)'s own parameter could not work around it. A null tenant keeps the previous store-global behaviour.
  • Diagnostic event-store reads answer "no results" instead of throwing (#5511, #5512), decided from the database rather than from configuration.
  • Progression and dead-letter reads answer nothing for an event store nothing provisions (#5509, #5510).

Diagnosing a missing source-generated dispatcher

The JasperFx 2.75.2 bump (#5518) carries three changes that came out of #5495, where a user's aggregates compiled with no dispatcher and the runtime message could not tell them why:

  • The generator now leaves [assembly: JasperFxSourceGeneratorApplied] in every assembly it processes, so "the generator never ran in that assembly" — a csproj problem — is distinguishable at runtime from "the generator ran and declined your type" — a code-shape problem. The exception says which.
  • Two copies of the generator in one compilation no longer collide with CS0433. This happens when a project references the standalone JasperFx.Events.SourceGenerator package as well as Marten, which bundles it: both copies emit identical file paths and the file-scoped evolvers mangle to the same name. If you hit that error — recognisable because it names the same assembly on both sides — remove the standalone package reference; Marten already carries the analyzer.
  • Opt in to a build-time check with <JasperFxEventsRequireSourceGenerator>true</JasperFxEventsRequireSourceGenerator> on projects that declare aggregates, and a missing analyzer becomes build error JFXEVT900 rather than a failure at the first FetchForWriting.

Compliance coverage

Marten is now enrolled in DocumentConjoinedTenancyCompliance (#5517) — the first shared document coverage of conjoined tenancy on any store — with all 10 facts running. ConjoinedEventTenancyCompliance goes from 16 passing with 2 skipped to 18 passing with none, the two skips having been waiting on the read-only overload above.

Two of these enrollments exposed that Marten's own commit-listener compliance facts had been passing vacuously — the harness never installed the listener the suites register, so "no phantom deletion was reported" was trivially true of a store reporting nothing at all. Fixed in the #5518 bump; no product change was needed, and both halves of that fact now pass for real.

Dependencies

JasperFx 2.75.2 and Weasel 9.35.2.

Weasel 9.35.x is mostly EF Core and SQL Server work, plus weasel#634: an exhausted DropSchema retry is now reported instead of returning success.

Docs

The bundled analyzer does flow transitively across a ProjectReference (#5508) — the composite-configuration page claimed the opposite. The claim was true of the standalone analyzer package, which Marten sets PrivateAssets="all" on, but #4557 bundles the analyzer DLL into Marten's own package and that copy flows.