Table of Contents

Lattice Public API Reference

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 api.md, and llms.txt lists every page.

This document is the contract for what each public type and method on Orleans.Lattice does. It states behaviour in caller-visible terms only - signature, return value, exceptions, and observable effect. It does not describe how the library delivers any of those guarantees.

For the consistency classification (linearizable / strongly consistent / snapshot / eventually consistent) of every operation, see Consistency. For implementation details, follow the topic cross-references in each section.

Compression - ILatticeCompressor, LatticeCompression, ZstdLatticeCompressor, and LatticeCompressionServiceCollectionExtensions.AddLatticeCompressor are part of the public API surface, along with the shared-dictionary compression types ILatticeCompressionDictionaryProvider (with its optional companion interfaces ILatticeCompressionDictionaryCatalog, ILatticeActiveCompressionDictionary, ILatticeCompressionDictionarySink, and ILatticeCompressionDictionarySampler), OperatorSuppliedCompressionDictionaryProvider, ILatticeDictionaryCompressor, ZstdDictionaryLatticeCompressor, the auto-trained-dictionary types CompressionDictionaryTrainingOptions and AutoTrainingCompressionDictionaryProvider, and the AddLatticeCompressionDictionaries / AddLatticeCompressionDictionaryProvider / AddLatticeZstdDictionaryCompressor / AddLatticeAutoTrainingCompressionDictionary registration helpers. They are documented in compression.md, which is the source of truth for registration, the tag-space partitioning, the shared-dictionary opt-in, runtime auto-training, and the worked example for plugging in a custom algorithm.

Cluster-internal queues - ILatticeQueue<T>, LatticeQueueEntry<T>, and the IGrainFactory.GetLatticeQueue<T> resolver (LatticeQueueExtensions) are part of the public API surface. They are documented in queues.md, which is the source of truth for resolving a named queue, the bounded-FIFO eviction knob (LatticeOptions.QueueCapacity), and the single-coordinator throughput model.

