Cratis Chronicle bounded event reads for history views
Use bounded event reads in Cratis Chronicle when a history view needs one page at a time rather than the whole sequence. In .NET, IEventSequence.GetPage and IEventLog use keyset pagination: the next request starts at a sequence number, not a row offset.
Each request returns at most Count events and a nullable Next sequence number. Events arrive in read order: oldest first for Forward, newest first for Backward. Chronicle reads one extra matching event to decide whether another page exists, without counting the entire sequence.
Read from the beginning
Section titled “Read from the beginning”Pass an existing event sequence and a cancellation token to this function. It follows Next while keeping the same direction, stop bound, and filters:
using Cratis.Chronicle.EventSequences;
public static class ForwardEventPages{ public static async Task ReadHistory(IEventSequence sequence, CancellationToken cancellationToken) { var request = EventSequencePageRequest.Forward(null, 100); while (true) { var page = await sequence.GetPage(request, cancellationToken); foreach (var appended in page.Events) { Console.WriteLine(appended.Context.SequenceNumber); }
if (!page.HasMore) break; request = request with { From = page.Next }; } }}From is inclusive. A null start means the beginning for forward reads and the tail for
backward reads. Do not pass EventSequenceNumber.Unavailable to mean the tail.
Read the newest matching events
Section titled “Read the newest matching events”For an existing IEventSequence sequence, this reads at most 50 matching events, newest
first:
using Cratis.Chronicle.EventSequences;
public static class LatestEventPages{ public static async Task ReadLatest(IEventSequence sequence, CancellationToken cancellationToken) { var filter = new EventSequenceFilter(Tags: ["billing", "priority"]); var latest = await sequence.GetLatest(50, filter, cancellationToken); if (latest.HasMore) { var older = await sequence.GetPage( EventSequencePageRequest.Backward(latest.Next, 50, filter), cancellationToken); foreach (var appended in older.Events) { Console.WriteLine(appended.Context.SequenceNumber); } } }}GetLatest is shorthand for GetPage(EventSequencePageRequest.Backward(null, 50, filter), cancellationToken). To read older matching events, call GetPage with
EventSequencePageRequest.Backward(latest.Next, 50, filter) only when latest.HasMore
is true. Passing null again starts over at the tail.
Bounds and continuation
Section titled “Bounds and continuation”Until is also inclusive. It is an upper bound for forward reads and a lower bound for
backward reads. For example, EventSequencePageRequest.Backward(200, 50) with { Until = 100 }
reads newest first within sequence numbers 100 through 200. Reversed bounds, an empty
sequence, or a filter with no matches return an empty page with no continuation.
Next is one sequence number past the last returned event in the read direction, and is
present only if another matching event exists inside the bounds. It need not identify an
existing event: sequence numbers and filtered results can have gaps. Use the returned
value unchanged instead of adding or subtracting Count. A full page can still have no
continuation.
Each page is an independent read, not a frozen snapshot. To exclude later appends during a forward traversal, capture the tail sequence number before starting and set Until to that value. Check that the sequence is nonempty before using its tail as a bound. The bound does not freeze event content: a revision or redaction between requests can still change what a later page returns.
Filters and limits
Section titled “Filters and limits”EventSequenceFilter supports event source identifier, source type, stream type, stream
identifier, event types, and tags. Filters are combined; a tag filter matches any of
its supplied tags. Filtering happens in storage before the page limit. Null or empty
sets do not narrow the results. The usual unspecified/default routing sentinels also
mean no restriction.
Count must be between 1 and 1000, inclusive. Invalid counts are rejected, not
clamped. Cancellation is supported by both client methods. Stored compliance-protected
content is released for returned events, just as for other event-content reads; a page
is not a metadata-only read. Authorize access to the store, namespace, and event content
before reading.
Existing GetFromSequenceNumber reads remain available; Getting events explains when to use them. If you supply a custom IEventSequence implementation, implement GetPage before using this API. Its default interface member preserves source compatibility but throws NotSupportedException when called. The in-process Testing package uses the same client and kernel query path.