def perform_op(method, req_hash, options={}, &blk)
      set_defaults(options)
      max_retries = options[:max_retries] || 5
      max_follows = options[:max_follows] || 10
      if options[:follow_count] && options[:follow_count] >= max_follows
        raise Rest::RestError, "Too many follows. #{options[:follow_count]}"
      end
      current_retry = 0
      current_follow = 0
      success = false
      tries = 0
      res = nil
      #  todo: typhoeus does retries in the library so it shouldn't do retries here. And we should use the max_retries here as a parameter to typhoeus
      while current_retry < max_retries && current_follow < max_follows do
        tries += 1
        begin
          res = yield blk
          res.tries = tries
          if res.code >= 300 && res.code < 400
            # try new location
            #p res.headers
            loc = res.headers["location"]
            @logger.debug "#{res.code} Received. Trying new location: #{loc}"
            if loc.nil?
              raise InvalidResponseError.new("No location header received with #{res.code} status code!")
            end
            # options.merge({:max_follows=>options[:max_follows-1]}
            options[:follow_count] ||= 0
            options[:follow_count] += 1
            res = perform_op(method, req_hash, options) do
              res = @wrapper.send(method, loc, req_hash)
            end
            #puts 'X: ' + res.inspect
            return res
          end
          # If it's here, then it's all good
          break
        rescue Rest::HttpError => ex
          if ex.code == 503
            raise ex if current_retry == max_retries - 1

            pow = (4 ** (current_retry)) * 100 # milliseconds
            #puts 'pow=' + pow.to_s
            s = rand * pow
            #puts 's=' + s.to_s
            sleep_secs = 1.0 * s / 1000.0
            #puts 'sleep for ' + sleep_secs.to_s
            current_retry += 1
            @logger.debug "#{ex.code} Received. Retrying #{current_retry} out of #{max_retries} max in #{sleep_secs} seconds."
            sleep sleep_secs
          else
            raise ex
          end
        end
      end
      res
    end
def perform_op(method, req_hash, options={}, &blk)
      set_defaults(options)
      max_retries = options[:max_retries] || 5
      max_follows = options[:max_follows] || 10
      if options[:follow_count] && options[:follow_count] >= max_follows
        raise Rest::RestError, "Too many follows. #{options[:follow_count]}"
      end
      current_retry = 0
      current_follow = 0
      success = false
      tries = 0
      res = nil
      #  todo: typhoeus does retries in the library so it shouldn't do retries here. And we should use the max_retries here as a parameter to typhoeus
      while current_retry < max_retries && current_follow < max_follows do
        tries += 1
        begin
          res = yield blk
          res.tries = tries
          if res.code >= 300 && res.code < 400
            # try new location
            #p res.headers
            loc = res.headers["location"]
            @logger.debug "#{res.code} Received. Trying new location: #{loc}"
            if loc.nil?
              raise InvalidResponseError.new("No location header received with #{res.code} status code!")
            end
            # options.merge({:max_follows=>options[:max_follows-1]}
            options[:follow_count] ||= 0
            options[:follow_count] += 1
            res = perform_op(method, req_hash, options) do
              res = @wrapper.send(method, loc, req_hash)
            end
            #puts 'X: ' + res.inspect
            return res
          end
          # If it's here, then it's all good
          break
        rescue Rest::HttpError => ex
          if ex.code == 503
            raise ex if current_retry == max_retries - 1

            pow = (4 ** (current_retry)) * 100 # milliseconds
            #puts 'pow=' + pow.to_s
            s = rand * pow
            #puts 's=' + s.to_s
            sleep_secs = 1.0 * s / 1000.0
            #puts 'sleep for ' + sleep_secs.to_s
            current_retry += 1
            @logger.debug "#{ex.code} Received. Retrying #{current_retry} out of #{max_retries} max in #{sleep_secs} seconds."
            sleep sleep_secs
          else
            raise ex
          end
        end
      end
      res
    end
This will attempt to perform the operation with an exponential backoff on 503 errors.
 Amazon services throw 503
 todo: just make perform_op a method and have it call the wrapper. The block is a waste now.