• Stefy Lanza (nextime / spora )'s avatar
    a busy model is waited for and then evicted, with no exception · 6176ff05
    Stefy Lanza (nextime / spora ) authored
    Measured on zeiss 2026-11-01: eviction freed 12.2 GB of the 18.0 GB needed and
    stopped 1.5 GB short -- "held by a busy model or another process" -- and
    Z-Image-Turbo loaded with CPU/disk offload. The room it needed was not
    unavailable. It was in use, by a request that was about to finish.
    
    Waiting for a model to go idle and then taking its VRAM was already the house
    rule for the active model and the keep-resident set. Ordinary LRU models were
    the exception: pass 1 printed "is busy serving a request — not evicting it" and
    moved on. Three deadlines elsewhere were the same exception in a different
    dress, because a deadline here does not avert a hang -- it converts "this load
    waits a few seconds" into "this load runs on the CPU for its whole life", with
    nothing in the log to say why.
    
    So every pass now waits:
    
        idle LRU      evict (first: it costs nothing)
        busy LRU      wait, then evict      was "not evicting it"
        active model  wait, then evict      was 120s, else leave loaded
        keep-resident wait, then evict      was 120s, else leave loaded
        _evict_one    wait, then evict      was 120s, else "Skipping eviction"
    
    and the give-up branches are deleted rather than left as dead code describing
    an exception that no longer exists. _wait_until_idle defaults to no deadline.
    
    The busy ones are a SECOND sweep rather than inline waits, so a cheap idle
    eviction further down the LRU order never queues behind a wait that may turn
    out to be unnecessary.
    
    Two things keep an unbounded wait from being a hang, and both already held: the
    pass never waits on `protect` -- the key the room is being made FOR, the one
    model that provably cannot go idle because it is blocked on the memory being
    freed -- and a model unloaded under us ends the wait. The wait also repeats
    itself every 15 s, so a genuinely hung generation is legible AS a hung
    generation instead of as an eviction that stopped responding.
    
    Two waits stay bounded, deliberately, and are now asserted as such so neither
    drifts back by accident:
    
    * unload_model, an operator action with a caller waiting on an answer, where
      "not unloaded" is a perfectly good answer. Eviction has no such option.
    * the host-RAM evictor, untouched. Waiting there risks the kernel's OOM killer
      taking the whole engine -- four times in five hours the night that code was
      written. Different resource, different failure mode, separate decision.
    
    test_the_source_never_waits_on_the_protected_model read src[i:i+6000] and
    failed because this function grew past it, while the guarantee it protects was
    intact; it is now bounded by the next method, like the other source tests here.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_014T3rZozi1Ehk96NG1WesgC
    6176ff05
test_wait_then_evict_has_no_exception.py 5.38 KB