Move backoff classes to the Zulip API

(imported from commit 76c5d499874f0397c505ab3fcda631a1a46847b6)
This commit is contained in:
Zev Benjamin 2014-03-11 14:09:15 -04:00
parent ee04f7b3e8
commit 41a782e011
3 changed files with 37 additions and 35 deletions

View file

@ -28,7 +28,6 @@ import traceback
import signal import signal
from zephyr_mirror_backend import parse_args from zephyr_mirror_backend import parse_args
from zephyr_mirror_backend import RandomExponentialBackoff
def die(signal, frame): def die(signal, frame):
# We actually want to exit, so run os._exit (so as not to be caught and restarted) # We actually want to exit, so run os._exit (so as not to be caught and restarted)
@ -38,6 +37,9 @@ signal.signal(signal.SIGINT, die)
(options, args) = parse_args() (options, args) = parse_args()
sys.path[:0] = [os.path.join(options.root_path, 'api')]
from zulip import RandomExponentialBackoff
args = [os.path.join(options.root_path, "user_root", "zephyr_mirror_backend.py")] args = [os.path.join(options.root_path, "user_root", "zephyr_mirror_backend.py")]
args.extend(sys.argv[1:]) args.extend(sys.argv[1:])

View file

@ -37,39 +37,8 @@ import signal
import logging import logging
import hashlib import hashlib
import tempfile import tempfile
import random
import select import select
class CountingBackoff(object):
def __init__(self, maximum_retries=10):
self.number_of_retries = 0
self.maximum_retries = maximum_retries
def keep_going(self):
return self.number_of_retries < self.maximum_retries
def succeed(self):
self.number_of_retries = 0
def fail(self):
self.number_of_retries = min(self.number_of_retries + 1,
self.maximum_retries)
class RandomExponentialBackoff(CountingBackoff):
def fail(self):
self.number_of_retries = min(self.number_of_retries + 1,
self.maximum_retries)
# Exponential growth with ratio sqrt(2); compute random delay
# between x and 2x where x is growing exponentially
delay_scale = int(2 ** (self.number_of_retries / 2.0 - 1)) + 1
delay = delay_scale + random.randint(1, delay_scale)
message = "Sleeping for %ss [max %s] before retrying." % (delay, delay_scale * 2)
try:
logger.warning(message)
except NameError:
print message
time.sleep(delay)
DEFAULT_SITE = "https://api.zulip.com" DEFAULT_SITE = "https://api.zulip.com"
class States: class States:
@ -478,7 +447,7 @@ def quit_failed_initialization(message):
sys.exit(1) sys.exit(1)
def zephyr_init_autoretry(): def zephyr_init_autoretry():
backoff = RandomExponentialBackoff() backoff = zulip.RandomExponentialBackoff()
while backoff.keep_going(): while backoff.keep_going():
try: try:
# zephyr.init() tries to clear old subscriptions, and thus # zephyr.init() tries to clear old subscriptions, and thus
@ -493,7 +462,7 @@ def zephyr_init_autoretry():
quit_failed_initialization("Could not initialize Zephyr library, quitting!") quit_failed_initialization("Could not initialize Zephyr library, quitting!")
def zephyr_load_session_autoretry(session_path): def zephyr_load_session_autoretry(session_path):
backoff = RandomExponentialBackoff() backoff = zulip.RandomExponentialBackoff()
while backoff.keep_going(): while backoff.keep_going():
try: try:
session = file(session_path, "r").read() session = file(session_path, "r").read()
@ -508,7 +477,7 @@ def zephyr_load_session_autoretry(session_path):
quit_failed_initialization("Could not load saved Zephyr session, quitting!") quit_failed_initialization("Could not load saved Zephyr session, quitting!")
def zephyr_subscribe_autoretry(sub): def zephyr_subscribe_autoretry(sub):
backoff = RandomExponentialBackoff() backoff = zulip.RandomExponentialBackoff()
while backoff.keep_going(): while backoff.keep_going():
try: try:
zephyr.Subscriptions().add(sub) zephyr.Subscriptions().add(sub)

View file

@ -30,6 +30,7 @@ import os
import optparse import optparse
import platform import platform
import urllib import urllib
import random
from distutils.version import LooseVersion from distutils.version import LooseVersion
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
@ -46,6 +47,36 @@ requests_json_is_function = callable(requests.Response.json)
API_VERSTRING = "v1/" API_VERSTRING = "v1/"
class CountingBackoff(object):
def __init__(self, maximum_retries=10):
self.number_of_retries = 0
self.maximum_retries = maximum_retries
def keep_going(self):
return self.number_of_retries < self.maximum_retries
def succeed(self):
self.number_of_retries = 0
def fail(self):
self.number_of_retries = min(self.number_of_retries + 1,
self.maximum_retries)
class RandomExponentialBackoff(CountingBackoff):
def fail(self):
self.number_of_retries = min(self.number_of_retries + 1,
self.maximum_retries)
# Exponential growth with ratio sqrt(2); compute random delay
# between x and 2x where x is growing exponentially
delay_scale = int(2 ** (self.number_of_retries / 2.0 - 1)) + 1
delay = delay_scale + random.randint(1, delay_scale)
message = "Sleeping for %ss [max %s] before retrying." % (delay, delay_scale * 2)
try:
logger.warning(message)
except NameError:
print message
time.sleep(delay)
def _default_client(): def _default_client():
return "ZulipPython/" + __version__ return "ZulipPython/" + __version__