Orleans.Lattice is a platform for building durable, distributed state systems on Microsoft Orleans.
See the codeOrleans.Lattice is a platform for building durable, distributed state systems on Microsoft Orleans.
At its centre is a sorted, horizontally-scalable, conflict-free key-value store that runs inside your own cluster. Around it are the concerns a real system acquires once it outgrows one machine - storage, identity, governance, replication, administration, observability - each implemented as a companion package behind a seam in the core, rather than baked into it.
It is local-first. A complete deployment runs on a single machine with no cloud dependency, and the same programming model carries through to a globally distributed, active-active estate. What changes between those two points is which companion packages a host registers, not the code that reads and writes data.
| If you want to | Go to |
|---|---|
| Get the idea in three minutes, with no background needed | Orleans.Lattice in three minutes, a video |
| Understand what the platform is and how it is put together | This page |
| Browse the full capability catalogue | FEATURES.md |
| Find the right package for a concern | PACKAGES.md |
| See a production deployment blueprint | reference-architecture.md |
| Write code now | Quick Start |
| Read the day-to-day reference docs | Documentation |
The core is a sorted, durable, horizontally-scalable key-value store embedded
in your Orleans cluster. Keys are string, values are byte[], and typed-value
helpers layer automatic serialization on top. No external database, no
coordinator service, no external queue.
The keyspace is split across self-balancing B+ sub-trees that rebalance themselves online. The durability boundary is a write-ahead log. Conflict resolution is algebraic rather than lock-based or consensus-based, which is what lets any cluster accept a write to any key.
The core alone supports:
IAsyncEnumerable sources.System.Diagnostics.Metrics instruments.The name comes from its use of lattice-based state primitives - mathematical structures where merges are commutative, associative, and idempotent - which is what makes the system conflict-free and recoverable without distributed locks or consensus (provided you use its CRDT Primitives).
Every durable distributed system ends up solving the same set of problems: sharding, online rebalancing, crash-safe durability, conflict resolution, backup, tenancy, identity, replication, and an operator surface. They are usually assembled from a database, a cache, a queue, an identity provider, and a layer of glue - each with its own operational model, its own failure modes, and its own consistency story to reconcile with the others.
Orleans already supplies the hard parts of a distributed runtime: virtual actors, single-threaded execution per grain, location transparency, and failover. What it does not supply is an ordered, durable, shardable store to put underneath them. Orleans.Lattice fills that gap, and takes three positions about how:
The result is a platform rather than a product: it is not tied to one application category, and the deployment topology is a configuration decision taken late, not an architecture decision taken up front.
Orleans.Lattice is a substrate for durable distributed state, so the categories below are examples of what the same primitives compose into, not a fixed feature set.
| Category | Why the platform fits | Relevant capabilities |
|---|---|---|
| Knowledge systems | Ordered keyspaces with per-key revision history and server-side filtering, so a corpus can be browsed, versioned, and queried without a separate metadata store. | Change history, predicate operations, tag indexes |
| AI memory systems | Conflict-free records with per-entry TTL and approximate nearest-neighbour search over vectors held in the same store the records live in. | Vector search, TTL, MCP server |
| Digital twins | One grain per entity with durable, ordered state behind it, converging deterministically when a device and the cloud both write. | Conflict-free merges, grain indexes, events |
| Search and indexing platforms | Materialised views maintained off the write-ahead log, so secondary access paths are derived rather than hand-maintained, alongside tag indexes queried by tag intersection or union. | Materialised views, tag indexes, vector search |
| Distributed control planes | Fail-closed authorization, atomic multi-key writes, fencing-token leases, and a saga coordinator for changes that must apply all-or-nothing. | Atomic writes, atomic action, distributed lock |
| Multi-tenant SaaS platforms | Keyspace-partitioned tenants with per-tenant quotas, metering, rate limiting, and optional region residency, layered on the core through null seams. | Multi-tenancy, schema enforcement, tenant administration |
| Collaborative applications | Active-active replication where any cluster may write any key, with deterministic convergence and no coordinator to elect. | Cross-cluster replication, state primitives, change history |
A deployment grows in three stages. The application code that reads and writes
data is identical in all three: it resolves ILattice and calls it. Each stage
adds companion packages and configuration, not a rewrite.
One machine, no cloud account, no external services. This is a first-class deployment target, not a degraded development mode.
A shared cluster with real users, so identity, policy and data shape start to matter.
Multiple regions, each serving reads and writes.
| Stage | Add | Programming model |
|---|---|---|
| Local | File WAL, Explorer, MCP | ILattice |
| Team | Membership, Auth, Schema, Tenancy | ILattice |
| Global | Replication, Backup, Scaling | ILattice |
The distinctive structural property of Orleans.Lattice is that major concerns are not implemented in the core. Each is a seam the core defines and a companion package fills. A host composes the platform it needs by registering packages; nothing it leaves out is present at runtime.
flowchart TD
App["Applications<br/>knowledge systems, AI memory, digital twins, search,<br/>control planes, multi-tenant SaaS, collaboration"]
App --> Explorer["Explorer console<br/>(in progress)"]
App --> Apis["API facades<br/>state, data, auth, schema, backup, replication,<br/>telemetry, tree admin, tenant admin, apps"]
App --> Mcp["MCP server<br/>tools for AI agents"]
Explorer --> Core
Apis --> Core
Mcp --> Core
App -. "in-process ILattice" .-> Core
Core["Orleans.Lattice core<br/>sharded CRDT B+ tree, write-ahead log,<br/>durability boundary, grain catalogue"]
Core --> Storage["Storage"]
Core --> Identity["Identity"]
Core --> Governance["Governance"]
Core --> Replication["Replication"]
Core --> Administration["Administration"]
Core --> Observability["Observability"]
Storage --> StoragePkgs["Storage.AzureTable<br/>Storage.File<br/>Backup.AzureBlob"]
Identity --> IdentityPkgs["Membership<br/>Membership.Oidc<br/>Membership.Entra<br/>Auth"]
Governance --> GovernancePkgs["Schema<br/>Tenancy<br/>Apps"]
Replication --> ReplicationPkgs["Replication<br/>Replication.Grpc"]
Administration --> AdminPkgs["Backup<br/>Api.TreeAdmin<br/>Api.TenantAdmin"]
Observability --> ObsPkgs["Dashboards<br/>Scaling<br/>Api.Telemetry"]
Three consequences follow from this shape, and they are worth understanding before reading the catalogues:
The complete inventory lives in PACKAGES.md, and the capability each package delivers is catalogued in FEATURES.md.
Behaviour is validated end-to-end by a suite of chaos tests that hammer a live cluster with concurrent reads, writes, scans, splits, resizes, and reshards - optionally with random storage-write faults - and assert both live consistency and eventual convergence. The concurrency-critical protocols go further: the atomic-commit protocol, the WAL seams, the distributed lock, and the atomic-action coordinator are driven by pure deterministic cores that a verification tier machine-checks with Coyote and, for atomic commit, a TLA+ specification.
Register Lattice on a silo. AddLattice registers the grain catalogue, the grain storage provider (via the supplied callback), and the in-memory write-ahead-log backend in a single call:
siloBuilder.AddLattice((silo, storageName) =>
silo.AddMemoryGrainStorage(storageName));
// AddLattice registers the in-memory WAL by default. In production, use durable grain
// storage and a durable WAL backend instead - see below.
// elsewhere - on the client or inside a grain - resolve a tree by name:
var lattice = grainFactory.GetGrain<ILattice>("my-tree");
// Values are byte[] at the core, but the typed extensions serialize for you, so
// application code rarely touches a byte[]. These overloads default to JSON:
await lattice.SetAsync("user/42", new User("Ada", 36));
var user = await lattice.GetAsync<User>("user/42");
Console.WriteLine(user?.Name);
// Pass an ILatticeSerializer<T> to choose your own format, or use the raw
// byte[] surface directly when you want to own the encoding:
await lattice.SetAsync("hello", "world"u8.ToArray());
For production, make both storage surfaces durable: the grain-storage provider that holds tree state, including each leaf's state row and snapshot, and the write-ahead log, in place of the in-memory WAL. For example, Azure Table Storage for both, from the Microsoft.Orleans.Persistence.AzureStorage and Orleans.Lattice.Storage.AzureTable packages:
using Azure.Data.Tables;
using Microsoft.Extensions.DependencyInjection;
using Orleans.Lattice.Storage.AzureTable;
var connectionString = "DefaultEndpointsProtocol=https;...";
siloBuilder.AddLattice((silo, storageName) =>
{
silo.AddAzureTableGrainStorage(storageName, options =>
{
options.TableServiceClient = new TableServiceClient(connectionString);
});
});
siloBuilder.AddAzureTableWalStorage(o =>
{
o.ConnectionString = connectionString;
});
Add cross-cluster replication on top by registering AddLatticeReplication(...) alongside the WAL. See the Orleans.Lattice.Replication overview for the full multi-cluster setup.
For a local-first alternative to Azure Table Storage, pair the file write-ahead log with a durable local grain-storage provider - the RepoContext container runs Orleans ADO.NET grain storage over a single SQLite file - and keep the whole deployment on one machine.
RepoContext is an MCP server that gives an AI agent durable, conflict-free memory about a codebase: a structural record and content digest per file, symbol outlines and a reverse cross-reference graph, agent-authored notes and decisions with optional TTL, semantic search over embeddings, and a budgeted context bundle with reuse accounting. It can also run memory-only, disabling file and symbol indexing while keeping agent memory. It runs as a single local container alongside its embedding companion.
It is worth reading as a worked example because it composes most of the platform at once, and does so without a line of bespoke storage code:
Orleans.Lattice.Storage.File makes the
container restart-durable with no cloud account.Orleans.Lattice.Vector provides the
approximate nearest-neighbour index behind semantic search, persisted on a
tree so a restart reloads it rather than rebuilding it.Orleans.Lattice.Api.Mcp supplies the
agent-facing surface and the fail-closed authorization gate; RepoContext adds
no authorization path of its own.Orleans.Lattice.Api.Mcp.RepoContext.Replication
turns the same store into a multi-cluster one by choosing a per-tree merge
mode, which is the deployment journey applied to a
real application.RepoContext demonstrates Orleans.Lattice. It does not define it. It is one application category among many, and nothing in the platform is shaped around it.
See the Repo-context MCP documentation and the container sample.
Use these documents for day-to-day use and operations:
ILattice interface, batch operations, options, and serializable types.ILattice is guaranteed to observe, operation by operation.SetManyAtomicAsync: all-or-nothing multi-key batches within a tree, and across trees through the IGrainFactory overload.IAtomicActionGrain saga / TCC coordinator: an ordered plan of steps that commits all-or-nothing, compensating completed steps in strict reverse order.ILatticeLockGrain: a FIFO-fair lock / lease keyed by name, with bounded leases and monotonic fencing tokens.SetAsync and on typed CRDT writes, with absolute server-side expiry.BulkLoadAsync for a one-shot import into an empty tree, its streaming and resumable chunked forms, and how it compares with SetManyAsync.ReshardAsync: growing or shrinking a tree's physical shard count while it keeps serving reads and writes.ResizeAsync: changing a live tree's MaxLeafKeys and MaxInternalChildren, its phase machine, and its undo window.ITreeOwnershipGuard seam that bounds alias changes.ILatticeQueue<T> cluster-internal FIFO primitive, bounded-queue eviction, and throughput guidance.ILatticeCompressor seam, AddLatticeCompressor registration, tag-space partitioning, and how to plug in a custom algorithm.ScanEntryHistoryAsync, the State API, or the Explorer.DiagnoseAsync: a point-in-time health snapshot of a tree for dashboards, health probes, and post-mortem investigation.System.Diagnostics.Metrics instrument catalogue, its tag conventions, OpenTelemetry registration, and the bundled Grafana dashboards.DiagnoseAsync report, storage-provider write failures, split activity, slow scans, and stale reads.For internals (the "how"):
IWalStorageProvider durability seam, in-memory default, optional Azure Table and local-file backends.WalMaxPendingBatches and WalPartitions interact with a durable backend's throughput envelope; default sizing rules and the storage-account ceiling above which the cap stops helping.IWalSaturationSignal, IWalSaturationObserver) that lets callers throttle offered load before silent queueing on the writer-side admission gate.For the complete catalogues:
.md.Orleans.Lattice inherits the asymptotic properties of a B+ tree. In a single shard containing n keys with branching factor b:
| Operation | Time Complexity |
|---|---|
Point read (GetAsync) | O(logb n) |
Insert / update (SetAsync) | O(logb n) |
Delete (DeleteAsync) | O(logb n) |
Ordered scan (ScanKeysAsync) | O(n) |
Count (CountAsync) | O(n), across O(n / b) leaf calls |
| Space | O(n) |
With the default branching factor (~128 children per node), a shard with two million keys is only three levels deep. Depth adds no grain calls on the steady-state path: the shard root caches each internal node's routing table, so a single-key lookup crosses just three grains - the tree's router, the shard root, and the owning leaf or its per-silo read cache. Sharding (default 64) reduces per-shard n further; cross-shard operations scatter-gather across all shards.
Measured single-silo throughput and latency against real Azure Tables are in the single-silo performance guide, and how throughput responds as silos are added is in the multi-silo scaling guide.
See CHANGELOG.md for the release notes, in dated sections that name every package version shipped, and docs/RELEASING.md for the per-package tag-and-publish protocol.
Contributions are welcome! To get started:
main.Please open an issue first to discuss significant changes or new features before starting work.
This project is licensed under the MIT License. See LICENSE for details.
C#
96.4%
PowerShell
1.5%
Orleans.Lattice is a platform for building durable, distributed state systems on Microsoft Orleans.
See the codeOrleans.Lattice is a platform for building durable, distributed state systems on Microsoft Orleans.
At its centre is a sorted, horizontally-scalable, conflict-free key-value store that runs inside your own cluster. Around it are the concerns a real system acquires once it outgrows one machine - storage, identity, governance, replication, administration, observability - each implemented as a companion package behind a seam in the core, rather than baked into it.
It is local-first. A complete deployment runs on a single machine with no cloud dependency, and the same programming model carries through to a globally distributed, active-active estate. What changes between those two points is which companion packages a host registers, not the code that reads and writes data.
| If you want to | Go to |
|---|---|
| Get the idea in three minutes, with no background needed | Orleans.Lattice in three minutes, a video |
| Understand what the platform is and how it is put together | This page |
| Browse the full capability catalogue | FEATURES.md |
| Find the right package for a concern | PACKAGES.md |
| See a production deployment blueprint | reference-architecture.md |
| Write code now | Quick Start |
| Read the day-to-day reference docs | Documentation |
The core is a sorted, durable, horizontally-scalable key-value store embedded
in your Orleans cluster. Keys are string, values are byte[], and typed-value
helpers layer automatic serialization on top. No external database, no
coordinator service, no external queue.
The keyspace is split across self-balancing B+ sub-trees that rebalance themselves online. The durability boundary is a write-ahead log. Conflict resolution is algebraic rather than lock-based or consensus-based, which is what lets any cluster accept a write to any key.
The core alone supports:
IAsyncEnumerable sources.System.Diagnostics.Metrics instruments.The name comes from its use of lattice-based state primitives - mathematical structures where merges are commutative, associative, and idempotent - which is what makes the system conflict-free and recoverable without distributed locks or consensus (provided you use its CRDT Primitives).
Every durable distributed system ends up solving the same set of problems: sharding, online rebalancing, crash-safe durability, conflict resolution, backup, tenancy, identity, replication, and an operator surface. They are usually assembled from a database, a cache, a queue, an identity provider, and a layer of glue - each with its own operational model, its own failure modes, and its own consistency story to reconcile with the others.
Orleans already supplies the hard parts of a distributed runtime: virtual actors, single-threaded execution per grain, location transparency, and failover. What it does not supply is an ordered, durable, shardable store to put underneath them. Orleans.Lattice fills that gap, and takes three positions about how:
The result is a platform rather than a product: it is not tied to one application category, and the deployment topology is a configuration decision taken late, not an architecture decision taken up front.
Orleans.Lattice is a substrate for durable distributed state, so the categories below are examples of what the same primitives compose into, not a fixed feature set.
| Category | Why the platform fits | Relevant capabilities |
|---|---|---|
| Knowledge systems | Ordered keyspaces with per-key revision history and server-side filtering, so a corpus can be browsed, versioned, and queried without a separate metadata store. | Change history, predicate operations, tag indexes |
| AI memory systems | Conflict-free records with per-entry TTL and approximate nearest-neighbour search over vectors held in the same store the records live in. | Vector search, TTL, MCP server |
| Digital twins | One grain per entity with durable, ordered state behind it, converging deterministically when a device and the cloud both write. | Conflict-free merges, grain indexes, events |
| Search and indexing platforms | Materialised views maintained off the write-ahead log, so secondary access paths are derived rather than hand-maintained, alongside tag indexes queried by tag intersection or union. | Materialised views, tag indexes, vector search |
| Distributed control planes | Fail-closed authorization, atomic multi-key writes, fencing-token leases, and a saga coordinator for changes that must apply all-or-nothing. | Atomic writes, atomic action, distributed lock |
| Multi-tenant SaaS platforms | Keyspace-partitioned tenants with per-tenant quotas, metering, rate limiting, and optional region residency, layered on the core through null seams. | Multi-tenancy, schema enforcement, tenant administration |
| Collaborative applications | Active-active replication where any cluster may write any key, with deterministic convergence and no coordinator to elect. | Cross-cluster replication, state primitives, change history |
A deployment grows in three stages. The application code that reads and writes
data is identical in all three: it resolves ILattice and calls it. Each stage
adds companion packages and configuration, not a rewrite.
One machine, no cloud account, no external services. This is a first-class deployment target, not a degraded development mode.
A shared cluster with real users, so identity, policy and data shape start to matter.
Multiple regions, each serving reads and writes.
| Stage | Add | Programming model |
|---|---|---|
| Local | File WAL, Explorer, MCP | ILattice |
| Team | Membership, Auth, Schema, Tenancy | ILattice |
| Global | Replication, Backup, Scaling | ILattice |
The distinctive structural property of Orleans.Lattice is that major concerns are not implemented in the core. Each is a seam the core defines and a companion package fills. A host composes the platform it needs by registering packages; nothing it leaves out is present at runtime.
flowchart TD
App["Applications<br/>knowledge systems, AI memory, digital twins, search,<br/>control planes, multi-tenant SaaS, collaboration"]
App --> Explorer["Explorer console<br/>(in progress)"]
App --> Apis["API facades<br/>state, data, auth, schema, backup, replication,<br/>telemetry, tree admin, tenant admin, apps"]
App --> Mcp["MCP server<br/>tools for AI agents"]
Explorer --> Core
Apis --> Core
Mcp --> Core
App -. "in-process ILattice" .-> Core
Core["Orleans.Lattice core<br/>sharded CRDT B+ tree, write-ahead log,<br/>durability boundary, grain catalogue"]
Core --> Storage["Storage"]
Core --> Identity["Identity"]
Core --> Governance["Governance"]
Core --> Replication["Replication"]
Core --> Administration["Administration"]
Core --> Observability["Observability"]
Storage --> StoragePkgs["Storage.AzureTable<br/>Storage.File<br/>Backup.AzureBlob"]
Identity --> IdentityPkgs["Membership<br/>Membership.Oidc<br/>Membership.Entra<br/>Auth"]
Governance --> GovernancePkgs["Schema<br/>Tenancy<br/>Apps"]
Replication --> ReplicationPkgs["Replication<br/>Replication.Grpc"]
Administration --> AdminPkgs["Backup<br/>Api.TreeAdmin<br/>Api.TenantAdmin"]
Observability --> ObsPkgs["Dashboards<br/>Scaling<br/>Api.Telemetry"]
Three consequences follow from this shape, and they are worth understanding before reading the catalogues:
The complete inventory lives in PACKAGES.md, and the capability each package delivers is catalogued in FEATURES.md.
Behaviour is validated end-to-end by a suite of chaos tests that hammer a live cluster with concurrent reads, writes, scans, splits, resizes, and reshards - optionally with random storage-write faults - and assert both live consistency and eventual convergence. The concurrency-critical protocols go further: the atomic-commit protocol, the WAL seams, the distributed lock, and the atomic-action coordinator are driven by pure deterministic cores that a verification tier machine-checks with Coyote and, for atomic commit, a TLA+ specification.
Register Lattice on a silo. AddLattice registers the grain catalogue, the grain storage provider (via the supplied callback), and the in-memory write-ahead-log backend in a single call:
siloBuilder.AddLattice((silo, storageName) =>
silo.AddMemoryGrainStorage(storageName));
// AddLattice registers the in-memory WAL by default. In production, use durable grain
// storage and a durable WAL backend instead - see below.
// elsewhere - on the client or inside a grain - resolve a tree by name:
var lattice = grainFactory.GetGrain<ILattice>("my-tree");
// Values are byte[] at the core, but the typed extensions serialize for you, so
// application code rarely touches a byte[]. These overloads default to JSON:
await lattice.SetAsync("user/42", new User("Ada", 36));
var user = await lattice.GetAsync<User>("user/42");
Console.WriteLine(user?.Name);
// Pass an ILatticeSerializer<T> to choose your own format, or use the raw
// byte[] surface directly when you want to own the encoding:
await lattice.SetAsync("hello", "world"u8.ToArray());
For production, make both storage surfaces durable: the grain-storage provider that holds tree state, including each leaf's state row and snapshot, and the write-ahead log, in place of the in-memory WAL. For example, Azure Table Storage for both, from the Microsoft.Orleans.Persistence.AzureStorage and Orleans.Lattice.Storage.AzureTable packages:
using Azure.Data.Tables;
using Microsoft.Extensions.DependencyInjection;
using Orleans.Lattice.Storage.AzureTable;
var connectionString = "DefaultEndpointsProtocol=https;...";
siloBuilder.AddLattice((silo, storageName) =>
{
silo.AddAzureTableGrainStorage(storageName, options =>
{
options.TableServiceClient = new TableServiceClient(connectionString);
});
});
siloBuilder.AddAzureTableWalStorage(o =>
{
o.ConnectionString = connectionString;
});
Add cross-cluster replication on top by registering AddLatticeReplication(...) alongside the WAL. See the Orleans.Lattice.Replication overview for the full multi-cluster setup.
For a local-first alternative to Azure Table Storage, pair the file write-ahead log with a durable local grain-storage provider - the RepoContext container runs Orleans ADO.NET grain storage over a single SQLite file - and keep the whole deployment on one machine.
RepoContext is an MCP server that gives an AI agent durable, conflict-free memory about a codebase: a structural record and content digest per file, symbol outlines and a reverse cross-reference graph, agent-authored notes and decisions with optional TTL, semantic search over embeddings, and a budgeted context bundle with reuse accounting. It can also run memory-only, disabling file and symbol indexing while keeping agent memory. It runs as a single local container alongside its embedding companion.
It is worth reading as a worked example because it composes most of the platform at once, and does so without a line of bespoke storage code:
Orleans.Lattice.Storage.File makes the
container restart-durable with no cloud account.Orleans.Lattice.Vector provides the
approximate nearest-neighbour index behind semantic search, persisted on a
tree so a restart reloads it rather than rebuilding it.Orleans.Lattice.Api.Mcp supplies the
agent-facing surface and the fail-closed authorization gate; RepoContext adds
no authorization path of its own.Orleans.Lattice.Api.Mcp.RepoContext.Replication
turns the same store into a multi-cluster one by choosing a per-tree merge
mode, which is the deployment journey applied to a
real application.RepoContext demonstrates Orleans.Lattice. It does not define it. It is one application category among many, and nothing in the platform is shaped around it.
See the Repo-context MCP documentation and the container sample.
Use these documents for day-to-day use and operations:
ILattice interface, batch operations, options, and serializable types.ILattice is guaranteed to observe, operation by operation.SetManyAtomicAsync: all-or-nothing multi-key batches within a tree, and across trees through the IGrainFactory overload.IAtomicActionGrain saga / TCC coordinator: an ordered plan of steps that commits all-or-nothing, compensating completed steps in strict reverse order.ILatticeLockGrain: a FIFO-fair lock / lease keyed by name, with bounded leases and monotonic fencing tokens.SetAsync and on typed CRDT writes, with absolute server-side expiry.BulkLoadAsync for a one-shot import into an empty tree, its streaming and resumable chunked forms, and how it compares with SetManyAsync.ReshardAsync: growing or shrinking a tree's physical shard count while it keeps serving reads and writes.ResizeAsync: changing a live tree's MaxLeafKeys and MaxInternalChildren, its phase machine, and its undo window.ITreeOwnershipGuard seam that bounds alias changes.ILatticeQueue<T> cluster-internal FIFO primitive, bounded-queue eviction, and throughput guidance.ILatticeCompressor seam, AddLatticeCompressor registration, tag-space partitioning, and how to plug in a custom algorithm.ScanEntryHistoryAsync, the State API, or the Explorer.DiagnoseAsync: a point-in-time health snapshot of a tree for dashboards, health probes, and post-mortem investigation.System.Diagnostics.Metrics instrument catalogue, its tag conventions, OpenTelemetry registration, and the bundled Grafana dashboards.DiagnoseAsync report, storage-provider write failures, split activity, slow scans, and stale reads.For internals (the "how"):
IWalStorageProvider durability seam, in-memory default, optional Azure Table and local-file backends.WalMaxPendingBatches and WalPartitions interact with a durable backend's throughput envelope; default sizing rules and the storage-account ceiling above which the cap stops helping.IWalSaturationSignal, IWalSaturationObserver) that lets callers throttle offered load before silent queueing on the writer-side admission gate.For the complete catalogues:
.md.Orleans.Lattice inherits the asymptotic properties of a B+ tree. In a single shard containing n keys with branching factor b:
| Operation | Time Complexity |
|---|---|
Point read (GetAsync) | O(logb n) |
Insert / update (SetAsync) | O(logb n) |
Delete (DeleteAsync) | O(logb n) |
Ordered scan (ScanKeysAsync) | O(n) |
Count (CountAsync) | O(n), across O(n / b) leaf calls |
| Space | O(n) |
With the default branching factor (~128 children per node), a shard with two million keys is only three levels deep. Depth adds no grain calls on the steady-state path: the shard root caches each internal node's routing table, so a single-key lookup crosses just three grains - the tree's router, the shard root, and the owning leaf or its per-silo read cache. Sharding (default 64) reduces per-shard n further; cross-shard operations scatter-gather across all shards.
Measured single-silo throughput and latency against real Azure Tables are in the single-silo performance guide, and how throughput responds as silos are added is in the multi-silo scaling guide.
See CHANGELOG.md for the release notes, in dated sections that name every package version shipped, and docs/RELEASING.md for the per-package tag-and-publish protocol.
Contributions are welcome! To get started:
main.Please open an issue first to discuss significant changes or new features before starting work.
This project is licensed under the MIT License. See LICENSE for details.
C#
96.4%
PowerShell
1.5%