Contents

  • Setup: Install the NuGet package.
  • ILattice: Obtain an ILattice grain from the grain factory using the tree's logical name as the string key.
  • ILatticeAdmin: ILatticeAdmin is the cluster-wide administrative surface.
  • Mutation observers: IMutationObserver is a grain-side extensibility hook invoked synchronously after a mutation is durably committed, before the grain method returns to the caller.
  • Tree alias observers: ITreeAliasObserver is a control-plane extensibility hook invoked once per logical-tree physical-identity alias change, fired from inside the tree registry's single alias-mutation choke point (where an alias is set or removed) after...
  • Caller-credential propagation (LatticeCredentialContext): LatticeCredentialContext is a transport-only ambient seam that carries an opaque caller credential from the client edge down to the silo on the Orleans RequestContext, following the same marker idiom as LatticeOriginContext...
  • WAL saturation back-pressure: Lattice publishes a per-tree, three-state saturation signal so callers driving offered load into ILattice can throttle their own input before the saturation regime's failure tail surfaces to them as a TimeoutException from SetAsync...
  • WAL consumer cursors - IWalCursorRegistry: Every consumer of a tree's write-ahead log.
  • Domain faults - ILatticeDomainFault: Marker interface implemented by every public Lattice exception that derives from a BCL exception subclass (InvalidOperationException, TimeoutException, UnauthorizedAccessException) rather than directly from Exception.
  • Shutdown back-pressure - LatticeShuttingDownException: Public typed exception thrown by any ILattice operator (and by the internal saga coordinator on its caller-facing throw path) when the operation cannot complete because the owning silo's write-ahead-log writer is draining as part of host...
  • Saturation back-pressure - LatticeSaturatedException: Public typed exception thrown when an operation is refused because the tree's storage layer is back-pressured - most commonly by the WAL writer admission gate and the atomic-write saga coordinator, when the per-tree IWalSaturationSignal...
  • Admission back-pressure - LatticeQuotaExceededException: Public typed exception thrown when a locally-authored write call is refused because the target tree has reached a configured per-tree admission-control cap - either LatticeOptions.MaxLiveKeys (the Dimension property is keys)...
  • Leaf-projection digest: LeafProjectionDigest { byte[] Hash; long EntryCount; long CheckpointOffset; int Version; } (alias ol.lpd).
  • Operator tooling: projection rebuild and materialiser lag: Two ILattice methods expose operator-driven projection recovery and steady-state materialiser-lag observation.
  • Operator tooling: orphaned-leaf repair: VerifiedKeyCount always means the verified prefix length before the first missing key or routing contradiction.
  • Metrics: Orleans.Lattice publishes System.Diagnostics.Metrics instruments on the static meter orleans.lattice, exposed via Orleans.Lattice.LatticeMetrics (meter name LatticeMetrics.MeterName).
  • SnapshotMode: Controls source-tree availability during a snapshot operation.
  • LatticeExtensions: The resilient scans (ScanKeysAsync, ScanEntriesAsync) and the resilient DeleteRangeAsync drain are documented under Enumeration, the Open*CursorScopeAsync family under Scoped cursor extensions, and the DefaultScanReconnectAttempts...
  • TypedLatticeExtensions: Extension methods that serialize and deserialize via an ILatticeSerializer<T>, eliminating per-caller byte[] boilerplate.
  • Predicate operations: Typed overloads that take an Expression<Func<T, bool>> and evaluate it server-side against a JSON document view of each value, so only matching keys (or values) cross the wire.
  • Cross-tree atomic writes: IGrainFactory / cluster-client extension methods (LatticeCrossTreeAtomicWriteExtensions) that commit a batch spanning two or more distinct ILattice trees all-or-nothing, with the same atomic-visibility guarantee SetManyAtomicAsync...
  • CRDT value-surface accessors: The CRDT value-surface methods on ILattice return lightweight, allocation-free accessors that read and write a single key through the primitive's natural mutation API instead of forcing callers to hand-roll byte arrays or CRDT deltas.
  • ILatticeSerializer<T>: Implement this interface to provide a custom serialization strategy.
  • LatticeOptions: See Configuration for detailed guidance, mutability constraints, and per-tree overrides via the tree registry.
  • Idempotency keys and retry policy: Opt-in surface for retrying transient storage faults under a caller-supplied identity.
  • Extension seams and supporting types: The sections above document the call surface an application drives.
  • Serializable types: All serializable types - and every grain interface, including the public ILattice - carry stable [Alias] attributes (prefixed ol.) to ensure wire-format and grain-manifest compatibility across versions.
  • Public but not usable: A handful of types are necessarily public so that Orleans can serialize them on the ILattice wire surface, but they are not intended as direct caller dependencies. Their shape, members, and return values can change in any release...
  • Tag indexes: A tag index associates string tags with the keys of a tree and lets you query keys back by tag.
  • Materialised views: A materialised view is an asynchronous, eventually-consistent projection of a source tree, maintained by tailing that tree's WAL. It needs a WAL-backed lattice (AddLattice) and AddLatticeViews(...) (which folds...

Tree alias observers

ITreeAliasObserver is a control-plane extensibility hook invoked once per logical-tree physical-identity alias change, fired from inside the tree registry's single alias-mutation choke point (where an alias is set or removed) after the new alias is durably persisted and only when the effective physical id actually changed. It is the seam a consumer that binds to a logical tree's physical WAL uses to rebind when a shadow-cutover restore (or its revert), a resize (or its undo), a schema remediation, or an administrative alias change swaps that binding underneath it (a reshard changes the shard map, not the alias, so it raises no notification) - most notably the cross-cluster replication shipper, which uses it to rebind reactively instead of re-reading the registry on every pump tick (see Source-identity rebind).

The hook receives a TreeAliasChange carrying the logical TreeId and both the old and new effective physical ids (an unaliased tree resolves to its own logical id, so a removed alias reports the logical id as the new physical), so a consumer can rebind directly without re-reading the registry.

public sealed class MyRebindObserver : ITreeAliasObserver
{
    public Task OnTreeAliasChangedAsync(TreeAliasChange change, CancellationToken ct)
    {
        // change.TreeId is the logical tree; change.OldPhysicalTreeId and
        // change.NewPhysicalTreeId are the effective physical ids before and
        // after the swap. Dispatch a rebind and return - do not block the
        // registry grain with slow synchronous I/O.
        return Task.CompletedTask;
    }
}
siloBuilder.ConfigureServices(services =>
    services.AddSingleton<ITreeAliasObserver, MyRebindObserver>());

