From 4513a285e8027d475bbc0a5404019e97cc402d83 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 12 May 2026 20:44:34 -0700 Subject: [PATCH] perf(for-you): cap my_saved_artists to 200 most-recent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The similar_artists CTE in /v1/users/{id}/feed/for-you self-joins saves against my_saved_artists. For long-tenure users with thousands of saved artists, my_saved_artists explodes and the planner times out (observed: prod request hangs >60s for users with deep save history). Replace the unbounded DISTINCT with a GROUP BY on owner_id ordered by the most-recent save_at, capped at 200. Recency is the right axis — old saves are a weaker signal of current taste anyway — and 200 artists still gives the collaborative-filter step enough surface to find similar-artists candidates. The shape of the CTE (column `artist_id`) is preserved, so downstream consumers `t1.owner_id IN (SELECT artist_id FROM my_saved_artists)` and the `NOT IN` exclusion in similar_artists are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- api/v1_users_feed_for_you.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/api/v1_users_feed_for_you.go b/api/v1_users_feed_for_you.go index e378d811..1e3140b1 100644 --- a/api/v1_users_feed_for_you.go +++ b/api/v1_users_feed_for_you.go @@ -115,10 +115,23 @@ func (app *ApiServer) v1UsersFeedForYou(c *fiber.Ctx) error { AND is_current = true AND is_delete = false ), + -- The set of artists I save anchors the collaborative-filter join + -- below. For long-tenure power users with thousands of saved artists + -- the saves self-join explodes and the planner times out, so we cap + -- to the 200 most recently saved artists. Recency is the right axis: + -- old saves are weaker signal of current taste anyway, and a 200-artist + -- anchor still gives the similar-artists CTE enough to work with. my_saved_artists AS ( - SELECT DISTINCT t.owner_id AS artist_id - FROM my_saved_tracks mst - JOIN tracks t ON t.track_id = mst.track_id + SELECT t.owner_id AS artist_id, MAX(s.created_at) AS last_saved_at + FROM saves s + JOIN tracks t ON t.track_id = s.save_item_id + WHERE s.user_id = @userId + AND s.save_type = 'track' + AND s.is_current = true + AND s.is_delete = false + GROUP BY t.owner_id + ORDER BY last_saved_at DESC + LIMIT 200 ), -- 1-hop collaborative filter on the saves graph: artists saved by -- users who *also* save my saved-artists, but who I haven't saved myself.