timescale.info
Sign inSign up

How OpenChronology was built

Every merged pull request of openchronology/timeline-app, its first week of commits and its releases; and before them the 2020 PureScript editor, from snapshots of its deleted repositories in the Wayback Machine. Written to this server through the HTTP API.

327 moments · 2020-03-09T00:15:08Z CE – 2026-11-02T20:11:21Z CE · by @athan · @athan

#52 · Store browser drafts record by record

  • timeline-app
  • pull request

Fixes #40. ## Change A signed-in user editing a timeline kept only in the browser used to have the whole timeline saved after every change: `model.document()` plus an IndexedDB put that structured-clones on the main thread. - **`TimelineIndex` keeps a change journal.** `put`, `delete`, `putDuration`, `deleteDuration`, `relate` and `unrelate` record what they touched (`m:`, `d:`, `l:`, and `r:` for a deleted moment's last time). `takeChanges()` hands it over and clears it. - **`src/drafts.ts`:** - turns journal entries into record writes; - writes every record for a timeline's first save; - rebuilds a document from stored records with `TimelineIndex.fromParts(...).document()`. This keeps links and anchors to deleted moments, so the result is exactly the document the index exports. - **IndexedDB version 2:** one store each for moments, durations, links and deleted moments' times, plus settings and embedded assets under `drafts/`. - Opening a timeline writes every record once, in one transaction. Later saves write only the journal's records, plus the small settings record. - Assets, which can be megabytes, are rewritten only when they change. - A failed transaction makes the next save a full rewrite. - The timeline counts as stored as soon as its full write is queued, because IndexedDB runs writers in order. Edits made while that first save is still running then write only their changes. - **Old whole-document drafts** (`drafts/current`) still restore. The first save converts them and removes the old record. ## Results (Chromium, `bench/capacity.mjs`) | Moments | Draft save, before | Draft save, now | Same, 4× slower CPU | Pause after an edit (incl. redraw) | |--:|--:|--:|--:|--:| | 1,000 | n/a | 0.1 ms | 0.1 ms | 33 ms | | 10,000 | ~35 ms | 0.2 ms | <0.1 ms | 67 ms | | 100,000 | ~400 ms | 0.1 ms | 0.3 ms | 150 ms | | 200,000 | ~850 ms | 0.2 ms | 0.8 ms | 300 ms (was 350 ms) | The "before" figures are from Node (V8), as in the issue. The draft save is now measured in the browser from the editor's new `openchronology:draft-changes` performance measures, and the benchmark records it as its own metric. Opening's one full draft write is excluded from these timings. The benchmark now waits for it before measuring edits. **Honest caveat:** in Chromium the draft save was a smaller share of the pause than the Node figures suggested. The remaining pause after an edit (300 ms at 200,000) is the redraw, the same cost as navigating. Further gains there would come from the in-memory renderer, not from drafts. The docs now say so. ## Tests - **`test/drafts.test.mjs`:** 400 random rounds of edits through the index's own methods: - renames and moves; - deletions with anchored durations and links; - undone deletions, new moments, duration puts and deletes, link changes; - title and asset changes, with several edits per save. After every save, the records are applied to an in-memory store (as structured clones) and the rebuilt document must `deepEqual` `index.document()`. The test also checks fewer than 12 records per save, includes full rewrites when another timeline opens, and has a journal unit test. It fails if a deleted moment's last time isn't journalled. - **`test/drafts-browser.mjs`** (in `browser.mjs`, Chromium and Firefox): - a version-1 whole-document draft restores and is converted to per-record stores, with the old record removed; - adding a moment and renaming the timeline store records; - after a reload, the timeline returns with its title, four moments and the added moment. - 186 unit tests, the typecheck, and `browser.mjs` plus `offline-browser.mjs` in Chromium and Firefox all pass locally.

Sources

Open #52 · Store browser drafts record by record in the timeline