perf(home): cut cold home-feed latency from 3.9s to 1.5s #8

Merged
techfather merged 2 commits from perf/home-feed-latency into main 2026-10-18 14:01:55 -07:00
Owner

Cold /api/v1/home took ~4s on the live instance. Two costs dominated it:

  1. Discovery sources were awaited one at a time. A cold upstream cache paid one round-trip per source (trending, popularity discovers, release-date discover, then genre discovers) until the 240-title pool filled. They are now fetched in bounded concurrent batches (fanout 6) while the pool is still consumed in strict priority order. Trending is fetched alone first, so a pool it fills by itself still makes no discovery call.
  2. The in-library probe is one Seerr status call per title and ran over every candidate in the pool. It now runs for the ranking head only: exploration rails probe rank_exploration’s exact pickable fringe (an exact bound), ranked rails probe the top AVAILABILITY_SCAN_LIMIT candidates by base score. The boost is worth GAMMA_AVAILABILITY (~0.1) of score, so the far tail cannot reach the rail.

Measured in an isolated container against a copy of the live database (fresh caches, same data):

  • /api/v1/home cold 3.98s -> 1.47s; warm unchanged at 0.15s
  • Picks You Wouldn’t Usually Watch 1.97s -> 0.55s
  • Recommended for You 1.68s -> 0.79s
  • availability probes 3.15s over 5 calls -> 0.86s over 2 calls

Tests: backend 100 passed in the touched suites (5 new cases cover the batching, the priority order and the bounded probe); ruff check/format clean.

Cold /api/v1/home took ~4s on the live instance. Two costs dominated it: 1. **Discovery sources were awaited one at a time.** A cold upstream cache paid one round-trip per source (trending, popularity discovers, release-date discover, then genre discovers) until the 240-title pool filled. They are now fetched in bounded concurrent batches (fanout 6) while the pool is still consumed in strict priority order. Trending is fetched alone first, so a pool it fills by itself still makes no discovery call. 2. **The in-library probe is one Seerr status call per title and ran over every candidate in the pool.** It now runs for the ranking head only: exploration rails probe rank_exploration’s exact pickable fringe (an exact bound), ranked rails probe the top `AVAILABILITY_SCAN_LIMIT` candidates by base score. The boost is worth GAMMA_AVAILABILITY (~0.1) of score, so the far tail cannot reach the rail. Measured in an isolated container against a copy of the live database (fresh caches, same data): - /api/v1/home cold **3.98s -> 1.47s**; warm unchanged at 0.15s - Picks You Wouldn’t Usually Watch 1.97s -> 0.55s - Recommended for You 1.68s -> 0.79s - availability probes 3.15s over 5 calls -> 0.86s over 2 calls Tests: backend 100 passed in the touched suites (5 new cases cover the batching, the priority order and the bounded probe); ruff check/format clean.
perf(home): cut cold home-feed latency from 3.9s to 1.5s
Some checks failed
gate / check (pull_request) Failing after 19s
gate / e2e (pull_request) Successful in 33s
gate / container-smoke (pull_request) Successful in 27s
security / advisories (pull_request) Successful in 14s
f99c6089dc
Two costs dominated a cold /api/v1/home request:

- Discovery sources were awaited one at a time, so a cold upstream cache paid
  one round-trip per source. Sources are now fetched in bounded concurrent
  batches while the pool is still consumed in strict priority order (trending
  first, then popularity/release discovers, then genre discovers). Trending is
  fetched on its own first, so a pool it fills alone still makes no discovery
  call.
- The in-library probe is one Seerr status call per title, and it ran over
  every candidate in the 240-title pool. It now runs for the ranking head only:
  unexplored rails probe rank_exploration's exact pickable fringe, ranked rails
  probe the top AVAILABILITY_SCAN_LIMIT candidates by base score (the boost is
  worth GAMMA_AVAILABILITY ~= 0.1, so the far tail cannot reach the rail).

Measured in an isolated container against a copy of the live database:
cold /api/v1/home 3.98s -> 1.47s, "Picks You Wouldn't Usually Watch" 1.97s ->
0.55s, "Recommended for You" 1.68s -> 0.79s, availability probes 3.15s across
5 calls -> 0.86s across 2 calls; warm requests stay at 0.15s.
techfather force-pushed perf/home-feed-latency from f99c6089dc
Some checks failed
gate / check (pull_request) Failing after 19s
gate / e2e (pull_request) Successful in 33s
gate / container-smoke (pull_request) Successful in 27s
security / advisories (pull_request) Successful in 14s
to 89d516124a
All checks were successful
gate / e2e (pull_request) Successful in 29s
gate / container-smoke (pull_request) Successful in 31s
security / advisories (pull_request) Successful in 15s
gate / check (pull_request) Successful in 2m16s
2026-10-18 13:47:47 -07:00
Compare
forgejo-review-bot left a comment

Local automated review (forgejo-autocheck).

Required checks are green on 89d516124a: 3/3.

  • gate / e2e (pull_request): success
  • gate / container-smoke (pull_request): success
  • security / advisories (pull_request): success
  • gate / check (pull_request): success

Detected stack: repo, python, container.
Approving so the change is not blocked on a second human reviewer.

Local automated review (forgejo-autocheck). Required checks are green on 89d516124a: 3/3. - gate / e2e (pull_request): success - gate / container-smoke (pull_request): success - security / advisories (pull_request): success - gate / check (pull_request): success Detected stack: repo, python, container. Approving so the change is not blocked on a second human reviewer.
Merge branch 'main' into perf/home-feed-latency
All checks were successful
gate / e2e (pull_request) Successful in 27s
gate / container-smoke (pull_request) Successful in 26s
security / advisories (pull_request) Successful in 14s
gate / check (pull_request) Successful in 2m1s
fe2edf03ee
techfather referenced this pull request from a commit 2026-10-18 14:01:57 -07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
techfather/tasterr!8
No description provided.