• Stefy Lanza (nextime / spora )'s avatar
    renting a fleet must not starve the process serving it (v0.4.20) · c3361e42
    Stefy Lanza (nextime / spora ) authored
    0.4.19 fixed the lease -- pages now reach the pool, where in-flight had been
    stuck at zero -- and the run still produced nothing: 56 pods, 27 healthy,
    1,040 pages in flight, ZERO `POST /v1/ocr` in any pod's log, and
    /v1/runpod/status unanswerable while /healthz answered in 2.9 ms.
    
    The ramp was eating the process. Measured on that run: coderai-cpu at 75%,
    event-loop stalls of 57.4 s and 44.9 s, and 406 candidate rejections in 120
    seconds with every GPU type re-probed 33 to 72 times. Two duplicated costs,
    both paid once per BOOT and so multiplied by 32 parallel boots:
    
      * Each boot thread swept the whole ~70-candidate list itself, so each
        independently rediscovered that A100 SXM has no capacity in any of seven
        regions. The verdict is now shared and short-lived (`_unavail`, 90 s):
        one thread learns it, the other 31 skip it. On this run's numbers that is
        2,240 probes reduced to 70. A successful deploy clears the entry, and the
        TTL is short because capacity returns without telling us.
    
      * Each boot fetched and parsed the FULL GPU price catalogue
        (`list_gpu_types` was uncached), so 32 boots meant 32 identical large JSON
        bodies parsed within seconds. Cached 60 s process-wide, each caller
        getting its own copy so a mutating caller cannot corrupt it, with
        `fresh=True` for a caller showing prices to a person rather than picking a
        card in a storm.
    
    Both costs are Python, so they hold the GIL, and CPython gives it to whoever
    is running: the event loop carrying the page forwards simply did not get
    scheduled. That is why in-flight climbed while no socket was ever opened to a
    pod -- and the undelivered queue then read as demand, so the scaler rented
    more pods, which starved the loop harder. The fleet grew because it could not
    serve and could not serve because it was growing.
    
    No blocking call is involved, which is why the 0.4.17 static check passes and
    the loop watchdog was the thing that found it.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01KnfkxBpcZ1v6ypRFFyhrVM
    c3361e42
Name
Last commit
Last update
..
admin Loading commit data...
api Loading commit data...
backends Loading commit data...
broker Loading commit data...
cluster Loading commit data...
compose Loading commit data...
frontproxy Loading commit data...
models Loading commit data...
ocr Loading commit data...
openai Loading commit data...
pydantic Loading commit data...
queue Loading commit data...
tasks Loading commit data...
util Loading commit data...
__init__.py Loading commit data...
cli.py Loading commit data...
config.py Loading commit data...
main.py Loading commit data...
platform_paths.py Loading commit data...
system_app.py Loading commit data...