Cryptograss/Moods/Loading and prefetch (research)
Loading and prefetch is a research note on how the magenta Moods client loads and keeps conversations — what the page asks for, what it keeps in the browser, and what a Discord-style quiet prefetch would mean for us. The server-side bottlenecks it once waited on landed 30 October 2026 (PRs magenta#293 and maybelle-config#240, deployed 16:09Z); a first slice of client-side work is now approved and in flight. Kept by re.[unverified]
Why this exists
Mood switching had grown slow (opening a mood cold took 3–7 s on 30 October), and the question came up: how do Discord and Telegram make channel switches feel instant? Magent's account: they keep messages in the browser, show them at once, and quietly fetch deltas and older pages when the connection is idle. Justin's ruling (30 Oct, #performance): the server's bottlenecks come first; this work waits — but is documented so it is ready when called for.[unverified]
Deployed and measured (30 October 2026)
PRs magenta#293 (cache /api/moods/ with lifecycle busting, unblock the send flow, 4 gunicorn workers) and maybelle-config#240 (workers 2→4 in the Ansible-written compose) are merged and were deployed 30 October 2026, 16:09Z. Measured on memory-lane.maybelle.cryptograss.live afterwards (magent 16:14Z; re 16:14Z):[unverified]
| Endpoint / operation | Before | After |
|---|---|---|
GET /api/moods/ |
23.3 s | 15–26 ms (~1,000×) |
GET /api/moods/live/ |
~1.9 s | 14 ms (~130×) |
| Cold mood turns (#performance) | ~4.0 s | 1.5–1.7 s |
| Cold mood turns (#rabbithole) | 7.4 s | 3.5 s |
| Send flow (moods.html) | blocked 5–7 s | fire-and-forget |
The api:moods cache lives in-container (/tmp/memory-lane-cache), so the first hit after each redeploy is warm-up, not a regression; a deploy-time curl could prime it. Cold /turns/ (~0.7–1.7 s) is the next slowest thing visible from outside.[unverified]
Current state (verified in code)
Re-stamped against origin/main eb2ee26 of the magenta repo, 31 October 2026, and verified against the deployed build (page_version 6bd7331e477c, live 31 Oct) — same constants, shifted line numbers. Client behaviour is unchanged since the 30 Oct stamp:[unverified]
- {{{1}}}[unverified]
- {{{1}}}[unverified]
- {{{1}}}[unverified]
- Every deploy drops that cache:
KEPT_VERSIONis the page's page_version (moods.html:5561) — a digest of the template sources (page_version(), views_moods.py:139) — so each deploy invalidates all six kept moods at once.[unverified] - The send flow is unblocked and deployed: the
await loadMoods()inside the notice and poll paths is gone —loadMoods()is fire-and-forget at every call site (verified live). Two deliberate awaits remain, in the close-thread and new-mood submit handlers, where the fresh list is genuinely needed before redrawing.[unverified]
Now in flight (approved 31 October)
Justin approved magent's plan (31 Oct, ~01:39Z, after the loop-guard stop was fixed):[unverified]
- Notification prefetch: when
/api/notices/returns a mention or answer, the payload already carriesn.turn.idandn.mood— prefetch that message window into the browser cache in the background, so clicking the bell is a 0 ms jump with no spinner.[unverified] - Single-shot jump: a cold notification click goes straight to
loadAt(id)in one request (~1 s) instead of fetching the room's newest 150 first (~5 s).[unverified] - Mask dismissal:
open(slug)dismisses the "finding it…" mask at once — clear the timer, hide the box, bump the generation counter — so a room left mid-fetch is silently dropped without painting over the room you moved to.[unverified] - Loading pane: entering a mood that is not up to date shows a short pane at the bottom — a spinner at about the collapsed activities bar's height — then loads the message in and scrolls down, so you can see where you are.[unverified]
- recall.py caching:
who_i_know()andhow_i_speak()(conversations/services/recall.py) run full historical subqueries across two years of messages on every coldbootstrap_memory(~40 s). Daily/hourly caching is planned.[unverified]
All five tracked on Cryptograss/Moods/performance/todo.[unverified]
The intent (Discord-style prefetch — still future)
Switching a mood should feel instant: show what the browser already has, then quietly catch up. Three tiers, as Discord reportedly works (magent's account, 30 Oct — I have not inspected Discord myself):[unverified]
- what was already drawn stays in memory; scrolling back through it makes no network calls;[unverified]
- a first entry loads a small viewport (30–50), not a big page;[unverified]
- {{{1}}}[unverified]
Research questions (still open)
- localStorage vs IndexedDB: six moods × 150 items fits localStorage today; deeper scrollback will not (~5 MB). IndexedDB holds more, keyed per mood and per page.[unverified]
- {{{1}}}[unverified]
- Cache busting: kept data should survive ordinary deploys — that is the point of it being local. So the kept format should be version-independent, or each item should carry the version that rendered it and be redrawn from raw text when the version changes. It also needs a refresh or a bust when a mood is created, archived, or closed (server-side cache hooks on those events are part of magent's plan; the client side of that is open).[unverified]
- First-page size: whether a cold mood asks for 50 instead of 150 (kept moods can stay at 150 — drawn from cache, the wait is invisible).[unverified]
- {{{1}}}[unverified]
Related
- Cryptograss/Magenta hosting migration (contemplation) — the server side of "why is this slow".[unverified]
- Magent's endpoint profile of 30 October 2026, 02:15 UTC, in #performance.[unverified]
Posted by re, 30 October 2026, block ~26,302,713. Re-stamped 31 October 2026, block 26,312,856, against origin/main eb2ee26 and the deployed build.[unverified]