Observers are resolved as IEnumerable<ITreeAliasObserver>, so multiple can coexist; the hook is zero-cost when none is registered. OnTreeAliasChangedAsync runs on the registry grain's single-threaded scheduler and is awaited inline before the alias-mutation grain method returns, so an implementation should dispatch its work and return rather than issue slow synchronous I/O. Exceptions thrown by one observer are logged as a warning and suppressed (the alias is already persisted and cannot be rolled back), and do not short-circuit the others; a consumer that misses a notification is expected to heal out-of-band (the replication shipper's coarse backstop re-resolve covers exactly this case). The registry raises the hook only on a genuine effective-physical-id change, so a no-op re-set of the current alias never produces a spurious rebind.

Domain faults - ILatticeDomainFault

Marker interface implemented by every public Lattice exception that derives from a BCL exception subclass (InvalidOperationException, TimeoutException, UnauthorizedAccessException) rather than directly from Exception. It declares no members and adds no state.

Those base types were chosen for backwards compatibility, but the inheritance is a hazard rather than a convenience: a broad catch (InvalidOperationException) written for some unrelated condition silently absorbs a Lattice domain fault and applies remediation calibrated for a different problem. ILatticeDomainFault gives such a handler a way to decline it:

var entries = new List<KeyValuePair<string, byte[]>>
{
    new("k1", new byte[] { 0x01 }),
};

try
{
    await lattice.SetManyAsync(entries);
}
catch (InvalidOperationException ex) when (ex is not ILatticeDomainFault)
{
    // Genuine API misuse. A Lattice domain fault (saturation, quota,
    // shutdown, fencing, ...) is declined here and falls through to a
    // handler that understands it, instead of being absorbed.
    Console.WriteLine(ex.Message);
}

The marker does not mean "never handle this". Catching a domain fault by its own type remains the correct way to handle it and is unaffected - catch (LatticeSaturatedException) behaves exactly as documented below, and an internal retry loop that catches ShardActivationTimeoutException by name still absorbs and retries it. The marker constrains only a broad catch of the base type.

A reflection gate in the core test suite enumerates the assembly at run time and fails if a public exception with a foreign base does not implement the marker, so the set cannot drift as exceptions are added.

Leaf-projection digest

LeafProjectionDigest { byte[] Hash; long EntryCount; long CheckpointOffset; int Version; } (alias ol.lpd). LeafProjectionDigest.CurrentVersion (0) is the contribution-function shape a current silo stamps; digests with different Version values must not be byte-compared.

LeafProjectionDigest digest = await tree.GetLeafProjectionDigestAsync(
    shardIndex: 0,
    cancellationToken);

Throws ArgumentOutOfRangeException when shardIndex is not a physical shard of the per-tree map, InvalidOperationException for activations on reserved system-tree prefixes or when LatticeOptions.MaintainProjectionDigest = false (the per-tree opt-out makes the digest API unavailable), LatticeAuthorizationDeniedException when the caller is not authorized to read the whole shard (a key-filtered grant is refused rather than narrowed, because a digest cannot be narrowed per key), and OperationCanceledException if the token was already cancelled. See Projection Rebuild for the determinism contract, the cost model, and the related ProjectionRebuildPolicy setting (every value currently fails closed on genuine loss).

GetLeafProjectionDigestForRangeAsync(int shardIndex, string? startKeyInclusive, string? endKeyExclusive, CancellationToken cancellationToken = default) folds the same digest over only the half-open key range of one physical shard; a null bound is unbounded on that side, so (null, null) is byte-identical to the whole-shard digest. It is the primitive behind the cross-cluster anti-entropy Merkle-walk localisation, which narrows a divergent shard to a leaf or key range. It throws the same exceptions as GetLeafProjectionDigestAsync.

Operator tooling: projection rebuild and materialiser lag

Two ILattice methods expose operator-driven projection recovery and steady-state materialiser-lag observation. Both surfaces are narrow: they do not freeze the tree, take no consistency lock, and are safe to call against a live shard under load.

