Module: Garcon::Retry

Extended by:
Retry
Included in:
Provider, Retry
Defined in:
lib/garcon/utility/retry.rb

Overview

Class methods that are added when you include Garcon::Retry

Instance Method Summary collapse

Instance Method Details

#patiently(wait = 8, delay = 0.1) {|Proc| ... } ⇒ Proc

Similar to ‘#poll`, `#patiently` also executes an arbitrary code block. If the passed block runs without raising an error, execution proceeds normally. If an error is raised, the block is rerun after a brief delay, until the block can be run without exceptions. If exceptions continue to raise, `#patiently` gives up after a bit (default 8 seconds) by re-raising the most recent exception raised by the block.

Examples:

Returns immedialtely if no errors or as soon as error stops.
  patiently { ... }

Increase patience to 10 seconds.
  patiently(10)    { ... }

Increase patience to 20 seconds, and delay for 3 seconds before retry.
  patiently(20, 3) { ... }

Parameters:

  • seconds (Integer)

    number of seconds to be patient, default is 8 seconds

  • delay (Integer) (defaults to: 0.1)

    seconds to wait after encountering a failure, default is 0.1 seconds

Yields:

  • (Proc)

    A block that will be run, and if it raises an error, re-run until success, or patience runs out.

Returns:

  • (Proc)

    the value of the passed block.

Raises:

  • (Exception)

    the most recent Exception that caused the loop to retry before giving up.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/garcon/utility/retry.rb', line 220

def patiently(wait = 8, delay = 0.1)
  try_until = Time.now + wait
  failure   = nil

  while Time.now < try_until do
    begin
      return yield
    rescue Exception => e
      failure = e
      sleep delay
    end
  end
  failure ? (raise failure) : (raise TimeoutError)
end

#poll(wait = 8, delay = 0.1) {|Proc| ... } ⇒ Proc

‘#poll` is a method for knowing when something is ready. When your block yields true, execution continues. When your block yields false, poll keeps trying until it gives up and raises an error.

Examples:

wait up to 30 seconds for the TCP socket to respond.

def wait_for_server
  poll(30) do
    begin
      TCPSocket.new(SERVER_IP, SERVER_PORT)
      true
    rescue Exception
      false
    end
  end
end

Parameters:

  • wait (Integer) (defaults to: 8)

    The number of seconds seconds to poll.

  • delay (Integer) (defaults to: 0.1)

    Number of seconds to wait after encountering a failure, default is 0.1 seconds

Yields:

  • (Proc)

    A block that determines whether polling should continue. Return ‘true` if the polling is complete. Return `false` if polling should continue.

Returns:

  • (Proc)

    The value of the passed block.

Raises:

  • (Garcon::PollingError)

    Raised after too many failed attempts.



175
176
177
178
179
180
181
182
183
184
# File 'lib/garcon/utility/retry.rb', line 175

def poll(wait = 8, delay = 0.1)
  try_until = Time.now + wait

  while Time.now < try_until do
    result = yield
    return result if result
    sleep delay
  end
  raise TimeoutError
end

#retrier(opts = {}) {|Proc| ... } ⇒ Proc

Runs a code block, and retries it when an exception occurs. It is configured using four optional parameters ‘:tries`, `:on`, `:sleep`, `:match`, `:ensure` and runs the passed block. Should an exception occur, it’ll retry for (n-1) times. Should the number of retries be reached without success, the last exception will be raised.

Examples:

open an URL, retry up to two times when an OpenURI::HTTPError

occurs.
  retrier(tries: 3, on: OpenURI::HTTPError) do
    xml = open('http://example.com/test.html').read
  end

do something, retry up to four times for either ArgumentErro

or TimeoutError exceptions.
  retrier(tries: 5, on: [ArgumentError, TimeoutError]) do
    # _something_ code
  end

ensure that block of code is executed, regardless of whether an

exception was raised. It doesn't matter if the block exits normally,
if it retries to execute block of code, or if it is terminated by an
uncaught exception -- the ensure block will get run.
  f = File.open('testfile')
  ensure_cb = Proc.new do |retries|
    puts "total retry attempts: #{retries}"
    f.close
  end
  retrier(insure: ensure_cb) do
    # process file
  end

sleeping: by default Retrier waits for one second between

retries. You can change this and even provide your own exponential
backoff scheme.
  retrier(sleep:  0) { }              # don't pause between retries
  retrier(sleep: 10) { }              # sleep 10s between retries
  retrier(sleep: ->(n) { 4**n }) { }  # sleep 1, 4, 16, etc. each try

matching error messages: you can also retry based on the

exception message:
  retrier(matching: /IO timeout/) do |retries, exception|
    raise "yo, IO timeout!" if retries == 0
  end

block parameters: your block is called with two optional

parameters; the number of tries until now, and the most recent
exception.
  retrier do |tries, exception|
    puts "try #{tries} failed with error: #{exception}" if retries > 0
    # keep trying...
  end

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :tries (Fixnum)

    Number of attempts to retry before raising the last exception

  • :sleep (Fixnum)

    Number of seconds to wait between retries, use lambda to exponentially increasing delay between retries.

  • :on (Array(Exception))

    The type of exception(s) to catch and retry on

  • :matching (Regex)

    Match based on the exception message

  • :ensure (Block)

    Ensure a block of code is executed, regardless of whether an exception is raised

Yields:

  • (Proc)

    A block that will be run, and if it raises an error, re-run until success, or timeout is finally reached.

Returns:

  • (Proc)

    The value of the passed block.

Raises:

  • (Exception)

    Last Exception that caused the loop to retry before giving up.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/garcon/utility/retry.rb', line 111

def retrier(opts = {}, &_block)
  tries  = opts.fetch(:tries,            4)
  wait   = opts.fetch(:sleep,            1)
  on     = opts.fetch(:on,   StandardError)
  match  = opts.fetch(:match,         /.*/)
  insure = opts.fetch(:ensure, Proc.new {})

  retries         = 0
  retry_exception = nil

  begin
    yield retries, retry_exception
  rescue *[on] => e
    raise unless e.message =~ match
    raise if retries + 1 >= tries

    begin
      sleep wait.respond_to?(:call) ? wait.call(retries) : wait
    rescue *[on]
    end

    retries += 1
    retry_exception = exception
    retry
  ensure
    insure.call(retries)
  end
end