Table of Contents

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.