circuit_breakage 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/lib/circuit_breakage.rb +3 -3
- data/lib/circuit_breakage/breaker.rb +6 -3
- data/lib/circuit_breakage/version.rb +1 -1
- data/spec/breaker_spec.rb +15 -4
- data/spec/redis_backed_breaker_spec.rb +6 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5aeb1835130f99946eddad6d46d8f01dd44a0fed
|
4
|
+
data.tar.gz: bdd92ef38ba1a708da8ac1cab7e9bb34c1b4fe8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73d6b93182c3bd07d75196ac21c3fb7a7ca47259183c57a512ae131821eddfb035abc65509cd3d62a9cb3deae30f148aa3d352d9a12c4362e9297ddf397bcc18
|
7
|
+
data.tar.gz: f7f86f802260ab60af42d2db4031ac62110ff8081bcc681e74be062a4a376af5e00a721e33df9cd50f3dd14d33472710d70cf7fe7bdaf864de72cc41a444892a
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# CircuitBreakage
|
2
2
|
|
3
|
-
A simple
|
4
|
-
|
3
|
+
A simple circuit breaker implementation in Ruby with a timeout. A circuit
|
4
|
+
breaker wraps a potentially troublesome block of code and will "trip" the
|
5
5
|
circuit (ie, stop trying to run the code) if it sees too many failures. After
|
6
6
|
a configurable amount of time, the circuit breaker will retry.
|
7
7
|
|
@@ -24,9 +24,9 @@ breaker.timeout = 0.5 # 500 milliseconds allowed before auto-fail
|
|
24
24
|
|
25
25
|
begin
|
26
26
|
breaker.call(*some_args) # args are passed through to block
|
27
|
-
rescue
|
27
|
+
rescue CircuitBreakage::CircuitOpen
|
28
28
|
puts "Too many recent failures!"
|
29
|
-
rescue
|
29
|
+
rescue CircuitBreakage::CircuitTimeout
|
30
30
|
puts "Operation timed out!"
|
31
31
|
end
|
32
32
|
```
|
@@ -34,7 +34,7 @@ end
|
|
34
34
|
A "failure" in this context means that the block either raised an exception or
|
35
35
|
timed out.
|
36
36
|
|
37
|
-
### Redis-
|
37
|
+
### Redis-Backed "Shared" Circuit Breakers
|
38
38
|
|
39
39
|
The unique feature of this particular Circuit Breaker gem is that it also
|
40
40
|
supports shared state via Redis, using the SETNX and GETSET commands. This
|
data/lib/circuit_breakage.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "circuit_breakage/version"
|
2
|
+
require_relative "circuit_breakage/breaker"
|
3
|
+
require_relative "circuit_breakage/redis_backed_breaker"
|
@@ -9,7 +9,7 @@ module CircuitBreakage
|
|
9
9
|
#
|
10
10
|
class Breaker
|
11
11
|
attr_accessor :failure_count, :last_failed, :state, :block
|
12
|
-
attr_accessor :failure_threshold, :duration, :timeout
|
12
|
+
attr_accessor :failure_threshold, :duration, :timeout, :last_exception
|
13
13
|
|
14
14
|
DEFAULT_FAILURE_THRESHOLD = 5 # Number of failures required to trip circuit
|
15
15
|
DEFAULT_DURATION = 300 # Number of seconds the circuit stays tripped
|
@@ -54,7 +54,7 @@ module CircuitBreakage
|
|
54
54
|
|
55
55
|
return ret_value
|
56
56
|
rescue Exception => e
|
57
|
-
handle_failure
|
57
|
+
handle_failure(e)
|
58
58
|
end
|
59
59
|
|
60
60
|
def time_to_retry?
|
@@ -66,12 +66,15 @@ module CircuitBreakage
|
|
66
66
|
self.state = 'closed'
|
67
67
|
end
|
68
68
|
|
69
|
-
def handle_failure
|
69
|
+
def handle_failure(error)
|
70
70
|
self.last_failed = Time.now
|
71
71
|
self.failure_count += 1
|
72
72
|
if self.failure_count >= self.failure_threshold
|
73
73
|
self.state = 'open'
|
74
74
|
end
|
75
|
+
|
76
|
+
self.last_exception = error
|
77
|
+
raise(error)
|
75
78
|
end
|
76
79
|
end
|
77
80
|
end
|
data/spec/breaker_spec.rb
CHANGED
@@ -10,7 +10,7 @@ module CircuitBreakage
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe '#call' do
|
13
|
-
subject { -> { breaker.call(arg) } }
|
13
|
+
subject { -> { breaker.call(arg) rescue nil} }
|
14
14
|
let(:arg) { 'This is an argument.' }
|
15
15
|
|
16
16
|
context 'when the circuit is closed' do
|
@@ -29,10 +29,15 @@ module CircuitBreakage
|
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'and the call fails' do
|
32
|
-
let(:block) { -> { raise 'some error' } }
|
32
|
+
let(:block) { ->(_) { raise 'some error' } }
|
33
33
|
|
34
34
|
it { is_expected.to change { breaker.failure_count }.by(1) }
|
35
35
|
it { is_expected.to change { breaker.last_failed } }
|
36
|
+
it { is_expected.to change { breaker.last_exception }.from(nil) }
|
37
|
+
|
38
|
+
it 'raises the exception that caused the failure' do
|
39
|
+
expect { breaker.call(arg) }.to raise_exception('some error')
|
40
|
+
end
|
36
41
|
|
37
42
|
context 'and the failure count exceeds the failure threshold' do
|
38
43
|
before { breaker.failure_count = breaker.failure_threshold }
|
@@ -46,7 +51,11 @@ module CircuitBreakage
|
|
46
51
|
before { breaker.timeout = 0.1 }
|
47
52
|
|
48
53
|
it 'counts as a failure' do
|
49
|
-
expect { breaker.call(arg) }.to change { breaker.failure_count }.by(1)
|
54
|
+
expect { breaker.call(arg) rescue nil }.to change { breaker.failure_count }.by(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises CircuitBreakage::CircuitTimeout' do
|
58
|
+
expect { breaker.call(arg) }.to raise_exception(CircuitBreakage::CircuitTimeout)
|
50
59
|
end
|
51
60
|
end
|
52
61
|
end
|
@@ -57,7 +66,9 @@ module CircuitBreakage
|
|
57
66
|
context 'before the retry_time' do
|
58
67
|
before { breaker.last_failed = Time.now - breaker.duration + 30 }
|
59
68
|
|
60
|
-
it
|
69
|
+
it 'raises CircuitBreakage::CircuitOpen' do
|
70
|
+
expect { breaker.call(arg) }.to raise_error(CircuitOpen)
|
71
|
+
end
|
61
72
|
end
|
62
73
|
|
63
74
|
context 'after the retry time' do
|
@@ -9,7 +9,7 @@ module CircuitBreakage
|
|
9
9
|
let(:block) { ->(x) { return x } }
|
10
10
|
|
11
11
|
describe '#call' do
|
12
|
-
subject { -> { breaker.call(arg) } }
|
12
|
+
subject { -> { breaker.call(arg) rescue nil } }
|
13
13
|
let(:arg) { 'This is an argument.' }
|
14
14
|
|
15
15
|
context 'when the circuit is closed' do
|
@@ -28,7 +28,7 @@ module CircuitBreakage
|
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'and the call fails' do
|
31
|
-
let(:block) { -> { raise 'some error' } }
|
31
|
+
let(:block) { ->(_) { raise 'some error' } }
|
32
32
|
|
33
33
|
it { is_expected.to change { breaker.failure_count }.by(1) }
|
34
34
|
it { is_expected.to change { breaker.last_failed } }
|
@@ -45,7 +45,7 @@ module CircuitBreakage
|
|
45
45
|
before { breaker.timeout = 0.1 }
|
46
46
|
|
47
47
|
it 'counts as a failure' do
|
48
|
-
expect { breaker.call(arg) }.to change { breaker.failure_count }.by(1)
|
48
|
+
expect { breaker.call(arg) rescue nil }.to change { breaker.failure_count }.by(1)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -56,7 +56,9 @@ module CircuitBreakage
|
|
56
56
|
context 'before the retry_time' do
|
57
57
|
before { breaker.last_failed = Time.now - breaker.duration + 30 }
|
58
58
|
|
59
|
-
it
|
59
|
+
it 'raises CircuitBreakage::CircuitOpen' do
|
60
|
+
expect { breaker.call(arg) }.to raise_error(CircuitOpen)
|
61
|
+
end
|
60
62
|
end
|
61
63
|
|
62
64
|
context 'after the retry time' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: circuit_breakage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hyland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|