• Stefy Lanza (nextime / spora )'s avatar
    ocr: pdfium is not thread-safe, and 0.3.65 found out in production (v0.3.66) · 43cc57a5
    Stefy Lanza (nextime / spora ) authored
    0.3.65 moved PDF rendering into asyncio.to_thread so a long document would stop
    stalling the event loop. That was correct in intent and broke OCR within the
    hour: until then `load_pages` ran ON the loop, which serialised every
    rasterisation BY ACCIDENT. Removing the accident let documents into pdfium
    concurrently, and pdfium permits exactly one caller at a time. Concurrent use
    corrupts its state and reports
    
        failed to rasterise PDF (pypdfium2): PDFium: Data format error
    
    on documents that are perfectly valid -- so the failure accuses the input, and
    the operator goes looking at their files. Three valid Diogene PDFs that poppler
    parsed cleanly were rejected this way.
    
    Measured: 1 document OK, 8 concurrent = 8 failures, 24 = 24 failures. After the
    fix, 48 concurrent 47-page PDFs all return 47 pages.
    
    Every pdfium call -- document open, page size probe, page render -- now runs
    under one lock, held per CALL rather than per document so documents still
    interleave page by page instead of queueing behind each other's full length.
    Rendering still happens off the event loop, so this is strictly better than
    0.3.64, which serialised AND blocked the loop.
    
    Cost, stated plainly: rasterisation is single-threaded, a ceiling of about one
    core. That is what 0.3.64 effectively was, so it is not a regression against it.
    Rasterising in a process pool (pdfium is safe one-document-per-process) is the
    scalability fix and is deliberately not bolted onto this hotfix.
    
    Two process lessons, in the tests so they outlive the memory of this:
    - this path is only ever exercised concurrently, so testing it sequentially
      tests the one case that cannot fail. I shipped it tested sequentially.
    - the error STRING predated the change, and I used that to argue the behaviour
      did too. "This message is not new" is not evidence of "this is not new".
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01HXRMe9ZPUQCCCtBai9uqt3
    43cc57a5
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...