Skip to content

Commit cc5f129

Browse files
authored
Removing the threading.Lock locks and replacing them with RLock objects to avoid deadlocks. (#3677)
1 parent b1e5b01 commit cc5f129

File tree

6 files changed

+18
-28
lines changed

6 files changed

+18
-28
lines changed

redis/client.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,7 @@ def __init__(
368368
]:
369369
raise RedisError("Client caching is only supported with RESP version 3")
370370

371-
# TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
372-
# TODO: Remove this before next major version (7.0.0)
373-
self.single_connection_lock = threading.Lock()
371+
self.single_connection_lock = threading.RLock()
374372
self.connection = None
375373
self._single_connection_client = single_connection_client
376374
if self._single_connection_client:
@@ -776,9 +774,7 @@ def __init__(
776774
else:
777775
self._event_dispatcher = event_dispatcher
778776

779-
# TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
780-
# TODO: Remove this before next major version (7.0.0)
781-
self._lock = threading.Lock()
777+
self._lock = threading.RLock()
782778
if self.encoder is None:
783779
self.encoder = self.connection_pool.get_encoder()
784780
self.health_check_response_b = self.encoder.encode(self.HEALTH_CHECK_MESSAGE)

redis/connection.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ def __init__(
810810
self,
811811
conn: ConnectionInterface,
812812
cache: CacheInterface,
813-
pool_lock: threading.Lock,
813+
pool_lock: threading.RLock,
814814
):
815815
self.pid = os.getpid()
816816
self._conn = conn
@@ -1422,13 +1422,7 @@ def __init__(
14221422
# release the lock.
14231423

14241424
self._fork_lock = threading.RLock()
1425-
1426-
if self.cache is None:
1427-
self._lock = threading.RLock()
1428-
else:
1429-
# TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
1430-
# TODO: Remove this before next major version (7.0.0)
1431-
self._lock = threading.Lock()
1425+
self._lock = threading.RLock()
14321426

14331427
self.reset()
14341428

redis/event.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def __init__(
152152
self,
153153
connection,
154154
client_type: ClientType,
155-
connection_lock: Union[threading.Lock, asyncio.Lock],
155+
connection_lock: Union[threading.RLock, asyncio.Lock],
156156
):
157157
self._connection = connection
158158
self._client_type = client_type
@@ -167,7 +167,7 @@ def client_type(self) -> ClientType:
167167
return self._client_type
168168

169169
@property
170-
def connection_lock(self) -> Union[threading.Lock, asyncio.Lock]:
170+
def connection_lock(self) -> Union[threading.RLock, asyncio.Lock]:
171171
return self._connection_lock
172172

173173

@@ -177,7 +177,7 @@ def __init__(
177177
pubsub_connection,
178178
connection_pool,
179179
client_type: ClientType,
180-
connection_lock: Union[threading.Lock, asyncio.Lock],
180+
connection_lock: Union[threading.RLock, asyncio.Lock],
181181
):
182182
self._pubsub_connection = pubsub_connection
183183
self._connection_pool = connection_pool
@@ -197,7 +197,7 @@ def client_type(self) -> ClientType:
197197
return self._client_type
198198

199199
@property
200-
def connection_lock(self) -> Union[threading.Lock, asyncio.Lock]:
200+
def connection_lock(self) -> Union[threading.RLock, asyncio.Lock]:
201201
return self._connection_lock
202202

203203

tests/test_cluster_transaction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def test_retry_transaction_on_connection_error(self, r, mock_connection):
285285
mock_pool = Mock(spec=ConnectionPool)
286286
mock_pool.get_connection.return_value = mock_connection
287287
mock_pool._available_connections = [mock_connection]
288-
mock_pool._lock = threading.Lock()
288+
mock_pool._lock = threading.RLock()
289289

290290
_node_migrating, node_importing = _find_source_and_target_node_for_slot(r, slot)
291291
node_importing.redis_connection.connection_pool = mock_pool
@@ -310,7 +310,7 @@ def test_retry_transaction_on_connection_error_with_watched_keys(
310310
mock_pool = Mock(spec=ConnectionPool)
311311
mock_pool.get_connection.return_value = mock_connection
312312
mock_pool._available_connections = [mock_connection]
313-
mock_pool._lock = threading.Lock()
313+
mock_pool._lock = threading.RLock()
314314

315315
_node_migrating, node_importing = _find_source_and_target_node_for_slot(r, slot)
316316
node_importing.redis_connection.connection_pool = mock_pool

tests/test_connection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def test_clears_cache_on_disconnect(self, mock_connection, cache_conf):
442442
mock_connection.credential_provider = UsernamePasswordCredentialProvider()
443443

444444
proxy_connection = CacheProxyConnection(
445-
mock_connection, cache, threading.Lock()
445+
mock_connection, cache, threading.RLock()
446446
)
447447
proxy_connection.disconnect()
448448

@@ -492,7 +492,7 @@ def test_read_response_returns_cached_reply(self, mock_cache, mock_connection):
492492
mock_connection.can_read.return_value = False
493493

494494
proxy_connection = CacheProxyConnection(
495-
mock_connection, mock_cache, threading.Lock()
495+
mock_connection, mock_cache, threading.RLock()
496496
)
497497
proxy_connection.send_command(*["GET", "foo"], **{"keys": ["foo"]})
498498
assert proxy_connection.read_response() == b"bar"
@@ -554,7 +554,7 @@ def test_triggers_invalidation_processing_on_another_connection(
554554
mock_connection.can_read.return_value = False
555555

556556
proxy_connection = CacheProxyConnection(
557-
mock_connection, mock_cache, threading.Lock()
557+
mock_connection, mock_cache, threading.RLock()
558558
)
559559
proxy_connection.send_command(*["GET", "foo"], **{"keys": ["foo"]})
560560

tests/test_credentials.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ def test_re_auth_all_connections(self, credential_provider):
323323
}
324324
mock_pool.get_connection.return_value = mock_connection
325325
mock_pool._available_connections = [mock_connection, mock_another_connection]
326-
mock_pool._lock = threading.Lock()
326+
mock_pool._lock = threading.RLock()
327327
auth_token = None
328328

329329
def re_auth_callback(token):
@@ -382,7 +382,7 @@ def test_re_auth_partial_connections(self, credential_provider):
382382
mock_another_connection,
383383
mock_failed_connection,
384384
]
385-
mock_pool._lock = threading.Lock()
385+
mock_pool._lock = threading.RLock()
386386

387387
def _raise(error: RedisError):
388388
pass
@@ -442,7 +442,7 @@ def test_re_auth_pub_sub_in_resp3(self, credential_provider):
442442
mock_another_connection,
443443
]
444444
mock_pool._available_connections = [mock_another_connection]
445-
mock_pool._lock = threading.Lock()
445+
mock_pool._lock = threading.RLock()
446446
auth_token = None
447447

448448
def re_auth_callback(token):
@@ -502,7 +502,7 @@ def test_do_not_re_auth_pub_sub_in_resp2(self, credential_provider):
502502
mock_another_connection,
503503
]
504504
mock_pool._available_connections = [mock_another_connection]
505-
mock_pool._lock = threading.Lock()
505+
mock_pool._lock = threading.RLock()
506506
auth_token = None
507507

508508
def re_auth_callback(token):
@@ -560,7 +560,7 @@ def test_fails_on_token_renewal(self, credential_provider):
560560
}
561561
mock_pool.get_connection.return_value = mock_connection
562562
mock_pool._available_connections = [mock_connection, mock_another_connection]
563-
mock_pool._lock = threading.Lock()
563+
mock_pool._lock = threading.RLock()
564564

565565
Redis(
566566
connection_pool=mock_pool,

0 commit comments

Comments
 (0)