Beispiel #1
0
class TokenHandler(IPythonHandler):
    @retry(wait=wait_fixed(3),
           retry=(retry_if_exception_type(RuntimeError)
                  & retry_if_exception_message(
                      match=r'[Nn]o authorization code found.*')),
           stop=stop_after_delay(AssembleApp.instance().access_token_timeout))
    async def get_token(self, url, data):
        response = requests.post(url, json=data)
        d = response.json()
        if response.ok:
            # TODO also store other token info
            return d['access_token']
        else:
            reason = d.get('message', '').lower()
            raise RuntimeError(reason)

    @tornado.web.authenticated
    async def post(self):
        """request token if we have just authenticated"""
        app = AssembleApp.instance()
        base = app.oauth_gateway_url
        state = app.state
        url = urljoin(base, '/api/v1/access_token')
        data = {'state': state}

        try:
            token = await self.get_token(url, data)
            app.set_github_token(token)
            self.finish()
        except RetryError:
            self.send_error(status_code=400, reason='timeout')
        except RuntimeError as e:
            self.send_error(status_code=400, reason=str(e))

        app.reset_state()
Beispiel #2
0
def retry_on_db_error(func, retry=None):
    """Decorates the given function so that it retries on DB errors.

    Note that the decorator retries the function/method only on some
    of the DB errors that are considered to be worth retrying, like
    deadlocks and disconnections.

    :param func: Function to decorate.
    :param retry: a Retrying object
    :return: Decorated function.
    """
    if not retry:
        retry = tenacity.Retrying(
            retry=(
                tenacity.retry_if_exception_type(_RETRY_ERRORS) |
                tenacity.retry_if_exception_message(
                    match='Too many connections'
                )
            ),
            stop=tenacity.stop_after_attempt(50),
            wait=tenacity.wait_incrementing(start=0, increment=0.1, max=2)
        )

    # The `assigned` arg should be empty as some of the default values are not
    # supported by simply initialized MagicMocks. The consequence may
    # be that the representation will contain the wrapper and not the
    # wrapped function.
    @functools.wraps(func, assigned=[])
    def decorate(*args, **kw):
        # Retrying library decorator might potentially run a decorated
        # function within a new thread so it's safer not to apply the
        # decorator directly to a target method/function because we can
        # lose an authentication context.
        # The solution is to create one more function and explicitly set
        # auth context before calling it (potentially in a new thread).
        auth_ctx = context.ctx() if context.has_ctx() else None

        return retry.call(_with_auth_context, auth_ctx, func, *args, **kw)

    return decorate
Beispiel #3
0
 def test_retry_if_exception_message_negative_too_many_inputs(self):
     with self.assertRaises(TypeError):
         tenacity.retry_if_exception_message(message="negative",
                                             match="negative")
Beispiel #4
0
 def test_retry_if_exception_message_negative_no_inputs(self):
     with self.assertRaises(TypeError):
         tenacity.retry_if_exception_message()
Beispiel #5
0
    return thing.go()


@retry(stop=tenacity.stop_after_attempt(3),
       retry=tenacity.retry_unless_exception_type(NameError))
def _retryable_test_with_unless_exception_type_name_attempt_limit(thing):
    return thing.go()


@retry(retry=tenacity.retry_unless_exception_type())
def _retryable_test_with_unless_exception_type_no_input(thing):
    return thing.go()


@retry(stop=tenacity.stop_after_attempt(5),
       retry=tenacity.retry_if_exception_message(
           message=NoCustomErrorAfterCount.derived_message))
def _retryable_test_if_exception_message_message(thing):
    return thing.go()


@retry(retry=tenacity.retry_if_not_exception_message(
    message=NoCustomErrorAfterCount.derived_message))
def _retryable_test_if_not_exception_message_message(thing):
    return thing.go()


@retry(retry=tenacity.retry_if_exception_message(
    match=NoCustomErrorAfterCount.derived_message[:3] + ".*"))
def _retryable_test_if_exception_message_match(thing):
    return thing.go()
