• Stefy Lanza (nextime / spora )'s avatar
    ocr: the batch was not stalled, it was re-reading pages (v0.3.45) · 4b99fcb8
    Stefy Lanza (nextime / spora ) authored
    Read from the pod's own drained log, ten minutes of batch OCR on 2026-10-13:
    
      Running: 15-19 reqs, Waiting: 0 reqs, GPU KV cache usage: 5-7%
      Avg generation throughput: ~1,900-2,100 tokens/s, every minute
      524 x "POST /v1/chat/completions" 200 OK, no aborts
      2,644 generated tokens PER REQUEST (second pod: 4,010)
      MM cache hit rate: 25-27%
    
    Three things in those numbers, and only the third is a defect.
    
    The GPU was never the bottleneck: 5-7% of the KV cache and `Waiting: 0` for
    ten solid minutes. So a card to itself cannot be the fix, and the co-loaded
    pyannote cost nothing measurable — the limit was never VRAM or batch slots.
    
    Oversubscription converts throughput into latency, not into stalling. 64
    pages into max_pods x scale_up_inflight_per_pod = 32 slots queues correctly,
    but every page's latency rises by the queue, and a client counting finished
    DOCUMENTS sees nothing until a document's LAST page lands: spread 64 pages
    over sixteen documents and all sixteen advance while none completes. The
    pool now says so at build time instead of leaving it to be inferred.
    
    The defect: 2,644 generated tokens for a page whose text is ~400. surya
    re-runs a page whose generation repeated itself (`_should_retry`) up to three
    more times, each at a rising temperature and each able to reach its token
    cap — four full generations to arrive at the same unreadable answer — and its
    repeat detector cannot tell "the model broke" from "this page really does
    repeat", which a tax table does. The 25-27% multimodal cache hit rate is the
    direct evidence: a quarter of all requests re-sent an image the pod had
    already read. None of it was visible: a page generated four times over and a
    page that was merely slow look identical from outside.
    
    `max_retries` is unreachable from surya's settings — the vLLM backend calls
    chat_completions_batch without it, so the default 3 was the only value there
    has ever been. So the decision is hooked, beside the token-trace hook already
    in that module, and `ocr.surya_max_retries` is a CEILING (asking for 10 where
    surya allows 3 gets 3): 1 by default, 0 to disable, -1 to hand it back.
    
    Every retry is logged, and a page that needed more than one call carries
    `model_attempts` in its result — always, not only under surya_token_trace. A
    page read first time stays byte-identical to what it always returned. The
    counter is per THREAD, like the trace, or one lane would be blamed for
    another lane's retries.
    
    Also pins SURYA_INFERENCE_PARALLEL=1 in the served modes: a lane is our unit
    of concurrency, and left unset surya sizes its own thread pool from a
    built-in GPU table and fans out inside the worker, so instances x lanes would
    stop being the real number of pages in flight.
    
    19 tests in tests/test_surya_retry_budget.py; settings carry the new knob.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01PxYHWbCAjjjD7A1rhFKtaW
    4b99fcb8
Name
Last commit
Last update
..
schemas_builtin Loading commit data...
workers Loading commit data...
__init__.py Loading commit data...
base.py Loading commit data...
chandra.py Loading commit data...
detect.py Loading commit data...
doctr.py Loading commit data...
extract.py Loading commit data...
gateway.py Loading commit data...
manager.py Loading commit data...
olmocr.py Loading commit data...
paddle.py Loading commit data...
schemas.py Loading commit data...
served_model.py Loading commit data...
subprocess_engine.py Loading commit data...
surya.py Loading commit data...
venv_build.py Loading commit data...