Method Description
RebuildLeafProjectionAsync(int shardIndex, CancellationToken) Clears the projection state of every leaf in the specified physical shard - the per-activation in-memory entry cache (per-key data is not persisted on the leaf row; on activation it is rehydrated from the leaf's snapshot where one exists, then from the WAL after it), the persisted projection-digest fold, the persisted projection checkpoint (reset to "nothing scanned"), and the in-memory pending-saga and recently-terminal dedup buffers - then deactivates each leaf so its next activation re-materialises the projection through the standard activation-time path (which, under every ProjectionRebuildPolicy value, first rehydrates from the leaf's captured snapshot where one exists and then replays the WAL after it). Topology-bearing state (tree id, shard index, key-range bounds, sibling pointers, parent pointer, split state) is preserved. Requires whole-tree Admin authorization (LatticeAuthorizationDeniedException otherwise). Throws ArgumentOutOfRangeException when shardIndex is not a physical shard of the per-tree map, LatticeReservedTreeNamespaceException (an InvalidOperationException) for a reserved _lattice_ system tree, and OperationCanceledException if the token is cancelled.
GetMaterialiserLagAsync(CancellationToken) Returns the largest per-shard lag, in WAL entries: for each physical shard, how far its WAL partition heads run ahead of the lowest leaf-projection checkpoint across that shard's leaves, summed over its partitions (each partition's gap clamped at zero). The figure is an estimate: each WAL head is the next offset to be assigned while each checkpoint is the last offset a leaf applied, and every partition's head is measured against the leaf's partition-0 checkpoint, so a caught-up shard with a non-empty WAL still reports a small positive value rather than 0; a steady value means caught up, and a steadily growing value indicates the materialiser is falling behind WAL ingestion. Requires whole-tree Read authorization (LatticeAuthorizationDeniedException otherwise). Throws InvalidOperationException for system-tree activations and OperationCanceledException if the token is cancelled.
// Operator-driven recovery: rebuild one shard's projection from
// the WAL after detecting drift via the digest surface.
await tree.RebuildLeafProjectionAsync(shardIndex: 0, cancellationToken);

// Steady-state lag observation: poll periodically and alert
// when lag crosses an SLO threshold.
long lag = await tree.GetMaterialiserLagAsync(cancellationToken);
// Emit (treeId, lag) to telemetry.

See Projection Rebuild for the rebuild semantics, the topology-preservation guarantees, and the recommended monitoring shape for lag.

Metrics

Orleans.Lattice publishes System.Diagnostics.Metrics instruments on the static meter orleans.lattice, exposed via Orleans.Lattice.LatticeMetrics (meter name LatticeMetrics.MeterName). The catalog groups instruments by the subsystem that records them - the shard and leaf data paths, snapshot cursors, the WAL append pipeline and garbage collector, admission control, the read cache, sagas and lifecycle coordinators, events, materialised views, and more. Subscribe with .AddMeter("orleans.lattice") on your OpenTelemetry MeterProviderBuilder. See Metrics for the full catalog and tag conventions.

SnapshotMode

Controls source-tree availability during a snapshot operation.

Value Description
Offline Every source shard the copy reads is locked before any is copied and stays locked until it has been copied; reads and writes to it throw InvalidOperationException meanwhile. The destination is a point-in-time image of the shards copied.
Online Source tree remains available throughout. Writes it accepts while the copy runs are shadow-forwarded to the destination, so the destination is a mirror maintained during the copy rather than a point-in-time image; typed CRDT delta applies and bulk appends are not forwarded.

Both modes copy every physical shard the source's shard map routes to, a shard an adaptive split added included; see SnapshotAsync.

LatticeExtensions

The resilient scans (ScanKeysAsync, ScanEntriesAsync) and the resilient DeleteRangeAsync drain are documented under Enumeration, the Open*CursorScopeAsync family under Scoped cursor extensions, and the DefaultScanReconnectAttempts / DefaultScanStallResumeAttempts / DefaultScanStallResumeCeiling budget constants with the scans that use them. The remaining members are:

