---
title: "ILatticeAdmin - Lattice Public API Reference"
url: "https://nsta1.github.io/Orleans.Lattice/docs/lattice/api/ilatticeadmin.html"
source: "https://github.com/NSTA1/Orleans.Lattice/blob/release/9.9/docs/lattice/api.md?plain=1#L658-L701"
package: "Orleans.Lattice"
version: "9.9.0"
documents: "Orleans.Lattice 9.9.0 (release line 9.9)"
built: "2026-10-04"
all-pages: "https://nsta1.github.io/Orleans.Lattice/llms.txt"
bundle: "https://nsta1.github.io/Orleans.Lattice/docs/lattice/llms-full.txt"
---
# `ILatticeAdmin`

Part of [Lattice Public API Reference](../api.md).

`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 `TreeStorageUsageReport`s 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. |

```csharp verify
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. |

Previous: [ILattice: Maintenance operations](ilattice-2.md). Next: [Mutation observers](mutation-observers.md). Contents: [Lattice Public API Reference](../api.md).
