Skip to content

Bump JasperFx from 2.49.0 to 2.54.0 - #16

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/samples/Waymark.MartenSample/JasperFx-2.54.0
Closed

Bump JasperFx from 2.49.0 to 2.54.0#16
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/samples/Waymark.MartenSample/JasperFx-2.54.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 23, 2026

Copy link
Copy Markdown

Updated JasperFx from 2.49.0 to 2.54.0.

Release notes

Sourced from JasperFx's releases.

2.54.0

Two additive features for the Spec Driven Development track (master: JasperFx/stoat#​9). Minor rather than patch because both add public surface; nothing existing is removed, renamed or obsoleted, and every existing constructor and JSON payload round-trips unchanged.

Event Modeling semantic model v2, overlay API and discovery bridge (#​687, #​689 — PR #​691)

JasperFx.Events.EventModeling is now the one slice vocabulary every source writes into and every viewer reads from:

  • EventModelSliceDescriptor keeps its positional shape and gains Pattern (SlicePattern: Command / View / Automation / Translation), TriggerKind (Http / Grpc / MessageHandler / JobScheduler / Human / External), TriggerOrigin, AggregateTypes (a list — one handler may load several projected models), PublishedMessages, ExternalSystems, Hotspots, Specifications (typed: {Feature}/{Scenario} identity + resolved types, stamped by sources), Domain, plus the computed rendering contract Elements (stable id, kind → canonical colour, lane) and Edges (directed, by id), and Merge(other).
  • EventModelDescriptor gains Aggregates and a static Merge(name, descriptors).
  • HandlerRelationshipDescriptor.ToSliceDescriptor() folds the older handler-relationship vocabulary into a slice.
  • The fluent API is an overlay: EventModelDefinition / EventModelBuilder / EventModelSliceBuilder name, group (InDomain), annotate (TriggeredBy(label)) and link (LinksToSpecification) — roles are derived by the sources that can see them. The role-declaring methods live behind one documented escape hatch, ForFlowNotOwnedHere(...).
  • The discovery bridge: EventModelDefinitionSource, services.AddEventModel<T>() / AddEventModel(type | instance | name, lambda) / AddEventModelsFromAssembly / AddEventModelSource, and EventModelDiscovery.DiscoverAsync / AssembleAsync. IEventModelDefinitionSource finally has implementations.
  • #​689: HotspotDescriptor.PendingSpecification(specIdentity) — a pending specification renders as a hotspot on the slice it binds to.

ProjectionScenario exposes its plan and observes each step (#​688 — PR #​692)

ProjectionScenario<TOperations, TQuerySession> gains PlannedSteps (number, kind, description — readable before ExecuteAsync) and an Observer (IProjectionScenarioObserver: StepStarted / StepSucceeded / StepFailed / StepSkipped), numbered as the exception report numbers them. Marten, Polecat and Fisher subclasses need no change.

2.53.0

One fix and one documentation change. Minor rather than patch because the fix adds a member to a public interface — additive, with a default, so nothing has to change to keep compiling.

IProjectionStorage.IsThreadSafe (#​683, #​685)

AggregationRunner applies every slice in a range through a fixed 10-wide block, and every one of them gets the same IProjectionStorage instance. That is what the products' own document storage is built for. It is not what an EF Core storage can take: it wraps one DbContext per tenant/batch, and a DbContext is not thread-safe. A multi-stream projection with custom grouping fans one event out into many slices, so up to ten concurrently call Entry() / FindAsync and mutate the same change tracker — surfacing as InvalidOperationException out of Dictionary.TryInsert and NullReferenceException out of ChangeDetector.DetectChanges (JasperFx/marten#​5266).

A storage can now say it cannot take that:

public bool IsThreadSafe => false;

and the runner applies its slices one at a time. Resolved per tenant group, so a store may answer differently per tenant.

Defaults to true, so no existing storage changes. The declaration is on the storage rather than on AsyncOptions because the storage is the thing that is or is not safe and it already knows — a parallelism knob would work, but it would make correctness a configuration problem a user has to know they have.

⚠️ Serializing calls inside a storage implementation is not a substitute. A lock around each member still leaves the aggregation on one thread mutating entities while another thread's Entry() runs change detection over them, which is exactly the reported ChangeDetector failure. The fan-out itself has to stop, and nothing reachable from inside the storage can stop it.

Both routes run the same handler and collect into the same exception list, so MarkSliceAction, the single-vs-aggregate throw and ApplyPendingCacheUpdates are unchanged. The exception collector is now a ConcurrentQueue — it had one unguarded writer already, and the serial route adds a second.

Adopters: Marten JasperFx/marten#​5266, Polecat JasperFx/polecat#​489, Fisher JasperFx/fisher#​108 — each returns false from its EF Core projection storage.

One trap worth knowing if you write tests around this: NSubstitute proxies a default interface member rather than inheriting it, so Substitute.For<IProjectionStorage<,>>().IsThreadSafe is false and silently takes the serial route. Harmless, since serial is always correct, but a substitute cannot exercise the concurrent route and cannot be trusted to report what a real storage would.

The projection base classes now say they are not equivalent to yours (#​649, #​686)

Documentation only, no behavior change.

JasperFxSingleStreamProjectionBase and JasperFxEventProjectionBase are the shared implementations behind each store's own SingleStreamProjection<TDoc,TId> / EventProjection — and deriving from them directly is not the same as deriving from the store's subclass. Nothing about it fails at compile time.

As of Marten 9.23, Marten.Events.Aggregation.SingleStreamProjection<TDoc,TId> adds two behaviors the base omits:

  • BuildSlicer returns a TenantedEventSlicer with ForceSingleTenancy from the store's TenancyStyle — the fix for JasperFx/wolverine#​2053
  • ConfigureAggregateMapping sets UseVersionFromMatchingStream = true, which changes how an aggregate's version metadata is persisted

Take the base instead and you get a projection that builds, runs, and slices or versions differently. Polecat's subclass is an empty class body, so the divergence is Marten-shaped today — which is what makes it a trap: checking one store tells you nothing.

The XML docs now say this on the types themselves, and record the two routes that do work for a projection meant to compile against several stores: a per-flavour alias bound to each store's own subclass, or — where the document owns its stream — a self-aggregating document registered with Snapshot<T>(), which sidesteps it entirely because the store then constructs its own subclass.

This is the cheap half of #​649. Closing the gap properly — hoisting the behavior into the base, or a seam each store fills in — is still open.

Full Changelog: JasperFx/jasperfx@V2.52.1...V2.53.0

2.52.1

A single fix. Patch release — no API additions, no behavior change for anything that was not already broken.

ShardState.Mode now reports a rebuild (#​681, #​682)

ShardMode.rebuilding had no writer anywhere in the tree. Every ShardState a SubscriptionAgent published went out with the property's default of continuous — including the ones published during a replay — so a subscriber watching ShardStateTracker could not tell a projection catching up under a rebuild from one running normally. That is the only distinction the enum exists to draw.

The agent already knew which it was; it just knew it on a different enum (ShardExecutionMode) that was never copied onto what it published. So this is propagation, not new state.

What you can now key on. An IObserver<ShardState> sees Mode == ShardMode.rebuilding for every state a shard publishes while replaying — the start, per-batch progress, and a failure — and ShardMode.continuous otherwise. That is enough to tell an operator "this projection is rebuilding, so the sequence you are watching is catch-up progress toward the rebuild's ceiling, not lag".

ShardExecutionMode.CatchUp deliberately maps to continuous. A shard behind the high water mark under normal operation is still continuous: the number being watched is lag either way, and only a rebuild changes what it means.

A finished replay drops back to continuous before its teardown, so the Stopped state disposal publishes does not still claim to be rebuilding — otherwise a consumer tracking the last state it saw would latch on "rebuilding" permanently, from a rebuild that ended cleanly.

ShardMode is now documented, which it was not, because an enum with no writer had no stated meaning. Note in particular that none means "no mode was stored" — it is the default for a persisted progression row, not a running state.

Upgrading

Nothing to do. Marten, Polecat and Fisher all use JasperFx's SubscriptionAgent and ShardStateTracker directly — none of them implements ISubscriptionAgent — so all three pick this up on the package bump with no store-side change and no store release to wait for.

One thing this does not change: Marten's persisted mode column has no writer either, so a ShardState hydrated from the progression table still reports none. That is a separate path from the live tracker and out of scope here.

Full Changelog: JasperFx/jasperfx@V2.52.0...V2.52.1

2.52

What's Changed

Full Changelog: JasperFx/jasperfx@V2.51.0...v2.52

2.51.0

Three changes to the store-agnostic contracts, all additive. No store breaks on this bump.

IDocumentSessionOperations.PendingStreams (#​673, #​675)

A consumer holding the shared document session contract can now read the StreamActions a session has queued but not yet committed:

IReadOnlyList<StreamAction> PendingStreams { get; }

All three stores already surfaced the same JasperFx.Events.StreamAction collection under three different names — Marten's PendingChanges.Streams(), Polecat's PendingChanges.Streams, Fisher's Events.PendingStreams — so this closes a naming gap, not a capability gap. It sits on the committable tier for the same reason Events does: a stream action can only be pending in a session that can append, and IDocumentWriteOperations (the tier a projection's RaiseSideEffects receives) cannot.

The member carries a throwing default rather than an empty one. An empty list is indistinguishable from a session with nothing pending, so a silent default would let a consumer's derived work be discarded with a clean build and green tests.

New opt-in PendingStreamActionsCompliance holds stores to the behavior.

IAggregateWriteCache — a shared second-level snapshot cache for FetchForWriting (#​674, #​676)

The aggregate snapshot cache built and measured for Marten is now a shared contract in JasperFx.Events.Fetching, so Marten, Polecat and Fisher get one implementation of it rather than three.

  • IAggregateWriteCache, AggregateCacheKey, NulloAggregateWriteCache
  • RecentlyUsedAggregateWriteCache — the default, bounded and node-local
  • AggregateWriteCacheOptionsopt-in per aggregate type, off by default
  • EventRegistry.CacheAggregatesForWriting<T>() — inherited by every store's event options

The cached snapshot is a baseline only: the stream version and every event after the cached version are still read from the database on every call, and the optimistic concurrency assertion on append is untouched. A stale entry costs a larger delta query — never a wrong aggregate, never a suppressed concurrency failure. What it removes is the snapshot load, which on the measured workload was 4.5–4.9 ms of a 13.2 ms round.

The default implementation is backed by JasperFx.Core's existing RecentlyUsedCache, so this adds no new package dependency — a deliberate reversal of the prototype's Microsoft.Extensions.Caching.Memory reference, which would have pushed that onto every store.

New opt-in AggregateWriteCacheCompliance asserts that turning caching on is unobservable except in latency, including when the cached baseline is stale, ahead of the stream, or evicted.

Compliance suite fixes (#​672, #​677)

DocumentComplianceConfig gains a nullable StreamIdentity, so a document compliance suite can declare the stream identity style it needs instead of leaving each fixture to guess. DocumentSessionEventsCompliance appends by stream key and had no way to say so, which failed three of its five facts on every store defaulting to Guid identity — a correct store failing an undocumented precondition, which is a suite bug by definition. Additive: null means "leave the store on its own default", so no existing fixture changes.

BinaryEventAttribute is no longer sealed. A store that shipped its own before this one was promoted cannot delete it and could not derive from a sealed one, leaving it checking two attribute types indefinitely. Unsealing lets it subclass instead and collapse back to a single lookup, since attribute lookup matches by assignability. Stores without a pre-existing attribute should keep using the promoted one directly.

Downstream

Adoption is tracked per store: Marten JasperFx/marten#​5248, #​5249, #​5250, #​5251 · Polecat JasperFx/polecat#​477, #​478, #​479 · Fisher JasperFx/fisher#​96, #​97, #​98.

Full Changelog: JasperFx/jasperfx@V2.49.0...V2.51.0

2.50

What's Changed

New Contributors

Full Changelog: JasperFx/jasperfx@V2.49.0...v2.50

Commits viewable in compare view.

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

---
updated-dependencies:
- dependency-name: JasperFx
  dependency-version: 2.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Aug 23, 2026
@dependabot @github

dependabot Bot commented on behalf of github Aug 23, 2026

Copy link
Copy Markdown
Author

Superseded by #17.

@dependabot dependabot Bot closed this Aug 23, 2026
@dependabot
dependabot Bot deleted the dependabot/nuget/samples/Waymark.MartenSample/JasperFx-2.54.0 branch August 23, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants