Understanding Uvicorn asynchrounous behavior
I am trying to understand the behavior of Uvicorn. I have create a sample fastapi app which mainly sleep for 5 seconds.
import time
from datetime import datetime
from fastapi import FastAPI
app = FastAPI()
counter = 0
@app.get("/")
def root():
global counter
counter = counter + 1
my_id = counter
print(f'I ({my_id}) am feeling sleepy')
time.sleep(5)
print(f'I ({my_id}) am done sleeping')
return {}
I called my app using the following command of Apache Bench:
ab -n 5 -c 5 http://127.0.0.1:8000/
Output:
I (1) am feeling sleepy -- 0s
I (1) am done sleeping -- 5s
I (2) am feeling sleepy -- 5s
I (3) am feeling sleepy -- 5s
I (4) am feeling sleepy -- 5s
I (5) am feeling sleepy -- 5s
I (2) am done sleeping -- 10s
I (4) am done sleeping -- 10s
I (3) am done sleeping -- 10s
I (5) am done sleeping -- 10s
Why are requests running concurrently? I ran the app as:
uvicorn main:app --workers 1
Please note that I did not use the async keyword so for me everything should be completely synchronous.
From the FastAPI docs:
When you declare a path operation function with normal def instead of async def, it is run in an external threadpool that is then awaited, instead of being called directly (as it would block the server).
Where is this threadpool? As I am using sleep, I though the only worker available would be completely blocked.