-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enabling of responses compression causes blocking of the async loop #7192
Comments
If you can run a modified version, maybe just try wrapping it in an |
I assume |
It's the zlib module, it should be C code which releases the GIL, and should only be operating on the input, so I don't think there should be any threading issue (I could be wrong though). |
The actual call being made is: https://docs.python.org/3/library/zlib.html#zlib.Compress.compress |
Thanks @Dreamsorcerer import asyncio
import zlib
import time
import logging
import random
import string
import concurrent.futures
MB = 1024 * 1024
logging.basicConfig(level=logging.DEBUG)
async def main(chunk: bytes) -> None:
zlib_mode = zlib.MAX_WBITS
compress = zlib.compressobj(wbits=zlib_mode, strategy=zlib.Z_DEFAULT_STRATEGY)
# option 1: blocking call
# res = compress.compress(chunk)
# option 2: multiprocess pool
# with concurrent.futures.ProcessPoolExecutor() as pool:
# res = await asyncio.get_event_loop().run_in_executor(pool, compress.compress, chunk)
# option 3: thread pool
# res = await asyncio.to_thread(compress.compress, chunk)
print(len(res))
if __name__ == '__main__':
length = 10 * MB
data = bytes(''.join(random.choice(string.ascii_letters) for _ in range(length)), 'utf-8')
started_sec = time.perf_counter()
asyncio.run(main(data), debug=True)
print(time.perf_counter() - started_sec) Output for option1:
Output for option2:
Output for option3:
So I assume we could go with Option3. I've also tried to increase the chunk size to 100MB, but still got no blocking delay on my M1 mac, which should be just fine. |
I can also observe the res = await asyncio.get_running_loop().run_in_executor(None, compress.compress, chunk) |
We can just put it in a version_info check. I wouldn't even be too bothered if we didn't thread it at all on older versions, as they won't be supported that much longer, and seems that nobody else has noticed the issue till now. |
It is also important to mention another place where the sync compression is done: aiohttp/aiohttp/web_response.py Line 702 in 3a8e65f
Setting zlib_executor_size to 0 in the Response class constructor should make the body compression non blocking:
Not sure though why the |
Appears to be done for performance: Ideally, we should reuse that code then, if we can get access to the same variables. |
Yes, it definitely makes sense to avoid this unnecessary duplication by extracting the common code into a separate helper. |
It's plausible, but maybe not worth the effort. If you want to create a PR for either part, then we'll look at getting it merged. |
Created a PR. It's my first PR to this repository, so please let me know what needs to be done there in order to get it merged. |
<!-- Thank you for your contribution! --> ## What do these changes do? Addresses issue #7192 Refactors the logic to have the zlib-related stuff concentrated into a single module ## Are there changes in behavior for the user? No ## Related issue number #7192 ## Checklist - [x] I think the code is well written - [x] Unit tests for the changes exist - [ ] Documentation reflects the changes - [ ] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [ ] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]>
<!-- Thank you for your contribution! --> Addresses issue #7192 Refactors the logic to have the zlib-related stuff concentrated into a single module No #7192 - [x] I think the code is well written - [x] Unit tests for the changes exist - [ ] Documentation reflects the changes - [ ] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [ ] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]> (cherry picked from commit 3ff81dc)
) <!-- Thank you for your contribution! --> Backport #7223 Addresses issue #7192 Refactors the logic to have the zlib-related stuff concentrated into a single module No #7192 - [x] I think the code is well written - [x] Unit tests for the changes exist - [ ] Documentation reflects the changes - [ ] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [ ] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull <[email protected]> (cherry picked from commit 3ff81dc) <!-- Thank you for your contribution! --> ## What do these changes do? <!-- Please give a short brief about these changes. --> ## Are there changes in behavior for the user? <!-- Outline any notable behaviour for the end users. --> ## Related issue number <!-- Are there any issues opened that will be resolved by merging this change? --> ## Checklist - [ ] I think the code is well written - [ ] Unit tests for the changes exist - [ ] Documentation reflects the changes - [ ] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [ ] Add a new news fragment into the `CHANGES` folder * name it `<issue_id>.<type>` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --------- Co-authored-by: Mykola Mokhnach <[email protected]>
@mykola-mokhnach If you still have performance issues with compression overhead you might try I'm currently using it via https://github.com/bdraco/aiohttp-zlib-ng. It might be useful to integrate here in the future |
Describe the bug
Enabling
response.enable_compression()
on aweb.Response
instance causes async loop blocking delays up to 2 seconds long on our server.I assume the original issue is coming from the fact that the compression is synchronous and eats lots of CPU cycles in case of large chunks/high core load:
aiohttp/aiohttp/http_writer.py
Line 97 in 2a1024f
Perhaps it would be possible to "offload" the compression to another CPU core in order to unblock the main event loop.
To Reproduce
Create a simple GET endpoint, which returns a JSON response like
Then fetch the endpoint multiple times with json payload large enough (up to 10 megabytes in my case) and deflate compression accepted in the request headers. Async event loop debug must be enabled on the server side to see blocking events occurrences
Expected behavior
No async loop blocks should be observed longer as 100ms, although our charts show delays up to 2 seconds. This is not observed if I disable the compression.
Logs/tracebacks
Python Version
aiohttp Version
multidict Version
yarl Version
OS
python:3.8-slim-bullseye Docker image
Related component
Server
Additional context
No response
Code of Conduct
The text was updated successfully, but these errors were encountered: