Skip to content

cost(P2): search_users unbounded Scan+contains on /users/search → cap + search index #35

Closed

Description

@jjbaumert

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.

Activity

  1. jjbaumert commented on Jul 16, 2026

    @jjbaumert
    OwnerAuthor

    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.

  2. jjbaumert commented on Oct 15, 2026

    @jjbaumert
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions