Repository navigation
Marten 9.38.0
Eight changes since 9.37.0. Most of this release is silent-wrongness: a permanent sequence gap that stalls the async daemon, telemetry spans attached to the wrong parent, a DbContext nothing ever disposed, and a vector projection that embedded stale text. Three of them were reported from production by the community.
⚠️ Behaviour change
A VectorProjection declaring MapFromAggregate registered anything other than Async is now refused when the store is built (#5451, #5468). The class remarks always said Async was required; nothing enforced it, so an Inline registration built without complaint and then embedded text that missed the event that triggered it. Measured: the first save wrote no row at all, because live aggregation reads committed events and the stream had none yet; every later save wrote the state as of the previous event. The content hash recorded that stale text as current and nothing failed.
If you have such a registration it will now fail at DocumentStore.For(...) with a message naming MapFromAggregate and ProjectionLifecycle.Async. Event-mapped vector projections registered Inline are unaffected and remain supported.
Event store
-
A failed batch no longer leaves a permanent gap in the event sequence (#5454, #5456). Under
QuickWithServerTimestamps— the V9 default — aStartStreamtook a per-event INSERT route whoseseq_idcame from an inlinednextval()that nothing read back. The events keptSequence0, and tombstone creation skips exactly those, so any failure later in the same batch (a losingUpdateRevision, a duplicate document INSERT) rolled back with sequence numbers drawn and nothing to fill them. The async daemon's high-water detector then stalled for every tenant in the database until it had seen the gap forStaleSequenceThreshold. AStartStreamnow goes through the samemt_quick_append_eventsfunction an ordinary append already used, which returns its sequence numbers. Reported and fixed by @vpetrevski. -
mt_quick_append_eventsno longer writes a new stream'smt_streamsrow twice (#5464). It inserted the row with version 0 and then let the trailingUPDATEset the final version — two heap tuples and two WAL records per stream creation, in one transaction, where nothing outside it could observe the intermediate value. This became the default stream-creation path with #5456, so it was worth removing. A one-eventStartStreammeasured 471µs on the old per-event route, 511µs through the function, and 480µs with this change (PostgreSQL 17.10, medians of 3).FetchForWritingagainst a stream that does not exist yet gets the same saving.
Projections and read models
-
An EF Core projection's
DbContextis disposed (#5457, #5466).EfCoreProjectionStorageowns a context built once per tenant per batch, and nothing disposed it on any path —IProjectionStorage<,>declares no disposal contract, so the type that owned it had nothing to hook. Disposal now belongs to the transaction participant, whose lifetime already equals a batch. A second, unconditional leak is fixed alongside it: the live-aggregation fallback registered its owner only when the session was anITransactionParticipantRegistrar, and a plainIQuerySession— whatAggregateStreamAsyncpasses — is not one, so nobody released an eagerly-opened connection. Reported by @wpei-infotrack, with investigation from @arnelirobles. -
An absent key no longer projects as an explicit JSON null in a
Select()(#5461, #5462).
Telemetry
- Marten activities attach to the current activity, not to its parent (#5459, #5465).
MartenTracingpassedparentActivity?.ParentIdtoActivitySource.StartActivity, which parents a span to the parent of the activity it is handed — its grandparent. With an incoming Server/Consumer span carrying a remotetraceparent, everymarten.connectionspan hung off the sending service's span instead of the one doing the work. It stayed hidden because it is invisible in the two easy cases: with no ambient activity, or with an ambient root,ParentIdis null andActivitySourcefalls back toActivity.Current, which is the right answer by accident. Diagnosed from the source by @bohdan-hukivskyi.
Documentation
-
Unit testing event-sourced handlers with
StubEventStreamis now documented (#5455, #5458, #5463). Also fixesStubEventStream(aggregate, StoreOptions), which built a secondEventGraphsoMapEventTypealiases never reached it. -
The pgvector docs no longer describe the
ef_searchcap that 9.37 removed (#5452, #5467), including the non-obvious case that a hybrid search asks for itsCandidateDepthrather than its limit and so reaches pgvector's 1000-row ceiling at a limit above 250. Contributed by @arnelirobles.
Dependencies
JasperFx and JasperFx.Events 2.73.2; Weasel 9.32.0.