Release 2026-08-18
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-18.md, and llms.txt lists every page.Part of the changelog.
A per-package patch advances Orleans.Lattice.Replication to 9.0.2 and Orleans.Lattice to 9.0.5 (see Changed and Fixed); all other packages remain at their 9.0.x/9.0.0 versions.
Changed
MvRegisterwrites and single-valued reads no longer allocate on the steady-state hot path. A write (Set) previously built a freshListplus its backing array on every call, and a single-valued read (Values(), which the typedMvRegister<T>accessor read path funnels through) allocated a new one-element array on every read.Setnow compacts its live entries in place, and the single-valued read snapshot is cached and reused between writes, so a steady-state (single-valued) register - the overwhelmingly common shape - writes and reads with zero per-operation allocation. No public API, behaviour, or wire-format change; ordering and conflict semantics are identical. (Orleans.Lattice9.0.5, #1509, #1510)
Fixed
- Replicated cross-tree atomic writes now converge on peer clusters backed by Azure Table grain storage instead of stalling. The receiver-side barrier grain for a replicated cross-tree atomic write (
LatticeCrossTreeReceiverGrain) is keyed by a compound of(originClusterId, operationId). That key was built by joining the two halves with the ASCII Unit Separator (0x1F) control char - chosen so neither half could be confused with the other - but the receiver grain persists through a storage provider, and Azure Table grain storage carries the primary key into the request URL and the Partition/Row key columns, both of which reject control chars0x00-0x1F. On a real Azure deployment the receiver therefore failed to activate with an HTTP 400 "Bad Request - Invalid URL" onReadStateAsync/WriteStateAsync, so the receiver barrier for cross-tree atomic writes stalled and those writes converged across regions only slowly and erratically; single-tree atomic writes and plain writes were unaffected. The whole test and chaos suite stayed green because in-memory grain storage has no key-character restriction, hiding the defect from CI. The key is now a length-prefixed encoding (the decimal length oforiginClusterId, an underscore, then the two halves concatenated), which stays a single deterministic, control-char-free string while remaining unambiguous regardless of the characters in either half. Changing the key changes the receiver grain's identity, which is acceptable because the Azure Table path was non-functional and had no durable state to preserve. (Orleans.Lattice9.0.5, #1529) - Cross-tree atomic writes now become visible on peer clusters instead of hanging invisibly forever. A cross-tree atomic write (
IGrainFactory.SetManyAtomicAsync(IReadOnlyList<LatticeTreeBatch>, operationId, ct)) committed on its origin cluster but its keys never appeared on peer clusters, while single-tree atomic writes and plain writes on the same trees converged normally. The receiver's multi-entry batch apply path (ReplicationApplier.ApplyBatchAsync->ApplyOriginRunAsync) had no case for saga terminal-mark records (TxCommit/TxAbort): unlike the single-entryApplyAsyncpath, which intercepts terminals and routes them through the terminal seam, the batch path let a terminal fall through toApplyPointAsync, whose op switch threwUnsupported point-apply op. Because the production shipper coalesces a saga's contiguous WAL entries - its prepared writes and its terminal - into a single inbound batch, the terminal arrived batched, the whole batch faulted on apply, and the cross-tree receiver barrier (which holds every participating tree's keys pending until a terminal has arrived for each) never released, so the peer's keys stayed invisible indefinitely. The batch path now interceptsTxCommit/TxAbortand routes them through the same terminal seam as the single-entry path (flushing any deferred LWW/CRDT batch first so the terminal linearizes in WAL order, and staying high-water-mark-neutral), forwarding theCrossTreeOperationId/CrossTreeParticipantsbarrier metadata intact. Single-tree atomic convergence and local idempotency semantics are unchanged. (Orleans.Lattice.Replication9.0.2, #1525)