Skip to content

feat(emails): SQL thread-head winnowing — removes the 2000-row accuracy/CPU window#936

Open
seonghobae wants to merge 1 commit into
developfrom
feat/get-emails-sql-thread-heads
Open

feat(emails): SQL thread-head winnowing — removes the 2000-row accuracy/CPU window#936
seonghobae wants to merge 1 commit into
developfrom
feat/get-emails-sql-thread-heads

Conversation

@seonghobae

Copy link
Copy Markdown
Contributor

Gap (reliability audit #2)

get_emails fetched up to 2000 rows into memory and grouped threads in Python: beyond that window, thread heads, reply_count, and requires_reply silently went wrong, and every request paid full grouping CPU.

What

  • Thread heads in SQL: row_number() OVER (PARTITION BY canonical-thread-key ORDER BY date DESC) → rank 1 → ordered + LIMIT'd. Accuracy no longer depends on mailbox size. (Window functions, not DISTINCT ON, keep it portable.)
  • Bounded second query for the page's threads only → reply_count, requires_reply, sent-folder filter keep their exact semantics (incl. app-side parsed-address matching). Sent folder over-fetches a bounded limit*5 heads instead of a fixed global window.
  • Defensive app-side dedupe kept (no-op on PostgreSQL).
  • LimitAwareMockSession updated to emulate the head-winnowing contract.

Verification (local)

  • Full email API suite 48 passed — including exact_distinct_threads_beyond_overfetch_window and orders_interleaved_threads_by_latest_message_date which encode the correctness this fixes; ruff clean.
  • Honest note: test_get_emails_reply_tracking_real_postgres_smoke fails identically on origin/develop (verified baseline against fresh pgvector:pg16) — pre-existing, unrelated; worth a separate look.

Pairs with

#934 (search hybrid retrieval) — together they remove both O(N)-cliff read paths flagged in the reliability audit.

🤖 Generated with Claude Code

…window

get_emails previously fetched up to 2000 owner rows into memory and grouped
threads in Python: past that window, thread heads, reply counts, and
requires_reply silently went wrong, and every request paid the full
grouping CPU.

- Thread heads now come from SQL: row_number() over (partition by the
  canonical thread key, newest first) filtered to rank 1, ordered by date,
  LIMIT'd — accuracy no longer depends on mailbox size. Window functions
  keep the statement portable (no DISTINCT ON).
- Reply counts, requires_reply, and the sent-folder filter are computed from
  a second bounded query fetching only the page's threads, preserving the
  existing semantics (including app-side parsed-address matching for sent).
  The sent folder over-fetches a bounded limit*5 of heads instead of a fixed
  global window.
- Defensive app-side dedupe kept (no-op on PostgreSQL) so non-window mock
  backends stay exact.
- LimitAwareMockSession now emulates the head-winnowing contract and records
  the head query's LIMIT.

Verification: full email API suite 48 passed (incl. exact-threads-beyond-
window and interleaved-ordering tests), ruff clean. The postgres smoke test
(test_get_emails_reply_tracking_real_postgres_smoke) fails identically on
origin/develop (pre-existing, unrelated to this change; verified by baseline
run against a fresh pgvector instance).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RkKdtHRLG4wSLh6PVsp8J
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant