I create simple app using (python 3.11, uvicorn 0.38.0, fastapi 0.120.2):
import os
import time
import asyncio
import uvicorn
from fastapi import FastAPI
from fastapi import BackgroundTasks
app = FastAPI()
async def background_task():
print(os.getpid())
print(id(asyncio.get_running_loop()))
print(id(asyncio.current_task()))
time.sleep(10)
@app.get("/background_task")
async def get(background_tasks: BackgroundTasks):
print(os.getpid())
print(id(asyncio.get_running_loop()))
print(id(asyncio.current_task()))
background_tasks.add_task(background_task)
return {200: "ok"}
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
workers=1,
port=8000,
reload=True
)
But it behaves strange:
- reload = True or workers > 1: I immediately get response in my browser and then background task blocks eventloop.
- reload = False and workers = 1: I will wait for response in my browser until background task complete execution.
Seems like reload flag and workers count shouldn't change app behavior so as I understand both setups should return response and then start background tasks or vise versa firstly run background tasks which in this case implies eventloop blocking and then return response but how it behaves look strange for me. I add logs to verify that in all cases both view and background task run in the same eventloop, use the same task and run in same the process.
Log example(same for all cases):
8420
1825609933264
1825610022336
INFO: 127.0.0.1:53184 - "GET /background_task HTTP/1.1" 200 OK
8420
1825609933264
1825610022336
Even though logs are the same, response is not returned under conditions described above. Is it intended behavior or bug, if intended can someone explain me how and why it works in this way?
time.sleep()represents in a task in your real case stackoverflow.com/questions/41063331/…