File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 33## Not released
44
55- Added ` _ExpandedRequestOptions.refresh ` to satisfy Pyright type checking.
6+ - Revert ` lru_cache ` for request lock to avoid binding to multiple event loops (` RuntimeError: ...Lock is bound to a different event loop ` )
67
78## 0.14.3 (2026-01-07)
89
Original file line number Diff line number Diff line change 55import sys
66import warnings
77from asyncio import Lock
8- from collections import defaultdict
98from contextlib import asynccontextmanager
109from logging import getLogger
1110from typing import TYPE_CHECKING , cast
11+ from weakref import WeakValueDictionary
1212
1313from aiohttp import ClientSession
1414from aiohttp .typedefs import StrOrURL
@@ -61,7 +61,8 @@ def __init__(
6161 ** kwargs ,
6262 ):
6363 self .cache = cache or CacheBackend ()
64- self ._locks : dict [str , Lock ] = defaultdict (Lock )
64+ # Drops a key's Lock as soon as nothing is contending for it
65+ self ._locks : WeakValueDictionary [str , Lock ] = WeakValueDictionary ()
6566 self ._null_lock = nullcontext ()
6667
6768 # Pass along any valid kwargs for ClientSession (or custom session superclass)
@@ -89,7 +90,10 @@ async def _request(
8990 if actions .skip_read :
9091 lock : Lock | nullcontext = self ._null_lock
9192 else :
92- lock = self ._locks [key ]
93+ try :
94+ lock = self ._locks [key ]
95+ except KeyError :
96+ lock = self ._locks [key ] = Lock ()
9397
9498 async with lock :
9599 response = await self .cache .request (actions )
Original file line number Diff line number Diff line change @@ -194,6 +194,22 @@ class CustomSession(CacheMixin, ClientSession):
194194 assert mock_request .called is False
195195
196196
197+ @patch .object (ClientSession , '_request' , return_value = FakeClientResponse )
198+ async def test_session__locks_do_not_leak (mock_request ):
199+ """Locks are only needed while requests are actively contending for a cache key, so distinct
200+ keys should not accumulate indefinitely.
201+ """
202+ cache = MagicMock (spec = CacheBackend )
203+ cache .request .return_value = None
204+ cache .create_key .side_effect = lambda method , url , ** kwargs : str (url )
205+
206+ async with CachedSession (cache = cache ) as session :
207+ for i in range (1000 ):
208+ await session .get (f'http://test.url/{ i } ' )
209+
210+ assert len (session ._locks ) == 0
211+
212+
197213@patch .object (ClientSession , '_request' , return_value = FakeCachedResponse )
198214async def test_session__cache_include_headers (mock_request ):
199215 async with CachedSession (cache = CacheBackend (include_headers = True )) as session :
You can’t perform that action at this time.
0 commit comments