Skip to content

chore: Bump Marten and 3 others - #537

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/Nexus.Application/multi-8a4bac02e8
Closed

chore: Bump Marten and 3 others#537
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/Nexus.Application/multi-8a4bac02e8

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 30, 2026

Copy link
Copy Markdown
Contributor

Updated Marten from 8.37.4 to 9.21.0.

Release notes

Sourced from Marten's releases.

9.21.0

Highlights

A small, low-risk release: two bug fixes reported against 9.20.x, a LINQ ordering fix, a Newtonsoft serialization fix, and a new health-check overload for Wolverine-managed daemon distribution.

[!NOTE]
There is a change to the mt_quick_append_events PostgreSQL function in this release, and applying it is NOT mandatory or required.

You do not need to patch your database, schedule a migration, or coordinate a deployment window to take 9.21.0. The client-side half of the #​5062 fix ships in the assembly, so upgrading the NuGet package alone is sufficient — 9.21.0 is correct against the function version you already have deployed.

Under the default AutoCreate.CreateOrUpdate the function is simply refreshed the next time Marten ensures event storage exists (a CREATE OR REPLACE FUNCTION, no lock on your event data). If you run AutoCreate.None with db-patch / db-apply, your next patch will contain one extra CREATE OR REPLACE FUNCTION … mt_quick_append_events statement — apply it whenever it suits your normal cadence. See the migration guide for details.

Bug Fixes

