Skip to content

V9.27.0

Choose a tag to compare

@jeremydmiller jeremydmiller released this 18 Aug 01:37
· 2 commits to master since this release

Adopts JasperFx 2.51.0 and the four capabilities it promotes into the shared Critter Stack contracts. One of them is a genuinely new Marten feature; the other three close gaps where Marten had the capability but not under the shared spelling.

Aggregate snapshot caching for FetchForWriting (#5251)

An opt-in, node-local cache of aggregate snapshots that lets FetchForWriting skip loading the stored snapshot and read only the events after it — effectively an identity map for aggregates with a lifetime longer than a session. Off for every aggregate type; enabled per type, because the win is proportional to how often one stream is fetched for writing:

opts.Events.CacheAggregatesForWriting<Order>(sizeLimit: 1000);

The cached snapshot is only ever a baseline. The stream version and every event after the cached version are still read on every call, and the optimistic concurrency assertion on append is untouched — so a stale entry costs a larger delta query, never a wrong aggregate and never a suppressed EventStreamUnexpectedMaxEventIdException. That is what makes a deliberately incoherent, node-local cache the right shape here, and why there is no distributed-cache option: a distributed cache would reintroduce exactly the round trip this exists to remove.

Both the Async and Inline lifecycles are supported, and they genuinely differ. An Inline snapshot is written in the same transaction as the events, so it is always exactly at the stream head — a hit needs an exact version match, and the entry is written back only after a successful commit, because the inline projection mutates the very instance FetchForWriting handed out.

IAggregateWriteCache and friends live in JasperFx.Events.Fetching, so one cache implementation serves Marten, Polecat and Fisher alike. No new package lands on core Marten: the default is backed by JasperFx.Core's existing LRU rather than Microsoft.Extensions.Caching.Memory.

Docs: Optimizing Performance → Caching Aggregate Snapshots for FetchForWriting.

IDocumentSessionOperations.PendingStreams (#5250)

Store-agnostic code can now read the StreamActions a session has queued but not yet committed — for a listener or a pre-commit hook deciding something from the events the session is about to write — without naming a store.

⚠️ Worth knowing if you are implementing these contracts yourself: this is the same non-covariance trap as 9.26's Events accessor. Marten's PendingChanges.Streams() returns IList<StreamAction>, and IList<T> is not assignable to IReadOnlyList<T>, so the member bound to the interface's throwing default with no compile error anywhere. It now carries an explicit implementation, pinned by PendingStreamActionsCompliance.

In Marten this is the same collection as IDocumentSession.PendingChanges.Streams(), handed back as a live view rather than a copy.

BinaryEventAttribute — one lookup instead of two (#5248)

Marten.Events.BinaryEventAttribute now derives from the promoted JasperFx.Events.BinaryEventAttribute, which 2.51.0 unsealed for exactly this. EventGraph.ResolveBinarySerializerFor drops back to a single attribute lookup. Entirely non-breaking — existing [BinaryEvent] usages compile and resolve unchanged.

Compliance (#5249)

DocumentComplianceConfig.StreamIdentity is now replayed by Marten's document compliance fixture rather than inferred, and two new shared suites are enrolled: PendingStreamActionsCompliance and AggregateWriteCacheCompliance (wave 10).

Upgrading

No breaking changes and no migration. Everything new here is opt-in.

Full changelog: V9.26.0...V9.27.0