Release 2026-09-03
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-09-03.md, and llms.txt lists every page.Part of the changelog.
Single-package patch release: Orleans.Lattice advances to 9.5.1 and the rest of the family stays at 9.5.0. This is a targeted hotfix cut from the lattice-v9.5.0 tag rather than from main, so it carries exactly one change and none of the unreleased work queued behind it.
Fixed
- Every dot-based CRDT now keeps at most one dot per replica per slot, instead of growing without bound each time a slot is re-asserted. Enabling an already-enabled flag, or re-adding an element already present, minted a fresh
OrSetDotand appended it while nothing ever removed the dot it superseded, so a slot re-asserted N times carried N dots forever and every read, merge and serialisation of that one row paid O(N). N is unbounded in any workload that re-asserts on a schedule, which makes presence marking - a loop ofOrFlag(key).EnableAsync(...)- the shape that reaches it first: a single presence row was observed growing until both reads and writes on its tree exceeded Orleans' 30 second response deadline, taking that tree out of service, and a unit probe retained 1,000 dots after 1,000 enables of one key. New shared machinery,OrSetDotCompaction, now collapses each slot to at most one dot per replica per side acrossOrFlag,OrSet,RwFlagandRwSet, so state is proportional to the number of replicas rather than to the number of assertions. The collapse is lossless only because a replica mints its own dots in counter order and cancellation is coverage-based - a cancelling dot from replica R at countertcancels every dot from R at counter at or belowt, rather than only the one it equals - so the two halves ship together and neither is sound alone. Convergence is unchanged: dots are never merged across replicas, so a concurrent assertion elsewhere keeps its own distinct dot and still wins or loses its primitive's tie-break exactly as before, which add-wins and remove-wins assert-versus-retract tests pin directly alongside commutativity, associativity and idempotence. No migration or operator step is needed, becauseCompact()runs on every mutation and every merge - including the delta fold used by replication and by write-ahead-log replay - so any key that is written or merged collapses its inherited history in place, and a key that is never re-asserted never bloated in the first place.OrMapis deliberately excluded, because its per-key entries carry a value rather than only a dot, so collapsing two entries from one replica would be silent data loss (#1937). (#1938) (Orleans.Lattice9.5.1)