From 1f98a253fa48309da139ccc8ac32f8d5819e8b81 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Aug 2024 18:28:26 -0500 Subject: [PATCH 1/7] Improve performance of keepalive rescheduling If not all handlers were done, the keep alive would get rescheduled every second until the current time was greater than self._keepalive_time + self._keepalive_timeout Instead we will schedule the timer for either 1s later or self._keepalive_time + self._keepalive_timeout whichever is greater to avoid many timer handles --- aiohttp/web_protocol.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 4d50465b2ef..428e777a6fc 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -448,17 +448,19 @@ def _process_keepalive(self) -> None: return next = self._keepalive_time + self._keepalive_timeout + now = self._loop.time() # handler in idle state - if self._waiter: - if self._loop.time() > next: - self.force_close() - return + if self._waiter and now > next: + self.force_close() + return # not all request handlers are done, - # reschedule itself to next second - self._keepalive_handle = self._loop.call_later( - self.KEEPALIVE_RESCHEDULE_DELAY, + # reschedule itself to next or + # the next second, whichever is later + reschedule_time = max(next, now + self.KEEPALIVE_RESCHEDULE_DELAY) + self._keepalive_handle = self._loop.call_at( + reschedule_time, self._process_keepalive, ) From 1e47cbccf0e69d57c6d076b8046e1b8a1ca549da Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Aug 2024 19:21:47 -0500 Subject: [PATCH 2/7] changelog --- CHANGES/8662.misc.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGES/8662.misc.rst diff --git a/CHANGES/8662.misc.rst b/CHANGES/8662.misc.rst new file mode 100644 index 00000000000..2bb38c3081f --- /dev/null +++ b/CHANGES/8662.misc.rst @@ -0,0 +1,3 @@ +Reduce frequency of HTTP keep-alive checks -- by :user:`bdraco`. + +Previously, when processing a request for a keep-alive connection, the keep-alive check would happen every second; the check now occurs when the keep-alive is expected to timeout or 1s later, whichever is later. From af5b65a51c652a755133c08a64d9fad6ceae8677 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Aug 2024 19:26:50 -0500 Subject: [PATCH 3/7] Update CHANGES/8662.misc.rst --- CHANGES/8662.misc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/8662.misc.rst b/CHANGES/8662.misc.rst index 2bb38c3081f..ad14e943fe7 100644 --- a/CHANGES/8662.misc.rst +++ b/CHANGES/8662.misc.rst @@ -1,3 +1,3 @@ -Reduce frequency of HTTP keep-alive checks -- by :user:`bdraco`. +Improved performance of HTTP keep-alive checks -- by :user:`bdraco`. Previously, when processing a request for a keep-alive connection, the keep-alive check would happen every second; the check now occurs when the keep-alive is expected to timeout or 1s later, whichever is later. From 9130a290648e59c06bf25e5eb522e8dbe8652b8a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Aug 2024 09:23:08 -0500 Subject: [PATCH 4/7] avoid 1s KEEPALIVE_RESCHEDULE_DELAY --- aiohttp/web_protocol.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 7c5b0c8c2be..d7b1ccbb85e 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -149,8 +149,6 @@ class RequestHandler(BaseProtocol): """ - KEEPALIVE_RESCHEDULE_DELAY = 1 - __slots__ = ( "_request_count", "_keepalive", @@ -158,7 +156,7 @@ class RequestHandler(BaseProtocol): "_request_handler", "_request_factory", "_tcp_keepalive", - "_keepalive_time", + "_next_keepalive_close_time", "_keepalive_handle", "_keepalive_timeout", "_lingering_time", @@ -208,7 +206,7 @@ def __init__( self._tcp_keepalive = tcp_keepalive # placeholder to be replaced on keepalive timeout setup - self._keepalive_time = 0.0 + self._next_keepalive_close_time = 0.0 self._keepalive_handle: Optional[asyncio.Handle] = None self._keepalive_timeout = keepalive_timeout self._lingering_time = float(lingering_time) @@ -448,22 +446,17 @@ def _process_keepalive(self) -> None: if self._force_close or not self._keepalive: return - next = self._keepalive_time + self._keepalive_timeout - now = self._loop.time() + loop = self._loop + now = loop.time() + close_time = self._next_keepalive_close_time + if now <= close_time: + # Keep alive fired close fired too early, reschedule + self._keepalive_handle = loop.call_at(close_time, self._process_keepalive) + return # handler in idle state - if self._waiter and now > next: + if self._waiter: self.force_close() - return - - # not all request handlers are done, - # reschedule itself to next or - # the next second, whichever is later - reschedule_time = max(next, now + self.KEEPALIVE_RESCHEDULE_DELAY) - self._keepalive_handle = self._loop.call_at( - reschedule_time, - self._process_keepalive, - ) async def _handle_request( self, @@ -610,11 +603,12 @@ async def start(self) -> None: if self._keepalive and not self._close: # start keep-alive timer if keepalive_timeout is not None: - now = self._loop.time() - self._keepalive_time = now + now = loop.time() + close_time = now + keepalive_timeout + self._next_keepalive_close_time = close_time if self._keepalive_handle is None: self._keepalive_handle = loop.call_at( - now + keepalive_timeout, self._process_keepalive + close_time, self._process_keepalive ) else: break From 34fb62ce706cb83065d40a7f5d78e98c46fdc5c8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Aug 2024 09:28:18 -0500 Subject: [PATCH 5/7] make sure handle is set to none on fire --- aiohttp/web_protocol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index d7b1ccbb85e..1dba61257e3 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -443,6 +443,7 @@ def log_exception(self, *args: Any, **kw: Any) -> None: self.logger.exception(*args, **kw) def _process_keepalive(self) -> None: + self._keepalive_handle = None if self._force_close or not self._keepalive: return From 84a3e2ac2ea078b98fe058d637794b2d99bf470f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Aug 2024 09:30:04 -0500 Subject: [PATCH 6/7] Update aiohttp/web_protocol.py --- aiohttp/web_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 1dba61257e3..c95ea5477ce 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -451,7 +451,7 @@ def _process_keepalive(self) -> None: now = loop.time() close_time = self._next_keepalive_close_time if now <= close_time: - # Keep alive fired close fired too early, reschedule + # Keep alive close check fired too early, reschedule self._keepalive_handle = loop.call_at(close_time, self._process_keepalive) return From 63531a8ed7ed85f681d36bc6a527a46aaf6dfde5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Aug 2024 09:30:58 -0500 Subject: [PATCH 7/7] Update CHANGES/8662.misc.rst --- CHANGES/8662.misc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/8662.misc.rst b/CHANGES/8662.misc.rst index ad14e943fe7..efe30a60cb2 100644 --- a/CHANGES/8662.misc.rst +++ b/CHANGES/8662.misc.rst @@ -1,3 +1,3 @@ Improved performance of HTTP keep-alive checks -- by :user:`bdraco`. -Previously, when processing a request for a keep-alive connection, the keep-alive check would happen every second; the check now occurs when the keep-alive is expected to timeout or 1s later, whichever is later. +Previously, when processing a request for a keep-alive connection, the keep-alive check would happen every second; the check is now rescheduled if it fires too early instead.