Skip to content
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

Improve performance of WebSockets when there is no timeout #8660

Merged
merged 5 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES/8660.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Improved performance of :py:meth:`~aiohttp.ClientWebSocketResponse.receive` and :py:meth:`~aiohttp.web.WebSocketResponse.receive` when there is no timeout. -- by :user:`bdraco`.

The timeout context manager is now avoided when there is no timeout as it accounted for up to 50% of the time spent in the :py:meth:`~aiohttp.ClientWebSocketResponse.receive` and :py:meth:`~aiohttp.web.WebSocketResponse.receive` methods.
13 changes: 10 additions & 3 deletions aiohttp/client_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bo
return False

async def receive(self, timeout: Optional[float] = None) -> WSMessage:
receive_timeout = timeout or self._timeout.ws_receive

while True:
if self._waiting:
raise RuntimeError("Concurrent call to receive() is not allowed")
Expand All @@ -304,9 +306,14 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage:
try:
self._waiting = True
try:
async with async_timeout.timeout(
timeout or self._timeout.ws_receive
):
if receive_timeout:
# Entering the context manager and creating
# Timeout() object can take almost 50% of the
# run time in this loop so we avoid it if
# there is no read timeout.
async with async_timeout.timeout(receive_timeout):
msg = await self._reader.read()
else:
msg = await self._reader.read()
self._reset_heartbeat()
finally:
Expand Down
10 changes: 9 additions & 1 deletion aiohttp/web_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage:

loop = self._loop
assert loop is not None
receive_timeout = timeout or self._receive_timeout
while True:
if self._waiting:
raise RuntimeError("Concurrent call to receive() is not allowed")
Expand All @@ -524,7 +525,14 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage:
try:
self._waiting = True
try:
async with async_timeout.timeout(timeout or self._receive_timeout):
if receive_timeout:
# Entering the context manager and creating
# Timeout() object can take almost 50% of the
# run time in this loop so we avoid it if
# there is no read timeout.
async with async_timeout.timeout(receive_timeout):
msg = await self._reader.read()
else:
msg = await self._reader.read()
self._reset_heartbeat()
finally:
Expand Down
Loading