The request remains open/active until the background task finishes running, making the purpose of BackgroundTasks kind of useless.
On the snippet below the request takes about 10 seconds to complete.
I followed the documentation guide but it didnt seem to work.
async def background_task(text: str, seconds: int):
import time
time.sleep(seconds)
print(text)
@app.get("/run", status_code=status.HTTP_200_OK)
async def test(background_tasks: BackgroundTasks, text: str = "Test", secs: int = 10):
background_tasks.add_task(background_task, text, secs)
return {"text": text, "secs": secs}

The request to finish right away and the task to run in background.
I'm not 100% sure about this but here's a few guesses:
Have you tried to run it with await asyncio.sleep(seconds) instead of the time.sleep function? time.sleep is synchronous as far as I know and since background tasks that are defined as "async" just use the event loop as their "background engine", using a synchronous method in that background task will still globally block the process.
Alternatively, you can try removing the "async" from def background_task. In that case the task should run in a thread pool instead which would then also not block.
Sorry but how do you run the app? Uvicorn? Could you post a self-contained app that can run?
Sorry but how do you run the app? Uvicorn? Could you post a self-contained app that can run?
Im running with uvicorn using this code as entry
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=5000, debug=True, reload=True)
I'm not 100% sure about this but here's a few guesses:
1. Have you tried to run it with `await asyncio.sleep(seconds)` instead of the `time.sleep` function? `time.sleep` is synchronous as far as I know and since background tasks that are defined as "async" just use the event loop as their "background engine", using a synchronous method in that background task will still globally block the process. 2. Alternatively, you can try removing the "async" from `def background_task`. In that case the task should run in a thread pool instead which would then also not block.
The code above I wrote just to simplify my problem. Removing async in this example seemed to work. Im going to try this with my real code. Thanks
@soares7vinicius just fyi this: https://github.com/encode/starlette/issues/436 explains your problem es well. The tldr:
@daviskirk
Seems I have similar issue, tried to narrow down to middleware.
You can run this snippet and requests are getting blocked when middleware is added.
On purpose, I added middleware which does nothing and it is visible that there is no blocking code block.
If you try to comment app.add_middleware(DoNothingMiddleware) then behaviour is as expected.
Might be some issue there, can this be a bug or is it a limitation ?
import asyncio
from fastapi import BackgroundTasks, FastAPI
from starlette import status
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response
class DoNothingMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
response = await call_next(request)
return response
async def background_task(text: str, seconds: int):
print(f"Task {text} started")
await asyncio.sleep(seconds)
print(f"Task {text} ended")
app = FastAPI(debug=True)
# if you add this middleware then requests are getting blocked
app.add_middleware(DoNothingMiddleware)
@app.get("/run", status_code=status.HTTP_200_OK)
async def test(background_tasks: BackgroundTasks, text: str = "Test", secs: int = 10):
background_tasks.add_task(background_task, text, secs)
return {"text": text, "secs": secs}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, debug=True, reload=True)
@demonno This is a bug in Starlette - see encode/starlette#919
Thanks for the help here everyone! :clap: :bow:
@soares7vinicius yeah, as @daviskirk says, you shouldn't use time.sleep() in a function with async def. When in doubt, just use def everywhere.
Check more info about all that in the docs: https://fastapi.tiangolo.com/async/
I got same problem.
This code sends requests to the specified links. and it is executed so that the client expects a result. code does not execute in the background.
background_tasks.add_task(await Request.package_send(urls=urls*100, callback=callback)
from pydantic import BaseModel
from services.core.models import TaskStatement
class Task(BaseModel):
id: uuid.UUID
def with_client_session(coro):
async def wrapper(
method: str, url: str, data: dict = None,
headers: dict = None, raw: bool = None
):
session = ClientSession(loop=loop)
result = None
if method == 'GET':
result = await coro(
method=session.get, url=url, data=data,
headers=headers, raw=raw)
elif method == 'POST':
result = await coro(
method=session.post, url=url, data=data,
headers=headers, raw=raw)
elif method == 'PUT':
result = await coro(
method=session.put, url=url, data=data,
headers=headers, raw=raw)
elif method == 'DELETE':
result = await coro(
method=session.delete, url=url, data=data,
headers=headers, raw=raw)
elif method == 'PATCH':
result = await coro(
method=session.patch, url=url, data=data,
headers=headers, raw=raw)
await session.close()
return result
return wrapper
class Request:
@staticmethod
@with_client_session
async def send(method, url: str, data: list = None, headers: dict = None, raw: bool = None):
response: ClientResponse = await method(
url=url, data=json.dumps(data), headers=headers, ssl=ssl_context)
if raw:
return response, response.status, await response.text()
try:
return await response.json(), response.status
except:
return await response.text(), response.status
@staticmethod
async def package_send(urls, callback):
parent_task = await TaskStatement.create(
uuid=uuid.UUID(uuid.uuid4().hex),
)
async for url in generator(urls):
future: asyncio.Future = asyncio.ensure_future(Request.send(
method=url.method, url=url.url, data=url.data, headers=url.headers, raw=True
))
child = await TaskStatement.create(
uuid=uuid.UUID(uuid.uuid4().hex),
parent=parent_task
)
await parent_task.children.add(child)
await parent_task.save()
future.self = child
future.parent = parent_task
future.add_done_callback(callback)
future: asyncio.Task
return Task(id=parent_task.uuid)
I'm not 100% sure about this but here's a few guesses:
1. Have you tried to run it with `await asyncio.sleep(seconds)` instead of the `time.sleep` function? `time.sleep` is synchronous as far as I know and since background tasks that are defined as "async" just use the event loop as their "background engine", using a synchronous method in that background task will still globally block the process. 2. Alternatively, you can try removing the "async" from `def background_task`. In that case the task should run in a thread pool instead which would then also not block.The code above I wrote just to simplify my problem. Removing
asyncin this example seemed to work. Im going to try this with my real code. Thanks
if you removed async, then it turns out that your code is synchronous. unless tasks will block each other if they are performed without event loop?
@codefather-labs I'm not sure I understand your code, it seems you are adding @staticmethod to a custom Request, that will probably override the normal Request.
Check the docs for background tasks: https://fastapi.tiangolo.com/tutorial/background-tasks/
And for async: https://fastapi.tiangolo.com/async/
@codefather-labs I'm not sure I understand your code, it seems you are adding
@staticmethodto a customRequest, that will probably override the normalRequest.Check the docs for background tasks: https://fastapi.tiangolo.com/tutorial/background-tasks/
And for async: https://fastapi.tiangolo.com/async/
i can't fetch coroutine in background task wright? on function can?
@codefather-labs please create a new issue with a minimal, self-contained, example, that will let me (us) help you better by being able to check what you are trying to achieve and might be going on.
Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.
@tiangolo could this be reopened - while this is an upstream bug, it is not really resolved
i have same issue and if i add
await asyncio.sleep(1)
inside any method that is triggered by task, it is working fine. I don't understand how it can work but it is working.
even i put time.sleep(30) after asyncio.sleep, i get result immediately but process finish 30sec later which is correct.
Most helpful comment
@tiangolo could this be reopened - while this is an upstream bug, it is not really resolved