Let go of old message bodies and of exported files #387

Closed
opened 2026-09-16 17:59:55 +00:00 by jcoffey-dev · 0 comments
Owner

Summary

Two client-side memory items from the performance review.

Message bodies are held for the 40 most recently wanted messages.

  • Before: every message opened kept its full copy for as long as the tab was open: HTML and text bodies up to 2 MB each, parsed headers, and the attachment list.
  • What gets tracked: getEmails(ids, true) records which messages have been wanted in full, most recent last.
  • Releasing old ones: past BODIES_KEPT (40), releaseBodies takes the least recent messages back to their LIST_PROPS and removes them from fullIds. Messages in the open conversation are skipped.
  • Why this is safe: everything that needs a body fetches it through getEmails(…, true) (the thread view, reply and forward, appointments), and the reading pane only renders messages in fullIds. A released message is simply fetched in full again when it's next opened.
  • Account switch: the list of held bodies is cleared.

Exported files are released. A new lib/download.ts downloadFile() creates the object URL, clicks the link and releases the URL straight away, as the calendar export already did. The contact-book export, the single-contact vCard and the settings export now use it; none of the three released their URL before. The calendar export uses the helper too.

Related issues

None.

Translations

Adds none. npm run i18n:check reports the same counts as main.

Testing

  • New body-eviction.test.ts covers four cases:

    • past the limit, the oldest messages lose their bodies but keep their list fields;
    • reopening a message counts it as recent again;
    • the open conversation is never released;
    • a released message is fetched in full again.

    All 4 fail against main.

  • npm test -w web (1,335 tests), npm run typecheck, npm run build -w web and npm run i18n:check are clean.

  • Checked in Chrome against npm run dev:mock:

    1. Opened a three-message conversation, then moved to another one.
    2. Fetched 50 other messages in full through the app's store. That left 40 bodies held, and the first conversation's three messages released.
    3. Reopened the first conversation from the list. All three messages were fetched in full again, and the latest one rendered its HTML body.

Merged 2026-09-16 as coffey-labs/ihasmail@aa9bf1b9b2

Rebuilt from: git history, session transcript.

## Summary Two client-side memory items from the performance review. **Message bodies are held for the 40 most recently wanted messages.** - **Before:** every message opened kept its full copy for as long as the tab was open: HTML and text bodies up to 2 MB each, parsed headers, and the attachment list. - **What gets tracked:** `getEmails(ids, true)` records which messages have been wanted in full, most recent last. - **Releasing old ones:** past `BODIES_KEPT` (40), `releaseBodies` takes the least recent messages back to their `LIST_PROPS` and removes them from `fullIds`. Messages in the open conversation are skipped. - **Why this is safe:** everything that needs a body fetches it through `getEmails(…, true)` (the thread view, reply and forward, appointments), and the reading pane only renders messages in `fullIds`. A released message is simply fetched in full again when it's next opened. - **Account switch:** the list of held bodies is cleared. **Exported files are released.** A new `lib/download.ts` `downloadFile()` creates the object URL, clicks the link and releases the URL straight away, as the calendar export already did. The contact-book export, the single-contact vCard and the settings export now use it; none of the three released their URL before. The calendar export uses the helper too. ## Related issues None. ## Translations Adds none. `npm run i18n:check` reports the same counts as `main`. ## Testing - New `body-eviction.test.ts` covers four cases: - past the limit, the oldest messages lose their bodies but keep their list fields; - reopening a message counts it as recent again; - the open conversation is never released; - a released message is fetched in full again. **All 4 fail against `main`.** - `npm test -w web` (1,335 tests), `npm run typecheck`, `npm run build -w web` and `npm run i18n:check` are clean. - Checked in Chrome against `npm run dev:mock`: 1. Opened a three-message conversation, then moved to another one. 2. Fetched 50 other messages in full through the app's store. That left 40 bodies held, and the first conversation's three messages released. 3. Reopened the first conversation from the list. All three messages were fetched in full again, and the latest one rendered its HTML body. **Merged** 2026-09-16 as coffey-labs/ihasmail@aa9bf1b9b25e <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.