feat(hub): resume interrupted model downloads via HTTP Range (Node)#1715
Draft
renezander030 wants to merge 1 commit into
Draft
feat(hub): resume interrupted model downloads via HTTP Range (Node)#1715renezander030 wants to merge 1 commit into
renezander030 wants to merge 1 commit into
Conversation
FileCache now streams large downloads to a deterministic <key>.incomplete file plus an etag/size sidecar, and keeps the partial on failure instead of deleting it. On the next attempt the Node streaming path (return_path) asks getResumeInfo for the offset and sends Range + If-Range: the server appends via 206, or restarts cleanly via 200 if the file changed upstream. A per-key lock preserves the previous concurrent-writer guarantee by falling back to the legacy unique-temp path when the partial is held. Adds FileCache unit tests for resume, truncation, and restart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the discussion in #1220 (thanks @emojiiii for handing it over, and @sroussey for the Xet/CDC pointer).
This is step 1: HTTP Range-resume for the Node file-cache path, which is where large model weights are streamed to disk. It ships resume for how downloads work today, with no new transport dependency. Xet-native chunk caching (via the huggingface.js download path) is the natural follow-up and is intentionally out of scope here.
What changes
FileCachestreams large downloads to a deterministic<key>.incompletefile plus a small sidecar ({ etag, total }), and keeps the partial on failure instead of deleting it.getResumeInfo(request)reports{ size, etag, total }when a consistent partial exists.put()branches on the response status: 206 appends to the partial (offset/total parsed fromContent-Range); 200 restarts cleanly (truncates any stale partial). A truncated body (loaded < Content-Length) is rejected and the partial retained for the next attempt.put()falls back to the original unique-temp path (correct, non-resumable, never corrupts the shared partial). Stale locks are stolen after a TTL.hub.jsonly sendsRange/If-Rangeon the streaming path (IS_NODE_ENV && return_path), since a 206 on the buffered path would yield only the trailing bytes. It now accepts206alongside200and caches both.If-Range: <etag>makes the server fall back to a full 200 if the file changed upstream.CacheInterfacegains an optionalgetResumeInfoso custom caches can opt in.Scope / notes
Tests
New
tests/utils/file_cache.test.jscovers: no-partial, full 200 (no leftovers), truncated body (partial + sidecar retained,getResumeInfocorrect), 206 append to completion, and 200 restart over a stale partial. Existingcache/custom_cachesuites still pass; typecheck, prettier, and build are green.Closes #1220
Opening as a draft to get maintainer direction on the Xet question in #1220 before polishing. Happy to adjust.