django: handle cancelled requests more cleanly #3848
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
Description
Django's ASGI handler cancels tasks that have seen a disconnect. This manifests itself in requests potentially opening an OTel span through the Django middleware but not having
process_response
run in the existing mode, meaning theuse_span
(this one) closure + contextdetach
call isn't run.This results in dreaded "this token was created in a different context" errors, which happen because:
use_span
activation gets__exit__
'd inget_response
use_span
generator instead gets garbage collected after the factfinally
clause inside the context management code ofuse_span
detach
many miles away from the request contextThe current Django Middleware design dates back to supporting "old style" middleware, making this problem hard to solve cleanly. Given Django 1.x is now 5 years out of support, I think it's reasonable to drop Django 1.x support to make context management here clearer, through using the new-style
__call__
middleware along with atry/finally
block.Fixes # (issue)
This PR does some cleanup to the middleware to make sure that our context and span cleanup always happen, including when there's an
asyncio.CancelledError
during process handling. But this PR also drops support for Django 1.x in the process, by expecting this middleware to be called via__call__
(I'll handle doc changes etc if I can get approval on dropping Django 1.x support)
Type of change
How Has This Been Tested?
In order to see this "in the wild", I added the following sort of thing to a random Django view:
I then ran this app with
uvicorn
, and made requests. When I saw "sleeping" I would close the browser tabs, cancelling the requests. This would lead to the context error appearing.Does This PR Require a Core Repo Change?
Checklist:
See contributing.md for styleguide, changelog guidelines, and more.