GET /volumes/search?q= is slow: 6-7s on a cold query, even for queries that return 0 results.
Evidence (dev, 2026-09-01)
| query |
latency |
bytes |
q=c |
7.6s |
71827 |
q=cele |
7.4s |
1183 |
q=celestialvo (0 results) |
6.7s |
12 |
repeat q=celestial |
0.9s |
1183 (warm) |
Cold = full scan; warm is fast only because of WiredTiger cache + the route's CachePage wrapper.
Cause
catalog-data.go data.SearchVolumes (v0.15.0, data/volume.go:417) calls QueryVolumes(ctx, QueryParams{Limit: searchScanLimit}) to pull up to searchScanLimit fully-hydrated volume VOs, then filters in Go with strings.Contains(strings.ToLower(title), needle). Its own doc comment: "scanning the full collection ... in memory rather than pushing the match down to Mongo" (mirrors SearchPersons).
Suggested fix
Push the match to Mongo and cap hydration:
- a
title text index, or a normalized title_lower field with a regular index + anchored/prefix regex
- return lightweight
{id, title} projections, not full hydrated VOs
- consider the same for
SearchPersons and the other entity searches
Mitigation already shipped (frontend)
game-room-web now requires 3 chars and debounces 350ms before calling this endpoint (sweetrpg/game-room-web#434) — cuts the q=c worst case but not the 6s floor.
GET /volumes/search?q=is slow: 6-7s on a cold query, even for queries that return 0 results.Evidence (dev, 2026-09-01)
q=cq=celeq=celestialvo(0 results)q=celestialCold = full scan; warm is fast only because of WiredTiger cache + the route's
CachePagewrapper.Cause
catalog-data.godata.SearchVolumes(v0.15.0,data/volume.go:417) callsQueryVolumes(ctx, QueryParams{Limit: searchScanLimit})to pull up tosearchScanLimitfully-hydrated volume VOs, then filters in Go withstrings.Contains(strings.ToLower(title), needle). Its own doc comment: "scanning the full collection ... in memory rather than pushing the match down to Mongo" (mirrorsSearchPersons).Suggested fix
Push the match to Mongo and cap hydration:
titletext index, or a normalizedtitle_lowerfield with a regular index + anchored/prefix regex{id, title}projections, not full hydrated VOsSearchPersonsand the other entity searchesMitigation already shipped (frontend)
game-room-web now requires 3 chars and debounces 350ms before calling this endpoint (sweetrpg/game-room-web#434) — cuts the
q=cworst case but not the 6s floor.