Support concurrent BackgroundTasks #10682
-
First Check
Commit to Help
Example Codeimport asyncio
from fastapi import BackgroundTasks
class ConcurrentBackgroundTasks(BackgroundTasks):
"""This is all it takes to implement"""
async def __call__(self) -> None:
await asyncio.gather(*[task() for task in self.tasks])
# This doesn't work; still constructed as BackgroundTasks
@app.post("/foo")
def foo(background_tasks: ConcurrentBackgroundTasks):
pass
# Works, but we must now manually add back FastAPI schema and validation features
@app.post("/foo", response_model=Model)
def foo():
bgt = ConcurrentBackgroundTasks()
bgt.add_task(...)
return JSONResponse(
content=jsonable_encoder(
Model(...)
),
background=bgt,
)Descriptiontl;dr: I need to run background tasks concurrently. I found a way, but it required me to spend many hours figuring things out. It's essentially a difficult one-line change that makes FastAPI messier. It'd be delightful if FastAPI supported this directly. I need to run background tasks concurrently. My use case: make multiple HTTP requests after a response in a realtime application. As each request takes ~2s, if I can run them concurrently, N tasks complete in 2s rather than 2Ns. I'd like to propose a feature request to either
My implementation above works, but it requires returning a raw starlette Response. This means we have to manually add back schema generation ( Here's a sketch of each idea. 1. Allow concurrently-executing background tasksIn the BackgroundTasks implementation, provide an alternate instance or I opened a discussion on starlette (Kludex/starlette#2338) to see whether they'd consider supporting a 2. Allow providing a custom BackgroundTask subclass in the path operation functionI.e., make the following work: # Proposed: respect ConcurrentBackgroundTasks here rather than just returning a vanilla BackgroundTasks
@app.post("/foo")
def foo(background_tasks: ConcurrentBackgroundTasks):
passThis was asked previously in #8478, with the answer that it is impossible. A great comment there (#8478 (comment)) dug through the FastAPI internals to see why this can't be overridden. Detection: Setting (always constructs Would it be possible to instantiate the provided subclass instead? Many thanks for your consideration, and apologies if this isn't the right place for a feature request. FastAPI has been delightful and I am grateful for it. Operating SystemmacOS Operating System DetailsNo response FastAPI Version0.104.1 Pydantic Version2.5.0 Python Version3.10.6 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
|
Interesting idea, but I'm afraid I think it will not be implemented. Here is why: DetailsIt's not as simple as it seems to implement this. Alternative approach could be to add a parameter like # This doesn't work, it's just to illustrate possible approach
app = FastAPI(bg_tasks_class=ConcurrentBackgroundTasks)
@app.post("/foo")
def foo(response: Response, background_tasks: BackgroundTasks):
assert isinstance(background_tasks, ConcurrentBackgroundTasks)But.. As it's been said a lot of times, FastAPI tends not to add features if it's possible and easy to implement this without adding this feature. You can just combine your tasks in one function and add it as a task: import asyncio
from fastapi import BackgroundTasks, FastAPI
async def concurrent_tasks(tasks: list[asyncio.Task]) -> None:
await asyncio.gather(*tasks)
app = FastAPI()
async def sleeeep(n: int):
print(f"{n}: Sleeping...")
await asyncio.sleep(2)
print(f"{n}: Done sleeping!")
@app.post("/foo")
def foo(background_tasks: BackgroundTasks):
tasks = [sleeeep(n) for n in range(1, 4)]
background_tasks.add_task(concurrent_tasks, tasks)
return {"message": "foo"} |
Beta Was this translation helpful? Give feedback.
Interesting idea, but I'm afraid I think it will not be implemented.
Here is why:
Details
It's not as simple as it seems to implement this.
Imagine you added
bg_tasks: ConcurrentBackgroundTasksas a parameter to one dependency andbg_tasks: BackgroundTasksto another dependency that are both used in the same endpoint. How should FastAPI resolve this? In current implementation bothbg_taskswill point to the same instance ofBackgroundTasks.Alternative approach could be to add a parameter like
bg_tasks_classtoFastAPI()and then use this class to create an instance: