fix(worker): scale slskd download backstop to album size (stop abandoning big albums)
The absolute download backstop was a flat _MAX_XFER_POLLS=1200 (~60min). A large album (e.g. Drake "Scorpion", 521MB / 25 FLACs) from a slow-but-healthy serial peer (~400KB/s, serving one file at a time) needs ~60-70min, so it was cut off at 60min and — because an abandoned transfer is cancelled and re-enqueued from scratch — re-downloaded from zero every attempt, never finishing and eventually going needs_attention at the attempt cap. Replace the flat cap with _max_polls(total_bytes): budget the backstop at a conservative floor throughput (~64KB/s), clamped to [20min, 2h]. Scorpion now gets the full 2h ceiling instead of 60min. The 90s stall timeout is unchanged, so a truly dead/queued peer is still abandoned fast. Follow-up (not in this change): resume across retries (skip files slskd already completed) so an abandoned large download doesn't restart from zero. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,25 @@ _DEFAULT_DOWNLOADS_ROOT = "/slskd-downloads"
|
||||
_AUDIO_EXT = {"flac", "mp3", "m4a", "ogg", "opus", "wav", "aac"}
|
||||
_LOSSLESS_EXT = {"flac", "wav"}
|
||||
_SEARCH_POLLS = 40 # * 3s ≈ 2 min
|
||||
_MAX_XFER_POLLS = 1200 # * 3s ≈ 60 min absolute backstop for a slow-but-progressing peer
|
||||
_STALL_POLLS = 30 # * 3s ≈ 90s of no byte progress → abandon a queued/dead peer and fall through
|
||||
_POLL_SECONDS = 3
|
||||
# Absolute backstop for a slow-but-progressing peer. Scaled to the album's total bytes (a flat
|
||||
# cap starved big albums: a 500MB set from a ~400KB/s serial peer needs ~60-70min and was cut at
|
||||
# a flat 60min, then re-downloaded from scratch — never finishing). Budget at a conservative floor
|
||||
# throughput, clamped. The 90s stall timeout still abandons a truly dead/queued peer quickly.
|
||||
_MIN_XFER_POLLS = 400 # * 3s ≈ 20 min floor (small albums)
|
||||
_MAX_XFER_POLLS = 2400 # * 3s ≈ 2 h ceiling (bounds worst-case slot hold)
|
||||
_BACKSTOP_FLOOR_BPS = 64_000 # budget the backstop assuming >= ~64 KB/s sustained
|
||||
|
||||
|
||||
def _max_polls(total_bytes: int) -> int:
|
||||
"""Poll budget (each _POLL_SECONDS) for the absolute download backstop, scaled to album size at
|
||||
a conservative floor throughput so a large slow-but-healthy transfer isn't abandoned mid-flight.
|
||||
Clamped to [_MIN_XFER_POLLS, _MAX_XFER_POLLS]. Pure — unit-tested."""
|
||||
if total_bytes <= 0:
|
||||
return _MIN_XFER_POLLS
|
||||
budget = int(total_bytes / (_BACKSTOP_FLOOR_BPS * _POLL_SECONDS))
|
||||
return max(_MIN_XFER_POLLS, min(budget, _MAX_XFER_POLLS))
|
||||
|
||||
|
||||
def _basename(path: str) -> str:
|
||||
@@ -242,7 +258,7 @@ class SlskdClient:
|
||||
speed = 0.0 # bytes/sec, EWMA-smoothed so the ETA doesn't jitter poll to poll
|
||||
prev_bytes = 0
|
||||
prev_t = time.monotonic()
|
||||
for _ in range(_MAX_XFER_POLLS):
|
||||
for _ in range(_max_polls(total_bytes)):
|
||||
time.sleep(_POLL_SECONDS)
|
||||
data = self._get(f"/api/v0/transfers/downloads/{username}")
|
||||
states: list[str] = []
|
||||
|
||||
Reference in New Issue
Block a user