Git Product home page Git Product logo

retriable's Introduction

Retriable

Build Status Reviewed by Hound

Retriable is a simple DSL to retry failed code blocks with randomized exponential backoff time intervals. This is especially useful when interacting external APIs, remote services, or file system calls.

Requirements

Ruby 2.0.0+

If you need ruby 1.9.3 support, use the 2.x branch by specifying ~2.1 in your Gemfile.

If you need ruby 1.8.x to 1.9.2 support, use the 1.x branch by specifying ~1.4 in your Gemfile.

Installation

Via command line:

gem install retriable

In your ruby script:

require 'retriable'

In your Gemfile:

gem 'retriable', '~> 3.1'

Usage

Code in a Retriable.retriable block will be retried if an exception is raised.

require 'retriable'

class Api
  # Use it in methods that interact with unreliable services
  def get
    Retriable.retriable do
      # code here...
    end
  end
end

Defaults

By default, Retriable will:

  • rescue any exception inherited from StandardError
  • make 3 tries (including the initial attempt) before raising the last exception
  • use randomized exponential backoff to calculate each succeeding try interval.

The default interval table with 10 tries looks like this (in seconds, rounded to the nearest millisecond):

Retry # Min Average Max
1 0.25 0.5 0.75
2 0.375 0.75 1.125
3 0.563 1.125 1.688
4 0.844 1.688 2.531
5 1.266 2.531 3.797
6 1.898 3.797 5.695
7 2.848 5.695 8.543
8 4.271 8.543 12.814
9 6.407 12.814 19.222
10 stop stop stop

Options

Here are the available options, in some vague order of relevance to most common use patterns:

Option Default Definition
tries 3 Number of attempts to make at running your code block (includes initial attempt).
on [StandardError] Type of exceptions to retry. Read more.
on_retry nil Proc to call after each try is rescued. Read more.
sleep_disabled false When true, disable exponential backoff and attempt retries immediately.
base_interval 0.5 The initial interval in seconds between tries.
max_elapsed_time 900 (15 min) The maximum amount of total time in seconds that code is allowed to keep being retried.
max_interval 60 The maximum interval in seconds that any individual retry can reach.
multiplier 1.5 Each successive interval grows by this factor. A multipler of 1.5 means the next interval will be 1.5x the current interval.
rand_factor 0.5 The percentage to randomize the next retry interval time. The next interval calculation is randomized_interval = retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
intervals nil Skip generated intervals and provide your own array of intervals in seconds. Read more.
timeout nil Number of seconds to allow the code block to run before raising a Timeout::Error inside each try. nil means the code block can run forever without raising error. The implementation uses Timeout::timeout, which may be unsafe and even dangerous. Proceed with caution.

Configuring Which Options to Retry With :on

