#284 · The catalogue text rebuild reads one snapshot
- timeline-app
- pull request
Closes #261. **Cause.** The background catalogue text rebuild (`refreshText` in `server/store.mjs`) ran `oc_event_text` under READ COMMITTED. `oc_events` (and `oc_events_lineage` for forks) is a volatile PL/pgSQL function, so every node and moment lookup in its walk took a fresh snapshot. When a save or deletion committed partway through, the walk followed a node ID it had already read to a row that was gone, and raised "Broken timeline index". For a fork it's worse: a lookup tries one version (live or retired) and then the other in separate statements, so a row moving from live to retired between the two (a save to the upstream, or the upstream being deleted) is missed in both. This isn't limited to tests: deleting or saving a timeline, or saving a timeline that forks read through to, while a rebuild runs could log it. **Fix.** The rebuild's read now runs in a `REPEATABLE READ, READ ONLY` transaction, so the walk finishes on the index it started with. The optimistic update after it already applies only at the revision it read, so a deleted timeline's update matches no row, and a saved one's leaves the text stale for the save's own refresh. A genuinely broken index still raises. **Test.** `test/catalogue-text-postgres.mjs` holds `oc_moments` locked so the rebuild stops after reading the root and nodes, deletes the timeline there, and checks that nothing is logged. It failed 3/3 runs without the change (with the PL/pgSQL context `oc_events … line 17 at RAISE`) and passed 5/5 with it, in about 7 s. A heavier race (a 20,000-moment fork rebuilt while its upstream was saved and deleted) reproduced both the `oc_events` and `oc_events_lineage` errors in 3 of 7 runs before the fix and none in 10 after, but took over a minute, so it isn't included. Checked locally: `npm test` (305 pass), `npm run test:platform:postgres`, `shared-history-postgres.mjs` 6×, plus `incremental`, `fork-read-through`, `search` and `deletion` Postgres tests.
Sources
Open #284 · The catalogue text rebuild reads one snapshot in the timeline