Opened 6 months ago
Closed 5 months ago
#36281 closed Cleanup/optimization (fixed)
ASGIHandler perfoms sync writes when reading the chunks of a HTTP response's body
Reported by: | Brian Atkinson | Owned by: | shin wo jin |
---|---|---|---|
Component: | HTTP handling | Version: | dev |
Severity: | Normal | Keywords: | asgi async |
Cc: | Carlton Gibson | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
That write may end up resulting in IO actually hitting disk. This could result in the async loop stalling if the underlying write call is slow.
Change History (13)
comment:1 by , 6 months ago
comment:2 by , 6 months ago
Cc: | added |
---|---|
Component: | Uncategorized → HTTP handling |
Keywords: | asgi async added |
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Cleanup/optimization |
Version: | 5.1 → dev |
Thank you Brian for the report! I agree that this is worth addressing, as disk IO can indeed lead to stalls.
Careful consideration will be needed for how to handle the chunked file writing without blocking the event loop. I'm wondering if we should have a sort of abstraction for the chunked file writer and allow people to override. Depending on the project's constraints or current dependencies, some projects may choose to use a 3rd party library like asynctempfile (unclear whether this is currently maintained), or Django channels, or something else?
We're open to a patch that addresses this while ensuring compatibility and avoiding unintended regressions. Would you like to work on this?
(I'm torn whether to categorize this as Cleanup or New Feature, for now I'm going with the former but we can adjust later on.)
comment:3 by , 6 months ago
Summary: | Sync write in async code path → ASGIHandler perfoms sync writes when reading the chunks of a HTTP response's body |
---|
comment:4 by , 6 months ago
That's a good one. Yes. A consideration for being careful about allowed body sizes under ASGI. It'd be interesting to see what other ASGI frameworks do here.
…use a 3rd party library like asynctempfile (unclear whether this is currently maintained)…
It looks small and depends on aiofiles which is a package I'd trust. It could likely be vendored if needed, perhaps using the async version if aiofiles
was available.
(I'd have to think through the severity of the blocking I/O to say whether using such would be recommended or even required. We already mention aiofiles
in the docs, e.g. FileResponses.)
comment:5 by , 6 months ago
I've created a pull request to resolve this ticket
pr : https://github.com/django/django/pull/19359
Please let me know if there's anything further needed or if additional changes are required. Thank you!
comment:6 by , 6 months ago
Has patch: | set |
---|---|
Status: | new → assigned |
comment:7 by , 6 months ago
Patch needs improvement: | set |
---|
Setting patch needs improvement based on Carlton's comment.
comment:8 by , 6 months ago
Thanks for the feedback!
I've updated the patch based on Carlton’s suggestion — now using getattr
for the rollover check, with conditional branching and relevant tests.
comment:9 by , 6 months ago
Needs tests: | set |
---|---|
Patch needs improvement: | unset |
I think the patch here is looking good, but it needs a test that demonstrates the fix. (There are tests added but they don't fail without the patch.) I've left comments to that effect on the PR.
With that it should be close.
comment:10 by , 6 months ago
Needs tests: | unset |
---|---|
Owner: | set to |
Patch needs improvement: | set |
comment:11 by , 6 months ago
Patch needs improvement: | unset |
---|
I’m unsetting Patch needs improvement, as I assume the intention of the last edit was to mark the PR as ready for another review.
comment:12 by , 5 months ago
Triage Stage: | Accepted → Ready for checkin |
---|
I think you're right, the sync write in this async code path might cause problems if the disk I/O is slow. Maybe we could wrap the body_file.write call with sync_to_async to prevent blocking the event loop?