1+ from unittest .mock import patch
2+
13import pytest
24
35from redis .backoff import NoBackoff
6+ from redis .client import Redis
47from redis .connection import Connection , UnixDomainSocketConnection
5- from redis .exceptions import ConnectionError
8+ from redis .exceptions import (
9+ BusyLoadingError ,
10+ ConnectionError ,
11+ ReadOnlyError ,
12+ TimeoutError ,
13+ )
614from redis .retry import Retry
715
16+ from .conftest import _get_client
17+
818
919class BackoffMock :
1020 def __init__ (self ):
@@ -39,6 +49,37 @@ def test_retry_on_timeout_retry(self, Class, retries):
3949 assert isinstance (c .retry , Retry )
4050 assert c .retry ._retries == retries
4151
52+ @pytest .mark .parametrize ("Class" , [Connection , UnixDomainSocketConnection ])
53+ def test_retry_on_error (self , Class ):
54+ c = Class (retry_on_error = [ReadOnlyError ])
55+ assert c .retry_on_error == [ReadOnlyError ]
56+ assert isinstance (c .retry , Retry )
57+ assert c .retry ._retries == 1
58+
59+ @pytest .mark .parametrize ("Class" , [Connection , UnixDomainSocketConnection ])
60+ def test_retry_on_error_empty_value (self , Class ):
61+ c = Class (retry_on_error = [])
62+ assert c .retry_on_error == []
63+ assert isinstance (c .retry , Retry )
64+ assert c .retry ._retries == 0
65+
66+ @pytest .mark .parametrize ("Class" , [Connection , UnixDomainSocketConnection ])
67+ def test_retry_on_error_and_timeout (self , Class ):
68+ c = Class (
69+ retry_on_error = [ReadOnlyError , BusyLoadingError ], retry_on_timeout = True
70+ )
71+ assert c .retry_on_error == [ReadOnlyError , BusyLoadingError , TimeoutError ]
72+ assert isinstance (c .retry , Retry )
73+ assert c .retry ._retries == 1
74+
75+ @pytest .mark .parametrize ("retries" , range (10 ))
76+ @pytest .mark .parametrize ("Class" , [Connection , UnixDomainSocketConnection ])
77+ def test_retry_on_error_retry (self , Class , retries ):
78+ c = Class (retry_on_error = [ReadOnlyError ], retry = Retry (NoBackoff (), retries ))
79+ assert c .retry_on_error == [ReadOnlyError ]
80+ assert isinstance (c .retry , Retry )
81+ assert c .retry ._retries == retries
82+
4283
4384class TestRetry :
4485 "Test that Retry calls backoff and retries the expected number of times"
@@ -65,3 +106,85 @@ def test_retry(self, retries):
65106 assert self .actual_failures == 1 + retries
66107 assert backoff .reset_calls == 1
67108 assert backoff .calls == retries
109+
110+
111+ @pytest .mark .onlynoncluster
112+ class TestRedisClientRetry :
113+ "Test the standalone Redis client behavior with retries"
114+
115+ def test_client_retry_on_error_with_success (self , request ):
116+ with patch .object (Redis , "parse_response" ) as parse_response :
117+
118+ def mock_parse_response (connection , * args , ** options ):
119+ def ok_response (connection , * args , ** options ):
120+ return "MOCK_OK"
121+
122+ parse_response .side_effect = ok_response
123+ raise ReadOnlyError ()
124+
125+ parse_response .side_effect = mock_parse_response
126+ r = _get_client (Redis , request , retry_on_error = [ReadOnlyError ])
127+ assert r .get ("foo" ) == "MOCK_OK"
128+ assert parse_response .call_count == 2
129+
130+ def test_client_retry_on_error_raise (self , request ):
131+ with patch .object (Redis , "parse_response" ) as parse_response :
132+ parse_response .side_effect = BusyLoadingError ()
133+ retries = 3
134+ r = _get_client (
135+ Redis ,
136+ request ,
137+ retry_on_error = [ReadOnlyError , BusyLoadingError ],
138+ retry = Retry (NoBackoff (), retries ),
139+ )
140+ with pytest .raises (BusyLoadingError ):
141+ try :
142+ r .get ("foo" )
143+ finally :
144+ assert parse_response .call_count == retries + 1
145+
146+ def test_client_retry_on_error_different_error_raised (self , request ):
147+ with patch .object (Redis , "parse_response" ) as parse_response :
148+ parse_response .side_effect = TimeoutError ()
149+ retries = 3
150+ r = _get_client (
151+ Redis ,
152+ request ,
153+ retry_on_error = [ReadOnlyError ],
154+ retry = Retry (NoBackoff (), retries ),
155+ )
156+ with pytest .raises (TimeoutError ):
157+ try :
158+ r .get ("foo" )
159+ finally :
160+ assert parse_response .call_count == 1
161+
162+ def test_client_retry_on_error_and_timeout (self , request ):
163+ with patch .object (Redis , "parse_response" ) as parse_response :
164+ parse_response .side_effect = TimeoutError ()
165+ retries = 3
166+ r = _get_client (
167+ Redis ,
168+ request ,
169+ retry_on_error = [ReadOnlyError ],
170+ retry_on_timeout = True ,
171+ retry = Retry (NoBackoff (), retries ),
172+ )
173+ with pytest .raises (TimeoutError ):
174+ try :
175+ r .get ("foo" )
176+ finally :
177+ assert parse_response .call_count == retries + 1
178+
179+ def test_client_retry_on_timeout (self , request ):
180+ with patch .object (Redis , "parse_response" ) as parse_response :
181+ parse_response .side_effect = TimeoutError ()
182+ retries = 3
183+ r = _get_client (
184+ Redis , request , retry_on_timeout = True , retry = Retry (NoBackoff (), retries )
185+ )
186+ with pytest .raises (TimeoutError ):
187+ try :
188+ r .get ("foo" )
189+ finally :
190+ assert parse_response .call_count == retries + 1
0 commit comments