diff --git a/README.md b/README.md index b794857..98b3d24 100644 --- a/README.md +++ b/README.md @@ -561,7 +561,7 @@ stat -c "%h" "/data/media/music// ()/ - .flac" | `MUSE_MIN_MATCH` | `0.45` | filename-to-track similarity floor | | `MUSE_TITLE_FLOOR` | `0.5` | the title alone must clear this before the artist counts | | `MUSE_CANDIDATES` | `8` | ranked candidates kept per request, for fallback | -| `MUSE_MAX_ATTEMPTS` | `5` | peers tried before a request is given up on | +| `MUSE_MAX_ATTEMPTS` | `MUSE_CANDIDATES` | peers tried before a request is given up on | | `MUSE_ATTEMPT_BACKOFF` | `4` | seconds between candidate attempts | | `MUSE_SYSTEMIC_LIMIT` | `2` | consecutive connection failures before stopping | | `MUSE_STALL_MINUTES` | `20` | a transfer moving no bytes for this long is abandoned | diff --git a/app.py b/app.py index 8b82e4c..eab156a 100644 --- a/app.py +++ b/app.py @@ -80,7 +80,14 @@ TITLE_FLOOR = float(os.getenv("MUSE_TITLE_FLOOR", "0.5")) # one is tried instead of failing the request -- "File not shared." is a # per-file condition, so candidate two usually just works. CANDIDATES = int(os.getenv("MUSE_CANDIDATES", "8")) -MAX_ATTEMPTS = int(os.getenv("MUSE_MAX_ATTEMPTS", "5")) + +# Defaults to the size of the ladder, because keeping eight candidates and then +# refusing to try three of them is a cap with no reasoning behind it. Measured +# on a live instance: slskd answers 500 whenever it cannot open a connection to +# a peer, which is common enough that requests routinely need four or five +# attempts. One succeeded on exactly the fifth; another gave up at the cap with +# three candidates still in hand. +MAX_ATTEMPTS = int(os.getenv("MUSE_MAX_ATTEMPTS", str(CANDIDATES))) # A download that has moved no bytes for this long is not going to. The peer # queued it and vanished, or slskd lost the transfer; either way the job used diff --git a/tests/test_acquisition.py b/tests/test_acquisition.py index 7061840..feebae4 100644 --- a/tests/test_acquisition.py +++ b/tests/test_acquisition.py @@ -209,12 +209,18 @@ def main() -> int: # every peer refuses to accept the transfer fake2.enqueue_status = {f"u{i}": 500 for i in range(8)} app.slskd_client = fake2 + # The cap defaults to the ladder size, so lower it here or this only ever + # proves the list ran out -- which is a different branch. + cap, app.MAX_ATTEMPTS = app.MAX_ATTEMPTS, 3 make_job("j2", "Radiohead", "Creep") asyncio.run(app.pick_and_enqueue("j2", "Radiohead", "Creep")) r = job_row("j2") check("gives up at MAX_ATTEMPTS, not at the end of the list", - r["state"] == "failed" and r["attempts"] == app.MAX_ATTEMPTS, + r["state"] == "failed" and r["attempts"] == 3, f"state={r['state']} attempts={r['attempts']}") + check("and leaves the untried remainder alone", + len(json.loads(r["candidates"] or "[]")) > 0, r["candidates"]) + app.MAX_ATTEMPTS = cap check("unreachable peers are reported as such", "could not reach peer" in (r["tried"] or ""), r["detail"])