Advance filtered AllStreamSubscription checkpoints on server checkpointReached#554
Advance filtered AllStreamSubscription checkpoints on server checkpointReached#554alexeyzimarev wants to merge 2 commits into
Conversation
…ents Wire up the checkpointReached callback on SubscriptionFilterOptions so server-reported checkpoint positions flow through the same ordered commit machinery as real events, as payload-less contexts (Message is null, so Handler ignores and acks without touching the consume pipe). Without this, a filtered $all subscription's checkpoint only advanced when a filter-matched event was processed. A long unmatched stretch (sparse filters, quiet servers) left the checkpoint parked at the last matched event: restarts re-scanned everything since then, and any consumer comparing the checkpoint to the $all head saw a phantom, never-closing lag. Adds CheckpointReachedTests, which reproduces the bug with a filter that matches nothing and asserts the checkpoint still advances past written events.
PR Summary by QodoAdvance filtered $all checkpoints using server checkpointReached callbacks
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
…-less contexts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
AllStreamSubscription.SubscribeconstructsSubscriptionFilterOptions(filter, checkpointInterval)without the thirdcheckpointReachedparameter, so the callback defaults to a no-op. A filtered$allsubscription's checkpoint therefore only advances when a filter-matched event is processed:$allhead (including theeventuous.subscription.gap.countmetric) sees a phantom, never-closing lag.Fix
Wire
checkpointReachedto route the server-reported checkpoint position through the existing ordered commit machinery as a checkpoint-only message: a null-payloadMessageConsumeContext(type$checkpoint-reached) with the position and the subscription'sSequence++, handed toHandleInternal. The null-message arm inEventSubscription.Handleralready Ignores + Acknowledges such contexts, which lands inCheckpointCommitHandler.Commitwith an ordered sequence number — no new commit paths.Why the ordering is safe
eventAppearedandcheckpointReachedsequentially on one delivery loop (each awaited beforeMoveNextAsync), soSequence++never races event contexts and sequences reflect exact delivery order.CheckpointCommitHandler's contiguous-sequence gate (CommitPositionSequence.FirstBeforeGap) holds a gap position back until every lower-sequence event has acked. The checkpoint can never commit past an unprocessed event, so crash/restart semantics are unchanged.Sequence.Test
CheckpointReachedTests.CheckpointAdvancesPastUnmatchedEvents: a filtered subscription whose filter matches nothing, 500 unmatched events appended — asserts the handler saw zero events AND the stored checkpoint advances past the last event's position (bounded 30s poll + 60s timeout). Verified red without the fix, green with it.Notes
CreateContext.SubscriptionActivityfor async contexts when diagnostics are enabled; gap contexts now hit that arm periodically. Harmless inert allocation — happy to short-circuit it here or in a follow-up if preferred.🤖 Generated with Claude Code