def retry_on_connection_failure
      retries = 0
      begin
        connect if closed?
        yield(self)
      rescue ConnectionFailure => exception
        exc_str = exception.cause ? "#{exception.cause.class}: #{exception.cause.message}" : exception.message
        # Re-raise exceptions that should not be retried
        if !self.class.reconnect_on_errors.include?(exception.cause.class)
          logger.info "#retry_on_connection_failure not configured to retry: #{exc_str}" if respond_to?(:logger)
          raise exception
        elsif retries < @retry_count
          retries += 1
          logger.warn "#retry_on_connection_failure retry #{retries} due to #{exception.class}: #{exception.message}" if respond_to?(:logger)
          connect
          retry
        end
        logger.error "#retry_on_connection_failure Connection failure: #{exception.class}: #{exception.message}. Giving up after #{retries} retries" if respond_to?(:logger)
        raise ConnectionFailure.new("After #{retries} retries to host '#{server}': #{exc_str}", server, exception.cause)
      end
    end
def retry_on_connection_failure
      retries = 0
      begin
        connect if closed?
        yield(self)
      rescue ConnectionFailure => exception
        exc_str = exception.cause ? "#{exception.cause.class}: #{exception.cause.message}" : exception.message
        # Re-raise exceptions that should not be retried
        if !self.class.reconnect_on_errors.include?(exception.cause.class)
          logger.info "#retry_on_connection_failure not configured to retry: #{exc_str}" if respond_to?(:logger)
          raise exception
        elsif retries < @retry_count
          retries += 1
          logger.warn "#retry_on_connection_failure retry #{retries} due to #{exception.class}: #{exception.message}" if respond_to?(:logger)
          connect
          retry
        end
        logger.error "#retry_on_connection_failure Connection failure: #{exception.class}: #{exception.message}. Giving up after #{retries} retries" if respond_to?(:logger)
        raise ConnectionFailure.new("After #{retries} retries to host '#{server}': #{exc_str}", server, exception.cause)
      end
    end
Write and/or receive data with automatic retry on connection failure

 On a connection failure, it will create a new connection and retry the block.
 Returns immediately on exception Net::TCPClient::ReadTimeout
 The connection is always closed on Net::TCPClient::ConnectionFailure regardless of close_on_error

 1. Example of a resilient _readonly_ request:

    When reading data from a server that does not change state on the server
    Wrap both the write and the read with #retry_on_connection_failure
    since it is safe to write the same data twice to the server

    # Since the write can be sent many times it is safe to also put the receive
    # inside the retry block
    value = client.retry_on_connection_failure do
      client.write("GETVALUE:count\n")
      client.read(20).strip.to_i
    end

 2. Example of a resilient request that _modifies_ data on the server:

    When changing state on the server, for example when updating a value
    Wrap _only_ the write with #retry_on_connection_failure
    The read must be outside the #retry_on_connection_failure since we must
    not retry the write if the connection fails during the #read

    value = 45
    # Only the write is within the retry block since we cannot re-write once
    # the write was successful since the server may have made the change
    client.retry_on_connection_failure do
      client.write("SETVALUE:#{count}\n")
    end
    # Server returns "SAVED" if the call was successful
    result = client.read(20).strip

 Error handling is implemented as follows:
    If a network failure occurs during the block invocation the block
    will be called again with a new connection to the server.
    It will only be retried up to 3 times
    The re-connect will independently retry and timeout using all the
    rules of #connect