soulsync/services
Broque Thomas 871feb3997 Speed up playlist sync with a lazy per-artist track pool
Syncing a playlist where most tracks weren't in the library was burning
~30 SQL queries per missed track. `services/sync_service.py` walked
each Spotify track through `check_track_exists` with no
`candidate_tracks`, hitting the legacy title-variation × artist-variation
grid in `database/music_database.py:6041-6069` for every miss. The
`sync_match_cache` only covered matches, so misses re-paid the full
lookup cost every sync. A 30-track playlist with a 30% match rate
(Discover Weekly was 9/30 in the test run) was taking ~4m14s, almost
entirely in the matching phase.

`check_track_exists` already accepts a `candidate_tracks` kwarg that
skips the SQL widening and scores against an in-memory list (the
batched path at `music_database.py:6031`, originally added for artist
discography iteration). The sync service just wasn't using it.

This commit wires that path in via a lazy per-artist pool:

- `sync_playlist` creates an empty `candidate_pool` dict and passes it
  to each `_find_track_in_media_server` call.
- `_get_or_fetch_artist_candidates` runs SQL for an artist only on the
  first track that needs them — playlists where every track is already
  in `sync_match_cache` pay zero pool cost (no upfront delay).
- Subsequent misses for the same artist hit the memoized list and skip
  the per-variation SQL grid.
- Artists with no library tracks still get a cached empty list, which
  triggers the batched path's instant short-circuit instead of falling
  into the SQL widening.
- Any pool fetch failure returns None so the caller falls through to
  the original per-track SQL loop, so the worst case is the old
  behavior, never a regression.

On a 30-track / 25-unique-artist playlist with a cold cache the SQL
fan-out drops from ~900 queries to ~25; with a warm cache it drops to
zero (no pool fetches at all). Applies to every entry point that goes
through `sync_playlist`: manual sync, auto-sync schedules, the
`playlist_pipeline` automation action, and the Sync All button.
2026-05-24 22:43:32 -07:00
..
sync_service.py Speed up playlist sync with a lazy per-artist track pool 2026-05-24 22:43:32 -07:00