Commit c8556ba
feat(for-you): re-introduce /users/{id}/feed/for-you with lean 3-source pipeline (#817)
## Summary
Brings back `GET /v1/users/{id}/feed/for-you` (removed in #807) with a
leaner pipeline that drops the source that was causing the prod
timeouts.
The killer in the original was the `similar_artists` CTE — a 1-hop
saves-graph self-join that produced a 301M-row merge for power users.
Dropped entirely. The lean pipeline keeps the other three sources:
| Source | What it pulls | Cap |
|---|---|---|
| `in_network` | Tracks uploaded in the last 14 days by users I follow |
200 |
| `trending` | Top week-trending from `track_trending_scores` | 100 |
| `underground` | Week-trending tracks whose owner has < 1500 follower &
following count | 50 |
Same ranking formula as before:
```
recency_score = exp(-ln(2) * age_hours / 48)
engagement_score = ln(1 + 3*saves + 2*reposts + 1*plays) / 12
social_boost = 1.0 + least(affinity/4, 1.0)
source_weight = {in_network: 1.20, trending: 1.00, underground: 0.95}
final_score = (0.55*recency + 0.45*engagement) * social_boost * source_weight
```
Same Go-side diversity pass (per-artist `ROW_NUMBER()` cap + 5-position
lookahead to break consecutive-same-artist runs).
## Perf guardrails
Retained from #805 / #806:
- `follow_set` capped at 500 most-recently-followed.
- `my_artist_affinity` sub-selects capped (200 saves / 200 reposts / 500
plays, all by recency).
Additional:
- `my_artist_affinity` inner `JOIN tracks` is now further restricted to
tracks created in the last 90 days — old uploads can't pull the CTE
wide.
- New partial index `idx_track_trending_scores_for_you` on
`track_trending_scores (score DESC, track_id)` covering the `TRACKS /
pnagD / week / null-genre` slice. Without it, EXPLAIN showed a fixed
~12s scan of `track_trending_scores` for every request, regardless of
caller.
## Files
| File | What |
|---|---|
|
[`api/v1_users_feed_for_you.go`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/api/v1_users_feed_for_you.go)
| Handler + the 3-source candidate-pool SQL (no similar_artists) |
|
[`api/v1_users_feed_for_you_test.go`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/api/v1_users_feed_for_you_test.go)
| Basic tests: valid user_id required, empty feed for new user,
pagination |
|
[`api/server.go`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/api/server.go)
| Route re-registration |
|
[`api/auth_middleware.go`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/api/auth_middleware.go)
| Re-add the `/feed/for-you` exemption (query `user_id` is advisory;
path `:userId` controls personalization) |
|
[`api/swagger/swagger-v1.yaml`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/api/swagger/swagger-v1.yaml)
| Re-add the endpoint spec |
|
[`ddl/migrations/0198_track_trending_scores_for_you_idx.sql`](https://github.com/AudiusProject/api/blob/claude/eager-wilson-e22923/ddl/migrations/0198_track_trending_scores_for_you_idx.sql)
| The missing partial index |
## Test plan
- [x] `go build ./api/...` clean
- [x] `go vet ./api/...` clean
- [x] `go test -c ./api/...` compiles
- [ ] After deploy: hit
`/v1/users/{id}/feed/for-you?user_id={id}&limit=5` for a power-user
account that previously timed out — should now return 200 in < 2s.
- [ ] Eyeball the mix of in-network vs trending vs underground on a real
account.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent f7b9678 commit c8556ba
6 files changed
Lines changed: 660 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
347 | 347 | | |
348 | 348 | | |
349 | 349 | | |
350 | | - | |
351 | | - | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
352 | 360 | | |
353 | 361 | | |
354 | 362 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
454 | 454 | | |
455 | 455 | | |
456 | 456 | | |
| 457 | + | |
457 | 458 | | |
458 | 459 | | |
459 | 460 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9475 | 9475 | | |
9476 | 9476 | | |
9477 | 9477 | | |
| 9478 | + | |
| 9479 | + | |
| 9480 | + | |
| 9481 | + | |
| 9482 | + | |
| 9483 | + | |
| 9484 | + | |
| 9485 | + | |
| 9486 | + | |
| 9487 | + | |
| 9488 | + | |
| 9489 | + | |
| 9490 | + | |
| 9491 | + | |
| 9492 | + | |
| 9493 | + | |
| 9494 | + | |
| 9495 | + | |
| 9496 | + | |
| 9497 | + | |
| 9498 | + | |
| 9499 | + | |
| 9500 | + | |
| 9501 | + | |
| 9502 | + | |
| 9503 | + | |
| 9504 | + | |
| 9505 | + | |
| 9506 | + | |
| 9507 | + | |
| 9508 | + | |
| 9509 | + | |
| 9510 | + | |
| 9511 | + | |
| 9512 | + | |
| 9513 | + | |
| 9514 | + | |
| 9515 | + | |
| 9516 | + | |
| 9517 | + | |
| 9518 | + | |
| 9519 | + | |
| 9520 | + | |
| 9521 | + | |
| 9522 | + | |
| 9523 | + | |
| 9524 | + | |
| 9525 | + | |
| 9526 | + | |
| 9527 | + | |
| 9528 | + | |
| 9529 | + | |
| 9530 | + | |
| 9531 | + | |
| 9532 | + | |
| 9533 | + | |
| 9534 | + | |
| 9535 | + | |
| 9536 | + | |
| 9537 | + | |
| 9538 | + | |
| 9539 | + | |
| 9540 | + | |
| 9541 | + | |
| 9542 | + | |
| 9543 | + | |
| 9544 | + | |
| 9545 | + | |
| 9546 | + | |
9478 | 9547 | | |
9479 | 9548 | | |
9480 | 9549 | | |
| |||
0 commit comments