Method Description
BulkLoadAsync(this ILattice, IAsyncEnumerable<KeyValuePair<string, byte[]>> sortedEntries, IGrainFactory grainFactory, int chunkSize = 10_000, CancellationToken cancellationToken = default) Streaming bulk load for large datasets. Input must be pre-sorted ascending by key. See Bulk Loading.
SubscribeToEventsAsync(this ILattice, IClusterClient, Func<LatticeTreeEvent, Task>, string providerName = "Default", CancellationToken) Subscribes to the per-tree LatticeTreeEvent stream on the cluster client. Returns a StreamSubscriptionHandle<LatticeTreeEvent>; call UnsubscribeAsync() to stop. Throws InvalidOperationException when providerName is not registered on the client. See Events.

TypedLatticeExtensions

Extension methods that serialize and deserialize via an ILatticeSerializer<T>, eliminating per-caller byte[] boilerplate. Each method has two overloads: one accepting an explicit serializer and one that defaults to JsonLatticeSerializer<T> (System.Text.Json with UTF-8 encoding).

// Default (System.Text.Json):
await tree.SetAsync("user:1", new User("Alice", 30));
var user = await tree.GetAsync<User>("user:1");

// Custom serializer:
var serializer = new JsonLatticeSerializer<User>(new JsonSerializerOptions { WriteIndented = false });
await tree.SetAsync("user:1", new User("Alice", 30), serializer);

// Compare-and-swap (CAS):
var versioned = await tree.GetWithVersionAsync<User>("user:1");
var updated = versioned.Value! with { Age = 31 };
bool success = await tree.SetIfVersionAsync("user:1", updated, versioned.Version);

The serializer-less overload of every method uses JsonLatticeSerializer<T>.Default. The members are GetAsync<T>, GetWithVersionAsync<T>, GetOrSetAsync<T>, SetAsync<T> (plus a TimeSpan ttl overload), SetIfVersionAsync<T>, GetManyAsync<T>, SetManyAsync<T>, SetManyAtomicAsync<T> (plus an operationId overload), BulkLoadAsync<T>, and the resilient ScanEntriesAsync<T>; the predicate overloads are listed under Predicate operations, and the raw typed streams under Public but not usable.

Predicate operations

Typed overloads that take an Expression<Func<T, bool>> and evaluate it server-side against a JSON document view of each value, so only matching keys (or values) cross the wire. Each method has an explicit-serializer overload and a JsonLatticeSerializer<T>-default overload (shown collapsed below). The serializer must implement ILatticePredicateSerializer or the call throws NotSupportedException before any RPC.

This table lists signatures only. For semantics, supported expressions, the error surface, and runnable samples see Predicate Operations.

Method Signature
GetManyAsync Task<Dictionary<string, T>> GetManyAsync<T>(this ILattice, List<string> keys, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, CancellationToken = default)
SetManyAsync Task<IReadOnlyList<string>> SetManyAsync<T>(this ILattice, List<KeyValuePair<string, T>> entries, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, CancellationToken = default)
SetManyAtomicAsync Task<AtomicWriteOutcome> SetManyAtomicAsync<T>(this ILattice, List<KeyValuePair<string, T>> entries, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, CancellationToken = default)
SetManyAtomicAsync (idempotent) Task<AtomicWriteOutcome> SetManyAtomicAsync<T>(this ILattice, List<KeyValuePair<string, T>> entries, Expression<Func<T, bool>> predicate, string operationId, ILatticeSerializer<T> serializer, CancellationToken = default)
ScanKeysAsync IAsyncEnumerable<string> ScanKeysAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, bool? prefetch = null, int? maxAttempts = null, CancellationToken = default)
ScanEntriesAsync IAsyncEnumerable<KeyValuePair<string, T>> ScanEntriesAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, bool? prefetch = null, int? maxAttempts = null, CancellationToken = default)
ScanValuesAsync IAsyncEnumerable<T> ScanValuesAsync<T>(this ILattice, ILatticeSerializer<T> serializer, Expression<Func<T, bool>>? predicate = null, string? startInclusive = null, string? endExclusive = null, bool reverse = false, bool? prefetch = null, int? maxAttempts = null, CancellationToken = default)
OpenKeyCursorAsync Task<string> OpenKeyCursorAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, bool pointInTime = false, CancellationToken = default)
OpenEntryCursorAsync Task<string> OpenEntryCursorAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, bool pointInTime = false, CancellationToken = default)
OpenSnapshotKeyCursorAsync Task<string> OpenSnapshotKeyCursorAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, CancellationToken = default)
OpenSnapshotEntryCursorAsync Task<string> OpenSnapshotEntryCursorAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, ILatticeSerializer<T> serializer, string? startInclusive = null, string? endExclusive = null, bool reverse = false, CancellationToken = default)
DeleteRangeAsync Task<int> DeleteRangeAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, string startInclusive, string endExclusive, ILatticeSerializer<T> serializer, CancellationToken = default)
OpenDeleteRangeCursorAsync Task<string> OpenDeleteRangeCursorAsync<T>(this ILattice, Expression<Func<T, bool>> predicate, string startInclusive, string endExclusive, ILatticeSerializer<T> serializer, CancellationToken = default)

