Stream PDF from disk, s3 or zip files through memory - #124
Open
lfoppiano wants to merge 3 commits into
Open
Conversation
process_pdf could only read the document from disk, so callers holding a PDF in memory - fetched from an API, read out of a database or an object store - had to write it to a temporary file only for the client to open it again. It now takes the document itself as well: bytes, or any binary stream. Nothing says which of the two it is; the object does. A document also names itself, from the "name" attribute open() sets on files and that can be set on anything else, io.BytesIO included, so the identity of a document is not lost by going through memory - it travels with the request and comes back with the result. Bytes on their own have nothing to be named after and fall back to DEFAULT_IN_MEMORY_NAME. A stream is read once, up front, and re-served from memory afterwards: the 503 retry sends the same document again, and a consumed (or non-seekable) stream would silently post an empty body the second time around. That is also why the retry no longer recurses through the public entry point, which would have had to re-derive a name from a source that is by then exhausted. process_documents processes several of them concurrently, through the same ThreadPoolExecutor the file-based processing uses. Results come back in input order rather than in completion order: in-memory documents have no filenames to be matched back on afterwards, so the caller has nothing but the order to zip them onto. A single PDF passed by mistake raises instead of being iterated, which would otherwise send one request per byte. Resumes #67 Co-authored-by: Jan Göpfert <94385965+jangoepfert@users.noreply.github.com>
The archive and s3 streaming (#117) shipped with a known detour: every entry was written to a temporary directory only so that process_pdf could open it again from a path, with the commit itself noting this would go away once PR #67 landed. It has landed, so this plugs the two together: archive entries and loose s3 objects are now read straight into memory and posted from there, named after the entry (or the s3 basename), and nothing but the results ever touches the disk. process_batch accepts the in-memory documents alongside paths - an entry goes by the name it carries, and since process_pdf returns that same name, the result lands on the same output file it would have as a path. The one input that still takes the temp-dir route is processCitationList, whose .txt files are read by process_txt from a path. The archive tests asserted on the temp dirs the posts came from, which no longer exist; they now assert on what actually crossed the wire - each entry posted once, under its archive name, with its own bytes - plus explicitly that mkdtemp is never called on the pdf path. Completes what #117 left pending on #67.
An in-memory run keeps up to n documents in flight against the server, so a client concurrency above the server's engine pool only piles up requests that queue there or come back as 503, while one below it leaves engines idle. Neither is visible from the client side until the throughput disappoints. Before process_documents and the in-memory archive/s3 streaming start, the client now asks /api/health how many engines the server has (pool.maxActive) and logs a warning when n exceeds them - with the number to use instead - and an info message when they outnumber n. The check is advisory, not a gate: a server without the endpoint (older GROBID), an unreadable answer or a connection failure never blocks the run. A server answering ready: false is also surfaced as a warning. Plain file-based processing is unchanged and makes no extra call.
lfoppiano
force-pushed
the
feature/process-pdf-from-memory
branch
from
August 15, 2026 23:07
976bc40 to
c665ee5
Compare
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.
Superseed #67
Fixes #66