Beispiel #6
0
class RetryRequest:
    """
    Make requests to a URL, but retry under certain conditions to tolerate server graceful shutdown.
    """
    def __init__(self, request, method, url, auth, request_headers,
                 request_json, return_json):
        self.request = request
        self.method = method
        self.url = url
        self.auth = auth
        self.headers = request_headers
        self.json = request_json
        self.return_json = return_json

    def __handle_response(self, response):
        try:
            response.raise_for_status()
        except ClientResponseError as ex:
            raise ex
        else:
            logger.debug('successfully connected to service',
                         client_ip=self.request['client_ip'],
                         client_id=self.request['client_id'],
                         trace=self.request['trace'],
                         url=self.url)

    @retry(reraise=True,
           stop=stop_after_attempt(basic_attempt_limit),
           wait=wait_exponential(multiplier=wait_multiplier, exp_base=25),
           after=after_failed_basic,
           retry=(retry_if_exception_message(match='503.*')
                  | retry_if_exception_type(
                      (ClientConnectionError, ClientConnectorError))))
    async def _request_basic(self):
        # basic request without keep-alive to avoid terminating service.
        logger.info('request using basic connection',
                    client_ip=self.request['client_ip'],
                    client_id=self.request['client_id'],
                    trace=self.request['trace'])

        async with aiohttp.request(self.method,
                                   self.url,
                                   auth=self.auth,
                                   json=self.json,
                                   headers=self.headers) as resp:
            self.__handle_response(resp)
            if self.return_json:
                return await resp.json()
            else:
                return None

    @retry(stop=stop_after_attempt(pooled_attempts_limit),
           wait=wait_exponential(multiplier=wait_multiplier),
           after=after_failed_pooled,
           retry=(retry_if_exception_message(match='503.*')
                  | retry_if_exception_type(
                      (ClientConnectionError, ClientConnectorError))))
    async def _request_using_pool(self):
        async with self.request.app.http_session_pool.request(
                self.method,
                self.url,
                auth=self.auth,
                json=self.json,
                headers=self.headers,
                ssl=False) as resp:
            self.__handle_response(resp)
            if self.return_json:
                return await resp.json()
            else:
                return None

    async def make_request(self):
        """
        Make a request with retries.
        First the fast pooled connection will be tried, but if certain failures are detected, then it will be retried.
        If the retry limit is reached then a basic connection will be tried (and retried if necessary)
        Finally the error will be propagated.
        """
        logger.debug('making request with handler',
                     client_ip=self.request['client_ip'],
                     client_id=self.request['client_id'],
                     trace=self.request['trace'],
                     method=self.method,
                     url=self.url)
        try:
            try:
                return await self._request_using_pool()
            except RetryError as retry_ex:
                attempts = retry_ex.last_attempt.attempt_number
                logger.warn(
                    'Could not make request using normal pooled connection',
                    client_ip=self.request['client_ip'],
                    client_id=self.request['client_id'],
                    trace=self.request['trace'],
                    attempts=attempts)
                return await self._request_basic()
        except ClientResponseError as ex:
            if ex.status not in [400, 404, 429]:
                logger.error('error in response',
                             client_ip=self.request['client_ip'],
                             client_id=self.request['client_id'],
                             trace=self.request['trace'],
                             url=self.url,
                             status_code=ex.status)
            elif ex.status == 429:
                self.log_too_many_requests(ex)
            elif ex.status == 400:
                logger.warn('bad request',
                            client_ip=self.request['client_ip'],
                            client_id=self.request['client_id'],
                            trace=self.request['trace'],
                            url=self.url,
                            status_code=ex.status)
            raise ex
        except (ClientConnectionError, ClientConnectorError) as ex:
            logger.error('client failed to connect',
                         client_ip=self.request['client_ip'],
                         client_id=self.request['client_id'],
                         trace=self.request['trace'],
                         url=self.url)
            raise ex

    def log_too_many_requests(self, ex: ClientResponseError):
        ai_svc_url = self.request.app['ADDRESS_INDEX_SVC_URL']
        tracking = {
            "client_ip": self.request['client_ip'],
            "client_id": self.request['client_id'],
            "trace": self.request['trace']
        }
        if ai_svc_url in self.url:
            logger.error('error in AIMS response',
                         **tracking,
                         url=self.url,
                         status_code=ex.status)
        else:
            logger.warn('too many requests',
                        **tracking,
                        url=self.url,
                        status_code=ex.status)
