perf(library): load a collection when a view needs it, not at startup
All four collections were fetched at DOMContentLoaded and refetched on every invalidation, whichever view was showing. On a 26 138-track library the track list was 20.5 MB of that, and encoding it cost the backend ~170 MB of transient allocation — paid by someone looking at Home, which draws none of it. - The store warms only albums, artists and genres, on idle after first paint: 1.6 MB together, and what made those views instant. - A nav item prefetches on hover and on keyboard focus, which is the ~100 ms before the click that a cold open would otherwise wait. - An invalidation refetches what something had loaded, and nothing else — a scan no longer loads the track list of a library whose Tracks view nobody has opened. - `index.html`'s first-paint `<track-list>` is `view-hidden`, and `index.ts` no longer activates it: it is markup, not a decision about which view the launch lands on, and activating it was what fetched the whole list for a landing on Home. The track list itself loads on view activation, which the shell drives. Measured on 50 000 tracks: backend RSS at rest 543 → 296 MB, peak 571 → 296 MB, Go heap held 361 → 125 MB, JS heap 31.8 → 18 MB, binding bytes at rest 35.9 → 12.1 MB, heap after a browse 36.5 → 22.7 MB. Tracks first open 26 ms; slowest view open 57 ms. Closes #280
This commit is contained in:
1 parent
3d9828b847
commit
620151aa41
11 files changed
+538
-73
No files matched your search
@@ -43,16 +43,25 @@ function stubReads(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the cache and let the eager refetch settle, so each test starts
|
||||
* from the same place. The store has no reset of its own; a scan
|
||||
* completing is how the app itself clears it.
|
||||
* Start each test from a loaded store. The store has no reset of its
|
||||
* own; a scan completing is how the app itself clears it — and since
|
||||
* #280 it reloads only what something had loaded, so the four reads
|
||||
* here are what says "this test is about a loaded store".
|
||||
*/
|
||||
async function reload(): Promise<void> {
|
||||
stubReads();
|
||||
emit(Events.LibraryScanComplete);
|
||||
await flush();
|
||||
// The eager refetch the invalidation kicks off is recorded like any
|
||||
// other call; clear it, or every count in every test is off by one.
|
||||
resetHarness();
|
||||
stubReads();
|
||||
await Promise.all([
|
||||
libraryStore.getTracks(),
|
||||
libraryStore.getAlbums(),
|
||||
libraryStore.getArtists(),
|
||||
libraryStore.getGenres(),
|
||||
]);
|
||||
// Those reads are recorded like any other call; clear them, or every
|
||||
// count in every test is off by one.
|
||||
resetHarness();
|
||||
stubReads();
|
||||
}
|
||||
@@ -91,7 +100,7 @@ describe('library store: caching', () => {
|
||||
]).toEqual([['/a.mp3', '/b.mp3'], ALBUMS, ARTISTS, GENRES]);
|
||||
});
|
||||
|
||||
it('refetches everything when a scan completes', async () => {
|
||||
it('refetches everything a view had loaded when a scan completes', async () => {
|
||||
emit(Events.LibraryScanComplete);
|
||||
await flush();
|
||||
|
||||
|
||||
Reference in new issue
Block a user