A small sample project to profile the performance of asynchronous Python when used in typical Django views.
To evaluate the performance of async Django views in comparison to traditional sync Django views I created two views:
-
a classic sync view
(see: https://github.com/antonpirker/asyncdjango/blob/main/app/views.py#L21)
-
and an async view.
(see: https://github.com/antonpirker/asyncdjango/blob/main/app/views.py#L36)
Both views make two simple queries to the database:
- one query fetching a list of items and
- the second query fetching the details of one item.
I have used asyncpg
in the async view and have used psycopg2
in the sync view (without using the ORM of Django for better comparison, because there is async ORM yet).
Then I have run the project once in Gunicorn with the default sync workers to see how long the views take when running in a synchronous Gunicorn process.
-
You can try the project with sync Gunicorn here:
I also have run the project in Gunicorn but using the Uvicorn async workers to see how long the views take when running a asynchronous Uvicorn process.
-
You can try the project with async Uvicorn here:
(You can also look at the server output when you click on the “Logs” button in the widget on the lower right of the page. You need to be authenticated with your GitHub account to see the logs)
I ran both views in a Docker container connecting to Postgres also in a Docker container on my AMD CPU with 8 cores.
Here the moment when the slow down of the view starts: (at ~130 concurrent users)
And this is when the view starts to throw errors: (at ~310 concurrent users)
(Like the Uvicorn documentation tells me that Uvicorn should be run in production)
When the slow down of the view starts: (at ~100 concurrent users)
When the errors start: (at ~280 concurrent users)
I can not see substantial performance gains when using async database queries in Django views.
So until there is no full async ORM and you only make database queries and do not load data over HTTP from a third party API in your views, it is not worth the efford to change your views to async yet.
As the User KenWithsell in a comment the Django Forum points out, this will probably be due to Postgres everything memory resident. (Because it is only a tiny database, that can fit in memory easily)