feat: enhance model loading with prefetching and retry logic

- Introduced `from_pretrained_with_prefetch` to handle racy cache scenarios by re-prefetching and retrying on failures.
- Updated various model pipelines to utilize the new prefetching mechanism, ensuring robust loading of model components.
- Prefetch logic added to multiple models, including HunyuanVideo, LongCatImage, and QwenImage, to mitigate issues with incomplete cache states.
This commit is contained in:
tzhouam committed 2026-06-02 09:01:47 +00:00
1 parent b550709bba
commit 617c6ca55f
20 files changed
+678 -194

No files matched your search

+148 -33
View File
@@ -65,10 +65,22 @@ import errno
import logging
import os
import time
from collections.abc import Iterable, Iterator
from collections.abc import Callable, Iterable, Iterator
from typing import Any
logger = logging.getLogger(__name__)
# A racy / partially-evicted HF cache (the exact failure this module defends
# against) is transient: re-running ``snapshot_download`` blocks on the peer
# writer's per-blob ``.lock`` and then returns a complete tree. So a bounded
# retry with linear backoff is what actually closes the window that a single
# best-effort attempt left open (Buildkite vllm-omni-rebase #1858: both the
# ``cuda_ti2v_hsdp`` missing-shard ``OSError`` and the ``wan_2_1_vace`` default
# ``UMT5Config`` size-mismatch were a swallowed prefetch followed by a
# ``from_pretrained`` against the half-written cache).
_PREFETCH_MAX_ATTEMPTS = 3
_PREFETCH_BACKOFF_BASE_S = 1.0
def _node_lock_dir() -> str:
"""Return a node-local directory suitable for the prefetch lock files.
@@ -276,7 +288,11 @@ def prefetch_subfolders(
if local_files_only or not model or os.path.isdir(model):
return
logger.info("Prefetching %s subfolders: %s", model, list(subfolders))
# Materialise ``subfolders`` up-front: it may be a one-shot generator and
# we reference it again in the retry / logging paths below.
subfolders = list(subfolders)
logger.info("Prefetching %s subfolders: %s", model, subfolders)
try:
from huggingface_hub import snapshot_download
@@ -306,39 +322,66 @@ def prefetch_subfolders(
# snapshot is now warm. This is what makes the prefetch race-free
# even when many ``DiffusionWorker`` subprocesses (or multiple
# OmniServer instances on the same node) hit this code in parallel.
try:
with _repo_prefetch_lock(model):
snapshot_download(
repo_id=model,
allow_patterns=allow_patterns,
)
logger.info("Prefetch complete for %s", model)
except Exception as exc:
# Best-effort: propagate only via logging. The subsequent
# ``from_pretrained`` call will raise a clearer, call-site-specific
# error (auth, 404, disk full, ...) that we'd rather surface - EXCEPT
# for auth/gating, which we escalate here with an explicit hint so
# readers of CI logs don't have to correlate the generic "OSError:
# <repo> does not appear to have a file named ..." that
# ``from_pretrained`` would otherwise emit much later with an
# unrelated-looking message.
if _looks_like_auth_error(exc):
logger.error(
"Hub prefetch for '%s' failed with an authentication / gated "
"repository error (%s: %s). The CI HF_TOKEN must (1) be set "
"in the step env, (2) be valid, and (3) belong to an account "
"that has accepted the model license on huggingface.co. See "
"docs/contributing/ci/hf_credentials.md.",
model,
type(exc).__name__,
exc,
)
else:
# A single best-effort attempt is not enough: when several diffusion
# workers race a cold cache (and the node-wide lock fails to serialise
# them, as observed for HSDP / ring launches), ``snapshot_download`` can
# raise on a half-written tree. Swallowing that and proceeding straight
# to ``from_pretrained`` is exactly what turned a recoverable prefetch
# hiccup into a hard server crash. Retry with backoff so the snapshot
# actually completes before any loader reads the cache.
for attempt in range(1, _PREFETCH_MAX_ATTEMPTS + 1):
try:
with _repo_prefetch_lock(model):
snapshot_download(
repo_id=model,
allow_patterns=allow_patterns,
)
logger.info("Prefetch complete for %s", model)
return
except Exception as exc:
# Auth / gating never heals on retry - escalate immediately with
# an explicit hint so readers of CI logs don't have to correlate
# the generic "OSError: <repo> does not appear to have a file
# named ..." that ``from_pretrained`` would otherwise emit later.
if _looks_like_auth_error(exc):
logger.error(
"Hub prefetch for '%s' failed with an authentication / gated "
"repository error (%s: %s). The CI HF_TOKEN must (1) be set "
"in the step env, (2) be valid, and (3) belong to an account "
"that has accepted the model license on huggingface.co. See "
"docs/contributing/ci/hf_credentials.md.",
model,
type(exc).__name__,
exc,
)
return
if attempt < _PREFETCH_MAX_ATTEMPTS:
backoff = _PREFETCH_BACKOFF_BASE_S * attempt
logger.warning(
"Hub prefetch for repo '%s' subfolders %s failed on attempt "
"%d/%d (%s: %s); retrying in %.1fs",
model,
subfolders,
attempt,
_PREFETCH_MAX_ATTEMPTS,
type(exc).__name__,
exc,
backoff,
)
time.sleep(backoff)
continue
# Exhausted retries. Stay best-effort: propagate only via logging
# so the subsequent ``from_pretrained`` call surfaces the real,
# call-site-specific error (and ``from_pretrained_with_prefetch``
# gets a final chance to heal the cache).
logger.warning(
"Hub prefetch for repo '%s' subfolders %s failed (%s: %s); "
"falling back to on-demand download in from_pretrained",
"Hub prefetch for repo '%s' subfolders %s failed after %d attempts "
"(%s: %s); falling back to on-demand download in from_pretrained",
model,
list(subfolders),
subfolders,
_PREFETCH_MAX_ATTEMPTS,
type(exc).__name__,
exc,
)
@@ -376,6 +419,78 @@ def _looks_like_auth_error(exc: BaseException) -> bool:
return "401 client error" in msg or "403 client error" in msg or "gatedrepo" in msg
def from_pretrained_with_prefetch(
factory: Callable[..., Any],
model: str,
*,
subfolder: str,
prefetch_list: Iterable[str],
local_files_only: bool = False,
max_attempts: int = _PREFETCH_MAX_ATTEMPTS,
**from_pretrained_kwargs: Any,
) -> Any:
"""Call ``factory.from_pretrained`` healing a racy / partial HF cache.
``factory`` is a bound ``SomeModel.from_pretrained`` (or any callable with
the same ``(model, *, subfolder, local_files_only, **kwargs)`` signature).
This is a stronger sibling of :func:`retry_on_missing_shard`: that helper
only retries the missing-shard ``OSError`` and never re-prefetches, so it
cannot recover the second face of the same race. Two shapes of partial
-cache failure crash the diffusion server outright:
* ``OSError: <repo> does not appear to have a file named
text_encoder/model-0000X-of-0000Y.safetensors`` - a shard is still under
its ``.incomplete`` name.
* ``RuntimeError: You set 'ignore_mismatched_sizes' to 'False' ...`` -
``text_encoder/config.json`` was not present yet, so ``transformers`` v5
silently fell back to the default (tiny) config and then could not load
the real checkpoint into it.
Both heal once the cache is complete. So on those errors we re-run a
*verified* prefetch (which blocks on the peer writer and retries the
download) and reload, instead of letting the worker die. Local paths and
``local_files_only`` loads cannot be healed by re-fetching, so they raise
on the first failure exactly as before.
"""
prefetch_list = list(prefetch_list)
can_heal = not local_files_only and bool(model) and not os.path.isdir(model)
last_exc: BaseException | None = None
for attempt in range(1, max_attempts + 1):
try:
return factory(
model,
subfolder=subfolder,
local_files_only=local_files_only,
**from_pretrained_kwargs,
)
except (OSError, RuntimeError, ValueError) as exc:
last_exc = exc
if not can_heal or attempt >= max_attempts:
break
backoff = _PREFETCH_BACKOFF_BASE_S * attempt
logger.warning(
"from_pretrained(%s, subfolder=%s) failed on attempt %d/%d "
"(%s: %s); re-prefetching repo and retrying in %.1fs",
model,
subfolder,
attempt,
max_attempts,
type(exc).__name__,
exc,
backoff,
)
time.sleep(backoff)
# Force a fresh, verified snapshot of every component this pipeline
# needs - not just ``subfolder`` - so a sibling component that was
# also half-written gets repaired in the same pass.
prefetch_subfolders(model, prefetch_list, local_files_only=False)
assert last_exc is not None # loop only exits via return or a caught exc
raise last_exc
def retry_on_missing_shard(load_fn, *, max_retries: int = 3, base_delay: float = 5.0):
"""Call *load_fn* with retry on the transformers v5 shard-resolution race.