Created
April 2, 2017 02:11
-
-
Save jcahill/bcd4b5d165da3f8960ab1cd1cd50a0ab to your computer and use it in GitHub Desktop.
Chat Bridge Backoff
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
import random | |
import time | |
class ExponentialBackoff: | |
"""An implementation of the exponential backoff algorithm | |
Provides a convenient interface to implement an exponential backoff | |
for reconnecting or retrying transmissions in a distributed network. | |
Once instantiated, the delay method will return the next interval to | |
wait for when retrying a connection or transmission. The maximum | |
delay increases exponentially with each retry up to a maximum of | |
2^10 * base, and is reset if no more attempts are needed in a period | |
of 2^11 * base seconds. | |
Parameters | |
---------- | |
base | |
The base delay in seconds. The first retry-delay will be up to | |
this many seconds. Defaults to 1 | |
integral : bool | |
Set to True if whole periods of base is desirable, otherwise any | |
number in between may be returnd. Defaults to False. | |
""" | |
def __init__(self, base=1, *, integral=False): | |
self._base = base | |
self._exp = 0 | |
self._max = 10 | |
self._reset_time = base * 2 ** 11 | |
self._last_invocation = time.monotonic() | |
# Use our own random instance to avoid messing with global one | |
rand = random.Random() | |
rand.seed() | |
self._randfunc = rand.rand_range if integral else rand.uniform | |
def delay(self): | |
"""Compute the next delay | |
Returns the next delay to wait according to the exponential | |
backoff algorithm. This is a value between 0 and base * 2^exp | |
where exponent starts off at 1 and is incremented at every | |
invocation of this method up to a maximum of 10. | |
If a period of more than base * 2^11 has passed since the last | |
retry, the exponent is reset to 1. | |
""" | |
invocation = time.monotonic() | |
interval = invocation - self._last_invocation | |
self._last_invocation = invocation | |
if interval > self._reset_time: | |
self._exp = 0 | |
self._exp = min(self._exp + 1, self._max) | |
return self._randfunc(0, self._base * 2 ** self._exp) |
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
import logging | |
import asyncio | |
import aiohttp | |
import discord | |
import websockets | |
import backoff | |
async def keep_running(client, token): | |
retry = backoff.ExponentialBackoff() | |
while True: | |
try: | |
await client.login(token) | |
except (discord.HTTPException, aiohttp.ClientError): | |
logging.exception("Discord.py pls login") | |
await asyncio.sleep(retry.delay()) | |
else: | |
break | |
while client.is_logged_in: | |
if client.is_closed: | |
client._closed.clear() | |
client.http.recreate() | |
try: | |
await client.connect() | |
except (discord.HTTPException, aiohttp.ClientError, | |
discord.GatewayNotFound, discord.ConnectionClosed, | |
websockets.InvalidHandshake, | |
websockets.WebSocketProtocolError) as e: | |
if isinstance(e, discord.ConnectionClosed) and e.code == 4004: | |
raise # Do not reconnect on authentication failure | |
logging.exception("Discord.py pls keep running") | |
await asyncio.sleep(retry.delay()) | |
logging.basicConfig(level=logging.INFO) | |
client = discord.Client() | |
token = '<token>' | |
asyncio.get_event_loop().run_until_complete(keep_running(client, token)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment