osdc/hf-cache: fix prepare-host-mount init crash-loop on shadowed /mnt/hf_cache#941
Draft
huydhn wants to merge 1 commit into
Draft
osdc/hf-cache: fix prepare-host-mount init crash-loop on shadowed /mnt/hf_cache#941huydhn wants to merge 1 commit into
huydhn wants to merge 1 commit into
Conversation
…t/hf_cache pytorch#876's self-heal init can crash-loop instead of healing, leaving the cache mount silently absent on a node whose startup taint is already cleared — so CI jobs land on the node and read an empty /mnt/hf_cache (observed on 20 ue2 nodes, 29 job pods, incl. a failed pytorch inductor job). Root cause, all in the prepare-host-mount init: - `mount --bind /mnt /mnt` is non-recursive, so re-binding /mnt shadows a pre-existing /mnt/hf_cache submount (e.g. one left by an earlier init version that bound /mnt/hf_cache directly on the root fs). - the guard `grep -q " /mnt/hf_cache " /proc/mounts` matches that shadowed, now-unreachable mount, so the re-bind is skipped. - `mount --make-rshared /mnt/hf_cache` then runs on a live path that is not a mountpoint -> "not mount point or bad option" -> `set -e` -> the init exits non-zero and CrashLoopBackOffs forever (80+ restarts seen). Fix: - guard on the LIVE path with `mountpoint -q`, never a /proc/mounts scan, so a shadowed orphan can't make us skip the bind. - use `mount --rbind` for /mnt so an existing submount is carried into the new bind instead of being shadowed. - tolerate `make-rshared` failures (|| true) so a transient state can't turn into a crash-loop. - peel any occupant of the live /mnt/hf_cache (dead FUSE or stale xfs bind) with a bounded loop, not one gated on `stat` (a stale bind stats fine). just lint + just test pass. Smoke test's `make-rshared` assertion still holds.
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.
Problem
#876's self-heal init (
prepare-host-mount) can crash-loop instead of healing, leaving the rclone cache mount silently absent on a node whosenode-init.osdc.io/hf-cachestartup taint has already been cleared by an earlier healthy incarnation. Nothing re-adds the taint, so CI job pods keep landing on the node and read an empty/mnt/hf_cache.Observed on meta-prod-aws-ue2: 20 hf-cache mounts wedged (init
CrashLoopBackOff, 70–92 restarts ≈ 6h), 29 job/workflow pods running on those broken-cache nodes — including a failed pytorch inductor job that read the empty cache instead of the model bucket.Root cause (all in the
prepare-host-mountinit)mount --bind /mnt /mntis non-recursive, so re-binding/mntshadows a pre-existing/mnt/hf_cachesubmount (e.g. one created by an earlier init version that bound/mnt/hf_cachedirectly on the root fs). The shadowed mount becomes an unreachable orphan (parent = root fs, hidden under the/mntbind).grep -q " /mnt/hf_cache " /proc/mountsmatches that shadowed orphan (a raw table scan sees hidden mounts), so the re-bind is skipped.mount --make-rshared /mnt/hf_cachethen runs on a live path that is not a mountpoint →not mount point or bad option→set -e→ the init exits non-zero →CrashLoopBackOffforever.Because the init never completes, the rclone container never starts, so
/mnt/hf_cacheis just a bare dir. Verified on the host: the stale mount was anxfsself-bind (/dev/nvme0n1p1), andumount/make-rsharedby path failed because the orphan had no reachable live path.Fix
mountpoint -q, never a/proc/mountsscan — a shadowed orphan can no longer make us skip the bind.mount --rbindfor/mntso an existing submount is carried into the new bind instead of being shadowed.make-rsharedfailures (|| true) so a transient state can't become a crash-loop./mnt/hf_cache(dead FUSE or stale xfs bind) with a bounded loop, not one gated onstat(a stale bind stats fine, so the old loop missed it).Testing
just lint(all 13) ✅ andjust test(98.78% cov) ✅make-rsharedassertion still holds.Follow-up (separate PR)
Fail-closed taint: have the init re-add
node-init.osdc.io/hf-cacheon start and only let the taint-remover clear it once this incarnation's rclone is confirmed mounted, so a future unhealthy mount keeps jobs off the node instead of relying on the mount always recovering. (Requires an add-taint mode intaint_remover.py.)