fix(search): cap the encrypted-search IDB cache per room (bound disk growth)
The on-device search index grew unbounded over a long session. putRows now prunes each touched room to MAX_ROWS_PER_ROOM (5000) — deleting the oldest rows by [roomId, ts] via a self-chaining IDB cursor within the same write tx (never awaits a non-IDB promise mid-tx, so the transaction can't auto-commit and truncate the prune). Exposed a pure, unit-tested evictCount() for the decision; the cursor path itself is browser-only (node --test has no IndexedDB). Deliberate tradeoff (documented in code): the coverage window keeps claiming the evicted tail so the search doesn't re-fetch → re-evict it forever. Net effect — in a room past 5000 cached rows, an evicted old message is silently unsearchable rather than churning. Clear cached index / logout still wipe everything. Two review agents verified the IndexedDB-spec correctness (cursor delete+continue semantics, put-then-count ordering, roomRange bracketing with no prefix bleed, tx liveness, abort→cache-miss) since CI can't. Gate-green (tsc, eslint, prettier, 925 tests, build). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
computeCoverage,
|
||||
evictCount,
|
||||
mergeSearchResults,
|
||||
putRows,
|
||||
queryRoom,
|
||||
@@ -57,6 +58,18 @@ test('mergeSearchResults: missing ts sorts as 0 (last)', () => {
|
||||
|
||||
const row = (ts: number): Pick<SearchCacheRow, 'ts'> => ({ ts });
|
||||
|
||||
test('evictCount: 0 when under or at the cap, else the excess', () => {
|
||||
assert.equal(evictCount(0, 100), 0);
|
||||
assert.equal(evictCount(100, 100), 0); // exactly at cap → nothing evicted
|
||||
assert.equal(evictCount(101, 100), 1);
|
||||
assert.equal(evictCount(250, 100), 150);
|
||||
});
|
||||
|
||||
test('evictCount: uses the default per-room cap (5000)', () => {
|
||||
assert.equal(evictCount(5000), 0);
|
||||
assert.equal(evictCount(5001), 1);
|
||||
});
|
||||
|
||||
test('computeCoverage: derives oldest/newest from rows', () => {
|
||||
const cov = computeCoverage('!r', [row(30), row(10), row(20)], 3);
|
||||
assert.deepEqual(cov, { roomId: '!r', oldestTs: 10, newestTs: 30, count: 3 });
|
||||
|
||||
Reference in New Issue
Block a user