<commit_before># CircuitBreaker

A lightweight ruby gem that implements the famous [Michael Nygard](https://www.martinfowler.com/bliki/CircuitBreaker.html) circuit breaker pattern.

## Installation

This gem is purposely not on RubyGems.org yet as I'm still finalizing the API.  If you wish to help me get to it's first stable release, please do!

Add this line to your application's Gemfile:

```ruby
gem 'circuit_breaker', github: 'allcentury/circuit_breaker'
```

And then execute:

    $ bundle install

## Usage

### In Memory

Here's an example of how you could use the breaker while making routine calls to a third party service such as Twitter:

```ruby
require 'circuit_breaker'
require 'logger'

@logger = Logger.new(STDOUT)

handles = ["joe", "jane", "mary", "steve"]

def get_tweets(twitter_handle, _num)
  http_result = ["Success!", "Fail"].sample
  raise RuntimeError.new("Failed to fetch tweets for #{twitter_handle}") if http_result == "Fail"
  @logger.info "#{http_result} getting tweets for #{twitter_handle}"
end

breaker = CircuitBreaker::Memory.new do |cb|
  cb.circuit = -> (twitter_handle, num) { get_tweets(twitter_handle, num) }
  cb.failure_limit = 2
  cb.reset_timeout = 5
end

handles.each_with_index do |handle, i|
  begin
    breaker.call(handle, i)
  rescue CircuitBreaker::OpenError
    sleep breaker.reset_timeout
  end
end
```

You will see output similar to:
```
W, [2017-02-12T20:49:12.374971 #85900]  WARN -- : [RuntimeError] - Failed to fetch tweets for joe
W, [2017-02-12T20:49:12.375049 #85900]  WARN -- : [RuntimeError] - Failed to fetch tweets for jane
I, [2017-02-12T20:49:17.380771 #85900]  INFO -- : Success! getting tweets for steve
I, [2017-02-12T20:49:17.380865 #85900]  INFO -- : Circuit closed
```

Notice that we had two failures in a row for joe and jane.  The circuit breaker was configured to only allow for 2 failures via the `failuire_limit` method.  If another call comes in after two failures, it will raise a `CircuitBreaker::Open` error.  The only way the circuit breaker will be closed again is if the `reset_timeout` period has lapsed.  In our loop we catch the `CircuitBreaker::Open` exception and sleep (don't sleep in production - this is just an example) to allow the Circuit to close.  You can see the timestamp of this log,

```
I, [2017-02-12T20:49:17.380771 #85900]  INFO -- : Success! getting tweets for steve<commit_msg>Update README to include renamed error class<commit_after># CircuitBreaker

A lightweight ruby gem that implements the famous [Michael Nygard](https://www.martinfowler.com/bliki/CircuitBreaker.html) circuit breaker pattern.

## Installation

This gem is purposely not on RubyGems.org yet as I'm still finalizing the API.  If you wish to help me get to it's first stable release, please do!

Add this line to your application's Gemfile:

```ruby
gem 'circuit_breaker', github: 'allcentury/circuit_breaker'
```

And then execute:

    $ bundle install

## Usage

### In Memory

Here's an example of how you could use the breaker while making routine calls to a third party service such as Twitter:

```ruby
require 'circuit_breaker'
require 'logger'

@logger = Logger.new(STDOUT)

handles = ["joe", "jane", "mary", "steve"]

def get_tweets(twitter_handle, _num)
  http_result = ["Success!", "Fail"].sample
  raise RuntimeError.new("Failed to fetch tweets for #{twitter_handle}") if http_result == "Fail"
  @logger.info "#{http_result} getting tweets for #{twitter_handle}"
end

breaker = CircuitBreaker::Memory.new do |cb|
  cb.circuit = -> (twitter_handle, num) { get_tweets(twitter_handle, num) }
  cb.failure_limit = 2
  cb.reset_timeout = 5
end

handles.each_with_index do |handle, i|
  begin
    breaker.call(handle, i)
  rescue CircuitBreaker::OpenError
    sleep breaker.reset_timeout
  end
end
```

You will see output similar to:
```
W, [2017-02-12T20:49:12.374971 #85900]  WARN -- : [RuntimeError] - Failed to fetch tweets for joe
W, [2017-02-12T20:49:12.375049 #85900]  WARN -- : [RuntimeError] - Failed to fetch tweets for jane
I, [2017-02-12T20:49:17.380771 #85900]  INFO -- : Success! getting tweets for steve
I, [2017-02-12T20:49:17.380865 #85900]  INFO -- : Circuit closed
```

Notice that we had two failures in a row for joe and jane.  The circuit breaker was configured to only allow for 2 failures via the `failuire_limit` method.  If another call comes in after two failures, it will raise a `CircuitBreaker::OpenError` error.  The only way the circuit breaker will be closed again is if the `reset_timeout` period has lapsed.  In our loop we catch the `CircuitBreaker::OpenError` exception and sleep (don't sleep in production - this is just an example) to allow the Circuit to close.  You can see the timestamp of this log,

```
I, [2017-02-12T20:49:17.380771 #85900]  INFO -- : Success! getting tweets for steve
{'fraction_non_alphanumeric': 0.10238341968911917, 'fraction_numerical': 0.05305699481865285, 'mean_word_length': 4.440811724915445, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3558835', 'n_tokens_mistral': 1768, 'n_tokens_neox': 1521, 'n_words': 589}