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, andLatticeCompressionServiceCollectionExtensions.AddLatticeCompressorare part of the public API surface, along with the shared-dictionary compression typesILatticeCompressionDictionaryProvider(with its optional companion interfacesILatticeCompressionDictionaryCatalog,ILatticeActiveCompressionDictionary,ILatticeCompressionDictionarySink, andILatticeCompressionDictionarySampler),OperatorSuppliedCompressionDictionaryProvider,ILatticeDictionaryCompressor,ZstdDictionaryLatticeCompressor, the auto-trained-dictionary typesCompressionDictionaryTrainingOptionsandAutoTrainingCompressionDictionaryProvider, and theAddLatticeCompressionDictionaries/AddLatticeCompressionDictionaryProvider/AddLatticeZstdDictionaryCompressor/AddLatticeAutoTrainingCompressionDictionaryregistration helpers. They are documented incompression.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 theIGrainFactory.GetLatticeQueue<T>resolver (LatticeQueueExtensions) are part of the public API surface. They are documented inqueues.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 anILatticegrain from the grain factory using the tree's logical name as the string key.ILatticeAdmin:ILatticeAdminis the cluster-wide administrative surface.- Mutation observers:
IMutationObserveris a grain-side extensibility hook invoked synchronously after a mutation is durably committed, before the grain method returns to the caller. - Tree alias observers:
ITreeAliasObserveris 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):LatticeCredentialContextis a transport-only ambient seam that carries an opaque caller credential from the client edge down to the silo on the OrleansRequestContext, following the same marker idiom asLatticeOriginContext... - WAL saturation back-pressure: Lattice publishes a per-tree, three-state saturation signal so callers driving offered load into
ILatticecan throttle their own input before the saturation regime's failure tail surfaces to them as aTimeoutExceptionfromSetAsync... - 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 fromException. - Shutdown back-pressure -
LatticeShuttingDownException: Public typed exception thrown by anyILatticeoperator (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-treeIWalSaturationSignal... - 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 - eitherLatticeOptions.MaxLiveKeys(theDimensionproperty iskeys)... - Leaf-projection digest:
LeafProjectionDigest { byte[] Hash; long EntryCount; long CheckpointOffset; int Version; }(aliasol.lpd). - Operator tooling: projection rebuild and materialiser lag: Two
ILatticemethods expose operator-driven projection recovery and steady-state materialiser-lag observation. - Operator tooling: orphaned-leaf repair:
VerifiedKeyCountalways means the verified prefix length before the first missing key or routing contradiction. - Metrics: Orleans.Lattice publishes
System.Diagnostics.Metricsinstruments on the static meterorleans.lattice, exposed viaOrleans.Lattice.LatticeMetrics(meter nameLatticeMetrics.MeterName). SnapshotMode: Controls source-tree availability during a snapshot operation.LatticeExtensions: The resilient scans (ScanKeysAsync,ScanEntriesAsync) and the resilientDeleteRangeAsyncdrain are documented under Enumeration, theOpen*CursorScopeAsyncfamily under Scoped cursor extensions, and theDefaultScanReconnectAttempts...TypedLatticeExtensions: Extension methods that serialize and deserialize via anILatticeSerializer<T>, eliminating per-callerbyte[]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 distinctILatticetrees all-or-nothing, with the same atomic-visibility guaranteeSetManyAtomicAsync... - CRDT value-surface accessors: The CRDT value-surface methods on
ILatticereturn 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 (prefixedol.) to ensure wire-format and grain-manifest compatibility across versions. - Public but not usable: A handful of types are necessarily
publicso that Orleans can serialize them on theILatticewire 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) andAddLatticeViews(...)(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.