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

#133 · Let a self-hosted server set how many entries a timeline holds

  • timeline-app
  • pull request

Closes #129. ## Why the limit exists 200,000 is the largest size the capacity benchmarks measure. It also protects a shared server from work that handles a whole timeline at once: - **Saved revisions:** every 1,000 saves, or once the changes since the last full copy add up to the timeline's size, a save stores a full copy of the timeline. - **Whole-timeline reads:** exports, forks and pull requests read the whole timeline into server memory. Most other work (viewing, incremental saves, log ingestion) touches only part of a timeline. ## What changes `TIMELINE_ENTRY_LIMIT` in the environment file sets how many moments a timeline may hold, and as many durations and relationships. It takes a whole number of at least 1000, or `unlimited`. Empty or unset keeps 200,000, and an invalid value stops the server at startup. - **Where the limit is applied:** `src/entry-limit.ts` holds the limit for document validation (moments, durations, relationships) and the server's incremental saves. It is kept on `globalThis` under a `Symbol.for` key, because the Next.js platform's bundles each carry their own copy of the core module. - **Pages:** the session reports `entryLimit` (`null` when unlimited). Pages follow it, and the capacity notice warns at three quarters of the server's limit, or never when it is unlimited. - **Saved revisions:** PostgreSQL stores each full copy as one `jsonb` value. An array in a `jsonb` value can't exceed about 256 MB, and `jsonb` takes 1.2–1.3× the JSON size. A save whose full copy would exceed 200 MB as JSON is now refused with HTTP 413 and a message to split the timeline, instead of failing in the database. With the benchmark content that is about 1.1 million moments. - **Benchmarks:** the timeline generator (`bench/generate.mjs`) follows the limit, so benchmarks can be run past 200,000 moments. - **Docs:** the env templates (Debian/RPM/FreeBSD/macOS/Windows, `.env.example`, `compose.yml`) gain the setting. docs/administration.md gets a **Timeline size** section, and docs/capacity.md and README are updated. ## Measured cost Measured on an 8-core VM against a local PostgreSQL 17, with the benchmark timelines (titles, descriptions, tags, plus 1 duration and 1 relationship per 10 moments): | Moments | Saving 5,000 new moments | Saving with a full copy | Exporting | Full copy | Server memory | |---|--:|--:|--:|--:|--:| | 200,000 | 6 s | 10 s | 4 s | 35 MB | 0.4 GB | | 500,000 | 15 s | 22 s | 11 s | 87 MB | 0.7 GB | | 1,000,000 | 20 s | 49 s | 19 s | 173 MB | 1.3 GB | ## Limits that stay - **Offline HTML and the desktop app:** the offline HTML file and the desktop app's `.och` files (validated in Rust) keep 200,000. - **Request size:** a request is still at most 16 MiB, so a whole-timeline create or import is roughly ≤100,000 moments. Larger timelines grow through saves (the editor, `och-import-logs`, the syslog receiver). - **`.och` exchange:** `.och` files exchanged with the server are still at most 32 MiB. ## Tests - `test/limits.test.mjs`: parsing the setting. - `test/entry-limit.test.mjs`: validation following the limit, sharing it across module copies, and refusing an over-large revision before the database is touched. - `test/capacity.test.mjs`: the warning follows the server's limit. - `test/limits-postgres.mjs`: incremental saves stop at a server's limit, and pass it when unlimited; the session reports both. Locally, the unit tests (257), typecheck, the PostgreSQL suites and the browser suite pass.

Sources

Open #133 · Let a self-hosted server set how many entries a timeline holds in the timeline