Sync / foldkit-durable

Reading history

ts
const { snapshot, cursor } = yield* journal.load(key) // the state, and where it is
const later = yield* journal.read(key, cursor, { limit: 500 }) // what came after

read returns at most limit operations, so a server answers a far-behind replica in pages; Sync's exchange carries more for this. cursor(key) reads the last sequence without decoding a snapshot, for checking a client's cursor before anything is appended.

Sequence and Cursor are different brands on purpose: a committed operation's position cannot be passed where a read position is expected.

Compaction and checkpoints. compact(key, through) drops committed payloads up to through; floor(key) is the highest dropped sequence. A read from below the floor fails with CompactedCursorError, which tells the server to send a checkpoint, load's snapshot and cursor, instead of a misleading partial tail. Identity rows stay, so an old retry is still recognized.

Reset and epoch. A cursor only means something within one history. epoch(key) names it: the same while the operations are kept, new after reset(key) or on a new database file. Hand it to clients with every answer; a client that comes back with another epoch holds a cursor into history this journal does not have, and has to be answered from 0. reset drops a document's snapshot and operations but keeps replica bindings and effect records.

Change notification. journal.subscribe is a Stream of document keys a commit changed. It is a wake-up, not a history transport: it slides, a slow subscriber may miss wake-ups, and a subscriber never slows or fails a commit. Catch up with read.