Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions changelog.d/tsk-ilhlue-download-timeout-resume.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### Fixed

- Model downloads no longer hang forever on a stalled connection. The HTTP
transfer in `tinyagentos/download_manager.py` ran with `timeout=None`, which
disables the connect, read, write and pool timeouts together: a Wi-Fi drop, a
NAT table eviction or a CDN edge that stopped sending left the task at
`status="downloading"` with nothing ever erroring, showing a progress bar
frozen part-way with no way to tell it apart from a slow link. It now uses
finite timeouts and retries transport errors and 5xx responses with
exponential backoff, so a single transient failure from a mirror no longer
kills a multi-gigabyte transfer. Expect previously invisible stalls to start
surfacing as errors — that is the fix working.
- Interrupted model downloads resume instead of restarting. Bytes already on
disk are asked for with a `Range` header, so a 40 GB model that fails at
39 GB continues from where it stopped; a server that ignores the header and
answers `200` restarts cleanly rather than appending a second copy.
- A failed model download no longer leaves a corrupt file at the canonical
path, where every later "is this model installed?" existence check would take
it for a real weight. Bytes are staged in a `<dest>.part` file and renamed
onto the destination only after validation passes.
- Finished download tasks are pruned after an hour instead of staying resident
for the lifetime of the process, so `/api/models/downloads` no longer grows
without bound. Pending and downloading tasks are never pruned.
- A re-download of an already-installed model no longer deletes the existing
valid file when the new attempt fails before promoting anything: the
cleanup on failure now only removes `task.dest` when this attempt actually
renamed the `.part` stage file onto it.
9 changes: 9 additions & 0 deletions docs/design/model-torrent-mesh.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ Remaining: DownloadManager passkey fetch via the account-session proxy + clean H
3. If torrent completes: verify SHA256 against manifest, then done.
4. If HTTP completes first (web seed from inside libtorrent, or direct
fallback): still verify SHA256.
- **HTTP path robustness** — the `download_url` fallback is not a bare fetch:
finite connect/read/write/pool timeouts (a half-open connection surfaces as
an error instead of a progress bar frozen at 63% forever), retry with
exponential backoff on transport errors and 5xx, and `Range`-header resume
so a 40 GB pull that dies at 39 GB does not restart from zero. Bytes are
staged in a `<dest>.part` file and renamed onto the canonical path only
after the SHA256 check passes, so a failed download can never leave a
corrupt weight where a later "is this model installed?" check would find it.
The stage file is kept on failure — it is what the next attempt resumes from.
- **Seeding** — after a successful download, the torrent is kept in the
libtorrent session. Seeding runs in the background with user-configurable
upload limits.
Expand Down
Loading
Loading