1
+ from unittest .mock import patch
2
+
1
3
import pytest
2
4
3
5
from redis .backoff import NoBackoff
6
+ from redis .client import Redis
4
7
from 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
+ )
6
14
from redis .retry import Retry
7
15
16
+ from .conftest import _get_client
17
+
8
18
9
19
class BackoffMock :
10
20
def __init__ (self ):
@@ -39,6 +49,37 @@ def test_retry_on_timeout_retry(self, Class, retries):
39
49
assert isinstance (c .retry , Retry )
40
50
assert c .retry ._retries == retries
41
51
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
+
42
83
43
84
class TestRetry :
44
85
"Test that Retry calls backoff and retries the expected number of times"
@@ -65,3 +106,85 @@ def test_retry(self, retries):
65
106
assert self .actual_failures == 1 + retries
66
107
assert backoff .reset_calls == 1
67
108
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