Repository navigation
Marten 9.23.0
Highlights
Store-agnostic document abstractions (#5216)
Marten now implements the JasperFx.Events.Documents contract added in JasperFx 2.47.0, the shared document session surface behind the Wolverine aggregate-handler unification (wolverine#3907).
| Marten | JasperFx contract |
|---|---|
IQuerySession |
IDocumentReadOperations |
IDocumentOperations |
IDocumentWriteOperations |
IDocumentSession |
IDocumentSessionOperations |
IDocumentStore |
IDocumentSessionFactory<IDocumentSession, IQuerySession> |
MartenLinqQueryProvider |
IDocumentQueryExecutor |
Marten already had every operation with matching signatures, so this is additive — existing code is unaffected. Marten passes all 42 tests in the shared document storage compliance suite.
Fixes
#5210 — UseListenNotifyForEventAppends corrupted inline projections in the same transaction. NotifyEventAppendedOperation was marked NoDataReturnedCall but emitted select pg_notify(...), which returns a one-row result set. OperationPage.ApplyCallbacksAsync never advances the reader past a NoDataReturnedCall, so every later operation in the batch read the wrong result set — surfacing as spurious ConcurrencyExceptions and wrong document versions, far from the cause. Now uses the DO $$ BEGIN PERFORM pg_notify(...); END $$ form, which fires the identical notification and returns nothing.
HardDeleteWhere<T> could not remove already soft-deleted rows. It inherited the soft-delete exclusion filter, so a retention or purge sweep returned cleanly while leaving behind exactly the rows it existed to remove. HardDelete by id was unaffected, which made the API look correct when spot-checked.
#5159 — UseOptimisticConcurrency and UseNumericRevisions were asymmetric. UseNumericRevisions(true) cleared the competing Guid version metadata; UseOptimisticConcurrency(true) did not clear the numeric revision metadata. The order of the two fluent calls therefore decided whether the configuration worked or threw at bootstrap. Both orders are now last-wins.
The bootstrap guard is narrowed rather than dropped, and now reports which case it is:
- a projection-target document, where numeric revisions are load-bearing for the projection machinery, still throws
- a revision declared on your own type (
IRevisioned,ILongVersioned, along[Version]member) still throws, naming the member — honoring the override would leave that property permanently unmapped - everything else is last-wins
#5131 — StreamPagedByCursor<T> published typeof(void) as its OpenAPI response type, so endpoints returning it advertised a 200 with no schema at all. Now publishes a new CursorPagedResult<T>, mirroring what StreamPaged<T> already does with PagedResult<T>.
Hardening and docs
#5213 — an audit test over the NoDataReturnedCall invariant. Reflects over every implementation and asserts its SQL cannot return a result set, so the class of bug behind #5210 goes red at build time rather than surfacing as a version mismatch three operations later.
#5212 — regression pins ported from Polecat, covering DeleteWhere not re-stamping mt_deleted_at on already-deleted rows.
#5217 — documented Npgsql's Max Auto Prepare connection-string knob, including measurements showing no resolvable effect on read latency, and the reasons Marten does not enable prepared statements: generic plans displacing parameter-aware ones (which matters most under conjoined multi-tenancy, where a skewed tenant_id sits in nearly every predicate), per-connection plan memory, and runtime DDL invalidating cached plans.
New public API
Marten.AspNetCore.CursorPagedResult<T>IQuerySession,IDocumentOperations,IDocumentSessionandIDocumentStorenow implement the correspondingJasperFx.Events.Documentscontracts
Both are additive.
Not shipped
The opt-in LINQ query plan cache proposed in #5013 / #5018 was closed rather than merged. Benchmarking showed the cost it removes — LINQ parsing plus SQL generation — is 1.2 μs on the fastest realistic Marten query, against 28–56 μs of run-to-run variance on that same query. On a warm cache hit it allocated more than no cache at all, and in a conjoined multi-tenant store it cached nothing while paying two full LINQ parses per query. Details on #5018.
Dependencies
- JasperFx and JasperFx.Events 2.46.0 → 2.47.0
- Weasel 9.24.0 (unchanged)
Full Changelog: V9.22.6...V9.23.0