:on Can take the form:

  • An Exception class (retry every exception of this type, including subclasses)
  • An Array of Exception classes (retry any exception of one of these types, including subclasses)
  • A Hash where the keys are Exception classes and the values are one of:
    • nil (retry every exception of the key's type, including subclasses)
    • A single Regexp pattern (retries exceptions ONLY if their message matches the pattern)
    • An array of patterns (retries exceptions ONLY if their message matches at least one of the patterns)

Configuration

You can change the global defaults with a #configure block:

Retriable.configure do |c|
  c.tries = 5
  c.max_elapsed_time = 3600 # 1 hour
end

Example Usage

This example will only retry on a Timeout::Error, retry 3 times and sleep for a full second before each try.

Retriable.retriable(on: Timeout::Error, tries: 3, base_interval: 1) do
  # code here...
end

You can also specify multiple errors to retry on by passing an array of exceptions.

Retriable.retriable(on: [Timeout::Error, Errno::ECONNRESET]) do
  # code here...
end

You can also use a hash to specify that you only want to retry exceptions with certain messages (see the documentation above). This example will retry all ActiveRecord::RecordNotUnique exceptions, ActiveRecord::RecordInvalid exceptions where the message matches either /Parent must exist/ or /Username has already been taken/, or Mysql2::Error exceptions where the message matches /Duplicate entry/.

Retriable.retriable(on: {
  ActiveRecord::RecordNotUnique => nil,
  ActiveRecord::RecordInvalid => [/Parent must exist/, /Username has already been taken/],
  Mysql2::Error => /Duplicate entry/
}) do
  # code here...
end

You can also specify a timeout if you want the code block to only try for X amount of seconds. This timeout is per try.

The implementation uses Timeout::timeout, which may be unsafe and even dangerous. You can use this option, but you need to be very careful because the code in the block, including libraries or other code it calls, could be interrupted by the timeout at any line. You must ensure you have the right rescue logic and guards in place (Thread.handle_interrupt) to handle that possible behavior. If that's not possible, the recommendation is that you're better off impelenting your own timeout methods depending on what your code is doing than use this feature.

Retriable.retriable(timeout: 60) do
  # code here...
end

If you need millisecond units of time for the sleep or the timeout:

Retriable.retriable(base_interval: (200 / 1000.0), timeout: (500 / 1000.0)) do
  # code here...
end

Custom Interval Array

You can also bypass the built-in interval generation and provide your own array of intervals. Supplying your own intervals overrides the tries, base_interval, max_interval, rand_factor, and multiplier parameters.

Retriable.retriable(intervals: [0.5, 1.0, 2.0, 2.5]) do
  # code here...
end

This example makes 5 total attempts. If the first attempt fails, the 2nd attempt occurs 0.5 seconds later.

Turn off Exponential Backoff

Exponential backoff is enabled by default. If you want to simply retry code every second, 5 times maximum, you can do this:

Retriable.retriable(tries: 5, base_interval: 1.0, multiplier: 1.0, rand_factor: 0.0) do
  # code here...
end

This works by starting at a 1 second base_interval. Setting the multipler to 1.0 means each subsequent try will increase 1x, which is still 1.0 seconds, and then a rand_factor of 0.0 means that there's no randomization of that interval. (By default, it would randomize 0.5 seconds, which would mean normally the intervals would randomize between 0.5 and 1.5 seconds, but in this case rand_factor is basically being disabled.)

Another way to accomplish this would be to create an array with a fixed interval. In this example, Array.new(5, 1) creates an array with 5 elements, all with the value 1. The code block will retry up to 5 times, and wait 1 second between each attempt.

# Array.new(5, 1) # => [1, 1, 1, 1, 1]

Retriable.retriable(intervals: Array.new(5, 1)) do
  # code here...
end

If you don't want exponential backoff but you still want some randomization between intervals, this code will run every 1 seconds with a randomization factor of 0.2, which means each interval will be a random value between 0.8 and 1.2 (1 second +/- 0.2):

Retriable.retriable(base_interval: 1.0, multiplier: 1.0, rand_factor: 0.2) do
  # code here...
end

Callbacks

#retriable also provides a callback called :on_retry that will run after an exception is rescued. This callback provides the exception that was raised in the current try, the try_number, the elapsed_time for all tries so far, and the time in seconds of the next_interval. As these are specified in a Proc, unnecessary variables can be left out of the parameter list.

do_this_on_each_retry = Proc.new do |exception, try, elapsed_time, next_interval|
  log "#{exception.class}: '#{exception.message}' - #{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try."
end

Retriable.retriable(on_retry: do_this_on_each_retry) do
  # code here...
end

Ensure/Else

What if I want to execute a code block at the end, whether or not an exception was rescued (ensure)? Or what if I want to execute a code block if no exception is raised (else)? Instead of providing more callbacks, I recommend you just wrap retriable in a begin/retry/else/ensure block:

begin
  Retriable.retriable do
    # some code
  end
rescue => e
  # run this if retriable ends up re-raising the exception
else
  # run this if retriable doesn't raise any exceptions
ensure
  # run this no matter what, exception or no exception
end

Contexts

Contexts allow you to coordinate sets of Retriable options across an application. Each context is basically an argument hash for Retriable.retriable that is stored in the Retriable.config as a simple Hash and is accessible by name. For example:

Retriable.configure do |c|
  c.contexts[:aws] = {
    tries: 3,
    base_interval: 5,
    on_retry: Proc.new { puts 'Curse you, AWS!' }
  }
  c.contexts[:mysql] = {
    tries: 10,
    multiplier: 2.5,
    on: Mysql::DeadlockException
  }
end

This will create two contexts, aws and mysql, which allow you to reuse different backoff strategies across your application without continually passing those strategy options to the retriable method.

These are used simply by calling Retriable.with_context:

# Will retry all exceptions
Retriable.with_context(:aws) do
  # aws_call
end

# Will retry Mysql::DeadlockException
Retriable.with_context(:mysql) do
  # write_to_table
end

You can even temporarily override individual options for a configured context:

Retriable.with_context(:mysql, tries: 30) do
  # write_to_table with :mysql context, except with 30 tries instead of 10
end

Kernel Extension

If you want to call Retriable.retriable without the Retriable module prefix and you don't mind extending Kernel, there is a kernel extension available for this.

In your ruby script:

require 'retriable/core_ext/kernel'

or in your Gemfile:

gem 'retriable', require: 'retriable/core_ext/kernel'

and then you can call #retriable in any context like this:

retriable do
  # code here...
end

retriable_with_context(:api) do
  # code here...
end

Short Circuiting Retriable While Testing Your App

When you are running tests for your app it often takes a long time to retry blocks that fail. This is because Retriable will default to 3 tries with exponential backoff. Ideally your tests will run as quickly as possible.

You can disable retrying by setting tries to 1 in the test environment. If you want to test that the code is retrying an error, you want to turn off exponential backoff.

Under Rails, you could change your initializer to have different options in test, as follows:

# config/initializers/retriable.rb
Retriable.configure do |c|
  # ... default configuration

  if Rails.env.test?
    c.tries = 1
  end
end

Alternately, if you are using RSpec, you could override the Retriable confguration in your spec_helper.

# spec/spec_helper.rb
Retriable.configure do |c|
  c.tries = 1
end

If you have defined contexts for your configuration, you'll need to change values for each context, because those values take precedence over the default configured value.

For example assuming you have configured a google_api context:

# config/initializers/retriable.rb
Retriable.configure do |c|
  c.contexts[:google_api] = {
      tries:         5,
      base_interval: 3,
      on:            [
          Net::ReadTimeout,
          Signet::AuthorizationError,
          Errno::ECONNRESET,
          OpenSSL::SSL::SSLError
      ]
  }
end

Then in your test environment, you would need to set each context and the default value:

# spec/spec_helper.rb
Retriable.configure do |c|
  c.multiplier    = 1.0
  c.rand_factor   = 0.0
  c.base_interval = 0

  c.contexts.keys.each do |context|
    c.contexts[context][:tries]         = 1
    c.contexts[context][:base_interval] = 0
  end
end

Proxy Wrapper Object

@julik has created a gem called retriable_proxy that extends retriable with the ability to wrap objects and specify which methods you want to be retriable, like so:

# api_endpoint is an instance of some kind of class that connects to an API
RetriableProxy.for_object(api_endpoint, on: Net::TimeoutError)

Credits

The randomized exponential backoff implementation was inspired by the one used in Google's google-http-java-client project.

Development

Running Specs

bundle exec rspec

retriable's People

Contributors

apurvis avatar bartj3 avatar bruno- avatar brupm avatar bryant1410 avatar dependabot-preview[bot] avatar edwardbetts avatar ekohl avatar fatkodima avatar hanachin avatar jeremywadsack avatar kamui avatar kirikiriyamama avatar marcy avatar nilbus avatar olleolleolle avatar pikachuexe avatar salbertson avatar yauhenininjia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

retriable's Issues

Timeout::timeout is inherently unsafe

Just a heads up, Timeout::timeout is inherently unsafe (see https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/ and http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html).

Thankfully using it isn't on by default so that's good for those of us that don't wish to use it, but perhaps a note to users that they are better off implementing their own timeout methods (e.g. with socket timeout options) might save some people some headache down the line.

Anyway just throwing it out there, thanks for the awesome library!

require 'retriable', '~> 2.0'

In the docs it says:

"In your ruby script:
require 'retriable', '~> 2.0'"

This throws and error:
`require': wrong number of arguments (2 for 1) (ArgumentError)

Did you guys mean to put this in bundler? If so there's no 2.0 repo on rubygems.org so this breaks as well.

Request: Remove Gemfile.lock from .gitignore

I believe it is best practices to do so in ruby (as it helps ensure consistent testing/debugging) but it's especially relevant here, as nothing in the Gemfile is version locked.

An example why this is needed:
I just forked this repository and ran bundle update (I couldn't use bundle install due to the lack of a lockfile), and I am immediately seeing rubocop erroring out due to the fact two of the cops (Layout/IndentArray and Layout/IndentHash) listed in the config have since been renamed.

My choices are to either:
a) Guess at the intended version of rubocop using the times gem log .rubocop.yml, adding the version lock to the Gemfile and re-running bundle update, or;
b) Update the .rubocop.yml.

Obviously, either solution requires changes to a file tracked by git, which I would then have to pick around.

Error subclasses are not handled when matching patterns

code to recreate:

class SomeOtherError < StandardError; end

RETRY_PARAMS = {
  on: { StandardError => /pattern/ },
  on_retry: Proc.new do |exception, try, elapsed_time, next_interval|
    puts "#{exception.class}: '#{exception.message}' - #{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try."
    puts exception.backtrace.join("\n")
  end
}

Retriable.retriable(RETRY_PARAMS) do
  raise SomeOtherError.new('paxern')
end

# Block retries, even though the pattern is not matched

Retriable.retriable(RETRY_PARAMS) do
  raise StandardError.new('paxern')
end

# Block does not retry

I would expect SomeOtherError to have its message matched against /pattern/, but this does not happen. The gem instead tries to find the pattern with on[StandardError], comes up with nothing, decides it is an empty list, and retries even when the pattern is not matched.

Should the `on` argument to `with_context` be additive?

Assume the following context definition:

  c.contexts[:mysql] = {
    tries: 10,
    multiplier: 2.5,
    on: Mysql::DeadlockException
  }

When using the context and providing an on parameter, I first assumed it would be added to the list of exceptions handled by the context. Instead, it overwrites:

Retriable.with_context(:mysql, on: SomeOtherException) do
  # This will only retry on `SomeOtherException`!
end

At the very least, it might warrant a callout in the README.

This is the only parameter I'd expect to behave in an additive way. All of the others make sense as overrides to me.

Question: Short Circuiting Retriable While Testing Your App

The readme mentions being able to turn of retries in tests by doing the following:

Retriable.configure do |c|
  if Rails.env.test?
    c.tries = 1
end

But from what I can tell is only works when Retriable.retriable is itself called without the tries parameter. The default global config is being set, while the individual invocation parameters still take precedence. The same is probably true for all other parameters being set in this way.

Looking at the code this seems to indeed what is happening:

local_config = opts.empty? ? config : Config.new(config.to_h.merge(opts))

Is there something I'm missing? I could create my own wrapper function and check for the test environment there, but that is not what the readme suggests.

Testing setup

This is probably more of a question than an issue but what's the recommended way to set retriable up in the test environment. Without any specific setup I run into

  • The delay makes the tests really slow
  • The retry make mocks complain that the retried method is called more than once (which it is, obviously)

Current I made a config/initializers/retriable.rb with

if Rails.env.test?
  Retriable.configure do |c|
    c.tries = 1
  end
end

but I was wondering if there is a better way? Or if there is some documentation on how to do testing that I just didn't find...

Thanks for a great gem!

Too many call `on_retry`

Retriable.retriable(tries: 3, on_retry: ->(*args) { puts 'on_retry' }) do
  puts 'execute'
  raise 'err'
end

version 1.4.1

execute
on_retry
execute
on_retry
execute
RuntimeError: err

version 2.1.0

execute
on_retry
execute
on_retry
execute
on_retry
RuntimeError: err

Have a parameter to supply a proc that is only called on the final failure

Currently the on_retry parameter is called each time the passed in block of code raises a matching exception.

I am proposing that there be a on_final_failure (or similarly named) parameter which is a proc that is called after all retries have been exhausted.

The use case is that:

  • sometimes I'd like to perform a different action once all the retries have finished
  • sometimes I only want to perform an action once all the retries are finished.

I know there are current ways to do this outside of the gem, but I think the following interface could be nice:

do_this_on_each_retry = Proc.new { }
do_this_on_final_retry = Proc.new { }
Retriable.retriable(on_retry: do_this_on_each_retry, on_final_failure: do_this_on_final_retry) do
  # code here..
end

This allows me to have custom logic for the final failure, while still allowing the error to be raised by Retriable

Allow retrying based on the "cause" exception

Sometimes APIs wrap exceptions so that the top-level exception by itself isn't enough to determine whether or not to retry the code. However, the cause exception could be used to determine that the code should be retried.

Currently retriable only allows retrying based on the raised exception, and doesn't provide a way to examine the caused exception.

Here is an example. In this case, we want to retry based on the RootCauseError, but since the API we're using actually raises DemoError, the code block doesn't get retried.

class RootCauseError < StandardError; end
class DemoError < StandardError; end
begin
  i = 0
  Retriable.retriable on: [RootCauseError] do
    begin
      puts "Try #{i += 1}..."
      raise RootCauseError.new("BUT THIS IS THE CAUSE")
    rescue => e
      raise DemoError.new("RETRIABLE ONLY LOOKS AT THIS")
    end
  end
rescue => e
  puts "Raised error: #{e.inspect}"
  puts "Cause error: #{e.cause.inspect}"
end

Two ideas for solving this:

  1. Make retriable examine the cause of errors as well as the errors itself. This could either only go one level deep, or recurse...perhaps with a limit on recursion depth.
  2. Allow passing in a proc or method to the on option that would then be invoked with the error and would return true or false to determine if the error should be retried. This proc could then examine the error class and its cause, and would have the benefit of also supporting lots of advanced cases that are not possible right now.

"interval is not a valid option" config.rb line 32

Just got my production crash after 3.0.2 just updated

Exception is "interval is not a valid option"

/app/vendor/bundle/ruby/2.2.0/gems/retriable-3.0.2/lib/retriable/config.rb:32

This gem was pulled by google-api-client (that I locked at 0.7.1)

(still looking into what happened... but release of the gem just happened between me looking on staging and deploying prod. Testing staging after the gem update experience same error)

Callback if max elapsed time has been reached

Currently if retriable anticipates that the max_elapsed_time will be reached, it just raises the error right then.

This is fine however as a user, I'd like to know if this has occurred and if this is the reason the code is no longer being retried (as opposed to if it was just because we reached the number of tries)

So this issue proposes the following interface:

do_this_when_max_elapsed_time_reached = Proc.new do |exception, try, elapsed_time|
  # ex: log here that the max elapsed time has been reached
end
Retriable.retriable(on_max_elapsed_time_reached: do_this_when_max_elapsed_time_reached) do
  # code here...
end

This mainly comes up b/c I have an idea for how long the code will run, but it can sometimes run for longer and I'd like to log these instances

cannot load such file -- retriable

Hi, I'm trying to use your gem but when I test it I get this error"
cannot load such file -- retriable

This is my code:

module Integration
  require 'retriable'
  
  class Mailchimp

    def self.method1
      Retriable.retriable do
         raise StandardError
       end
    end

  end
end

If I comment out require 'retriable' I get the error uninitialized constant Integration::Mailchimp::Retriable

Thanks

Replace houndci

Wdyt about replacing noisy houndci by plain running of rubocop alongside of testing on travis?
Currently running rubocop locally is not working, but on hound is everything is ok, so how it checks for errors?!

gem 'retriable', '~> 2.0'

i added this ``gem 'retriable', '> 2.0'` into my gemfile as per README, when i run my bundle install it says 'Could not find gem 'retriable (> 2.0) ruby' in the gems available on this machine'

i just realised there is no 2.0 stable version, it's only beta, wondering when the stable version will be available

Retriable only trying for 20 times even though my tries is 360. Not sure why that's happpening.

I want to retry my logic for 1 hour if something is not true. So I added tries as 360 and base_interval: as 10. But for some reason, it always exists out in 20 times. Following is my code and log:

Code

        count = 0
        # 1 hour : 10 second interval x 360 tries = 3600 seconds
        Retriable.retriable(on: [MyModule::MyClass::CustomError, Net::OpenTimeout],
                            tries: 360, base_interval: 10) do
            count += 1
            print "count: #{count} \n"
            raise MyModule::MyClass::CustomError if @food != "tacos"
        end

Log:

[5] pry(main)> work.run
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 10
count: 11
count: 12
count: 13
count: 14
count: 15
count: 16
count: 17
count: 18
count: 19
count: 20
MyModule::MyClass::CustomError: MyModule::MyClass::CustomError

Am I missing anything? Any idea on how I can accomplish this?

Feature request: `retriable` macro

Hi,

First of all, thanks for this library, I like it very much.

What do you think of possibility to declare methods as retriable at class level. Like this:

class A
  include Retriable

  def my_method(a, b)
    # do something
  end
  retriable :my_method, on: Timeout::Error, tries: 3, base_interval: 1

  # equal to
  def noisy_method(a, b)
    retriable on: Timeout::Error, tries: 3, base_interval: 1 do
      # do something
    end
  end
end

I believe it's pretty easy to implement and it'll allow to remove some noisiness from "retriable" methods. I can implement it myself but first I want to be sure that you'll accept this feature into upstream :) What do you think about it?

Travis Build fails when installing rubygems-update

Travis build is currently failing with this:

gem update --system
Updating rubygems-update
Fetching: rubygems-update-3.0.3.gem (100%)
ERROR:  Error installing rubygems-update:
	rubygems-update requires Ruby version >= 2.3.0.
ERROR:  While executing gem ... (NoMethodError)
    undefined method `version' for nil:NilClass
The command "gem update --system" failed and exited with 1 during .

Relevant issue on rubygems

Error message on max_elapsed_time

If you set a high retry limit (e.g. 100), Retriable will exceed the max elapsed time, but the error message says it reached 100 tries:

27 tries in 872.859824735 seconds and 75.33571386221345 seconds until the next try.
Failed after 100 retries with RestClient::ServiceUnavailable: 503 Service Unavailable!

It would be nice if it could say that it reached the max elapsed time instead.

Dependabot can't resolve your Ruby dependency files

Dependabot can't resolve your Ruby dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Bundler::VersionConflict with message: Bundler found conflicting requirements for the Ruby version:
  In Gemfile:
    Ruby (~> 2.0.0.0)

    listen (~> 3.1) was resolved to 3.1.1, which depends on
      Ruby (~> 2.2)

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Any upgrade guide for 2.0?

I know there is the CHANGELOG.md file.
But it's unclear to me that what is breaking or not (since it got changes for each beta, not from 1.4.1 to 2.0.0.

Option to Ignore Specific Types of Errors

Hi,

I was wondering if you would accept a pull request which allowed the option to ignore specific types of errors. Right now it seems like the only option is to rescue from within the block.

Let me know if you have any questions or comments!

CI: bundler 1.17.3 seems not to work w/ ruby-head

An issue in the CI:

$ gem install bundler -v 1.17.3

Fetching: bundler-1.17.3.gem (100%)

ERROR:  Error installing bundler:

	invalid gem: package is corrupt, exception while verifying: wrong number of arguments (given 2, expected 1) (ArgumentError) in /home/travis/.rvm/gems/ruby-head/cache/bundler-1.17.3.gem

The command "gem install bundler -v 1.17.3" failed and exited with 1 during .

Dependabot can't resolve your Ruby dependency files

Dependabot can't resolve your Ruby dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Bundler::VersionConflict with message: Bundler found conflicting requirements for the Ruby version:
  In Gemfile:
    Ruby (~> 2.0.0.0)

    listen (~> 3.1) was resolved to 3.1.1, which depends on
      Ruby (~> 2.2)

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Using RUBY_VERSION in a gemspec doesn't work

Currently there's the following code in the gemspec:

if RUBY_VERSION < "2.3"
spec.add_development_dependency "ruby_dep", "~> 1.3.1"
spec.add_development_dependency "listen", "~> 3.0.8"
else
spec.add_development_dependency "listen", "~> 3.1"
end

This doesn't work because gemspecs evaluate tot static content. This means it's evaluated only when gem build is called. That Ruby version used there determines the actual resulting gem dependencies.

I'm not aware of any method to do this more dynamically for users.

[Question] Custom Interval

Hey all,

I am new to ruby and trying to use this gem.

Running following code:

Retriable.retriable intervals: [1, 2, 3, 4] do
  puts "time: #{Time.now}"
  raise 'An error has occured.'
end

the result is:

time: 2015-09-30 04:04:11 +0000

time: 2015-09-30 04:04:12 +0000

time: 2015-09-30 04:04:14 +0000

time: 2015-09-30 04:04:17 +0000

but should it re-try one more time? and the last interval is 4 seconds? I thought the number of execution is original one time plus the size of the interval array?

Thanks.

Feature request: allow `with_context` without context configuration

Hi ๐Ÿ‘‹

From:

if !config.contexts.key?(context_key)
raise ArgumentError, "#{context_key} not found in Retriable.config.contexts. Available contexts: #{config.contexts.keys}"
end
I understand that a configured context is required prior to using that context.

I am working on a SDK that has clients to several internal services and I would like to use with_context before hitting each service so I can let users configure the retry ability of each service independently and I would like to fallback to a default configuration if such context is not configured yet.

For example, mixing what's available from the current documentation:

Retriable.configure do |c|
  c.tries = 5
  c.max_elapsed_time = 3600 # 1 hour
  c.contexts[:aws] = {
    tries: 3,
    base_interval: 5,
    on_retry: Proc.new { puts 'Curse you, AWS!' }
  }
  c.contexts[:mysql] = {
    tries: 10,
    multiplier: 2.5,
    on: Mysql::DeadlockException
  }
end

If later I use postgres context, I would like to fallback to the global config:

Retriable.with_context(:postgres) do
  # use global config, tries: 5, max_elapsed_time: 3600
end

One idea on how to implement this feature would be as a new option to with_context, for example fallback_global_config: true, would you accept a PR with such a thing?

Retriable.with_context(:postgres, fallback_global_config: true) do
    # use global config, tries: 5, max_elapsed_time: 3600
end

Thanks for sharing this awesome project and considering my feature request ๐Ÿ’œ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.