mt_quick_append_events returned {NULL} for an empty event array (#​5062, #​5088)

array_length('{}', 1) is NULL in PostgreSQL rather than 0, so calling the bulk append function with no events returned a bigint[] whose single element was NULL. Npgsql could not read that into long[], and the resulting InvalidCastException was thrown from the batch's post-processing loop — where it displaced whatever exception had actually made the append fail. Callers were left with an unrelated, non-retryable error instead of the real one; for the reporter that dead-lettered Wolverine messages which would otherwise have been retried.

Fixed on three fronts:

  • The function now COALESCEs the array length, so an empty append means what it says: zero events appended, final version unchanged.
  • The append operation no longer reads the returned array when the batch carries no events — this is what makes the fix effective without any database change.
  • The one code path in Marten that could reach the function with empty arrays (ProjectionUpdateBatch.WaitForCompletion, for an Append side effect that ended up with no events) no longer issues the call.

OrderBy against a dictionary indexer dropped the key (#​5063, #​5073)

OrderBy(x => x.SomeDictionary["key"]) generated SQL that ignored the indexer key, so the ordering was wrong (or arbitrary) rather than failing loudly.

Lazy LINQ sequences serialized as objects under Newtonsoft (#​5076, #​5080)

A document property holding a deferred-execution sequence (Select(...), Where(...) without a materializing call) was written by Newtonsoft as an iterator object rather than a JSON array, so it would not round-trip. These are now written as plain arrays.

IMessageBatch is called concurrently (#​5065, #​5085)

Not a behavior change, but a documentation fix worth flagging if you implement IMessageBatch yourself: the async daemon raises projection side effects from multiple threads at once (measured at up to 8 concurrent publishers across 10 threads for a single-stream projection catching up). The interface previously said nothing about this. An implementation that appends to an unsynchronized collection will silently drop messages — the same hazard, in a real outbox, that showed up here as a "flaky" test.

New

Provider-aware databaseFilter for the high-water health check (#​5061, #​5089)

AddMartenHighWaterHealthCheck's databaseFilter is captured at registration time, so it cannot resolve services — which makes it unable to express "the databases this node currently owns" when ownership is runtime state. That is precisely the case under Wolverine-managed daemon distribution, where agents are assigned per (database, tenant) and rebalanced over a node's lifetime.

There is now an overload whose filter receives the IServiceProvider and is re-evaluated on every probe:

Services.AddHealthChecks().AddMartenHighWaterHealthCheck(
    (services, database) => services.GetRequiredService<IWolverineRuntime>()
        .Agents.AllLocallyOwnedDatabaseIds()
        .Any(id => id.Name.EqualsIgnoreCase(database.Identifier)),
    staleThreshold: TimeSpan.FromSeconds(30),
    includeExternallyManaged: true);
 ... (truncated)

## 9.20.2

## What's Changed
* Fix NgramIndex to match NgramSearch's unaccent-aware mt_grams_vector expression by @​dat-honguyen in https://github.com/JasperFx/marten/pull/5060

## New Contributors
* @​dat-honguyen made their first contribution in https://github.com/JasperFx/marten/pull/5060

**Full Changelog**: https://github.com/JasperFx/marten/compare/V9.20.1...V9.20.2

## 9.20.1

Two real improvements:
1. Less log noise and faster/cleaner shutdowns at production time
2. Adjustments to the "high water mark" detection to ignore idle transactions from advisory locks in advancing the high water mark. This was a side effect of the extra work we did in 9.18 to try to stop event skipping from slow transactions

## What's Changed
* fix(#​4953): allocation fence keeps idle advisory-lock sessions from holding gap skips forever by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5057
* Adopt JasperFx.Events 2.36.2: clear resolved daemons on coordinator stop, idempotent AddAsyncDaemon by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5058


**Full Changelog**: https://github.com/JasperFx/marten/compare/V9.20.0...V9.20.1

## 9.20.0

Bug fixes around permutations of the natural key usage, new convenience mechanisms for querying for event store data

## What's Changed
* chore(deps-dev): bump find-my-way from 9.5.0 to 9.7.0 by @​dependabot[bot] in https://github.com/JasperFx/marten/pull/5045
* chore(deps-dev): bump postcss from 8.5.14 to 8.5.23 by @​dependabot[bot] in https://github.com/JasperFx/marten/pull/5046
* Retire the previous natural key row when the key changes (#​5041) by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5049
* Add FetchStreamStatePlan + FetchStreamPlan: raw event stream fetches as batchable query plans by @​uniquelau in https://github.com/JasperFx/marten/pull/5043
* StreamEventState + StreamEvents result types for Marten.AspNetCore by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5053
* Natural key table: scope the FK guard, and land the partitioned-FK repro (#​5044) by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5050
* Adopt JasperFx.Events 2.36.0: shard failure classification, drain timeout docs, natural key extraction by @​jeremydmiller in https://github.com/JasperFx/marten/pull/5054


**Full Changelog**: https://github.com/JasperFx/marten/compare/V9.19.0...V9.20.0

## 9.19.0

Marten 9.19.0 is a maintenance release adopting the coordinated **JasperFx / JasperFx.Events 2.35.0** drop, with a new projection side-effect capability and a secondary-store fix.

## Event sourcing

- **`RaiseSideEffects` slice-identity overload** — JasperFx.Events 2.35.0 (jasperfx#​561) adds a backwards-compatible aggregation-projection overload `RaiseSideEffects(IDocumentOperations operations, TId id, IEventSlice<TDoc> slice)`. The new `id` parameter hands you the slice identity **even when `slice.Snapshot` is null** because the aggregate was deleted in the same batch — so a `MultiStreamProjection` can recover the aggregate key to emit a follow-on event or publish a message on deletion. The original two-argument overload is unchanged, and the new one delegates to it by default. Documented with a compiled sample in [Side Effects](https://martendb.io/events/projections/side-effects).

## Fixes

- **#​5039** — `SecondaryStoreConfig.Build` threw `UriFormatException` when a secondary store was registered with a **generic** marker interface (e.g. `AddMartenStore<IMartenStoreMarker<MyContext>>()`). A closed generic CLR type name contains a backtick + arity, which is not a valid URI host. The `marten://` subject is now sanitized (arity stripped, generic argument names folded in so distinct closed generics still map to distinct subjects).

## Dependencies

- JasperFx / JasperFx.Events / JasperFx.Events.SourceGenerator / JasperFx.SourceGenerator → **2.35.0**
- Weasel.Postgresql / Weasel.Storage → 9.17.0 (unchanged)

## Notes

- The LINQ query-plan cache proposal (#​5013 / #​5018) is **not** in this releasereview surfaced a correctness gap on null filter values; it remains open for a follow-up.
- #​5001 (`running_on_node` under managed distribution) is resolved on the Marten side and closed; the node-stamping half ships in the JasperFx 2.35.0 / Wolverine distribution layer.


## 9.18.0

Marten 9.18.0 focuses on **raw-JSON streaming endpoints for ASP.NET Core**, **server-side LINQ `Select()` projection**, and continued **event-store observability** work.

## ASP.NET Core streaming & pagination

- **`StreamPaged<T>`** — stream a paged JSON envelope (`pageNumber`/`pageSize`/`totalItemCount`/`pageCount`/`hasNextPage`/`hasPreviousPage`/`items`) in a **single** round trip via a `count(*) OVER()` window column (#​5014). Test coverage hardened to pin the camelCase wire contract, page-past-end behavior, and filtered totals (#​5028).
- **`StreamPagedByCursor<T>`** — keyset ("seek") pagination that costs the same regardless of depth, using an opaque continuation cursor (#​5016). Now streams the **raw, already-persisted `data` column** byte-identical to `StreamMany`/`StreamPaged` (no hydrate + re-serialize) by reading the next cursor's ORDER BY key values off the same reader; a malformed client-supplied cursor now returns a clean **400** instead of a 500 (#​5033).
- **ETag / `If-None-Match` (304)** conditional-request support on `StreamOne<T>` and `StreamAggregate<T>` (#​5015). `StreamOne<T>` now reads the document's `mt_version` **inline in the same single round trip** (no follow-up metadata query), and the `where T : notnull` constraint tightening was reversed (#​5030).

## LINQ

- Simple `Select()` projections (`x => new Dto { A = x.A, B = x.Nested.B }`) now translate to a server-side **`jsonb_build_object(...)`** expression that is streamable as raw JSON, instead of hydrating the full document and projecting on the client (#​5017). Value-preserving conversions — widening numerics (`int`→`long`/`decimal`), boxing to `object`, nullable wrapping, `enum`→integral under `EnumStorage.AsInteger` — stay translatable and streamable; only lossy/computed conversions fall back (#​5032).
- **DCB tag operators** usable in `Where()` over events (#​5004).
- **`AggregateToMany()`** LINQ operator — run an event query through a multi-stream projection (#​5003).

## Event store, projections & daemon

- **Marten.TimescaleDB** extension — projection + document hypertables (#​4995).
- **Extended-progression telemetry**: batched per-flush heartbeat writes (#​5008); fixed a shutdown telemetry race in `extended_progression_batch_write` (#​5023); `running_on_node` write-path regression coverage (#​5001 / #​5007). The cross-repo `running_on_node` population under Wolverine-managed distribution is completed via JasperFx 2.34.0 + Wolverine.
- **Tenant-scoped event/tag explorer reads** — `ReadStreamAsync` / `GetRecentStreamsAsync` overrides honor tenancy (#​5020, #​5025), plus a `MultiStreamProjection` stepthrough via the instrumented fold in `EventStoreExplorer` (#​5002).
- **HighWaterHealthCheck** scoped to owned databases, with per-tenant and `ExternallyManaged` daemon support (#​4992).
- Surface JasperFx's application-assembly-reuse warning (GH-3521) at startup (#​5000).

## Multi-tenancy & infrastructure

- Token-capable maintenance connection for tenant database provisioning (#​5006).
- Docs: connecting to **Azure Database for PostgreSQL with Entra ID / managed identity** (#​4993).

## Dependencies

- JasperFx / JasperFx.Events **2.34.0**, Weasel **9.17.0**.

**Full changelog:** https://github.com/JasperFx/marten/compare/V9.17.0...V9.18.0


## 9.17.0

## High-water health check: opt-in `autoRestart` + heartbeat primary signal (#​4986)

Builds on the detection-only check from 9.16 (#​4984). Requires JasperFx **2.32.0** (jasperfx#​539), which this release rolls up to (#​4987).

- **Opt-in `autoRestart`** — `AddMartenHighWaterHealthCheck(TimeSpan? staleThreshold = null, long minimumGap = 1, bool autoRestart = false)`. When the check is Unhealthy and `autoRestart` is on, it asks the local projection coordinator's daemon to restart the high-water agent's **poll loop only** — the mark is never advanced — capped to once per staleness window per database. The cycle is still reported **Unhealthy** so an alert still fires. Intended for Solo / leader nodes.
- **Heartbeat is now the primary staleness signal**when `EnableExtendedProgressionTracking` is on, the high-water agent stamps a liveness heartbeat on the `HighWaterMark` row every poll cycle. Heartbeat age proves the loop is *cycling* independent of whether the mark *advances*, so a quiet store is never a false positive, and a dead agent is caught even when projections are fully caught up (the exact #​4961 blind spot). The original sequence-gap heuristic is retained as the `ExtendedProgression`-off fallback.

**Full changelog:** https://github.com/JasperFx/marten/compare/V9.16.1...V9.17.0

## 9.16.1

Async daemon data-safety release: the high water detection can no longer advance past "outstanding" event sequence numbers — sequences reserved by transactions that are still in flight — which could silently skip those events in async projections under concurrent append load (bulk imports being the classic case). Root-caused and fixed from discussion #​4953.

The four closed mechanisms:

* The `GapDetector` command batched three statements, each reading its own READ COMMITTED snapshot — commits landing mid-command could defeat every gap check and silently advance the mark over an in-flight append, regardless of `StaleSequenceThreshold`. Detection is now a single statement / single snapshot.
* Projection rebuilds and forced catch-up looped the gap-skipping detection toward the *reserved* sequence `last_value`, mowing through in-flight gaps. `CheckNowAsync` (JasperFx.Events 2.29.1) now targets the highest *committed* sequence and simply waits for in-flight appends to land.
* The stale fallback could teleport the mark to `reserved last_value - 32` across thousands of in-flight reservations on an idle-then-suddenly-busy store, because its gate measured staleness against `mt_event_progression.last_updated`. The threshold is now measured from when each specific gap was first observed.
* Wall-clock stale skipping could not tell a slow transaction from a rolled-back one. Before skipping any stale gap, Marten now checks PostgreSQL for evidence that a transaction which could still fill the gap is alive (`pg_locks` on the mt_events tables, open transactions in `pg_stat_activity`, in-progress write xids from `pg_current_snapshot()`), and holds while any exists — by default Marten never knowingly skips past a live appender. Only provably-dead gaps (rolled-back appends) are skipped, bounded to the sequence ceiling observed with the gap, and every skip is logged at Warning with its exact range.

New knobs on `StoreOptions.Projections`: `UseTransactionEvidenceForGapSkipping` (default `true`; `false` restores the previous wall-clock behavior) and `SkipStaleGapsDespiteLiveTransactionsAfter` (default `null` = never skip a live appender; PostgreSQL's `idle_in_transaction_session_timeout` is the recommended backstop against leaked sessions).

## What's Changed
* fix(#​4953): high water detection never crosses outstanding event sequences by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4977
* Consumes JasperFx.Events 2.29.1 (https://github.com/JasperFx/jasperfx/pull/530)

**Full Changelog**: https://github.com/JasperFx/marten/compare/V9.16.0...V9.16.1


## 9.16.0

Lot of CritterWatch, couple bug fixes too

## What's Changed
* Fix AdvancedSql/raw-SQL scalar queries for reference-typed columns (byte[], IPAddress, etc.) by @​mdissel in https://github.com/JasperFx/marten/pull/4960
* fix(#​4961): PostgresqlListenWakeup falls back to a timeout wait when the DB is unreachable by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4965
* Bump JasperFx to 2.28.0; declare EventProjection doc types for rebuild teardown (#​4685 COPY) by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4969
* fix(#​4966): update natural key on projection rebuild (JasperFx 2.28.1) by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4970
* test(#​4963): verify + document the blue/green side-effect gate by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4971
* fix(#​4964): hold the Normal high-water mark before a leading sequence gap by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4972
* refactor(#​4968): route stream archive through the shared Weasel event-store seam by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4973
* feat(#​4962): targeted per-cell ReadProjectionProgressAsync on MartenDatabase by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4974
* feat(#​4975): exact ReadProjectionProgressAsync(ShardName) override + JasperFx 2.29.0 by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4976


**Full Changelog**: https://github.com/JasperFx/marten/compare/v9.15.4...V9.16.0

## 9.15.4

## What's Changed
* Support Where Any on collections when using strongly typed Ids by @​ximon in https://github.com/JasperFx/marten/pull/4957
* Bump ws from 8.19.0 to 8.21.1 by @​dependabot[bot] in https://github.com/JasperFx/marten/pull/4955
* #​4956lock down ForTenant() conjoined-document tenant isolation by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4958

## New Contributors
* @​ximon made their first contribution in https://github.com/JasperFx/marten/pull/4957

**Full Changelog**: https://github.com/JasperFx/marten/compare/v9.15.3...v9.15.4

## 9.15.3

**This addresses a potential vulnerability from SQL injection via non-string constant in a LINQ Select projection**

Not a common usage, but still. 

## What's Changed
* Parameterize non-string Select projection constants (SQL injection fix) by @​jeremydmiller in https://github.com/JasperFx/marten/pull/4954


**Full Changelog**: https://github.com/JasperFx/marten/compare/9.15.2...v9.15.3

## 9.15.2

# Marten 9.15.2

A patch release. Both fixes come out of the same 512-tenant-database production deployment, reported by [@​erdtsieck](https://github.com/erdtsieck), and **both turned out to be worse than the reports described.**

## Bulk event insert ran a full schema apply on every batch

**[#​4946](https://github.com/JasperFx/marten/issues/4946)** — *fixed in [#​4949](https://github.com/JasperFx/marten/pull/4949)*

The batch `BulkInsertEventsAsync` overloads opened with `Storage.ApplyAllConfiguredChangesToDatabaseAsync()` on **every call**.

That is not a cheap check. It calls `Tenancy.BuildDatabases()` and runs a full schema delta — partition introspection plus `information_schema` sweeps — across **every database in the store**. So a sharded store paid *one apply per database, per batch*. On the reporting deployment, each ~1,000-event batch was triggering **512 schema applies**.

The measured effect: import throughput collapsed to **~17 events/s**, against **>3,000/s** for the streaming overload. A 686k-event tenant projected to roughly **11 hours**. The connection pool filled with ~370 backends whose last statement was Weasel's partition-introspection query, which fed directly into the server-wide connection pressure that deployment was already fighting.

That the streaming overload `BulkInsertEventStreamAsync` has **no such call** and is fine is the tell: the schema apply was never part of the contract. It was a leftover.

The apply is now:

- **skipped entirely when the effective `AutoCreate` is `None`** — it is a no-op there *by contract*, so all that remained was the introspection cost; and
- otherwise **run at most once per database the import actually touches**, memoized on `IMartenDatabase.Identifier`.

One subtlety worth recording, because it is the kind of thing that bites later: the memoized apply deliberately does **not** take a caller's `CancellationToken`. The first caller to arrive owns the single in-flight task that every concurrent caller for that database awaits — so binding that shared task to one caller's token would let a single cancelled batch fail sibling batches that were never cancelled. Each caller applies its own token at the await site instead. A schema apply is short and idempotent, so letting it run to completion is the cheaper trade.

**Under `AutoCreate.None`, the event storage must already exist before import.** That is the documented contract and it matches the streaming overload — but if you were previously relying on the per-call apply to create it for you under a non-`None` store, note the change.

The **document** bulk-insert path (`BulkInsertAsync` / `BulkInsertDocumentsAsync`) is **unaffected**. It routes through the ordinary per-feature `EnsureStorageExistsAsync` that Weasel already memoizes, not a full-store delta.

## Tenant provisioning silently under-provisioned partitions

**[#​4944](https://github.com/JasperFx/marten/issues/4944)** — *fixed in [#​4950](https://github.com/JasperFx/marten/pull/4950)*

`AddPartitionToAllTables`, and the tenant-provisioning paths built on it, walked the **calling store's `StoreOptions`** to decide which tables needed a list partition for a new tenant.

So any tool or host that provisions tenants from a store which doesn't register *every* document type **silently under-provisioned**. Document types unknown to the caller never got their partitions — and the tenant then failed with a Postgres **`23514` check-constraint violation on first write** to the missing partition. Nothing failed at provisioning time; the damage surfaced later, somewhere else.

The workaround was "the provisioning tool must register all document types," which re-creates schema knowledge in a second place and drifts as document types are added.

The sweep is now **database-driven**: it enumerates tenant list-partitioned tables from the Postgres catalog, so a partially-registered store still provisions every partitioned table it finds.

Scoping is enforced **inside the catalog query** rather than filtered in memory afterward:

- **Schema**the store's own `AllSchemaNames()` only. Foreign partitioned tables in a shared database are never touched.
- **Partition shape** — LIST strategy, exactly one key column, and that column named `tenant_id`. This is the filter that matters most, and it is what keeps the sweep off Marten's **own** non-tenant list partitioning: `UseArchivedStreamPartitioning` keys `mt_events` on `is_archived`, and `ByList()` keys on its own field. Without it, a "helpful" sweep would start adding *tenant* partitions to tables partitioned on something else entirely.
- **External management** — tables marked `ByExternallyManagedListPartitions()` are subtracted.

Opt out with `SweepPartitionedTablesFromDatabase` (default on). No Weasel change was required.

**Known limitation, and it is a real one:** a document type registered into a schema the *calling* store has never heard of stays invisible to the schema filter — a store cannot own a schema it does not know exists. Single-schema stores (the default, and the reporting deployment's shape) are fully covered. Closing this properly would need a persisted table list alongside `mt_tenant_partitions`.

---
 ... (truncated)

## 9.15.1

Patch release for a **silent data-correctness regression**. If you use `ForTenant()` on an identity-mapped or dirty-tracked session, upgrade.

## Fixed

- **[#​4947](https://github.com/JasperFx/marten/issues/4947) — `ForTenant()` on an identity session stopped returning tenancy-neutral documents** (reported by @​dervagabund, with a repro — thank you). A `ForTenant()` view of an identity- or dirty-tracked session no longer saw **global (tenancy-neutral) documents** tracked by the parent session. Since a global document has exactly one row per id for the whole database, `LoadAsync` through the `ForTenant` view missed the identity map, went to the database, and returned **`null`** for a document that is there. A silent wrong answer, not an error.

  **Affected: 9.13.0, 9.14.x, 9.15.0.** Introduced by the fix for #​4801, which tenant-scoped the identity map and version tracker for `ForTenant` sessions. That was correct for **conjoined** documents — where the same id means a *different* document per tenant — but it was applied **per session** rather than **per document type**, so it also isolated document types that are tenancy-neutral and must be shared.

  Sharing is now decided **per document type**. A nested `ForTenant` session shares the parent's identity-map and version-tracker entry for a type only when the storage is identity-mapped, the type is **not** `Conjoined`, and the nested session's database is the **same instance** as the parent's (under database-per-tenant, the same id in another tenant's database is a different document even for a tenancy-neutral type). **The isolation introduced by #​4801 is preserved exactly**the `Bug_4801` suite still passes, and the new tests include guard rails asserting conjoined documents stay isolated.

**Full changelog**: https://github.com/JasperFx/marten/compare/9.15.0...9.15.1


## 9.15.0

## Closed issues

- **#​4942 — sharded tenancy: auto-assign never repaired half-provisioned tenants** (PR #​4945). `findOrAssignTenantDatabaseAsync` returned early on an existing assignment row, skipping `createPartitionsForTenant` + per-tenant event-sequence provisioning — so a tenant whose provisioning was interrupted (assignment committed, partitions missing) failed every write with `23514` forever. Both early-return paths (including a second race-window hole under the advisory lock) now run the same idempotent repair the explicit `AddTenantToShardAsync(tenantId, databaseId)` overload always ran, guarded to once per process per tenant via the resolution cache.
- **#​4941two-day silent projection outage** (closed with full mapping). Root cause was #​4942; the invisibility was JasperFx/jasperfx#​506/#​507, fixed in **JasperFx 2.27.0** which this release consumes.

## Also in this release

- Bundles the fixed `JasperFx.Events.SourceGenerator` analyzer (JasperFx/jasperfx#​505) — CS1061 compile break for no-parameterless-ctor aggregates with instance `Apply` returning the aggregate.
- Follow-up enhancement filed as #​4944 (database-driven partition sweep via `pg_inherits`) for the #​4943 provisioning-tool scenario.

Verified against Wolverine (full solution + CoreTests/MartenTests/distribution/Http suites, zero failures) and CritterWatch before publishing. Thanks to @​erdtsieck for the dump-verified root-cause analysis.

## 9.14.1

Marten 9.14.1 is a patch release focused on a substantial round of LINQ query-translation improvements, plus event-store partitioning, high-water, and AoT fixes, and refreshed Weasel/JasperFx dependencies.

## LINQ query translation

This release significantly expands what the LINQ provider can push down to PostgreSQL instead of falling back to slower strategies or throwing:

- **Collection `Any(predicate)` filters** now translate to JSONPath and OR-of-containment strategies, and the old explode/`ctid` fallback has been replaced by a correlated `EXISTS` strategy. `All()` shapes and duplicated array fields moved onto the same `EXISTS` strategy. The net effect is correct, index-friendlier SQL for nested-collection predicates.
- **Indexing into complex child collections** inside `Where()` clauses is now supported (e.g. `x.Children[0].Name == "..."`).
- **Aggregates over collections** — `Sum`/`Min`/`Max`/`Average` — can now be used inside `Where()` clauses.
- **`Regex.IsMatch()`** is translated in `Where()` clauses.
- **`IComparable.CompareTo()`** now works for non-string comparables such as `Guid` (#​4920), alongside broader `CompareTo()` coverage, `string` `IsOneOf` via the `?|` operator, and `CollectionIsEmpty` via `ICollectionAware`.
- **`GinIndexJsonDataMember()`** was added for member-scoped expression GIN indexes.

### #​4916subclass queries now use duplicated fields and the base id

Querying a document **subclass** and filtering on a `Duplicate()`'d field or the base-class id previously emitted a JSONB filter (`CAST(d.data ->> 'FarmId' as uuid)`) instead of the real column, missing the duplicated column and the primary-key index:

```csharp
o.Schema.For<Animal>().AddSubClass<Cow>().Duplicate(x => x.FarmId);

Query<Cow>().Where(x => x.FarmId == id)  // now: d.farm_id = :p0     (was: CAST(d.data ->> 'FarmId' ...))
Query<Cow>().Where(x => x.Id == id)      // now: d.id = :p0          (was: CAST(d.data ->> 'Id' ...))

A subclass shares its parent's table, so the parent's column-backed members (duplicated fields, the id, the soft-delete flag) are now inherited by the subclass's query member resolution. Querying the parent type was already correct and is unchanged.

Event store, partitioning & daemon

  • #​4924 — hyphenated / GUID tenant ids under UseTenantPartitionedEvents. Registering a tenant whose partition suffix contains a - (so every GUID tenant id) made ApplyAllConfiguredChangesToDatabaseAsync() throw 42601 because the per-tenant CREATE SEQUENCE / DROP SEQUENCE DDL emitted the identifier unquoted. The schema-apply statements are now quoted (matching the quick-append function and the imperative provisioning path), so hyphenated tenants migrate cleanly. Quote — not sanitize — so the append function can still resolve the sequence by its raw suffix.
  • #​4915 — projection coordinator shutdown. The projection coordinator now drains on disposal, and via the Weasel 9.16.3 bump the advisory-lock ObjectDisposedException path latches-and-rethrows so a HotCold cold node's leadership loop terminates instead of re-polling a disposed data source during shutdown.
  • #​4913 — high-water scan under partitioning (JasperFx 2.26.0). Under UseTenantPartitionedEvents the store-global high-water agent was continuously running select max(seq_id) from mt_events, an unfiltered scan that fans out across every tenant partition on every poll. That store-global mark is not used to advance tenant projections (they advance per-tenant), so the recurring scan is now skipped under partitioning; tenant high water is driven by the per-tenant coordinator and poll timer.
  • jasperfx#​502 (#​4922)GetProjectionStatusesAsync now resolves the correct named database.

AoT / trimming

  • #​4917 — corrected AoT annotations in the event graph.
  • The AddEventType / QueryRawEventDataOnly generic-constraint tightening was reversed, and event-mapping construction now routes through the cached GenericFactoryCache while preserving the trimming root (#​4930).

Dependencies

  • Weasel 9.16.3 (#​4932) — advisory-lock disposed-pool fix (marten#​4915).
  • JasperFx 2.26.0 — the #​4913 high-water fix, plus 2.25.0's ShardState.DatabaseIdentifier (jasperfx#​501).

Closed issues

#​4913, #​4915, #​4916, #​4917, #​4924, and jasperfx#​502.

9.14.0

Marten 9.14.0 is the recommended upgrade for all 9.x users. It combines the LINQ SQL-injection security fix (first shipped in 9.13.0) with the fix for the projection-coordinator shutdown race in #​4874 and the accompanying dependency updates.

Beyond the LINQ updates, this made the new Per-Tenant Event Partitioning much more robust as we're testing that in conjunction with a JasperFx client for ludicrous scalability.

🔒 Security — SQL injection in the LINQ provider (GHSA-rfx3-98h7-v3xp)

Several LINQ / tenant-management code paths interpolated a runtime, potentially attacker-influenced value into generated SQL as a single-quoted literal without escaping or parameterization. A value containing a single quote could break out of the literal and inject SQL. The primary vector — a Dictionary<,> indexer key in a Where filter (a common "filter by attribute name" / EAV pattern) — was reported privately with an executed proof-of-concept and enabled filter / multi-tenant authorization bypass and blind data exfiltration.

Fixed sinks (#​4911):

  • DictionaryItemMember — dictionary indexer key, e.g. Where(x => x.Attributes[key] == v)
  • DictionaryContainsKeyFilterDictionary.ContainsKey(key) (Newtonsoft serializer + the Enum branch, which bypass System.Text.Json's quote escaping)
  • SelectParser — a constant string projected through Select(x => new { L = runtimeString })
  • DeleteAllForTenant — tenant id reaching per-tenant projection teardown (now parameterized)
  • DatabaseScopedTenantPartitions — tenant id inlined into partition DDL
  • EventLoader — per-tenant partition-pruning literal (defense-in-depth)

Each sink now escapes embedded single quotes or binds the value as a parameter; regression tests lock down every vector, and a follow-up LINQ-wide audit cleared the rest of the query hot path (full-text search, string-method translations, comparisons, IsOneOf/Contains/subset operators, and patching paths). Affected versions: 7.0.0 – 9.12.0. Also patched in 8.37.4 (8.x line) and 9.13.0.

Reported responsibly by @​svenclaesson — thank you. See advisory GHSA-rfx3-98h7-v3xp (CVE pending assignment).

🛠️ Reliability — projection-coordinator shutdown drain race (#​4874)

On host shutdown, the native HotCold projection coordinator could abort with ObjectDisposedException: 'Npgsql.PoolingDataSource' — the coordinator's leadership poll issued an OpenAsync against an already-disposed data source while tenancy was tearing down. This is the "case B" ordering storm reported against #​4874 (distinct from the async-tenancy foundation laid in #​4907, which did not resolve it).

The fix ships through the dependency updates below, with a Marten-side regression test (Bug_4874_coordinator_drain_ordering, #​4912):

  • JasperFx 2.24.1 (jasperfx#​499/#​500) — ProjectionCoordinatorBase terminates the leadership loop on a disposed data source / wrapped cancellation instead of re-polling.
  • Weasel 9.16.2 (weasel#​349/#​350) — AdvisoryLock guards against a disposed NpgsqlDataSource during shutdown (short-circuits while disposing and treats a disposed-pool ObjectDisposedException as a non-acquire rather than propagating).

⬆️ Dependency updates

  • JasperFx 2.24.0 → 2.24.1
  • Weasel 9.16.1 → 9.16.2
  • Weasel.EntityFrameworkCore 9.2.1 → 9.16.2 (released from its prior version hold now that the Weasel line is published)

Full changelog since 9.13.0

  • #​4911 — SQL injection fix in the LINQ provider (carried into this release)
  • #​4912 — regression test for the #​4874 case-B coordinator-drain ordering storm
  • JasperFx 2.24.1 / Weasel 9.16.2 / EFCore 9.16.2 bump (#​4874 shutdown-race fix)

9.13.0

Security release. Fixes SQL injection in the LINQ provider via unescaped string literals (#​4911).

Several LINQ / tenant-management code paths interpolated a runtime, potentially attacker-influenced value into generated SQL as a single-quoted literal without escaping or parameterization; a value containing a single quote could break out and inject SQL. The primary vector — a Dictionary<,> indexer key in a Where filter — was reported privately with an executed proof-of-concept (filter / multi-tenant authorization bypass, blind exfiltration).

Fixed sinks:

  • DictionaryItemMember — dictionary indexer key
  • DictionaryContainsKeyFilterContainsKey key (Newtonsoft serializer + Enum branch)
  • SelectParser — constant string projected via Select(...)
  • DeleteAllForTenant — tenant id in per-tenant projection teardown (now parameterized)
  • DatabaseScopedTenantPartitions — tenant id in partition DDL
  • EventLoader — per-tenant partition-pruning literal (defense-in-depth)

All 9.x users should upgrade. The 8.x line is fixed in 8.37.4. See advisory GHSA-rfx3-98h7-v3xp.

9.12.0

A couple significant bug fixes, and yet more support for CritterWatch

What's Changed

Full Changelog: JasperFx/marten@V9.11.0...V9.12.0

9.11.0

What's Changed

Full Changelog: JasperFx/marten@V9.10.0...V9.11.0

9.10.0

The new option might help the async daemon perform better in the face of concurrency exceptions on event appending with the QuickAppend option. It's opt in to avoid folks needing to do schema migrations

What's Changed

New Contributors

Full Changelog: JasperFx/marten@V9.9.1...V9.10.0

9.9.1

This will be a valuable upgrade for anyone who experiences a high degree of optimistic concurrency failures while using QuickAppend options, which is the default behavior in V9. This will help stop gaps in the event sequence, which in turn will make the Async Daemon healthier.

Also though, see Wolverine for help in preventing concurrent access in the first place

What's Changed

Full Changelog: JasperFx/marten@V9.9.0...V9.9.1

9.9.0

What's Changed

Full Changelog: JasperFx/marten@V9.8.2...V9.9.0

9.8.2

Couple bug reports related to the Daemon, one performance related for folks using the archived partitioning on the event store

What's Changed

Full Changelog: JasperFx/marten@V9.8.1...V9.8.2

9.8.1

This might impact folks migrating from Marten 8 to Marten 9. Strictly an issue with database migrations

What's Changed

Full Changelog: JasperFx/marten@V9.8.0...V9.8.1

9.8.0

This was pretty well 100% about CritterWatch. The new APIs are all to support CritterWatch

What's Changed

Full Changelog: JasperFx/marten@V9.7.5...V9.8.0

9.7.5

What's Changed

Full Changelog: JasperFx/marten@V9.7.4...V9.7.5

9.7.4

What's Changed

Full Changelog: JasperFx/marten@V9.7.3...V9.7.4

9.7.3

Small release. Couple fixes for daemon resiliency and CritterWatch administration actions

What's Changed

Full Changelog: JasperFx/marten@V9.7.2...V9.7.3

9.7.2

What's Changed

Full Changelog: JasperFx/marten@V9.7.1...V9.7.2

9.7.1

What's Changed

Full Changelog: JasperFx/marten@V9.7.0...V9.7.1

9.7.0

There's a few bug fixes, and the new functionality is really for CritterWatch.

What's Changed

Full Changelog: JasperFx/marten@V9.6.0...V9.7.0

9.6.0

There's a couple tenant aware APIs that are new, so this had to be a minor point bump. The majority of the work in this release was stress testing projection rebuilds and ensuring there was never any concurrent access of un-thread safe dictionaries inside of the async daemon that happened as a side effect of 9.0 changes.

What's Changed

Full Changelog: JasperFx/marten@V9.5.3...V9.6.0

9.5.3

This is a little optimization to the new 9.* code that eliminated the runtime codegen, and a fix for the daemon being a little vulnerable to concurrency in its internals -- which is also an optimization here.

What's Changed

Full Changelog: JasperFx/marten@9.5.2...V9.5.3

9.5.2

Bug fixes

  • #​4619 (#​4632) — mt_archive_stream emits explicit column lists in its INSERT…SELECT, surviving ALTER TABLE ADD COLUMN migrations that reorder the physical column layout (previously failed with 42804 after a column was added to mt_events).
  • #​4625 (#​4633) — BulkInsertEventsAsync writes mt_streams.type from the StreamAction's AggregateType, restoring UseMandatoryStreamTypeDeclaration support on the bulk path.
  • #​4641 (#​4646) — AddMartenManagedTenantsAsync no longer leaves a half-installed schema under AutoCreate.None. The admin call eagerly applies the events feature via a per-feature CreateMigrationAsync + scoped CreateOrUpdate apply, so the next append succeeds end-to-end on a virgin schema (previously failed with 42P01 / 42883).
  • #​4645 (#​4647) — DCB non-HStore tag query JOIN now includes e.tenant_id = t.tenant_id, eliminating own-event duplication under per-tenant sequences with UseTenantPartitionedEvents.

Test coverage

  • #​4617 closed — full TenantPartitionedEventsTests project (~170 tests across 50 files) covering append / read / projections / admin / DCB / async daemon / regressions under UseTenantPartitionedEvents.
  • Three projection coverage gaps closed: #​4650 (FlatTableProjection), #​4651 (DetermineActionAsync), #​4652 (doc-tables-NOT-partitioned-by-default invariant).
  • #​4649 investigated and pinned as documented intentional asymmetry — AutoCreate.CreateOnly continues to work via the lazy schema-apply path, by design (no SUT change needed).

Known follow-up — NOT in this release

  • #​4648AddGlobalProjection × UseTenantPartitionedEvents fails MT002 because the global event decorator writes to the *DEFAULT* tenant slot, which can't be a Postgres partition suffix. Test pin is in master asserting the throw; the underlying fix requires either routing global-aggregate events through a sibling non-partitioned table or reserving a default-tenant partition suffix. Marked as an enhancement, deferred to a later release.

🤖 Release notes assembled with Claude Code

9.5.1

What's Changed

Full Changelog: JasperFx/marten@V9.5.0...V9.5.1

9.5.0

The minor point bump here is because of some CritterWatch related features, otherwise this is all bug fixes

What's Changed

New Contributors

Full Changelog: JasperFx/marten@V9.4.0...V9.5.0

9.4.0

Marten 9.4.0

Per-tenant event partitioning and a tenant-aware async projection daemon (#​4596 / CritterWatch#​209). Built on JasperFx 2.5.0.

Highlights

  • Per-tenant event partitioning — opt in with opts.Events.UseTenantPartitionedEvents = true. On top of conjoined event tenancy, Marten partitions mt_events / mt_streams by tenant_id (native PostgreSQL LIST partitioning), gives each tenant its own event sequence (mt_events_sequence_{suffix}), and keys mt_event_progression by (name, tenant_id). Removes the single shared event store as a scalability bottleneck across tenants.
  • Tenant-aware async daemon — vectorized per-tenant high-water detection (one query per database reports the high-water position for every active tenant), per-tenant projection rebuild isolation, and cross-tenant rebuild fan-out.
  • Composite single-pass rebuild executor — read-once / fan-out rebuild for composite projections, with progression keyed on the composite's single {Name}:All shard.

Constraints for per-tenant partitioning

Validated at DocumentStore construction:

  • Requires Events.TenancyStyle = TenancyStyle.Conjoined.
  • Requires a quick append mode (EventAppendMode.Quick / QuickWithServerTimestamps); EventAppendMode.Rich is out of scope.
  • Cannot currently be combined with Events.UseArchivedStreamPartitioning (sub-partitioning by both tenant_id and is_archived is a planned follow-up).

The flag defaults to false; existing stores keep the global append path byte-for-byte.

Dependencies

  • Consumes the released JasperFx 2.5.0 packages (per-tenant partitioning surface, tenant-aware daemon abstractions, composite rebuild executor, and the SubscriptionAgent optimized-rebuild double-load fix).

Documentation

9.3.5

What's Changed

Full Changelog: JasperFx/marten@V9.3.4...V9.3.5

9.3.4

What's Changed

New Contributors

Full Changelog: JasperFx/marten@V9.3.3...V9.3.4

9.3.3

What's Changed

Full Changelog: JasperFx/marten@V9.3.2...V9.3.3

9.3.2

What's Changed

Full Changelog: JasperFx/marten@V9.3.1...V9.3.2

9.3.1

Marten 9.3.1

Fix release — bumps all four JasperFx.* dependencies to 2.2.1.

Package From To
JasperFx 2.2.0 2.2.1
JasperFx.Events 2.2.0 2.2.1
JasperFx.Events.SourceGenerator 2.2.0 2.2.1
JasperFx.SourceGenerator 2.2.0 2.2.1

No Marten-side code changes — straight dependency bump (#​4585).

Full Changelog: JasperFx/marten@V9.3.0...V9.3.1

9.3.0

Marten 9.3.0

The big-ticket items in this release are binary event serialization (#​4515) and the PostGIS + pgvector companion packages lifted into the Marten repo from CritterWatch.

Major

  • Binary event serialization for the event store (#​4515 — landed across #​4578, #​4581, #​4583, #​4584). Opt individual event types into a binary wire format (MemoryPack out of the box, or any IEventBinarySerializer you bring) on a per-event-type basis. JSON-serialized and binary-serialized events coexist in the same mt_events table so the feature can be turned on in an existing system with no migration of existing data. Works on every EventAppendMode (Rich + Quick + QuickWithServerTimestamps) and through BulkEventAppender. New optional NuGet: Marten.MemoryPack. See the binary-serialization docs for the design, registration, and the versioned-event-types schema-evolution recommendation.

  • PostGIS + pgvector companion packages (#​4576). Two new optional NuGets imported from CritterWatch:

    • Marten.PostGISUsePostGIS() opt-in that enables the postgis extension on every database Marten manages (multi-tenant aware), wires NetTopologySuite + GeoJSON serialization, and exposes four spatial query helpers (NearestToAsync, WithinDistanceAsync, ContainingAsync, IntersectingAsync). See the PostGIS docs.
    • Marten.PgVectorUsePgVector() opt-in that enables the vector extension on every database (also addresses #​2515 — extensions in tenant databases). VectorSearchAsync for similarity search plus an embedding-aware VectorProjection base class. See the pgvector docs.

Fixes

  • #​4575: CreatedAt.MapTo() regression in v9 (#​4577). The closed-shape storage rewrite ported every other metadata-column read-back but missed mt_created_at; this restores the v8 behavior where a [CreatedAt]-annotated / m.CreatedAt.MapTo(...)-mapped member is populated after a load.

Build / Release

  • Pack target updated (#​4582). Marten.PostGIS, Marten.PgVector, and Marten.MemoryPack are now included in the Nuke Pack target — without this they would silently never reach NuGet. 9 packages ship in 9.3.0 (up from 6): Marten, Marten.Newtonsoft, Marten.NodaTime, Marten.AspNetCore, Marten.EntityFrameworkCore, Marten.SourceGenerator, Marten.PostGIS, Marten.PgVector, Marten.MemoryPack.

  • Weasel 9.0.2 dependency bump (JasperFx/weasel#​299). Fixes PostgresqlMigrator.executeWithConcurrencyRetryAsync to reopen a Closed/Broken connection between retry attempts — eliminates the intermittent Connection is not open failure surfaced under concurrent migration races.

Documentation updates

Pages added or updated in 9.3.0:

Local docker

The local docker-compose.yml (from #​4576) layers postgresql-17-postgis-3 + postgresql-17-pgvector on the official multi-arch postgres:17 image so the Marten test suite can exercise the new extensions locally. PLv8 was dropped — Marten core SQL no longer requires it.

Full Changelog: JasperFx/marten@V9.2.1...V9.3.0

9.2.1

What's Changed

Full Changelog: JasperFx/marten@V9.2.0...V9.2.1

9.2.0

Marten 9.2.0

Features & changes

  • Override IEventStore.AllDatabases() on DocumentStore (#​4570, #​4571). Implements the store-agnostic database accessor added to JasperFx.Events.IEventStore. Delegates straight to ITenancy (mirroring IMartenStorage.AllDatabases()) and projects to IEventDatabase, so store-neutral monitoring/tooling can reach every database to call the read abstractions (AllProjectionProgress, FetchDeadLetterCountsAsync / CountDeadLetterEventsAsync) even when only IEventStore is registered in DI.

Dependencies

  • Upgraded all JasperFx.* packages to 2.2.0 (JasperFx, JasperFx.Events, JasperFx.Events.SourceGenerator, JasperFx.SourceGenerator).

Full Changelog: JasperFx/marten@V9.0.2...V9.2.0

9.0.2

Marten 9.0.2

A patch release that fixes #​4557 — self-aggregating projections failing for consumers that reference only the Marten package.

Fixes

  • #​4557 — Self-aggregating projections now work out of the box. Marten 9 dispatches conventional Apply/Create/ShouldDelete projection methods through the compile-time `JasperFx.Event......

Description has been truncated

Bumps Marten from 8.37.4 to 9.21.0
Bumps WolverineFx.Http from 5.40.0 to 6.24.1
Bumps WolverineFx.Http.Marten from 5.40.0 to 6.24.1
Bumps WolverineFx.Marten from 5.40.0 to 6.24.1

---
updated-dependencies:
- dependency-name: Marten
  dependency-version: 9.21.0
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: WolverineFx.Http
  dependency-version: 6.24.1
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: WolverineFx.Http.Marten
  dependency-version: 6.24.1
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: WolverineFx.Marten
  dependency-version: 6.24.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github

dependabot Bot commented on behalf of github Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #540.

@dependabot dependabot Bot closed this Jul 30, 2026
@dependabot
dependabot Bot deleted the dependabot/nuget/Nexus.Application/multi-8a4bac02e8 branch July 30, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No source-generated dispatcher found for Marten.Events.Aggregation.SingleStreamProjection<MyType, System.Guid>

0 participants