You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(runtime): journal-backed replay through subscribe/poll (#659) (#709)
Complete the final slice of #659: DurableBroker's subscribe/poll now
fall back to the durable journal when a cursor predates the inner
broker's in-memory retention window, with cursor_expired + oldest-
available-cursor semantics carried through end to end and the
identical subject_id filter applied to both live delivery and durable
replay (spec 066 FR-003, FR-005, FR-007, FR-008).
- EventBroker gains two additive, default-no-op methods:
publish_with_cursor (lets a decorator inject a pre-assigned cursor
instead of self-incrementing) and seed_restart_floor (lets a
decorator establish a floor below which any cursor for an
unobserved event type is treated as potentially predating this
broker instance). InProcessBroker overrides both; every other
EventBroker implementor is unaffected.
- DurableBroker::publish now calls publish_with_cursor with the
journal-assigned cursor, unifying the live and durable cursor
spaces so a cursor obtained during live polling remains valid when
later resolved through durable replay.
- New JournalSource trait (replay_from) shared via
Arc<RwLock<DurableEventJournal>> between the writer thread and the
read path; DurableBroker::open is the new production constructor
that wires sink and source to the same underlying journal storage.
DurableBroker::new gains a `source` parameter for tests that need
independent write/read doubles.
- subscribe_for_subject tries the inner broker first; on
CursorExpired it checks the journal and either creates a
durable-mode subscription (own subscription table, "durable-sub-*"
ids) or surfaces the journal's own CursorExpired. poll routes by id
prefix: durable-mode subscriptions loop over replay_from (paging
internally on partial batches) applying the same event_type/
subject_id filtering as live delivery; other ids delegate to the
inner broker unchanged.
- Fixed a real restart-continuity gap: a freshly constructed inner
broker has no history for an event type it has never seen, so it
optimistically accepted any cursor as non-expired. DurableBroker::
open now seeds the inner broker's restart floor from the journal's
latest cursor at construction, so a resumed cursor from before a
process restart is correctly deferred to durable replay instead of
silently treated as caught-up.
- 25 new tests (63 total in the events module, all at 100% line
coverage for traverse-runtime): the fallback cascade, identical
subject filtering through fallback, double-expiry surfacing the
journal's oldest-available cursor, non-cursor journal read-error
propagation (Io/InvalidCursor/Corrupt), paging and multi-batch
scanning, cross-type filtering, real full-broker restart
continuity, and a production SharedJournalSink revocation exercised
through DurableBroker::open rather than a scripted test double.
Closes#659
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
0 commit comments