The renderer reads a profile’s entries once and then keeps that list current by change, never by reading it all again. A year of a power user’s entries is over 13,000 rows, and reading them whole costs about a third of a second over the desktop bridge and a second or two on a phone: too slow to repeat after every tap. Logging something reads nothing back.
A write returns what it changed
Each entry write in the repository has a form that returns EntryChanges: the rows it inserted or updated (put) and the ids it removed (gone), read off its own statements with RETURNING.
createReturning— the new row, and a Nothing it replaced on the same day;updateReturning— the row as it now is, and a Nothing it replaced;removeReturning— the rows deleted, and the parts of a routine deleted with it or taken out of it.
create, update and remove are the same writes for a caller that wants only the id. useEntries applies the changes to the loaded list (applyEntryChanges in apps/renderer/src/db/entry-cache.ts), Undo included, and marks only the small summaries and counts stale. The checks a write makes before writing (the value against its trackable, the routine it joins, the same day’s rows) stay in the database: a few small reads beside the row cost little next to reading the whole list.
A sync round says what it touched
A round that pulled anything returns the rows the merge may have written (Synced Databases). The renderer reads the touched entries again by id (entryRepo.byIds), drops those no longer there, and reads the other lists, which are all small, whole (catchUpWith). A trackable deleted on another device takes its entries with it here without a change of their own, so that one case reads the entries again whole.
What is worked out per entry is remembered
What the app works out from entries is cached on the objects it was worked out from, in WeakMaps, so a change recomputes only what it changed:
- each entry’s issues are kept while the entry, its trackable and its interval’s status stay the same, and a trackable with no time and nothing required is not read for them at all;
- an entry’s interval is parsed once, and again only if its value changes (
intervalOfEntry); - a list’s routine parts are worked out once per list (
partsByRoutine), and the derived evaluator indexes a list once; - the days on hold and the days anything was logged are worked out once per list;
- what a log earned is read from the loaded entries, which the log updates first (Achievements).
An updated list is a new array, so it is a new key, and nothing cached can be read against the wrong entries.
Code
packages/core/src/entry-repository.ts—EntryChanges, the…Returningwrites,byIds.apps/renderer/src/db/entry-cache.ts—applyEntryChanges,mergeChanges,catchUpWith;apps/renderer/src/hooks/useEntries.ts.packages/core/src/issues.ts,interval.ts,routines.ts,derived-evaluator.ts;apps/renderer/src/lib/earned.ts,apps/renderer/src/lib/series.ts— the per-entry and per-list caches.