PromptBase
Upgrade
Close icon
General
Home
Marketplace
Create
Hire
Login
Chat
Sell
Explore

Skill Details

Runtime
(claude-5-sonnet)
Example user message
[userMessage]: Our FastAPI service handles about 40 requests per second normally, but when our PDF vendor gets slow every single endpoint gets slow, including the health check. Four workers, uvicorn, gunicorn in front. The handler that calls the vendor looks roughly like this: @router.post("/receipts/render") async def render_receipt(body: RenderRequest): session = SessionLocal() receipt = session.query(Receipt).filter_by(id=body.receipt_id).one() pdf = requests.post("https://pdf.vendor.example/render", json=receipt.payload) session.add(RenderLog(receipt_id=receipt.id, bytes=len(pdf.content))) session.commit() return {"ok": True, "size": len(pdf.content)} My colleague says the fix is more workers. That feels wrong because doubling workers only doubles the number of requests that can be stuck. What is actually happening, and what should this handler look like?
Example agent response
## Surface FastAPI, one async POST handler, synchronous SQLAlchemy session created by hand, synchronous `requests` call to an external vendor. Four uvicorn workers. Your instinct is right and your colleague's fix is not. ## Findings 1. routers/receipts.py — Async Discipline rule 1. `requests.post` is a blocking socket read inside an `async def`. While it waits, the event loop for that worker cannot run anything, so the health check queued behind it waits too. Four workers means exactly four concurrent stalls before the whole service is unresponsive. Adding workers raises that number; it does not fix the mechanism. 2. routers/receipts.py — Async Discipline rule 4. No timeout on the vendor call. A vendor that hangs for 90 seconds holds a worker for 90 seconds. 3. routers/receipts.py — Dependencies rule 1. `SessionLocal()` is created by hand and never closed. On any raised exception the connection leaks, and a leaked connection pool is the second failure you will hit under load. 4. routers/receipts.py — Async Discipline rule 6. A synchronous session inside an async handler mixes the two models. Pick one path. 5. routers/receipts.py — Dependencies rule 4. A new connection pool is negotiated for every vendor call, which adds a TLS handshake to a request that is already slow. ## Patch The client is built once at startup, the session comes from a dependency with teardown, and the call is async with a timeout: import httpx from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession router = APIRouter(prefix="/receipts") def get_pdf_client(request) -> httpx.AsyncClient: return request.app.state.pdf_client @router.post("/render", response_model=RenderResponse) async def render_receipt( body: RenderRequest, db: AsyncSession = Depends(get_db), pdf: httpx.AsyncClient = Depends(get_pdf_client), ): receipt = await db.get(Receipt, body.receipt_id) if receipt is None: raise HTTPException(status_code=404, detail="Receipt not found.") try: response = await pdf.post("/render", json=receipt.payload) response.raise_for_status() except httpx.TimeoutException: raise HTTPException(status_code=504, detail="PDF vendor timed out.") size = len(response.content) db.add(RenderLog(receipt_id=receipt.id, bytes=size)) await db.commit() return RenderResponse(ok=True, size=size) The client and its pool live on the app, created once: @asynccontextmanager async def lifespan(app): app.state.pdf_client = httpx.AsyncClient( base_url="https://pdf.vendor.example", timeout=httpx.Timeout(connect=2.0, read=8.0, write=5.0, pool=2.0), limits=httpx.Limits(max_connections=40), ) yield await app.state.pdf_client.aclose() app = FastAPI(lifespan=lifespan) And the session dependency closes even when the handler raises: async def get_db(): async with AsyncSessionLocal() as session: yield session Now a slow vendor parks a coroutine, not a worker. The health check answers while forty receipt renders are in flight, and the connection limit of 40 means the vendor cannot consume unbounded memory on your side either. ## Checks to run - Point the vendor base URL at a local server that sleeps 30 seconds, then hit the health check while ten renders are in flight. Before the patch it hangs; after it should answer immediately. - Watch your database connection count during that test. It should stay flat, not climb with each request. - Decide what a 504 means for the caller. If the render is retryable, this endpoint should hand the job to a queue and return an id instead of waiting at all. - This review cannot run your service or reach the vendor. Run the timeout test yourself before deploying.
Add to Cart
PromptBase Select
Or get it with Select10 Select prompts/mo — under $2 each
Learn more
Instant accessInstant access
Usage rightsCommercial use
Money-back guaranteeMoney‑back
By purchasing this skill, you agree to our terms of service
CLAUDE-5-SONNET
Tested icon
Guide icon
Free credits icon
Cursor rule pack for production Django and FastAPI code: N plus one ORM queries and select_related use, transaction and atomic boundaries, FastAPI dependency lifetimes, Pydantic v2 validation and response models, sync work blocking the event loop, settings and secret handling, security headers and CSRF, pagination caps, and structured logging. It reviews and rewrites code you open or paste, and does not run migrations, start servers, or touch your database.
...more
Added 2 weeks ago
Report
Browse Marketplace