-
Notifications
You must be signed in to change notification settings - Fork 387
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(core): Proper retry count in KazooRetry
Make sure the number of attempts matches the `max_retry` parameter. Add unit tests to that effect.
- Loading branch information
Showing
2 changed files
with
80 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,107 @@ | ||
import unittest | ||
|
||
import pytest | ||
|
||
|
||
class TestRetrySleeper(unittest.TestCase): | ||
def _pass(self): | ||
pass | ||
def _sleep_func(_time): | ||
pass | ||
|
||
|
||
def _fail(self, times=1): | ||
from kazoo.retry import ForceRetryError | ||
def _makeOne(*args, **kwargs): | ||
from kazoo.retry import KazooRetry | ||
|
||
scope = dict(times=0) | ||
return KazooRetry(*args, sleep_func=_sleep_func, **kwargs) | ||
|
||
def inner(): | ||
if scope["times"] >= times: | ||
pass | ||
else: | ||
scope["times"] += 1 | ||
raise ForceRetryError("Failed!") | ||
|
||
return inner | ||
_try_counts = 0 | ||
|
||
def _makeOne(self, *args, **kwargs): | ||
from kazoo.retry import KazooRetry | ||
|
||
return KazooRetry(*args, **kwargs) | ||
def _make_try_fun(times=1): | ||
"""Returns a function that raises ForceRetryError `times` time before | ||
returning None.""" | ||
from kazoo.retry import ForceRetryError | ||
|
||
def test_reset(self): | ||
retry = self._makeOne(delay=0, max_tries=2) | ||
retry(self._fail()) | ||
assert retry._attempts == 1 | ||
retry.reset() | ||
assert retry._attempts == 0 | ||
global _try_counts | ||
|
||
def test_too_many_tries(self): | ||
from kazoo.retry import RetryFailedError | ||
_try_counts = 0 | ||
|
||
retry = self._makeOne(delay=0) | ||
with pytest.raises(RetryFailedError): | ||
retry(self._fail(times=999)) | ||
assert retry._attempts == 1 | ||
def inner(): | ||
global _try_counts | ||
|
||
def test_maximum_delay(self): | ||
def sleep_func(_time): | ||
if _try_counts >= times: | ||
pass | ||
else: | ||
_try_counts += 1 | ||
raise ForceRetryError("Failed!") | ||
|
||
return inner | ||
|
||
|
||
def test_reset(): | ||
global _try_counts | ||
|
||
retry = _makeOne(delay=0, max_tries=2) | ||
retry(_make_try_fun()) | ||
assert _try_counts == retry._attempts == 1 | ||
retry.reset() | ||
assert retry._attempts == 0 | ||
|
||
|
||
def test_too_many_tries(): | ||
global _try_counts | ||
|
||
from kazoo.retry import RetryFailedError | ||
|
||
retry = _makeOne(delay=0, max_tries=10) | ||
with pytest.raises(RetryFailedError): | ||
retry(_make_try_fun(times=999)) | ||
assert _try_counts == retry._attempts == 10 | ||
|
||
|
||
def test_maximum_delay(): | ||
global _try_counts | ||
|
||
retry = _makeOne(delay=10, max_tries=100, max_jitter=0) | ||
retry(_make_try_fun(times=2)) | ||
assert _try_counts == 2 | ||
assert retry._cur_delay == 10 * 2**2, "Normal exponential backoff" | ||
retry.reset() | ||
retry(_make_try_fun(times=10)) | ||
assert _try_counts == 10 | ||
assert retry._cur_delay == 60, "Delay capped by maximun" | ||
# gevent's sleep function is picky about the type | ||
assert isinstance(retry._cur_delay, float) | ||
|
||
retry = self._makeOne(delay=10, max_tries=100, sleep_func=sleep_func) | ||
retry(self._fail(times=10)) | ||
assert retry._cur_delay < 4000 | ||
# gevent's sleep function is picky about the type | ||
assert type(retry._cur_delay) == float | ||
|
||
def test_copy(self): | ||
def _sleep(t): | ||
return None | ||
def test_copy(): | ||
retry = _makeOne() | ||
rcopy = retry.copy() | ||
assert rcopy.sleep_func is retry.sleep_func | ||
|
||
retry = self._makeOne(sleep_func=_sleep) | ||
rcopy = retry.copy() | ||
assert rcopy.sleep_func is _sleep | ||
|
||
def test_connection_closed(): | ||
from kazoo.exceptions import ConnectionClosedError | ||
|
||
class TestKazooRetry(unittest.TestCase): | ||
def _makeOne(self, **kw): | ||
from kazoo.retry import KazooRetry | ||
retry = _makeOne() | ||
|
||
return KazooRetry(**kw) | ||
def testit(): | ||
raise ConnectionClosedError() | ||
|
||
def test_connection_closed(self): | ||
from kazoo.exceptions import ConnectionClosedError | ||
with pytest.raises(ConnectionClosedError): | ||
retry(testit) | ||
|
||
retry = self._makeOne() | ||
|
||
def testit(): | ||
raise ConnectionClosedError() | ||
def test_session_expired(): | ||
from kazoo.exceptions import SessionExpiredError | ||
from kazoo.retry import RetryFailedError | ||
|
||
with pytest.raises(ConnectionClosedError): | ||
retry(testit) | ||
retry = _makeOne(max_tries=1) | ||
|
||
def test_session_expired(self): | ||
from kazoo.exceptions import SessionExpiredError | ||
def testit(): | ||
raise SessionExpiredError() | ||
|
||
retry = self._makeOne(max_tries=1) | ||
with pytest.raises(RetryFailedError) as err: | ||
retry(testit) | ||
|
||
def testit(): | ||
raise SessionExpiredError() | ||
retry = _makeOne(max_tries=1, ignore_expire=False) | ||
|
||
with pytest.raises(Exception): | ||
retry(testit) | ||
with pytest.raises(SessionExpiredError) as err: | ||
retry(testit) |