parameters: (metric=None, metric_tags=None)
return_statement: return backoff.on_exception(
          backoff.expo, StatementError, max_tries=3, giveup=giveup_handler
      )
docstring: 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
docstring_summary: Function decorator to backoff and retry on MySQL lock failures.
docstring_tokens: Function decorator to backoff and retry on MySQL lock failures .
function: 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
      )
function_tokens: 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 )