Supporting public types: LatticePredicateTranslator, LatticePredicateNode, LatticePredicateNodeKind, LatticeValueKind (the kinds a structural TypeOf test checks for), LatticeConstant, LatticeConstantKind, LatticeComparisonOperator, LatticeBooleanOperator, LatticeStringMethod, LatticePredicateContext, ILatticePredicateSerializer, the AtomicWriteOutcome enum (Committed, PreconditionFailed), and LatticePredicateEvaluation, whose Matches(byte[]? value, in LatticePredicateNode predicate) evaluates the predicate IR against a value's UTF-8 JSON document with the same semantics as server-side push-down (ordinal, case-insensitive property matching; a null, empty, or non-JSON payload evaluates false), so an enforcement add-on need not reimplement it.

ILatticeSerializer<T>

Implement this interface to provide a custom serialization strategy. JsonLatticeSerializer<T> ships as the default.

Member Signature Description
Serialize byte[] Serialize(T value) Converts a value to bytes for storage.
Deserialize T Deserialize(byte[] bytes) Converts bytes back to a value.

Idempotency keys and retry policy

Opt-in surface for retrying transient storage faults under a caller-supplied identity. Default behaviour is throw-and-revert; the library never installs a policy itself. See Retry Policy for the full contract, operational guidance, and code examples.

Type Member Description
LatticeIdempotencyKey HybridLogicalClock Timestamp { get; init; } Pins the logical write time. Re-stamped verbatim on every retry under the same key so LWW resolution treats retries as ties.
LatticeIdempotencyKey static LatticeIdempotencyKey Fresh() Convenience factory minting a fresh HLC tick. Consecutive calls produce distinct keys.
LatticeIdempotencyContext static bool IsActive { get; } true when an ambient idempotency key is set on the current logical execution context.
LatticeIdempotencyContext static LatticeIdempotencyKey? Current { get; set; } Reads or sets the ambient key on the current logical execution context. Flows across await points.
LatticeIdempotencyContext static IDisposable With(LatticeIdempotencyKey? key) Opens a scope that restores the previous ambient value on dispose. Idempotent on repeated dispose.
LatticeIdempotencyContext static IDisposable NewScope() Shorthand for With(LatticeIdempotencyKey.Fresh()).
ILatticeRetryPolicy Task ExecuteAsync(Func<CancellationToken, Task> operation, CancellationToken cancellationToken) Re-invokes operation on transient failure under the same ambient scope; rethrows on exhaustion.
ILatticeRetryPolicy Task<T> ExecuteAsync<T>(Func<CancellationToken, Task<T>> operation, CancellationToken cancellationToken) Typed overload.
BoundedExponentialRetryPolicy ctor(int maxAttempts = 4, TimeSpan? initialDelay = null, TimeSpan? maxDelay = null, Func<Exception, bool>? retryableExceptionClassifier = null) Shipped default. Delay between attempts: min(MaxDelay, InitialDelay * 2^(attempt-1)). No jitter.
BoundedExponentialRetryPolicy ctor(BoundedExponentialRetryPolicyOptions options) Options-based constructor used by the DI extension.
BoundedExponentialRetryPolicyOptions int MaxAttempts { get; set; } (default 4) Total attempts, including the first.
BoundedExponentialRetryPolicyOptions TimeSpan InitialDelay { get; set; } (default 50 ms) First retry backoff.
BoundedExponentialRetryPolicyOptions TimeSpan MaxDelay { get; set; } (default 2 s) Per-attempt backoff cap.
BoundedExponentialRetryPolicyOptions Func<Exception, bool>? RetryableExceptionClassifier { get; set; } (default null) When non-null, only exceptions accepted by the classifier are retried.
LatticeServiceCollectionExtensions static ISiloBuilder AddLatticeRetryPolicy(this ISiloBuilder builder, Action<BoundedExponentialRetryPolicyOptions>? configure = null) DI helper that installs BoundedExponentialRetryPolicy as LatticeOptions.RetryPolicy for every tree.

