#36797: Implement fully async-capable support for
django.middleware.common.CommonMiddleware
-------------------------------------+-------------------------------------
Reporter: Mykhailo Havelia | Type: New
| feature
Status: new | Component: HTTP
| handling
Version: 6.0 | Severity: Normal
Keywords: async | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
From the Django documentation:
> You will only get the benefits of a fully asynchronous request stack if
you have no synchronous middleware loaded into your site.
Currently, all of Django's internal middleware uses `MiddlewareMixin` and
relies on `sync_to_async` under the hood. This makes it difficult for end
users to have a fully async request flow, because they would need to copy
and adapt all internal logic for each middleware, and maintain it across
Django releases.
Some internal middleware only performs `CPU`-bound operations and does not
require `async`/`AIO` logic, so it could safely be used in both `sync` and
`async` contexts without `sync_to_async`.
Options to solve this
- Implement `__acall__` without `sync_to_async`
Not recommended. Can lead to subtle bugs if users subclass and customize
the middleware:
{{{
class CustomCommonMiddleware(CommonMiddleware):
def process_response(self, request, response):
# custom logic
return super().process_response(request, response)
}}}
In an async context, this custom logic would be ignored or behave
unexpectedly.
- Rewrite middlewares using `sync_and_async_middleware` decorator
Safe and straightforward. Breaking change: would break all existing custom
middleware that inherits from the internal middleware.
- Implement separate async middleware (`AsyncCommonMiddleware`)
Compromise solution. Safe for users: they can explicitly replace
`CommonMiddleware` with `AsyncCommonMiddleware` in async setups.
When `MiddlewareMixin` is eventually removed, we can rename
`AsyncCommonMiddleware` -> `CommonMiddleware` without breaking changes.
I'm going to start by implementing an async version of CommonMiddleware as
a test case for this approach. Once we validate it, the plan is to extend
the same pattern to:
- django.contrib.auth.middleware.AuthenticationMiddleware
- django.middleware.locale.LocaleMiddleware
- django.middleware.security.SecurityMiddleware
If you have any thoughts or concerns about this approach, especially
regarding edge cases or backward compatibility, please share them.
--
Ticket URL: <
https://code.djangoproject.com/ticket/36797>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.