FastAPI BackgroundTasks vs Celery for jobs that may outlive the request timeout
0 reputation · 17 May 2022, 15:03 UTC
0 reputation · 17 May 2022, 15:03 UTC
I'm designing an endpoint in a FastAPI service (running behind Uvicorn) that triggers follow-up work after returning a response — things like sending notifications and updating an external system. Some of these jobs are quick, but a few can run for several seconds and must not be lost if the server restarts.
FastAPI's built-in BackgroundTasks is attractive because it needs no extra infrastructure, but my understanding is that tasks run in the same process, offer no retry or timeout mechanism, and failures are not surfaced anywhere. Celery with a Redis or RabbitMQ broker would give durability, retries, and monitoring, at the cost of operating a broker and worker fleet.
The constraint: jobs must survive a deploy or restart, and the HTTP response must not be delayed by slow work.
Is it correct that a BackgroundTask that blocks for several seconds can delay the client response or hit proxy timeouts, or does Starlette send the response first in current versions? For guaranteed delivery, is Celery the only sensible option, or is there a documented lighter-weight pattern within FastAPI? If I mix both, what is a reasonable rule for deciding which jobs go where?
FastAPI’s BackgroundTasks run in the same process after the HTTP response has been sent. They do not block the client’s receipt of the response, but they are volatile: if the worker process restarts, any queued task is lost. Celery, by contrast, stores tasks in an external broker (Redis, RabbitMQ, etc.), so the task survives a server restart, can be retried, and can be monitored. If your job must outlive the request timeout and survive restarts, Celery (or another durable queue) is the only reliable option. For lightweight, non‑critical side effects that can be lost on failure, BackgroundTasks is sufficient.
Typical use cases: logging, cache invalidation, one‑off notifications where occasional loss is acceptable.
Typical use cases: long‑running data processing, external API calls that may fail, operations that must be guaranteed to complete.
BackgroundTasks is fine.BackgroundTasks to enqueue a Celery task. The HTTP response is immediate, and the heavy work is handled by the worker pool.# app/main.py
from fastapi import FastAPI, BackgroundTasks
from celery import Celery
app = FastAPI()
c = Celery(broker="redis://localhost:6379/0")
@app.post("/notify")
async def notify(user_id: int, background: BackgroundTasks):
# Quick acknowledgement
background.add_task(_enqueue_notification, user_id)
return {"status": "queued"}
# Background task that just pushes to Celery
async def _enqueue_notification(user_id: int):
c.send_task("tasks.send_email", args=[user_id])
In the example above, the client receives a 200 immediately, while the actual email sending is handled by Celery, guaranteeing durability.
BackgroundTasks with a time.sleep(5) and confirm the client receives the response instantly.BackgroundTasks job and observe that the job is lost.Do you already have a message broker (Redis, RabbitMQ) available in your environment? This will influence whether Celery is immediately deployable or if another lightweight queue might be preferable.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.