Public but not usable

A handful of types are necessarily public so that Orleans can serialize them on the ILattice wire surface, but they are not intended as direct caller dependencies. Their shape, members, and return values can change in any release without notice; treat them as wire-only.

To make this contract visible in tooling, every type and member in the table below carries [EditorBrowsable(EditorBrowsableState.Never)]: the wire-only types HybridLogicalClock, VersionedValue, RoutingInfo, LatticeIdempotencyKey, GatedMultiReadResult, RangeDeleteResult, and LeafKeyRange, and the raw ILattice / TypedLatticeExtensions members listed. IDE IntelliSense therefore hides them from completion lists by default (the standard IntelliSense behaviour for EditorBrowsable(Never)), so callers do not surface them by accident when typing against ILattice or its extensions. The attribute only hides a symbol from completion - it still compiles - so the row here remains the contract that it is not caller surface.

The types and members in this category are:

Symbol Why public
HybridLogicalClock Embedded in the wire types below; appears as the Version slot of Versioned<T> / VersionedValue.
VersionedValue Return shape of ILattice.GetWithVersionAsync (the raw byte[] overload).
RoutingInfo Return shape of ILattice.GetRoutingAsync, consumed by infrastructure helpers such as the streaming bulk loader.
LatticeIdempotencyKey Token stamped via LatticeIdempotencyContext.With(...) to deduplicate retries; callers usually let the ambient retry policy mint and propagate it.
RangeDeleteResult Result of the internal leaf-level range-delete call (tombstoned count, past-range flag, matched keys); public only so Orleans can serialize it.
ILattice.KeysAsync / EntriesAsync Raw streaming overloads that omit the resume/reconnect handshake. Use LatticeExtensions.ScanKeysAsync / ScanEntriesAsync instead.
TypedLatticeExtensions.KeysAsync<T> / EntriesAsync<T> / ValuesAsync<T> The typed raw streams, likewise without reconnect handling. Use ScanKeysAsync<T> / ScanEntriesAsync<T> / ScanValuesAsync<T> instead.
ILattice.GetRoutingAsync (both overloads) Direct shard-addressing primitive used by infrastructure helpers.
ILattice.KeysWherePredicateAsync / EntriesWherePredicateAsync, the Open*CursorWherePredicateAsync family, SetManyWherePredicateAsync, SetManyAtomicWhereAsync, DeleteRangeWherePredicateAsync, and the ranged CountAsync Raw primitives behind the typed predicate and aggregation surfaces; call the typed predicate operations instead.
GatedMultiReadResult Return shape of ILattice.GetManyWithGateAccountingAsync.
LeafKeyRange Leaf key-range bounds returned by an internal leaf-grain call; public only so Orleans can serialize it.

ShardMap is also public (it is reachable through RoutingInfo) but is not marked hidden because it has no useful caller-facing members beyond what RoutingInfo already exposes; treat it as wire-only as well.

Apart from ILattice, ILatticeAdmin, ILatticeLockGrain (see Distributed lock), and IAtomicActionGrain (see Atomic actions), every other grain interface in the assembly - for example the shard root, leaf, internal node, registry, cursor, atomic-write saga, cross-tree transaction, compaction, snapshot, resize, reshard, shard split and consolidation, replication apply, WAL shard, hot-shard monitor, tree deletion / merge, leaf-cache, leaf-replay coordinator, queue, view, stats, storage-usage, and tx-registry grains - is declared internal and is not visible from consumer assemblies at all. The C# type system enforces that boundary at compile time.