circuit_breaker-ruby 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZTU0MWI4NmExMGFlNTY1YjlkNGYxNTEwMzk4MmQ4ZjhmNjU4MWQwZQ==
5
+ data.tar.gz: !binary |-
6
+ ODEyZGE5Y2Q1OTY2ODdjZDMwM2ExM2U1NjFjYWYwNTY3MGY1YjBjMg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NDM2NGNiYmZlNDAxNWQwY2EwNWVmYTYyYjBlYWQ3NzI5YjE5MjNhYmM4NzU4
10
+ MTcyNDZjOTk4MmU1ZDcxZGJkZmQ1NDRmYWM5NjVhYTA3OGViY2MwYTU1MTA1
11
+ YzJjMTU0MjliNjJjYjRmZDJkYjU4MDNlZmMzZmI0ZTUyMjA4MzM=
12
+ data.tar.gz: !binary |-
13
+ OTQyMzRmZDRjMzcwNjQ4YWIwMmMyMmIzMjYxNzM2ZmQ3ZDg1NTdlMTZjODVk
14
+ ZDlmZDZiNjQ2OTgwZWRlNmQ4YzZhODBkMDNlNDE3MzEzZmE2Nzc3ZWM3NTA1
15
+ Y2M2NmM5MzdjYTQyZjcyZjU3ZTY2ODFlNTUyN2NmYjQ3ZTAwNTE=
data/.gitignore ADDED
@@ -0,0 +1,51 @@
1
+ *.gem
2
+ *.rbc
3
+ *.DS_Store
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ # Gemfile.lock
47
+ # .ruby-version
48
+ # .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.8
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ deploy:
2
+ provider: rubygems
3
+ api_key:
4
+ secure: FVH5+GPQvp39HEE+uIQYwXVFtN7Vb9gTbKXH2KCGKq2KQVC6zJv58Is1TSmqtcay6heAhLju9G3MXb9ezNP3IusP8uNPPa/1qlIV90kWasyr3Y+2Ji4LGPqyb8Tvs7BAqhsdKI+do4ELrvvYqZ11nXrAlwuSyU34KluEh7apuebUD1KJczpryQ1OpSXo/dLqca5PP7pTRbULXKg9f6elade95oSrtROjqYb3/2rG0vK3B52Sl+nTibwrprg2JQRzFGFrq8mjPAgHlY4xzwUEAxZ7+zjupJB59/Lmj1pIDaxkhTkDAMBwFYFvG9RAbDC6PKDDLNoJZpafv66NZbzczNn1LlNFLUgnNyVNHpK/a2c1ye4+wsGA6/IFJ1TssDm20lMAaOJAiBCDaOnil758RrkeCixJB9WvZ33JAF327ZsQvCn0kJS5UFNEKjnMYZxDHq4XJa4Lu1Bn6cDXba+Ro3fqgr0r5CItL4wppOLydCCoblRMd5SJu0xgr/LLBhwkWu/9WZPecDE0kBVcl8DzCz+em/P45REeOLsqHj2o+aK5c3Q21TzmhV84nXPXt+YSD4f3FqSNMngOb++eWJRhXsdURGW+i0abmeRTn/TxfSV3LTxCAibXL0WVz+THavV8gHomHtCmkUSkfDEvRYEvxJ8Y9sVjvDMfJryLiXICsXI=
5
+ on:
6
+ tags: true
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Gemfile
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec', '>= 3.1'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ circuit_breaker-ruby (0.1)
5
+ rake (~> 10)
6
+ rspec (~> 3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.2.5)
12
+ rake (10.1.1)
13
+ rspec (3.5.0)
14
+ rspec-core (~> 3.5.0)
15
+ rspec-expectations (~> 3.5.0)
16
+ rspec-mocks (~> 3.5.0)
17
+ rspec-core (3.5.3)
18
+ rspec-support (~> 3.5.0)
19
+ rspec-expectations (3.5.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.5.0)
22
+ rspec-mocks (3.5.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.5.0)
25
+ rspec-support (3.5.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ circuit_breaker-ruby!
32
+ rspec (>= 3.1)
33
+
34
+ BUNDLED WITH
35
+ 1.11.2
data/MIT-LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2016-2018 Vasu Adari
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ ## Circuit Breaker
2
+ A circuit breaker which terminates a connection or block of code from executing when it reaches the failure threshold and a percentage. Also it gets reset when a connection succeeds. It also keeps monitoring if connections are working again by checking if it has attained a time to retry.
3
+
4
+ ## Installation
5
+
6
+ ```
7
+ gem install circuit_breaker-ruby
8
+ ```
9
+
10
+ ## Usage
11
+
12
+ ```
13
+ circuit_breaker = CircuitBreaker::Shield.new(
14
+ invocation_timeout: 1,
15
+ failure_threshold: 2,
16
+ failure_threshold_percentage, 0.2,
17
+ retry_timeout: 10
18
+ )
19
+ circuit_breaker.protect { sleep(10) }
20
+ ```
21
+
22
+ ### Running the specs
23
+
24
+ ```
25
+ bundle exec rspec spec
26
+ ```
27
+
28
+ ### Configuration
29
+
30
+ Add the following configuration to config/initializers/circuit_breaker.rb. These are the default values.
31
+
32
+ ```
33
+ CircuitBreaker.configure do |cb|
34
+ cb.failure_threshold = 10
35
+ cb.failure_threshold_percentage = 0.5
36
+ cb.invocation_timeout = 10
37
+ cb.retry_timeout = 60
38
+ end
39
+ ```
40
+
41
+ ### Contributing
42
+
43
+ If you have any issues with circuit_breaker-ruby,
44
+ or feature requests,
45
+ please [add an issue](https://github.com/scripbox/circuit_breaker-ruby/issues) on GitHub
46
+ or fork the project and send a pull request.
47
+ Please include passing specs with all pull requests.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new(:spec)
3
+
4
+ task default: :spec
@@ -0,0 +1,22 @@
1
+ require './lib/circuit_breaker-ruby/version'
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'circuit_breaker-ruby'
5
+ gem.version = CircuitBreaker::VERSION
6
+ gem.date = '2016-10-09'
7
+ gem.summary = 'Circuit breaker for ruby'
8
+ gem.description = 'Self-resetting breaker retries the protected call after a suitable interval, and it also resets when the call succeeds.'
9
+ gem.author = 'Scripbox'
10
+ gem.email = 'tech@sripbox.com'
11
+ gem.homepage = 'https://github.com/scripbox/circuit_breaker-ruby'
12
+ gem.license = 'MIT'
13
+
14
+ gem.require_path = 'lib'
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = Dir.glob('spec/**/*')
17
+
18
+ gem.required_ruby_version = '>= 2.1.8'
19
+
20
+ gem.add_dependency 'rspec', '~> 3'
21
+ gem.add_dependency 'rake', '~> 10'
22
+ end
@@ -0,0 +1,18 @@
1
+ require 'circuit_breaker-ruby/version'
2
+ require 'circuit_breaker-ruby/config'
3
+ require 'circuit_breaker-ruby/shield'
4
+ require 'timeout'
5
+
6
+ module CircuitBreaker
7
+ class Open < StandardError; end
8
+
9
+ class << self
10
+ def config
11
+ @config ||= CircuitBreaker::Config.new
12
+ end
13
+
14
+ def configure
15
+ yield @config = CircuitBreaker::Config.new
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module CircuitBreaker
2
+ class Config
3
+ FAILURE_THRESHOLD = 10
4
+ FAILURE_THRESHOLD_PERCENTAGE = 0.5
5
+ INVOCATION_TIMEOUT = 10
6
+ RETRY_TIMEOUT = 60
7
+
8
+ attr_accessor :invocation_timeout, :failure_threshold, :failure_threshold_percentage, :retry_timeout
9
+
10
+ def initialize
11
+ self.failure_threshold = FAILURE_THRESHOLD
12
+ self.failure_threshold_percentage = FAILURE_THRESHOLD_PERCENTAGE
13
+ self.invocation_timeout = INVOCATION_TIMEOUT
14
+ self.retry_timeout = RETRY_TIMEOUT
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ module CircuitBreaker
2
+ class Shield
3
+ module States
4
+ OPEN = :open
5
+ CLOSED = :closed
6
+ HALF_OPEN = :half_open
7
+ end
8
+
9
+ attr_reader :invocation_timeout, :retry_timeout, :failure_threshold, :failure_threshold_percentage,
10
+ :total_count, :failure_count
11
+
12
+ def initialize(**options)
13
+ @failure_count = 0
14
+ @total_count = 0
15
+ @failure_threshold = options[:failure_threshold] || config.failure_threshold
16
+ @failure_threshold_percentage = options[:failure_threshold_percentage] || config.failure_threshold_percentage
17
+ @invocation_timeout = options[:invocation_timeout] || config.invocation_timeout
18
+ @retry_timeout = options[:retry_timeout] || config.retry_timeout
19
+ end
20
+
21
+ def config
22
+ CircuitBreaker.config
23
+ end
24
+
25
+ def protect(&block)
26
+ case prev_state = state
27
+ when States::CLOSED, States::HALF_OPEN
28
+ connect(&block).tap { update_total_count(prev_state) }
29
+ when States::OPEN
30
+ raise CircuitBreaker::Open
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def state
37
+ case
38
+ when reached_failure_threshold? && reached_retry_timeout?
39
+ States::HALF_OPEN
40
+ when reached_failure_threshold?
41
+ States::OPEN
42
+ else
43
+ States::CLOSED
44
+ end
45
+ end
46
+
47
+ def reached_failure_threshold?
48
+ (failure_count >= failure_threshold) &&
49
+ (total_count != 0 &&
50
+ (failure_count.to_f / total_count.to_f) >= failure_threshold_percentage)
51
+ end
52
+
53
+ def reached_retry_timeout?
54
+ (Time.now - @last_failure_time) > @retry_timeout
55
+ end
56
+
57
+ def reset
58
+ @failure_count = 0
59
+ @state = States::CLOSED
60
+ end
61
+
62
+ def connect(&block)
63
+ begin
64
+ result = nil
65
+ Timeout::timeout(invocation_timeout) do
66
+ result = block.call
67
+ reset
68
+ end
69
+ rescue Timeout::Error => e
70
+ record_failure
71
+ end
72
+
73
+ result
74
+ end
75
+
76
+ def update_total_count(state)
77
+ if state == States::HALF_OPEN
78
+ @total_count = 0
79
+ else
80
+ @total_count += 1
81
+ end
82
+ end
83
+
84
+ def record_failure
85
+ @last_failure_time = Time.now
86
+ @failure_count += 1
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module CircuitBreaker
2
+ VERSION = 0.1
3
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'circuit_breaker-ruby'
3
+
4
+ CircuitBreaker.configure do |cb|
5
+ cb.invocation_timeout = 1
6
+ cb.retry_timeout = 1
7
+ cb.failure_threshold = 1
8
+ end
9
+
10
+ describe CircuitBreaker::Shield do
11
+ let(:circuit_breaker_shield) { CircuitBreaker::Shield.new }
12
+
13
+ it 'goes to closed state' do
14
+ circuit_breaker_shield.protect { sleep(0.1) }
15
+
16
+ expect(circuit_breaker_shield.send :state).to be(CircuitBreaker::Shield::States::CLOSED)
17
+ end
18
+
19
+ it 'goes to open state' do
20
+ no_of_tries = circuit_breaker_shield.failure_threshold * 2
21
+ no_of_failures = no_of_tries * circuit_breaker_shield.config.failure_threshold_percentage
22
+ no_of_success = no_of_tries - no_of_failures
23
+ no_of_success.to_i.times { circuit_breaker_shield.protect { sleep(0.1) } }
24
+ no_of_failures.to_i.times { circuit_breaker_shield.protect { sleep(2) } }
25
+
26
+ expect(circuit_breaker_shield.send :state).to be(CircuitBreaker::Shield::States::OPEN)
27
+ end
28
+
29
+ it 'goes to half open state' do
30
+ no_of_tries = circuit_breaker_shield.failure_threshold * 2
31
+ no_of_failures = no_of_tries * circuit_breaker_shield.config.failure_threshold_percentage
32
+ no_of_success = no_of_tries - no_of_failures
33
+ no_of_success.to_i.times { circuit_breaker_shield.protect { sleep(0.1) } }
34
+ no_of_failures.to_i.times { circuit_breaker_shield.protect { sleep(2) } }
35
+ sleep(1)
36
+
37
+ expect(circuit_breaker_shield.send :state).to be(CircuitBreaker::Shield::States::HALF_OPEN)
38
+ end
39
+
40
+ it 'raises CircuitBreaker::Shield::Open' do
41
+ no_of_tries = circuit_breaker_shield.failure_threshold * 2
42
+ no_of_failures = no_of_tries * circuit_breaker_shield.config.failure_threshold_percentage
43
+ no_of_success = no_of_tries - no_of_failures
44
+ no_of_success.to_i.times { circuit_breaker_shield.protect { sleep(0.1) } }
45
+
46
+ expect { (no_of_failures.to_i + 1).times { circuit_breaker_shield.protect { sleep(2) } } }.to(
47
+ raise_error(CircuitBreaker::Open)
48
+ )
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ RSpec.configure do |config|
5
+ config.tty = true
6
+ config.color = true
7
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: circuit_breaker-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Scripbox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10'
41
+ description: Self-resetting breaker retries the protected call after a suitable interval,
42
+ and it also resets when the call succeeds.
43
+ email: tech@sripbox.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .ruby-version
50
+ - .travis.yml
51
+ - Gemfile
52
+ - Gemfile.lock
53
+ - MIT-LICENSE
54
+ - README.md
55
+ - Rakefile
56
+ - circuit_breaker-ruby.gemspec
57
+ - lib/circuit_breaker-ruby.rb
58
+ - lib/circuit_breaker-ruby/config.rb
59
+ - lib/circuit_breaker-ruby/shield.rb
60
+ - lib/circuit_breaker-ruby/version.rb
61
+ - spec/circuit_breaker/shield_spec.rb
62
+ - spec/spec_helper.rb
63
+ homepage: https://github.com/scripbox/circuit_breaker-ruby
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.1.8
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.4.5
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Circuit breaker for ruby
87
+ test_files:
88
+ - spec/circuit_breaker/shield_spec.rb
89
+ - spec/spec_helper.rb