You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
cost(P2): search_users unbounded Scan+contains on /users/search → cap + search index #35
Priority: P2 (cost) — Tier 1 #2. Part of the 2026-07-11 DynamoDB query-cost review.
Problem
routes/users.rs:520 (GET /users/search) → user_repo.search_users runs a Scan + contains(email/name) and paginates to end-of-table with no .limit() / early-stop. Every people-picker / @-mention keystroke that reaches the server scans the entire user set. This is the single most expensive user-facing query.
Fix
Cheap, safe first step: cap it — pass .limit(), stop paging once enough matches are collected, so one search ≠ full-table scan. (No schema change.)
Proper fix: substring match can't use a GSI; route user search through the existing search index (qdrant) instead of a DDB scan.
CRDT impact: none — touches only PROFILE (user) rows.
Status note (triage): the cheap first step is already shipped — user_repo::search_users now bounds every call with MAX_SCANNED_ITEMS = 5000 (scanned-items budget), PAGE_LIMIT = 500, and an early-stop at MAX_MATCHES = 10. So a single people-picker/@-mention keystroke no longer scans the whole table; the P2 cost concern is resolved.
The remaining "proper fix" (route user search through a real index) is a separate, larger feature, and worth noting the ticket's suggested tool is off:
qdrant is the wrong index for this — it's semantic/vector similarity, poor for substring/prefix people-picker matching. The right tool is the existing Tantivy full-text index.
Users are not indexed anywhere today. Indexing them means: a dedicated Tantivy index (mixing users into the shared doc index would leak them into document/ask keyword search, which queries with doc_type = None), a boot backfill (the doc index is write-path-only/per-instance with no backfill — accepted v1 limitation), and index hooks on register / OAuth / SCIM / profile-update.
Recommend keeping this open as a deferred feature (the urgent cost issue is handled) rather than blocking on it. Deprioritized in favor of other Phase-5 work for now.
Fixed by PR #343 and deployed to the local self-hosted instance. API and worker writers were upgraded with indexed reads disabled, backfill_user_search_index indexed 1 existing profile, and a strongly consistent DynamoDB read returned META#USER_SEARCH_INDEX/COMPLETE state=complete. Indexed reads are now enabled on the API; /api/v1/status returns storage=ok. The 29-case user-search E2E suite and all PR CI checks passed. Follow-up #342 tracks bounded stale-posting compaction.
Activity
Mermaid polish: silent-misparse fixes + flowchart doc-parity (closes #32
perf(users): bound search_users scan with a 5000-row budget (#35)
jjbaumert commentedon Jul 16, 2026
Status note (triage): the cheap first step is already shipped —
user_repo::search_usersnow bounds every call withMAX_SCANNED_ITEMS = 5000(scanned-items budget),PAGE_LIMIT = 500, and an early-stop atMAX_MATCHES = 10. So a single people-picker/@-mention keystroke no longer scans the whole table; the P2 cost concern is resolved.The remaining "proper fix" (route user search through a real index) is a separate, larger feature, and worth noting the ticket's suggested tool is off:
askkeyword search, which queries withdoc_type = None), a boot backfill (the doc index is write-path-only/per-instance with no backfill — accepted v1 limitation), and index hooks on register / OAuth / SCIM / profile-update.Recommend keeping this open as a deferred feature (the urgent cost issue is handled) rather than blocking on it. Deprioritized in favor of other Phase-5 work for now.
jjbaumert commentedon Oct 15, 2026
Fixed by PR #343 and deployed to the local self-hosted instance. API and worker writers were upgraded with indexed reads disabled, backfill_user_search_index indexed 1 existing profile, and a strongly consistent DynamoDB read returned META#USER_SEARCH_INDEX/COMPLETE state=complete. Indexed reads are now enabled on the API; /api/v1/status returns storage=ok. The 29-case user-search E2E suite and all PR CI checks passed. Follow-up #342 tracks bounded stale-posting compaction.