Recipe corpus scalability: pre-compute browse counts + long-term search service migration #116
Labels
No labels
accessibility
backlog
beta-feedback
bug
duplicate
enhancement
feature-request
help wanted
invalid
needs-design
needs-triage
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Circuit-Forge/kiwi#116
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The recipe corpus is a 3.8 GB SQLite file (
/Library/Assets/kiwi/kiwi.db) mounted read-only into the cloud container. Every user request that touches the browse endpoints opens a connection to this file and runs an FTS5 MATCH query.SQLite WAL mode supports unlimited concurrent readers, so there is no write contention — but there are real pressure points:
_COUNT_CACHEinstore.pycaches browse counts per keyword set, but it is in-process. Every API restart wipes it. Multiple API replicas each build their own cache independently.kiwi.dbfiles ATTACH to the corpus DB on every request, meaning N concurrent users = N open file handles on the same large file.Recommended fix — three phases
Phase 1 (now): Pre-computed browse counts table
Complete.
app/services/recipe/browse_counts_cache.pyimplements the full caching layer:browse_countsSQLite file separate from corpus and per-user DBsmain.pylifespaninfer_recipe_tags.pytriggers refresh at end of pipeline run_COUNT_CACHEinstore.pypre-warmed on startup — FTS never hit for known keyword setsPhase 2 (medium term): Dedicated search sidecar
Replace the FTS5 index with Meilisearch or Typesense running as a sidecar container. Both handle concurrent search correctly, support faceted browse natively, and are purpose-built for this use case. Track separately.
Phase 3 (long term, only if needed): PostgreSQL corpus
Only if Phase 1 and 2 are insufficient at scale.
Acceptance criteria — Phase 1
browse_countstable added_COUNT_CACHEpre-warmed from persistent cache fileinfer_recipe_tags.py