fix: the taste arm round-robins across taste artists, and tag ties follow the order seed (#3880)
release / govulncheck (push) Successful in 31s
release / web (push) Successful in 1m37s
release / go (push) Successful in 1m46s
release / integration (push) Successful in 5m20s
release / android (push) Successful in 5m22s
release / Build signed APK (releases and dev) (push) Successful in 5m39s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m29s
release / Verify release artifacts (tag releases only) (push) Skipped

Measured on the operator's library (26,005 tracks, pool scale 2.28): the
arms the library scale grows all have far more rows than it asks for, and
two arms collapsed whatever their limits were.

- taste_overlap ordered by weight then track id, so the strongest taste
  artist's catalogue filled it: 20 tracks of saib. (197 tracks) out of 174
  taste artists, the same lowest-id tracks every time. It now takes one
  track per taste artist, strongest first, md5-seeded, as similar_artists
  does since #5297.
- tag_overlap broke ties in no order. A one-tag seed ties 7,804 tracks,
  and the LIMIT took whichever Postgres reached first, which the order
  seed did not control. Ties now follow md5(id || seed).
- library_scale.go records the per-arm measurement beside the reference
  size it leaves as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 12:12:58 -04:00
co-authored by Claude Opus 5.5
parent 484a81f779
commit 5bcbc80555
4 changed files with 152 additions and 20 deletions
+25 -9
View File
@@ -138,7 +138,12 @@ tag_overlap AS (
AND t.id <> $2
GROUP BY t.id
HAVING count(DISTINCT trim(g_split.g)) > 0
ORDER BY sim_score DESC
-- md5 breaks the ties (#3880). A seed with one tag gives every track of
-- that genre the same score, 7,804 tracks on the operator's library, and
-- with no tie-break the LIMIT took whichever rows Postgres reached first:
-- the same slice for every seed of the genre, and not one the order seed
-- controls.
ORDER BY sim_score DESC, md5(t.id::text || $12::text)
LIMIT $7
),
likes_overlap AS (
@@ -156,14 +161,25 @@ likes_overlap AS (
LIMIT $8
),
taste_overlap AS (
SELECT t.id AS track_id, 0.0::float8 AS sim_score
FROM taste_profile_artists tpa
JOIN tracks t ON t.artist_id = tpa.artist_id
WHERE tpa.user_id = $1
AND tpa.weight > 0
AND t.id NOT IN (SELECT id FROM excluded_ids)
AND t.id <> $2
ORDER BY tpa.weight DESC, t.id
-- Round-robin across the taste artists (#3880), as similar_artists is
-- (#5297): each artist's first track, strongest artist first, then each
-- one's second. Ordered by weight then track id, the strongest artist's
-- catalogue filled the whole arm with the same lowest-id tracks every
-- time; on the operator's library that was 20 tracks of one artist of
-- 174, whose catalogue is 197 tracks.
SELECT track_id, sim_score
FROM (
SELECT t.id AS track_id, 0.0::float8 AS sim_score, tpa.weight,
row_number() OVER (PARTITION BY tpa.artist_id
ORDER BY md5(t.id::text || $12::text)) AS artist_turn
FROM taste_profile_artists tpa
JOIN tracks t ON t.artist_id = tpa.artist_id AND t.missing_since IS NULL
WHERE tpa.user_id = $1
AND tpa.weight > 0
AND t.id NOT IN (SELECT id FROM excluded_ids)
AND t.id <> $2
) ranked
ORDER BY artist_turn, weight DESC, md5(track_id::text || $12::text)
LIMIT $10
),
coplay_artists AS (