(metric=None, metric_tags=None)
return backoff.on_exception(
        backoff.expo, StatementError, max_tries=3, giveup=giveup_handler
    )
Function decorator to backoff and retry on MySQL lock failures.

    This handles these MySQL errors:
    * (1205) Lock wait timeout exceeded
    * (1213) Deadlock when trying to get lock

    In both cases, restarting the transaction may work.

    It expects the errors to be wrapped in a SQLAlchemy StatementError, and
    that SQLAlchemy issued a transaction rollback, so it is safe to retry after
    a short sleep. It uses backoff.on_exception to implement the exponential
    backoff.

    Other exceptions are raised, and if limits are met, then the final
    exception is raised as well.

    :arg str metric: An optional counter metric to track handled errors
    :arg list metric_tags: Additional tags to send with the metric
    :return: A function decorator implementing the retry logic
Function decorator to backoff and retry on MySQL lock failures.
Function decorator to backoff and retry on MySQL lock failures .
def retry_on_mysql_lock_fail(metric=None, metric_tags=None):
    """Function decorator to backoff and retry on MySQL lock failures.

    This handles these MySQL errors:
    * (1205) Lock wait timeout exceeded
    * (1213) Deadlock when trying to get lock

    In both cases, restarting the transaction may work.

    It expects the errors to be wrapped in a SQLAlchemy StatementError, and
    that SQLAlchemy issued a transaction rollback, so it is safe to retry after
    a short sleep. It uses backoff.on_exception to implement the exponential
    backoff.

    Other exceptions are raised, and if limits are met, then the final
    exception is raised as well.

    :arg str metric: An optional counter metric to track handled errors
    :arg list metric_tags: Additional tags to send with the metric
    :return: A function decorator implementing the retry logic
    """

    def is_mysql_lock_error(exception):
        """Is the exception a retryable MySQL lock error?"""
        return (
            isinstance(exception, StatementError)
            and isinstance(exception.orig, MySQLError)
            and exception.orig.args[0] in (LOCK_DEADLOCK, LOCK_WAIT_TIMEOUT)
        )

    def count_exception(exception):
        """Increment the tracking metric for lock errors."""
        tags = ["errno:%s" % exception.orig.args[0]]
        if metric_tags:
            tags.extend(metric_tags)
        METRICS.incr(metric, 1, tags=tags)

    def giveup_handler(exception):
        """Based on this raised exception, should we give up or retry?

        If it is a SQLAlchemy wrapper for a retryable MySQL exception,
        then we should increment a metric and retry.

        If it isn't one of the special exceptions, then give up.
        """
        if is_mysql_lock_error(exception):
            if metric:
                count_exception(exception)
            return False  # Retry if possible
        return True  # Give up on other unknown errors.

    return backoff.on_exception(
        backoff.expo, StatementError, max_tries=3, giveup=giveup_handler
    )
def retry_on_mysql_lock_fail ( metric = None , metric_tags = None ) : def is_mysql_lock_error ( exception ) : """Is the exception a retryable MySQL lock error?""" return ( isinstance ( exception , StatementError ) and isinstance ( exception . orig , MySQLError ) and exception . orig . args [ 0 ] in ( LOCK_DEADLOCK , LOCK_WAIT_TIMEOUT ) ) def count_exception ( exception ) : """Increment the tracking metric for lock errors.""" tags = [ "errno:%s" % exception . orig . args [ 0 ] ] if metric_tags : tags . extend ( metric_tags ) METRICS . incr ( metric , 1 , tags = tags ) def giveup_handler ( exception ) : """Based on this raised exception, should we give up or retry?

        If it is a SQLAlchemy wrapper for a retryable MySQL exception,
        then we should increment a metric and retry.

        If it isn't one of the special exceptions, then give up.
        """ if is_mysql_lock_error ( exception ) : if metric : count_exception ( exception ) return False # Retry if possible return True # Give up on other unknown errors. return backoff . on_exception ( backoff . expo , StatementError , max_tries = 3 , giveup = giveup_handler )