From e081e9acbfefc23a299bc3c0e821ed0e5cf6adac Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Tue, 8 Sep 2026 16:32:30 +0200 Subject: [PATCH] Document performance changes and measured results --- docs/performance.md | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/performance.md diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 0000000..18c0ae7 --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,66 @@ +# Performance improvements + +The September 2026 audit produced twelve changes, implemented in separate backend, +frontend, and worker worktrees and verified together before release. + +## Changes + +| Area | Improvement | +| --- | --- | +| Database reads | Fetch the latest day snapshots, tasks, and effective revisions for the requested dates. Carry-enabled counts look back to the necessary prior reset or log. | +| Synchronization | Skip already-materialized habits and unchanged marker writes; expire tasks using an index of open occurrences. | +| Catch-up | Materialize bounded batches with bulk task inserts, yield between batches, and serialize same-account operations until their data is ready. | +| Calendar payloads | `compact=true` omits task lists and repeated requirements. Dated editing endpoints retain full detail. | +| Calendar caching | Cache account-scoped ranges using persisted per-habit change versions; fetch visible months first and extend on demand. Historical edits and local midnight invalidate the appropriate data. | +| React rendering | Preserve unchanged habit objects and callbacks; memoize calendar data and geometry. | +| Initial JavaScript | Split the showcase and optional forms, editors, settings, and sharing interfaces into lazy-loaded modules. | +| Browser fonts | Serve complete WOFF2 fonts separately with conditional caching; retain the original TTF files for native image rendering. | +| Image rendering | Share a bounded queue across server renderers, deduplicate identical in-flight requests within their owner, and release completed canvas surfaces. | +| Backups | Run SQLite snapshots, verification, durability, and replication in a subprocess, preserving startup health and overlap prevention. | +| Reminders | Check delivery-slot eligibility before evaluating habits; revalidate preferences and progress immediately before sending. | +| Avatars | Bound cached images/downloads, reuse concurrent downloads, and briefly cache failures. | + +## Local measurements + +These are synthetic measurements on a development Mac, not production capacity +estimates. Absolute timings vary with hardware and load. + +| Scenario | Before | After | +| --- | ---: | ---: | +| Dashboard service work, ten task habits with five years of history | 169.81 ms | 2.76 ms | +| Unchanged synchronization, ten habits | 34 queries / 10 row changes | 4 queries / 0 row changes | +| Combined year calendar, ten habits with ten tasks each | 10,693,864 JSON bytes | 1,267,884 compact JSON bytes | +| Catching up a year, ten habits with ten tasks each | ~1.17 s without yielding | ~1.11 s total; 112 timer ticks, largest gap 11.75 ms | +| Initial calendars, ten new manual habits | 1,947,301 JSON characters | 476,954 JSON characters | +| Unchanged focus refresh, same ten habits | 11 requests / 10 calendar requests | 1 request / 0 calendar requests | +| Switching identical habit filters | 920 `Date.parse` calls | 0 | +| Initial JavaScript, including static dependencies | 716,094 bytes | 286,641 bytes | +| Initial JavaScript, locally gzip-compressed | 199,622 bytes | 99,192 bytes | +| Main stylesheet | 273,268 bytes | 85,733 bytes | +| Main stylesheet, locally gzip-compressed | 106,410 bytes | 13,636 bytes | +| Fifty already-delivered reminder checks | 210 ms / 1,000 day evaluations | 14 ms / 0 day evaluations | +| Three renders with the same avatar URL | 3 downloads | 1 download | +| Sixteen identical concurrent image renders: timer delay | 68 ms | 1.1 ms | +| ~98 MiB replicated backup: main-thread timer delay | 421 ms | 1.9 ms | + +Calendar byte/character counts describe serialized JSON, not compressed wire +traffic. JavaScript/CSS gzip sizes are local compression estimates. Image RSS +measurements concern transient native allocations, not memory leaks. + +## Operation and verification + +The application remains one Bun process with SQLite/WAL and one set of Discord +delivery workers. The backup subprocess owns a separate connection. Render +admission defaults to two active jobs, sixteen total pending jobs, and two jobs +per account; an overloaded renderer returns a retryable busy response. + +Migrations add a persisted calendar revision and a partial index for open task +occurrences. Existing historical requirements, carryover rules, privacy boundaries, +rate-limit handling, and confirmed-deleted-message replacement remain covered by +regression tests. + +Run `bun run typecheck`, `bun test`, and `bun run test:smoke`. The smoke test uses +a disposable production database. Browser verification should cover lazy route +and dialog loading, count/task saves, historical corrections, calendar ranges, +both themes, narrow layouts, and separately served fonts. Timing tests should +measure event-loop availability as well as total job duration.