fix(worker): parallelize per-source searches in the match phase
Soulseek's slskd search polls up to ~2 min; searching adapters sequentially added that on top of Qobuz's instant search. Run the per-adapter searches concurrently via a ThreadPoolExecutor so total match time is the slowest single source, not their sum. Only Qobuz drives streamrip's shared asyncio loop and there's one Qobuz adapter, so no two threads use that loop at once. Results collected in adapter order (map preserves order) for deterministic candidate ordering; per-adapter failures still swallowed (down source = no candidates). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -195,6 +195,33 @@ def test_below_threshold_candidates_are_persisted_for_diagnosis(conn):
|
||||
assert cur.fetchone()[0] >= 1 # found-but-rejected candidate retained for diagnosis
|
||||
|
||||
|
||||
def test_searches_run_concurrently_across_adapters(conn):
|
||||
# Two sources that each take ~0.5s to search must run in parallel: total match time is ~the
|
||||
# slowest single search (~0.5s), not the sum (~1.0s). Also verifies concurrency drops no
|
||||
# candidates — both sources' contributions are persisted.
|
||||
import time as _t
|
||||
|
||||
class _SlowQobuz(FakeQobuz):
|
||||
def search(self, target):
|
||||
_t.sleep(0.5)
|
||||
return super().search(target)
|
||||
|
||||
class _SlowSoulseek(FakeSoulseek):
|
||||
def search(self, target):
|
||||
_t.sleep(0.5)
|
||||
return super().search(target)
|
||||
|
||||
job_id = insert_request(conn, artist="Radiohead", album="In Rainbows")
|
||||
claim_next(conn)
|
||||
start = _t.monotonic()
|
||||
run_pipeline(conn, job_id, [_SlowQobuz(), _SlowSoulseek()], dest_root="/tmp/lib")
|
||||
elapsed = _t.monotonic() - start
|
||||
assert elapsed < 0.9 # concurrent (~0.5s), not sequential (~1.0s)
|
||||
with conn.cursor() as cur:
|
||||
cur.execute('SELECT count(DISTINCT source) FROM "Candidate" WHERE "jobId" = %s', (job_id,))
|
||||
assert cur.fetchone()[0] == 2 # both adapters contributed candidates
|
||||
|
||||
|
||||
def test_duplicate_adapter_names_raise(conn):
|
||||
import pytest
|
||||
from lyra_worker.adapters.fakes import FakeQobuz
|
||||
|
||||
Reference in New Issue
Block a user