ILatticeAdmin
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 ilatticeadmin.md, and llms.txt lists every page.
Part of Lattice Public API Reference.
ILatticeAdmin is the cluster-wide administrative surface. Resolve the
singleton with grainFactory.GetGrain<ILatticeAdmin>("_lattice_admin")
(the admin grain uses a fixed string key).
| Method | Signature | Description |
|---|---|---|
GetTotalStorageUsageAsync |
Task<ClusterStorageUsageReport> GetTotalStorageUsageAsync(CancellationToken cancellationToken = default) |
Enumerates every registered tree, fans out to each tree's GetStorageUsageAsync (cache-respecting), and aggregates the per-tree TreeStorageUsageReports into a single cluster-wide roll-up, ordered by tree id. Partial is true when any tree's report was partial, including a tree whose report could not be fetched at all (which contributes a partial zero rather than aborting the roll-up). The fan-out is bounded by LatticeOptions.MaxConcurrentStorageUsageTrees (default 8), which multiplies against the per-tree MaxConcurrentStorageUsageSurfaces (default 16) for a peak of 128 concurrent grain calls regardless of cluster size. The roll-up is additionally bounded in time by LatticeOptions.StorageUsageRollupBudget (default 20 s): when the budget expires it stops dispatching and returns what it has, so trees never sampled report as not-answered and set Partial rather than the whole call failing on the response deadline. A non-positive budget disables the truncation. Activates each tree's shard roots and WAL partition grains but not its leaves: each shard root serves its byte totals in O(1) from incrementally-maintained counters rather than walking the leaf chain. This is the non-force deep path the per-silo poller can drive on its optional StorageUsageDeepPollInterval cadence to keep the snapshot / leaf-state / total-bytes gauges live. |
PollWalUsageAsync |
Task PollWalUsageAsync(CancellationToken cancellationToken = default) |
Cluster-wide WAL-only refresh. Fans out across every registered tree's WAL-only aggregator, touching only the per-partition WAL shard activations - never a leaf, internal node, snapshot storage, or shard-root grain. Drives the storage.wal_bytes / storage.policy.over_threshold observable gauges plus the byte-pressure WAL retention policy. This is the cheap path the per-silo background poller uses on its default 15 s cadence, so an idle tree is never activated by polling. |
RefreshStorageUsageAsync |
Task<ClusterStorageUsageReport> RefreshStorageUsageAsync(CancellationToken cancellationToken = default) |
Operator-driven deep refresh. Same return shape as GetTotalStorageUsageAsync, but bypasses every tree's StorageUsageCacheTtl cache and forces a fresh fan-out. Reserved for explicit operator action (post-migration validation, manual reconciliation); the background poller never invokes it. |
GetWalPlacementAsync |
Task<WalPlacement> GetWalPlacementAsync(string treeId, CancellationToken cancellationToken = default) |
Returns the tree's durable WAL placement pin: the default catalogue key, any per-partition key overrides, and the pin's compare-and-swap Version. A tree that has never been moved reports the default key for every partition. |
GetSplitActivityAsync |
Task<SplitActivityReport> GetSplitActivityAsync(CancellationToken cancellationToken = default) |
Returns a cluster-wide SplitActivityReport: how many shard migrations are in flight right now (InFlight) - every shard that is the source of an unfinished adaptive split or the donor of a consolidation fold, so an automatic healing fold counts too - across how many trees (ReportingTrees), plus the convenience AnyInFlight projection. This is the readable counterpart to the write-only orleans.lattice.split.in_flight histogram, which counts the same shards - metrics cannot be read back in-process, so anything that must decide from migration activity (the Orleans.Lattice.Scaling scale-in safety gate, an operator tool, a deployment guard that should not drain a silo mid-migration) queries this instead. Costs a single call to the cluster's split-admission singleton: it never fans out across trees or shards. The count comes from the per-tree footprints the hot-shard monitor publishes from its sampling passes, so it trails reality by about one HotShardSampleInterval and is not a strict lower bound: a pass counts the splits it is starting before their coordinators accept them, and a pass the monitor cuts short - while the tree is resized, resharded, merged into or snapshotted, has a bulk graft pending, or is younger than AutoSplitMinTreeAge - re-reports its last measured count without measuring, so migrations those operations start are not counted. Footprints expire, so a silo lost mid-migration cannot pin the count above zero indefinitely. Only the hot-shard monitor publishes, and it is not started on a tree whose autonomic splitting is disabled, so a deployment with autonomic splitting disabled reports zero even while an automatic healing fold runs. |
AuditWalPlacementAsync |
Task<WalPlacementAudit> AuditWalPlacementAsync(string treeId, CancellationToken cancellationToken = default) |
Like GetWalPlacementAsync but additionally reports, for the silo serving this call, whether every pinned catalogue key is registered there (AllResolvableOnThisSilo) plus the silo's known key set. The cheapest way to catch a missing-key misconfiguration before it fails a partition closed. |
PlanWalMoveAsync |
Task<WalMovePlan> PlanWalMoveAsync(string treeId, int partition, string targetProviderKey, CancellationToken cancellationToken = default) |
Read-only dry run. Reports what moving partition to targetProviderKey would copy (offset range, entry count), whether the partition is already at the target, and whether the target key resolves on the serving silo. Mutates nothing. |
PlanWalMoveAsync (batch) |
Task<WalMoveBatchPlan> PlanWalMoveAsync(string treeId, IEnumerable<(int Partition, string TargetProviderKey)> moves, CancellationToken cancellationToken = default) |
Batch dry run: one WalMovePlan per requested (partition, targetProviderKey) pair, plus AllTargetsResolvableOnThisSilo. Rejects an empty batch or a repeated partition with ArgumentException. Mutates nothing. |
ExecuteWalMoveAsync |
Task<WalMoveReceipt> ExecuteWalMoveAsync(string treeId, int partition, string targetProviderKey, WalMoveOptions? options = null, CancellationToken cancellationToken = default) |
Performs the quiesce-copy-cutover move saga: fences the partition's WAL grain, offset-preservingly copies the retained range to the target provider, re-converges on any appends that landed during the copy, flips the durable pin under compare-and-swap, then forces the WAL grain to deactivate so its next activation (any silo) binds the new provider. Non-destructive (WalMoveReceipt.SourceRetained); fails closed if the target key is unregistered on the serving silo, and throws InvalidOperationException without flipping the pin when the target cannot continue the partition's offsets (a reclaimed former source whose high-water mark covers retained offsets, or a fully-trimmed partition whose high-water mark the target does not record). Single partition per call; see the batch overload for multi-partition moves. |
ExecuteWalMoveAsync (batch) |
Task<WalMoveBatchReceipt> ExecuteWalMoveAsync(string treeId, IEnumerable<(int Partition, string TargetProviderKey)> moves, WalMoveOptions? options = null, CancellationToken cancellationToken = default) |
Moves several partitions all-or-nothing: each runs the same quiesce-copy-verify phases (bounded by WalMoveOptions.MaxConcurrentPartitionMoves), then the pin flips once under a single compare-and-swap so every partition reaches the same new placement version. Any phase failure aborts the whole batch with the pin unflipped and partial copies retained for a resumable retry; fails closed (LatticeWalProviderMissingException) if any target key is unresolvable. WalMoveBatchReceipt.Moves carries one receipt per partition in request order. |
ReclaimMovedWalSourceAsync |
Task<WalMoveReceipt> ReclaimMovedWalSourceAsync(string treeId, int partition, string sourceProviderKey, CancellationToken cancellationToken = default) |
Reclaims the now-redundant copy left on the source provider after a permanent move by trimming it. Refuses (throws) if the partition is still pinned to sourceProviderKey - you can only reclaim a placement the pin has already moved away from. Reports NoOp and trims nothing when the source already holds no live entries, so a re-run is idempotent. |
var admin = grainFactory.GetGrain<ILatticeAdmin>("_lattice_admin");
var cluster = await admin.GetTotalStorageUsageAsync(cancellationToken);
Console.WriteLine($"{cluster.TreeCount} trees, {cluster.TotalBytes} bytes total " +
$"(WAL={cluster.WalRetainedBytes}, snapshots={cluster.SnapshotBytes}, leaf-state={cluster.LeafStateBytes})");
foreach (var t in cluster.Trees)
{
Console.WriteLine($" {t.TreeId}: {t.TotalBytes} bytes");
}
The ClusterStorageUsageReport fields are:
| Field | Type | Meaning |
|---|---|---|
TreeCount |
int |
Number of trees included in the roll-up. |
WalRetainedBytes |
long |
Sum of every tree's WalRetainedBytes. |
SnapshotBytes |
long |
Sum of every tree's SnapshotBytes. |
LeafStateBytes |
long |
Sum of every tree's LeafStateBytes. |
TotalBytes |
long |
Sum of every tree's TotalBytes. |
Partial |
bool |
true when any tree's report was partial, or when a tree's report could not be fetched at all. |
Trees |
ImmutableArray<TreeStorageUsageReport> |
The per-tree reports that were aggregated, ordered by tree id. |
SampledAt |
DateTimeOffset |
When the roll-up was assembled. |