Commit 276fdeb
perf(for-you): bound my_artist_affinity and follow_set by recency (#806)
## Summary
Builds on #805 (which capped \`my_saved_artists\`). After that fix the
endpoint still times out on prod for power users — the remaining
unbounded CTEs are scanning the user's full history every request:
1. **\`my_artist_affinity\`** unions saves + reposts + plays for the
caller. \`plays\` is the biggest table by far — a heavy listener can
have hundreds of thousands of rows, all scanned on every request. Cap
each source to most recent N: **200 saves, 200 reposts, 500 plays**.
2. **\`follow_set\`** is every user the caller follows; for a power-user
with thousands of follows this becomes a wide hash join against every
recent-track upload. Cap to **500 most-recently followed**.
Recency is the right axis on all three: old engagement is a weak signal
of current taste, and the bounds match the magnitude of the hidden cost
(plays >> saves ≈ reposts).
## Diff (CTEs)
\`\`\`sql
follow_set AS (
SELECT followee_user_id AS user_id FROM follows
WHERE follower_user_id = @userid AND is_current AND NOT is_delete
ORDER BY created_at DESC LIMIT 500 -- new
),
my_artist_affinity AS (
SELECT t.owner_id, LN(1 + COUNT(*)) AS affinity
FROM (
(SELECT save_item_id ... ORDER BY created_at DESC LIMIT 200) -- new
UNION ALL
(SELECT repost_item_id ... ORDER BY created_at DESC LIMIT 200) -- new
UNION ALL
(SELECT play_item_id ... ORDER BY created_at DESC LIMIT 500) -- new
) eng JOIN tracks t ON ... GROUP BY t.owner_id
),
\`\`\`
## Test plan
- ✅ All 9 \`TestV1FeedForYou_*\` tests pass locally. Fixtures have <200
saves/<200 reposts/<500 plays/<500 follows so the caps don't kick in and
observable behavior is unchanged.
- ✅ \`go build ./api/...\` / \`go vet ./api/...\` clean.
- After deploy: \`/v1/users/eYZmn/feed/for-you?user_id=eYZmn&limit=5\`
(notjulian, deep history) — currently times out at the Cloudflare
upstream (>120s). Target: <2s.
## Follow-ups
Parallel EXPLAIN ANALYZE work happening to verify the bound shifts the
cost as expected and to flag any missing indexes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 5e3eb1e commit 276fdeb
1 file changed
Lines changed: 21 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
103 | 108 | | |
104 | 109 | | |
105 | 110 | | |
106 | 111 | | |
107 | 112 | | |
108 | 113 | | |
| 114 | + | |
| 115 | + | |
109 | 116 | | |
110 | 117 | | |
111 | 118 | | |
| |||
157 | 164 | | |
158 | 165 | | |
159 | 166 | | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
160 | 172 | | |
161 | 173 | | |
162 | 174 | | |
163 | 175 | | |
164 | | - | |
| 176 | + | |
165 | 177 | | |
166 | 178 | | |
| 179 | + | |
| 180 | + | |
167 | 181 | | |
168 | | - | |
| 182 | + | |
169 | 183 | | |
170 | 184 | | |
| 185 | + | |
| 186 | + | |
171 | 187 | | |
172 | | - | |
| 188 | + | |
173 | 189 | | |
| 190 | + | |
| 191 | + | |
174 | 192 | | |
175 | 193 | | |
176 | 194 | | |
| |||
0 commit comments