Skip to content

Commit 8e5d5ce

Browse files
Fix socket garbage collection (redis#2859)
1 parent 2c2860d commit 8e5d5ce

File tree

4 files changed

+69
-18
lines changed

4 files changed

+69
-18
lines changed

CHANGES

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Fix #2831, add auto_close_connection_pool=True arg to asyncio.Redis.from_url()
12
* Fix incorrect redis.asyncio.Cluster type hint for `retry_on_error`
23
* Fix dead weakref in sentinel connection causing ReferenceError (#2767)
34
* Fix #2768, Fix KeyError: 'first-entry' in parse_xinfo_stream.

redis/asyncio/client.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,13 @@ class Redis(
104104
response_callbacks: MutableMapping[Union[str, bytes], ResponseCallbackT]
105105

106106
@classmethod
107-
def from_url(cls, url: str, **kwargs):
107+
def from_url(
108+
cls,
109+
url: str,
110+
single_connection_client: bool = False,
111+
auto_close_connection_pool: bool = True,
112+
**kwargs,
113+
):
108114
"""
109115
Return a Redis client object configured from the given URL
110116
@@ -144,12 +150,13 @@ class initializer. In the case of conflicting arguments, querystring
144150
arguments always win.
145151
146152
"""
147-
single_connection_client = kwargs.pop("single_connection_client", False)
148153
connection_pool = ConnectionPool.from_url(url, **kwargs)
149-
return cls(
154+
redis = cls(
150155
connection_pool=connection_pool,
151156
single_connection_client=single_connection_client,
152157
)
158+
redis.auto_close_connection_pool = auto_close_connection_pool
159+
return redis
153160

154161
def __init__(
155162
self,

tests/test_asyncio/test_connection.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
_AsyncRESPBase,
1313
)
1414
from redis.asyncio import Redis
15-
from redis.asyncio.connection import Connection, UnixDomainSocketConnection
15+
from redis.asyncio.connection import Connection, UnixDomainSocketConnection, parse_url
1616
from redis.asyncio.retry import Retry
1717
from redis.backoff import NoBackoff
1818
from redis.exceptions import ConnectionError, InvalidResponse, TimeoutError
@@ -278,3 +278,39 @@ async def open_connection(*args, **kwargs):
278278
def test_create_single_connection_client_from_url():
279279
client = Redis.from_url("redis://localhost:6379/0?", single_connection_client=True)
280280
assert client.single_connection_client is True
281+
282+
283+
@pytest.mark.parametrize("from_url", (True, False))
284+
async def test_pool_auto_close(request, from_url):
285+
"""Verify that basic Redis instances have auto_close_connection_pool set to True"""
286+
287+
url: str = request.config.getoption("--redis-url")
288+
url_args = parse_url(url)
289+
290+
async def get_redis_connection():
291+
if from_url:
292+
return Redis.from_url(url)
293+
return Redis(**url_args)
294+
295+
r1 = await get_redis_connection()
296+
assert r1.auto_close_connection_pool is True
297+
await r1.close()
298+
299+
300+
@pytest.mark.parametrize("from_url", (True, False))
301+
async def test_pool_auto_close_disable(request, from_url):
302+
"""Verify that auto_close_connection_pool can be disabled"""
303+
304+
url: str = request.config.getoption("--redis-url")
305+
url_args = parse_url(url)
306+
307+
async def get_redis_connection():
308+
if from_url:
309+
return Redis.from_url(url, auto_close_connection_pool=False)
310+
url_args["auto_close_connection_pool"] = False
311+
return Redis(**url_args)
312+
313+
r1 = await get_redis_connection()
314+
assert r1.auto_close_connection_pool is False
315+
await r1.connection_pool.disconnect()
316+
await r1.close()

tests/test_connect.py

+21-14
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ def test_tcp_ssl_connect(tcp_address):
6161

6262
def _assert_connect(conn, server_address, certfile=None, keyfile=None):
6363
if isinstance(server_address, str):
64+
if not _RedisUDSServer:
65+
pytest.skip("Unix domain sockets are not supported on this platform")
6466
server = _RedisUDSServer(server_address, _RedisRequestHandler)
6567
else:
6668
server = _RedisTCPServer(
@@ -113,24 +115,29 @@ def get_request(self):
113115
return connstream, fromaddr
114116

115117

116-
class _RedisUDSServer(socketserver.UnixStreamServer):
117-
def __init__(self, *args, **kw) -> None:
118-
self._ready_event = threading.Event()
119-
self._stop_requested = False
120-
super().__init__(*args, **kw)
118+
if hasattr(socket, "UnixStreamServer"):
121119

122-
def service_actions(self):
123-
self._ready_event.set()
120+
class _RedisUDSServer(socketserver.UnixStreamServer):
121+
def __init__(self, *args, **kw) -> None:
122+
self._ready_event = threading.Event()
123+
self._stop_requested = False
124+
super().__init__(*args, **kw)
124125

125-
def wait_online(self):
126-
self._ready_event.wait()
126+
def service_actions(self):
127+
self._ready_event.set()
127128

128-
def stop(self):
129-
self._stop_requested = True
130-
self.shutdown()
129+
def wait_online(self):
130+
self._ready_event.wait()
131131

132-
def is_serving(self):
133-
return not self._stop_requested
132+
def stop(self):
133+
self._stop_requested = True
134+
self.shutdown()
135+
136+
def is_serving(self):
137+
return not self._stop_requested
138+
139+
else:
140+
_RedisUDSServer = None
134141

135142

136143
class _RedisRequestHandler(socketserver.StreamRequestHandler):

0 commit comments

Comments
 (0)