Beispiel #7
0
    else:
        thank_you = ""
    context = {
        "order": order,
        "thank_you": thank_you,
        "item_type": item_type,
        "prep": prep
    }
    if seller:
        context["seller"] = True
    return render_to_string("shop/email.html", context=context)

@retry(
    wait=wait_exponential(multiplier=1, min=4, max=10),
    stop=stop_after_attempt(4),
    retry=retry_if_exception_message(message='RETRY')
)
def send_email(recipient, subject, text):
    response = requests.post(
        settings.MAILGUN_BASE_URL + "messages",
        auth=("api", settings.MAILGUN_PRIVATE_KEY),
        data={
            "from": settings.MAILGUN_SENDER,
            "to": [recipient],
            "subject": subject,
            "html": text
        }
    )
    if response.status_code == 200:
        return {'status': response.status_code}
    elif response.status_code == 400:
Beispiel #8
0
# third parties libraries
import tenacity

from socket import timeout

# whre all the bad requests are tried again...
# https://developers.google.com/drive/api/v3/handle-errors#exponential-backoff
MAX_ATTEMPTS = 30
EXP_MULTIPLIER = 0.5
EXP_MAX_WAIT = 60

# In what case should tenacity try again?
retry_exceptions = (

    # https://developers.google.com/drive/api/v3/handle-errors#403_user_rate_limit_exceeded
    tenacity.retry_if_exception_message(match=r".+?User Rate Limit Exceeded\.")
    | tenacity.retry_if_exception_message(match=r".+?Rate limit exceeded\.")

    # https://developers.google.com/drive/api/v3/handle-errors#500_backend_error
    | tenacity.retry_if_exception_message(match=r".+?Internal Error")
    | tenacity.retry_if_exception_message(match=r".+?Transient failure")
    | tenacity.retry_if_exception_message(
        match=r".+?The read operation timed out")
    | tenacity.retry_if_exception_message(match=r".+?HttpError 500")
    | tenacity.retry_if_exception_type(timeout)
    | tenacity.retry_if_exception_type(ConnectionResetError))


def before_sleep_print(retry_state):
    """Before call strategy that logs to some logger the attempt."""
Beispiel #9
0
# third parties libraries
import tenacity

# whre all the bad requests are tried again...
# https://developers.google.com/drive/api/v3/handle-errors#exponential-backoff
MAX_ATTEMPTS = 30
EXP_MULTIPLIER = 0.5
EXP_MAX_WAIT = 60

# In what case should tenacity try again?
retry_exceptions = (
    # https://developers.google.com/drive/api/v3/handle-errors#403_user_rate_limit_exceeded
    tenacity.retry_if_exception_message(match=r".+?User Rate Limit Exceeded\.")
    |
    # https://developers.google.com/drive/api/v3/handle-errors#500_backend_error
    tenacity.retry_if_exception_message(match=r".+?Internal Error"))


@tenacity.retry(stop=tenacity.stop_after_attempt(MAX_ATTEMPTS),
                wait=tenacity.wait_exponential(multiplier=EXP_MULTIPLIER,
                                               max=EXP_MAX_WAIT),
                retry=retry_exceptions)
def call_endpoint(endpoint, params):
    return endpoint(**params).execute()


@tenacity.retry(stop=tenacity.stop_after_attempt(MAX_ATTEMPTS),
                wait=tenacity.wait_exponential(multiplier=EXP_MULTIPLIER,
                                               max=EXP_MAX_WAIT),
                retry=retry_exceptions)
def execute_request(request):