⚡ Bolt: Offload blocking I/O operations to worker threads in async FastAPI routes#221
⚡ Bolt: Offload blocking I/O operations to worker threads in async FastAPI routes#221
Conversation
In the FastAPI endpoints `render_pdf` and `render_resume_pdf`, synchronous, blocking operations such as `generator.generate()` (which performs CPU-bound tasks like LaTeX rendering), `output_pdf.exists()`, and `output_pdf.read_bytes()` were running on the main async event loop. This blocked the entire event loop, preventing the API from handling other concurrent requests effectively. This commit offloads these blocking operations to a worker thread using `anyio.to_thread.run_sync()`. `functools.partial` is used to pass keyword arguments to `run_sync` for the `generator.generate` call. This ensures the main async event loop remains free to process concurrent requests, drastically improving API performance under load. Co-authored-by: anchapin <6326294+anchapin@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Reviewer's GuideAsync FastAPI PDF rendering endpoints now offload blocking PDF generation and filesystem I/O to worker threads via anyio.to_thread.run_sync, improving event loop responsiveness, and the performance notes in .jules/bolt.md are updated with this pattern and its rationale. Sequence diagram for async PDF rendering with worker thread offloadingsequenceDiagram
actor Client
participant FastAPI as FastAPIApp
participant render_pdf as render_pdf_endpoint
participant EventLoop as AsyncEventLoop
participant AnyIO as anyio_to_thread
participant Worker as WorkerThread
participant Generator as TemplateGenerator
participant FS as FileSystem
Client->>FastAPI: HTTP POST /v1/render/pdf
FastAPI->>EventLoop: schedule render_pdf
EventLoop->>render_pdf: execute async handler
render_pdf->>AnyIO: run_sync(generate_func)
AnyIO->>Worker: execute generator.generate
Worker->>Generator: generate(variant, output_format, output_path)
Generator-->>Worker: PDF written to output.pdf
Worker-->>AnyIO: return
AnyIO-->>render_pdf: await result
render_pdf->>AnyIO: run_sync(output_pdf.exists)
AnyIO->>Worker: execute Path.exists
Worker->>FS: check output_pdf
FS-->>Worker: exists
Worker-->>AnyIO: return bool
AnyIO-->>render_pdf: exists
render_pdf->>render_pdf: validate exists or raise HTTPException
render_pdf->>AnyIO: run_sync(output_pdf.read_bytes)
AnyIO->>Worker: execute Path.read_bytes
Worker->>FS: read output_pdf bytes
FS-->>Worker: content bytes
Worker-->>AnyIO: return content
AnyIO-->>render_pdf: content bytes
render_pdf-->>FastAPI: Response(content=content, media_type=application/pdf)
FastAPI-->>Client: HTTP 200 PDF response
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The
render_pdfandrender_resume_pdfendpoints now share nearly identicalgenerate_funcandto_thread.run_synclogic; consider extracting a small helper (e.g.,async generate_pdf_to_path(...)or a genericasync run_blocking(...)) to reduce duplication and keep future changes to the offloading behavior in one place. - Since
anyio.to_thread.run_syncaccepts*args/**kwargs, you could simplify the calls by passing arguments directly instead of constructing afunctools.partial, which would make the offloading sites a bit more readable.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `render_pdf` and `render_resume_pdf` endpoints now share nearly identical `generate_func` and `to_thread.run_sync` logic; consider extracting a small helper (e.g., `async generate_pdf_to_path(...)` or a generic `async run_blocking(...)`) to reduce duplication and keep future changes to the offloading behavior in one place.
- Since `anyio.to_thread.run_sync` accepts `*args`/`**kwargs`, you could simplify the calls by passing arguments directly instead of constructing a `functools.partial`, which would make the offloading sites a bit more readable.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
💡 What: Offloaded blocking synchronous operations (
generator.generate,Path.exists,Path.read_bytes) in FastAPI endpointsrender_pdfandrender_resume_pdfto worker threads usinganyio.to_thread.run_syncandfunctools.partial.🎯 Why: FastAPI endpoints defined with
async defrun directly on the main async event loop. Executing blocking, CPU-bound tasks (like LaTeX PDF compilation ingenerator.generate) and synchronous file I/O operations on the main thread blocked the event loop. This caused a major concurrency bottleneck, completely starving the server from handling other incoming requests while a PDF was being generated or read.📊 Impact: Significantly improves the API's concurrent request handling capabilities. The event loop is no longer blocked during expensive PDF generations and file reads, avoiding total server freezes under load.
🔬 Measurement: Verify by running load tests against the
/v1/render/pdfor/v1/resumes/{id}/render/pdfendpoints. You should observe much better concurrent processing times without the server pausing entirely during individual request execution. Run test suite viapython -m pytestto confirm everything works as expected.PR created automatically by Jules for task 18003314079667917910 started by @anchapin
Summary by Sourcery
Offload blocking PDF generation and file I/O in async FastAPI PDF rendering endpoints to worker threads to improve concurrency and responsiveness.
Enhancements:
render_pdfandrender_resume_pdfvia worker threads instead of the main event loop.Documentation: