retry.py 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import socket
  2. from time import sleep
  3. from redis.exceptions import ConnectionError, TimeoutError
  4. class Retry:
  5. """Retry a specific number of times after a failure"""
  6. def __init__(
  7. self,
  8. backoff,
  9. retries,
  10. supported_errors=(ConnectionError, TimeoutError, socket.timeout),
  11. ):
  12. """
  13. Initialize a `Retry` object with a `Backoff` object
  14. that retries a maximum of `retries` times.
  15. `retries` can be negative to retry forever.
  16. You can specify the types of supported errors which trigger
  17. a retry with the `supported_errors` parameter.
  18. """
  19. self._backoff = backoff
  20. self._retries = retries
  21. self._supported_errors = supported_errors
  22. def update_supported_errors(self, specified_errors: list):
  23. """
  24. Updates the supported errors with the specified error types
  25. """
  26. self._supported_errors = tuple(
  27. set(self._supported_errors + tuple(specified_errors))
  28. )
  29. def call_with_retry(self, do, fail):
  30. """
  31. Execute an operation that might fail and returns its result, or
  32. raise the exception that was thrown depending on the `Backoff` object.
  33. `do`: the operation to call. Expects no argument.
  34. `fail`: the failure handler, expects the last error that was thrown
  35. """
  36. self._backoff.reset()
  37. failures = 0
  38. while True:
  39. try:
  40. return do()
  41. except self._supported_errors as error:
  42. failures += 1
  43. fail(error)
  44. if self._retries >= 0 and failures > self._retries:
  45. raise error
  46. backoff = self._backoff.compute(failures)
  47. if backoff > 0:
  48. sleep(backoff)