Release 2026-08-23
This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at 2026-08-23.md, and llms.txt lists every page.Part of the changelog.
A coordinated lockstep release advances the whole published package family to 9.2.0: every Orleans.Lattice and Orleans.Lattice.* package on the v9 line moves to 9.2.0 together. The minor bump is driven by the additive materialised-view provider registration and single-producer support for replicated ShipView topologies (#1573); the verified atomic-commit assurance work (#1588), the RGA/leaf and CRDT allocation trims, the materialised-view drain-loop allocation fix, the mid-saga reshard torn-read fix, and the family-wide NuGet dependency modernisation ride along. The not-yet-published Orleans.Lattice.Api.Mcp.RepoContext, Orleans.Lattice.Api.Mcp.RepoContext.Replication, and Orleans.Lattice.Storage.File packages remain unreleased and are unaffected.
Added
- The atomic-commit protocol behind atomic writes and online reshard is now driven by a verified core and machine-checked for its all-or-nothing safety and liveness. The distributed prepare / commit / abort saga, the per-tree transaction-registry decision, and the reader-visibility gate are extracted into pure, deterministic cores that both the production grains and a verification layer execute, so the property that a reader never observes a partially applied multi-key write is proven of the exact production logic rather than only exercised by integration tests. A Coyote concurrency tier model-checks those cores under adversarial interleavings, each model shipping a non-vacuous guard test that proves the checked property can actually fail; a safety-and-liveness property catalogue enumerates the full correctness contract; and a TLA+ specification checked by TLC pins the protocol design over bounded instances, with a refinement note mapping it to the cores. This is an assurance change with no public API or runtime-behaviour change; the verified guarantees are documented in Verified Atomic-Commit and demonstrated by the VerifiedAtomicCommit sample. (
Orleans.Lattice9.2.0, #1588)
Changed
Three allocation trims on the RGA projection and leaf-grain read/split hot paths, with no behaviour, ordering, or wire-format change. Three independent,
MemoryDiagnoser-verified optimisations on paths the tree runs after every mutation: (1)Rga.ToListrebuilds its parent/children projection through a flat counting-sort layout (a singledot->indexmap plus reusedint[]arrays and areadonly structsibling comparer) instead of aDictionary<OrSetDot, List<RgaNode>>that allocated one childListper parent - N single-element lists for the linear collaborative-text shape - plus a boxed ordering delegate. The output order is identical. (2)LeafEntryCachedrains its deferred materialisers by iterating the deferred map's keys directly rather than snapshotting them into a throwawaystring[]on every hand-out of the backing rows (the loop mutates only the separate canonical-row map, so no snapshot is needed for safety). (3) The leaf split selects its median pivot key by reading the ordered key view's O(1) count and enumerating to the midpoint, instead of materialising every key into a throwawayList<string>only to index its middle element. DeterministicMemoryDiagnoserAllocateddeltas on the microbenchmarks added with this change:Rga ToList (uncached rebuild)57,559 B to 22,296 B (-61%),Leaf cache drain deferred73,626 B to 71,552 B (the 2,074 B delta is exactly the eliminated per-drain key snapshot at 256 rows, and is the drain's entire allocation on an already-populated cache), andLeaf split midpoint2,320 B to 240 B (-90%) - every affected benchmark strictly lower, none regressed. No public API change (all three are internal implementation details;Rga.ToList's signature and output are unchanged). (Orleans.Lattice9.2.0, #1582)The remaining CRDT clone/merge hot paths shed their discarded empty-collection allocations, completing the shell-elimination sweep, with no behaviour, ordering, or wire-format change. #1558 routed the deep-copy
Clone(and, for the counters/vectors,Merge) ofGCounter,PnCounter,VersionVector,OrSet, andRwSetthrough direct-assign private constructors, eliminating the empty-collection "shell" a field initializer allocates only for an object initializer to immediately overwrite. This applies the identical pattern to the five primitives that sweep missed -GSet,OrFlag,RwFlag,MvRegister, andOrMap- so every CRDT primitive on the replication apply loop now clones and merges without a discarded shell (one collection per backing store). DeterministicMemoryDiagnoserAllocateddeltas on the existing microbenchmarks:Crdt gset clone1,664 B to 1,600 B,Crdt gset merge2,904 B to 2,840 B,OrFlag merge400 B to 336 B,RwFlag merge424 B to 328 B,Crdt mvregister clone (multi-entry)14,688 B to 14,576 B,OrMap clone2,208 B to 1,968 B, andOrMap clone (multi-replica context)16,152 B to 15,912 B - every affected benchmark strictly lower, none regressed. No public API change (the added constructors are private; the parameterless constructors are retained, so Orleans serialization is unaffected). (Orleans.Lattice9.2.0, #1572)The materialised-view drain loop no longer allocates a view-tree-id string on every steady-state tick of a non-replicated deployment. The per-write-pass replication-topology re-evaluation added by #1581 eagerly built the
view-{name}(or#g{N}) tree id and handed it to the resolver, but the resolver reads that argument only after its non-replicated early return, so the defaultDeriveLocally-without-replication path threw the string away every drain tick. The tree id is now materialised only when replication is enabled (passingnullotherwise), removing the allocation from the drain hot path with no behavioural change - the resolver ignores the argument on the non-replicated path. The built-in predicate projection codec also decodes its persisted payload directly from the span instead of copying it to a temporary array first. (Orleans.Lattice9.2.0, #1583)NuGet dependencies across the package family are modernised to their latest stable versions, with no source or behaviour change. Every outdated third-party
PackageReferenceis advanced to the current stable release: Orleans10.2.0to10.2.2; the gRPC stack (Grpc.AspNetCore,Grpc.Net.Client,Grpc.Net.ClientFactory, and companions)2.80.0to2.83.0; the Azure SDK (Azure.Coreto1.62.0,Azure.Identityto1.21.0,Azure.Data.Tablesto12.12.0,Azure.Extensions.AspNetCore.DataProtection.Blobsto1.5.4); the identity stack (Microsoft.Identity.Web3.14.1to4.14.2,Microsoft.Identity.Clientto4.88.0, theMicrosoft.IdentityModel.*family8.3.0to8.22.0);Microsoft.Graph5.105.0to6.5.0withMicrosoft.Kiota.Abstractions1.22.2to2.0.0;ModelContextProtocolandModelContextProtocol.AspNetCore1.4.1to2.2.0;Npgsql9.0.3to10.0.3;SQLitePCLRaw.bundle_e_sqlite32.1.12to3.0.5;Microsoft.Data.Sqliteto10.0.11; theMicrosoft.Extensions.*family andSystem.IO.Hashing/Microsoft.Bcl.Memoryto10.0.11; and the test toolchain (Microsoft.NET.Test.Sdkto18.9.0,NSubstitute5.3.0to6.2.0).Microsoft.CodeAnalysis.Common/Microsoft.CodeAnalysis.CSharpare deliberately held at5.0.0to stay aligned with the Roslyn version Orleans pins transitively (avoiding anNU1608downgrade constraint). The full solution builds clean and the non-chaos test suite stays green. (Orleans.Latticepackage family 9.2.0, #1598)
Fixed
- Atomic writes are no longer torn-read when a saga overlaps an online reshard. A
SetManyAtomicAsyncsaga that prepared a key directly on a destination shard's leaf during a physical reshard could lose that key's atomic isolation if the leaf then B+ split:CompleteSplitAsyncmoved the committedIsMigrated=truerow to the new sibling but left the saga's isolation state (_pendingTxprepared bucket and_shadowedSagasmarker) behind on the donor, so the sibling served the migrated pre-saga value ungated. A concurrent reader could then observe some keys of the atomic batch at their post-saga value while a moved key still showed its pre-saga value - a torn read that violated all-or-nothing visibility (surfaced as an intermittent failure of the mid-saga reshard chaos fixture). The split now re-arms the sibling's read gate before the migrated rows are merged onto it, installing a destination-side shadow marker for every saga still isolating one of the moved keys (sourced from both the donor's_shadowedSagasmarkers and its_pendingTxprepared buckets), so aCommitted-without-backstop read stays gated on the sibling until the saga's committed-values backstop terminal lands there and clears the marker; InFlight and Aborted sagas pass the gate unchanged. The fix is at the single leaf-split chokepoint shared by single-tree and cross-tree atomic writes, so it closes the gap for both. No public API change. (Orleans.Lattice9.2.0, #1584) ShipViewmaterialised views now enforce one producer when both source and view trees replicate. A new per-viewShipViewProducerClusterIdselects the single maintainer by the stable replication cluster id; every other cluster suppresses its maintainer even though it can read the replicated source. Startup declarations and runtime creation reject ambiguous source-plus-view replication without a producer, while the existing source-WAL inference remains unchanged for thin consumers that do not replicate the source. The materialised-view guide now documents every supported replication topology with compile-checked configuration examples. (Orleans.Lattice9.2.0,Orleans.Lattice.Replication9.2.0, #1573)- Runtime-created materialised views now fail closed unless their exact projection can be reconstructed after restart. Stateful runtime projections can attach a bounded opaque descriptor to a host-registered provider, while filter-only predicate views persist their predicate automatically; activation verifies the reconstructed projection kind, shape, and version instead of silently degrading to pass-through or treating lost instance state as an upgrade. Runtime registrations are now durably written before
CreateAsyncreturns and recover correctly after transient persistence failures. TreeAdmin, gRPC, and MCP can create provider-backed views without accepting executable code, and State API and Explorer diagnostics expose the provider key and projection version without disclosing payloads. The materialised-view guide also documents relative local resource costs for pass-through, filter, count, sum, and exact set-union shapes. (Orleans.Lattice9.2.0,Orleans.Lattice.Api.Abstractions9.2.0,Orleans.Lattice.Api.TreeAdmin9.2.0,Orleans.Lattice.Api.TreeAdmin.Grpc9.2.0,Orleans.Lattice.Api.Mcp9.2.0,Orleans.Lattice.Api.State9.2.0,Orleans.Lattice.Explorer9.2.0, #1573)