WAL consumer cursors - IWalCursorRegistry
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 wal-consumer-cursors-iwalcursorregistry.md, and llms.txt lists every page.
Part of Lattice Public API Reference.
Every consumer of a tree's write-ahead log - a leaf materialiser, a
materialised-view maintainer, a WAL subscriber, the replication shipper, or a
custom bridge - reports the highest HybridLogicalClock it has fully consumed
to the silo's IWalCursorRegistry. The WAL garbage collector
(ILatticeWalGc.RunOnceAsync) never trims past the minimum cursor across the
registered consumers, so a consumer that stops reporting holds the log rather
than losing entries. AddLattice registers the default
InMemoryWalCursorRegistry, which is process-local and lost on silo restart:
each consumer re-reports its cursor after a restart. AddWalCursorRegistry(Func<IServiceProvider, IWalCursorRegistry>? factory = null)
replaces that default with a host-supplied registry when a factory is given,
and in every case opts in to durable leaf-materialiser cursor reporting (so the
collector also respects each leaf's durable checkpoint across a restart) and
the shared WAL tailing subscriber. The durable WAL storage providers
(AddAzureTableWalStorage, AddFileWalStorage), AddLatticeViews, and
AddLatticeReplication call it for you; the storage providers and the
replication package also register the collector itself
(AddLatticeWalGc(Func<IServiceProvider, ILatticeWalGc>? factory = null)) and
the per-silo scheduler that runs it every LatticeOptions.WalGcInterval. See
WAL for the full trim predicate.
| Member | Signature | Description |
|---|---|---|
ReportCursorAsync |
Task ReportCursorAsync(string treeName, string consumerId, HybridLogicalClock cursor, CancellationToken cancellationToken = default) |
Reports the highest HLC the consumer has fully consumed. Monotonic per (treeName, consumerId): a lower report is coalesced into the existing entry rather than moving the cursor back. The cursor must be greater than HybridLogicalClock.Zero. |
ReportCursorAsync (blocked floor) |
Task ReportCursorAsync(string treeName, string consumerId, HybridLogicalClock cursor, HybridLogicalClock? blockedAtHlc, CancellationToken cancellationToken = default) |
Also reports the lowest HLC of any partially-buffered atomic batch the consumer holds; the collector never trims an entry at or above the lowest such pin. Accepts HybridLogicalClock.Zero as the cursor for a pin-only registration, which the cursor minimum ignores. Each report replaces the previous pin; null clears it. |
ReportCursorAsync (vector) |
Task ReportCursorAsync(string treeName, string consumerId, HybridLogicalClock cursor, VersionVector vector, CancellationToken cancellationToken = default) |
Also reports the per-origin VersionVector frontier the consumer has fully applied, coalesced by pointwise maximum. |
ReportCursorAsync (vector + blocked floor) |
Task ReportCursorAsync(string treeName, string consumerId, HybridLogicalClock cursor, VersionVector vector, HybridLogicalClock? blockedAtHlc, CancellationToken cancellationToken = default) |
Both of the above. |
UnregisterAsync |
Task UnregisterAsync(string treeName, string consumerId, CancellationToken cancellationToken = default) |
Removes a consumer so it no longer pins the log. Idempotent. |
GetMinCursorAsync |
Task<HybridLogicalClock?> GetMinCursorAsync(string treeName, CancellationToken cancellationToken = default) |
Minimum cursor across every registered consumer (pin-only registrations excluded), or null when none has reported. Includes cold consumers, because it feeds the trim floor. |
GetMinCursorForDrainLagAsync |
Task<HybridLogicalClock?> GetMinCursorForDrainLagAsync(string treeName, long reportedAtOrAfterTicks, CancellationToken cancellationToken = default) |
Minimum cursor across consumers that reported at or after the UTC tick floor, also excluding leaf materialisers whose position is stale (see WalDrainLagConsumerFreshness). Feeds only the saturation drain-lag input, never the trim floor. |
GetCausalStableAsync |
Task<VersionVector?> GetCausalStableAsync(string treeName, CancellationToken cancellationToken = default) |
Pointwise-minimum VersionVector across the consumers that reported one (an origin is kept only when every reporter names it), or null when none has. |
GetBlockedFloorAsync |
Task<HybridLogicalClock?> GetBlockedFloorAsync(string treeName, CancellationToken cancellationToken = default) |
Minimum blocked-floor pin across the consumers currently reporting one, or null. |
SnapshotAsync |
Task<IReadOnlyList<WalCursorSnapshot>> SnapshotAsync(string treeName, CancellationToken cancellationToken = default) |
Point-in-time copy of every registered consumer's entry, for diagnostics. |
WalCursorSnapshot is the readonly record struct SnapshotAsync returns:
| Member | Type | Meaning |
|---|---|---|
ConsumerId |
string |
The reporting consumer. |
Cursor |
HybridLogicalClock |
Highest HLC fully consumed; HybridLogicalClock.Zero for a pin-only registration. |
LastReportedAtTicks |
long |
UTC ticks of the most recent report, including a re-report of an unchanged position. |
Vector |
VersionVector? |
The reported per-origin frontier, or null for a consumer that reports HLC only. |
BlockedAtHlc |
HybridLogicalClock? |
The consumer's current blocked-floor pin, or null. |
CursorAdvancedAtTicks |
long? (init) |
UTC ticks of the most recent report that strictly advanced Cursor: 0 until the position has moved since the consumer registered, and null when the producing registry does not track position age. Read only by the drain-lag input, for leaf materialisers, so a leaf whose key range has seen no writes is not counted as lag; the trim floor ignores it. |