CRUMB a card from devarno-cloud

Timeline Reconstruction

chronicle advanced 6 min read

ELI5

Scrubbing the timeline is like asking the librarian “show me the book as it looked at 3:14 pm”. The librarian replays every sticky-note operation up to that moment, sorted by their LSEQ shelf coordinate, skipping the ones marked deleted, and hands you the resulting page.

Technical Deep Dive

Two-Channel Design (ADR-006)

ConcernRequestResponse
List of edit events for the UItimeline_metadata_requesttimeline_metadata
Document text at a specific momenttimeline_querytimeline_snapshot

Splitting metadata and content lets the UI render the timeline strip from a small payload while only fetching the heavyweight content when the user actually pauses on a moment.

Content Reconstruction

flowchart LR
Q[timeline_query target_hlc] --> Fetch[SELECT operations WHERE document_id = ? AND hlc_timestamp <= target_hlc ORDER BY hlc_timestamp, hlc_counter]
Fetch --> Map[BTreeMap position_id → char,deleted]
Map --> Apply{op.type}
Apply -- insert --> Ins[map.insert position_id → content,false]
Apply -- delete --> Del[map.get_mut → mark deleted=true]
Apply -- format/retain --> Skip[ignored]
Ins --> Build[Build content from sorted position_ids, skipping deleted]
Del --> Build
Build --> Resp[TimelineSnapshotMsg actual_hlc=last_op.hlc, content, operation_count]

actual_hlc may differ from target_hlc when no operation exists at the exact timestamp — it is the HLC of the last operation included in the replay.

Metadata Aggregation

const BUCKET_MS: i64 = 5000; // 5-second buckets

Operations group by (author_id, hlc_wall_time / BUCKET_MS). Each bucket becomes one TimelineEntry { hlc_timestamp, operation_count, author_id, author_name, summary? }. Entries are sorted descending and truncated to limit. Summaries are short human strings (“Inserted ‘Hello world’”, “Deleted 15 chars”).

End-to-End

sequenceDiagram
autonumber
participant UI as TimelineStore
participant Hook as useTimeline
participant WS
participant Relay
participant DB
UI->>Hook: enable timeline (mount)
Hook->>WS: timeline_metadata_request { limit: 1000 }
WS->>Relay: forward
Relay->>DB: SELECT ... ORDER BY hlc_timestamp
DB-->>Relay: rows
Relay-->>WS: timeline_metadata
WS-->>Hook: entries
UI->>UI: user scrubs to hlc=T
UI->>Hook: goToTimestamp(T)
Hook->>Hook: contentCacheRef miss
Hook->>WS: timeline_query { target_hlc: T }
WS->>Relay: forward
Relay->>DB: SELECT ops WHERE hlc_timestamp <= T
Relay->>Relay: reconstruct_content_from_operations
Relay-->>WS: timeline_snapshot { actual_hlc, content }
WS-->>Hook: cache + onTimeChange

Client Caching Layers

  1. contentCacheRef — in-memory Map<hlc, content> for snapshots already viewed in this session.
  2. TimelineStore.snapshots — coarser periodic snapshots that allow local replay.
  3. Relay fallback — for any uncached HLC, the round-trip above.

Key Terms

  • actual_hlc → the HLC of the latest operation ≤ target_hlc; sent back so the client can label “you are viewing the document as of T₀”.
  • 5-second bucketBUCKET_MS = 5000 in the relay aggregation; tuned to keep timeline strips readable at typing cadence.
  • operation_count (snapshot) → number of ops included in this reconstruction; lets the UI show “47 edits up to here”.

Q&A

Q: Does timeline reconstruction respect tombstones? A: Yes. delete ops mark the entry’s deleted flag; the final filter drops them before string assembly.

Q: Why a BTreeMap and not a Vec sort? A: BTreeMap keyed on position_id keeps insertion order consistent under repeated mutation and lets the same map serve both insert and delete updates without re-sorting.

Q: What if many operations share the same wall time bucket? A: They are merged into a single TimelineEntry per (author, bucket) regardless of count — the operation_count field tells you how dense it was.

Examples

User scrubs to T = 1700000050000. Relay loads ops with hlc_timestamp <= T, replays into a BTreeMap, returns content="Hello" and actual_hlc = 1700000049820 (last op before T). The client caches (T, "Hello") so dragging back to T is instant.

neighbors on the map