]> git.donarmstrong.com Git - dsa-puppet.git/blobdiff - 3rdparty/modules/aviator/lib/puppet/feature/faraday/request/retry.rb
try again, with puppetforge modules, correctly included now
[dsa-puppet.git] / 3rdparty / modules / aviator / lib / puppet / feature / faraday / request / retry.rb
diff --git a/3rdparty/modules/aviator/lib/puppet/feature/faraday/request/retry.rb b/3rdparty/modules/aviator/lib/puppet/feature/faraday/request/retry.rb
new file mode 100644 (file)
index 0000000..08bc837
--- /dev/null
@@ -0,0 +1,140 @@
+module Faraday
+  # Catches exceptions and retries each request a limited number of times.
+  #
+  # By default, it retries 2 times and handles only timeout exceptions. It can
+  # be configured with an arbitrary number of retries, a list of exceptions to
+  # handle, a retry interval, a percentage of randomness to add to the retry
+  # interval, and a backoff factor.
+  #
+  # Examples
+  #
+  #   Faraday.new do |conn|
+  #     conn.request :retry, max: 2, interval: 0.05,
+  #                          interval_randomness: 0.5, backoff_factor: 2
+  #                          exceptions: [CustomException, 'Timeout::Error']
+  #     conn.adapter ...
+  #   end
+  #
+  # This example will result in a first interval that is random between 0.05 and 0.075 and a second
+  # interval that is random between 0.1 and 0.15
+  #
+  class Request::Retry < Faraday::Middleware
+
+    IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
+
+    class Options < Faraday::Options.new(:max, :interval, :interval_randomness, :backoff_factor, :exceptions, :retry_if)
+      DEFAULT_CHECK = lambda { |env,exception| false }
+
+      def self.from(value)
+        if Fixnum === value
+          new(value)
+        else
+          super(value)
+        end
+      end
+
+      def max
+        (self[:max] ||= 2).to_i
+      end
+
+      def interval
+        (self[:interval] ||= 0).to_f
+      end
+
+      def interval_randomness
+        (self[:interval_randomness] ||= 0).to_i
+      end
+
+      def backoff_factor
+        (self[:backoff_factor] ||= 1).to_f
+      end
+
+      def exceptions
+        Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
+                                     Error::TimeoutError])
+      end
+
+      def retry_if
+        self[:retry_if] ||= DEFAULT_CHECK
+      end
+
+    end
+
+    # Public: Initialize middleware
+    #
+    # Options:
+    # max                 - Maximum number of retries (default: 2)
+    # interval            - Pause in seconds between retries (default: 0)
+    # interval_randomness - The maximum random interval amount expressed
+    #                       as a float between 0 and 1 to use in addition to the
+    #                       interval. (default: 0)
+    # backoff_factor      - The amount to multiple each successive retry's
+    #                       interval amount by in order to provide backoff
+    #                       (default: 1)
+    # exceptions          - The list of exceptions to handle. Exceptions can be
+    #                       given as Class, Module, or String. (default:
+    #                       [Errno::ETIMEDOUT, Timeout::Error,
+    #                       Error::TimeoutError])
+    # retry_if            - block that will receive the env object and the exception raised
+    #                       and should decide if the code should retry still the action or
+    #                       not independent of the retry count. This would be useful
+    #                       if the exception produced is non-recoverable or if the
+    #                       the HTTP method called is not idempotent.
+    #                       (defaults to return false)
+    def initialize(app, options = nil)
+      super(app)
+      @options = Options.from(options)
+      @errmatch = build_exception_matcher(@options.exceptions)
+    end
+
+    def sleep_amount(retries)
+      retry_index = @options.max - retries
+      current_interval = @options.interval * (@options.backoff_factor ** retry_index)
+      random_interval  = rand * @options.interval_randomness.to_f * @options.interval
+      current_interval + random_interval
+    end
+
+    def call(env)
+      retries = @options.max
+      request_body = env[:body]
+      begin
+        env[:body] = request_body # after failure env[:body] is set to the response body
+        @app.call(env)
+      rescue @errmatch => exception
+        if retries > 0 && retry_request?(env, exception)
+          retries -= 1
+          sleep sleep_amount(retries + 1)
+          retry
+        end
+        raise
+      end
+    end
+
+    # Private: construct an exception matcher object.
+    #
+    # An exception matcher for the rescue clause can usually be any object that
+    # responds to `===`, but for Ruby 1.8 it has to be a Class or Module.
+    def build_exception_matcher(exceptions)
+      matcher = Module.new
+      (class << matcher; self; end).class_eval do
+        define_method(:===) do |error|
+          exceptions.any? do |ex|
+            if ex.is_a? Module
+              error.is_a? ex
+            else
+              error.class.to_s == ex.to_s
+            end
+          end
+        end
+      end
+      matcher
+    end
+
+    private
+
+    def retry_request?(env, exception)
+      IDEMPOTENT_METHODS.include?(env[:method]) || @options.retry_if.call(env, exception)
+    end
+
+  end
+end