cb21 0.0.4
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 +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +36 -0
- data/lib/cb2/breaker.rb +63 -0
- data/lib/cb2/error.rb +4 -0
- data/lib/cb2/strategies/percentage.rb +16 -0
- data/lib/cb2/strategies/rolling_window.rb +72 -0
- data/lib/cb2/strategies/stub.rb +11 -0
- data/lib/cb2.rb +10 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 23f01fd05a90a50ecf546f56d48996ab4aa30035830d3e8450b2b28fb9d99850
|
4
|
+
data.tar.gz: 489095dbae4aa3f63ec29029acf596e01b3b61e5c10334582bc9353655737008
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0025340b77170a311ab048e712f782ffd5209b8047add8202aa553f26e4a6b4738fe94af73b1e5ed874eb242dce8e6dcae4b34129bde27dde41213d6eb2155e
|
7
|
+
data.tar.gz: abf0bee6068b84d851b0fd251110565db8d3accb744a5c7dec879558d2d5560badec66aff63dd1b34b61b430f95d10b1187cd7c5a7882a06d33add1f2790387c
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cb2 (0.0.3)
|
5
|
+
redis (~> 3.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.2.5)
|
11
|
+
rake (10.3.2)
|
12
|
+
redis (3.2.0)
|
13
|
+
rr (1.1.2)
|
14
|
+
rspec (3.1.0)
|
15
|
+
rspec-core (~> 3.1.0)
|
16
|
+
rspec-expectations (~> 3.1.0)
|
17
|
+
rspec-mocks (~> 3.1.0)
|
18
|
+
rspec-core (3.1.7)
|
19
|
+
rspec-support (~> 3.1.0)
|
20
|
+
rspec-expectations (3.1.2)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.1.0)
|
23
|
+
rspec-mocks (3.1.3)
|
24
|
+
rspec-support (~> 3.1.0)
|
25
|
+
rspec-support (3.1.2)
|
26
|
+
timecop (0.7.1)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
cb2!
|
33
|
+
rake (> 0)
|
34
|
+
rr (~> 1.1)
|
35
|
+
rspec (~> 3.1)
|
36
|
+
timecop (~> 0.7)
|
data/lib/cb2/breaker.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class CB2::Breaker
|
2
|
+
attr_accessor :service, :strategy
|
3
|
+
|
4
|
+
def initialize(options)
|
5
|
+
@service = options[:service] || "default"
|
6
|
+
@strategy = initialize_strategy(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
if open?
|
11
|
+
raise CB2::BreakerOpen
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
process_count
|
16
|
+
ret = yield
|
17
|
+
process_success
|
18
|
+
rescue => e
|
19
|
+
process_error
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
|
23
|
+
return ret
|
24
|
+
end
|
25
|
+
|
26
|
+
def open?
|
27
|
+
strategy.open?
|
28
|
+
rescue Redis::BaseError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_count
|
33
|
+
strategy.count if strategy.respond_to?(:count)
|
34
|
+
rescue Redis::BaseError
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_success
|
38
|
+
strategy.success if strategy.respond_to?(:success)
|
39
|
+
rescue Redis::BaseError
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_error
|
43
|
+
strategy.error if strategy.respond_to?(:error)
|
44
|
+
rescue Redis::BaseError
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_strategy(options)
|
48
|
+
strategy_options = options.dup.merge(service: self.service)
|
49
|
+
|
50
|
+
if options[:strategy].respond_to?(:open?)
|
51
|
+
return options[:strategy].new(strategy_options)
|
52
|
+
end
|
53
|
+
|
54
|
+
case options[:strategy].to_s
|
55
|
+
when "", "percentage"
|
56
|
+
CB2::Percentage.new(strategy_options)
|
57
|
+
when "rolling_window"
|
58
|
+
CB2::RollingWindow.new(strategy_options)
|
59
|
+
when "stub"
|
60
|
+
CB2::Stub.new(strategy_options)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/cb2/error.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class CB2::Percentage < CB2::RollingWindow
|
2
|
+
# keep a rolling window of all calls too
|
3
|
+
def count
|
4
|
+
@current_count = increment_rolling_window(key("count"))
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def should_open?(error_count)
|
10
|
+
# do not open until we have a reasonable number of requests
|
11
|
+
return false if @current_count < 5
|
12
|
+
|
13
|
+
error_perc = error_count * 100 / @current_count.to_f
|
14
|
+
return error_perc >= threshold
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class CB2::RollingWindow
|
2
|
+
attr_accessor :service, :duration, :threshold, :reenable_after, :redis
|
3
|
+
|
4
|
+
def initialize(options)
|
5
|
+
@service = options.fetch(:service)
|
6
|
+
@duration = options.fetch(:duration)
|
7
|
+
@threshold = options.fetch(:threshold)
|
8
|
+
@reenable_after = options.fetch(:reenable_after)
|
9
|
+
@redis = options[:redis] || Redis.current
|
10
|
+
end
|
11
|
+
|
12
|
+
def open?
|
13
|
+
@last_open = nil # always fetch the latest value from redis here
|
14
|
+
last_open && last_open.to_i > (Time.now.to_i - reenable_after)
|
15
|
+
end
|
16
|
+
|
17
|
+
def half_open?
|
18
|
+
last_open && last_open.to_i < (Time.now.to_i - reenable_after)
|
19
|
+
end
|
20
|
+
|
21
|
+
def last_open
|
22
|
+
@last_open ||= redis.get(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def success
|
26
|
+
if half_open?
|
27
|
+
reset!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def error
|
32
|
+
count = increment_rolling_window(key("error"))
|
33
|
+
if half_open? || should_open?(count)
|
34
|
+
trip!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset!
|
39
|
+
@last_open = nil
|
40
|
+
redis.del(key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def trip!
|
44
|
+
@last_open = Time.now.to_i
|
45
|
+
redis.set(key, @last_open)
|
46
|
+
end
|
47
|
+
|
48
|
+
# generate a key to use in redis
|
49
|
+
def key(id=nil)
|
50
|
+
postfix = id ? "-#{id}" : ""
|
51
|
+
"cb2-#{service}#{postfix}"
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def increment_rolling_window(key)
|
57
|
+
t = Time.now.to_i
|
58
|
+
pipeline = redis.pipelined do
|
59
|
+
# keep the sorted set clean
|
60
|
+
redis.zremrangebyscore(key, "-inf", t - duration)
|
61
|
+
# add as a random uuid because sorted sets won't take duplicate items:
|
62
|
+
redis.zadd(key, t, SecureRandom.uuid)
|
63
|
+
# just count how many errors are left in the set
|
64
|
+
redis.zcard(key)
|
65
|
+
end
|
66
|
+
return pipeline.last # return the count
|
67
|
+
end
|
68
|
+
|
69
|
+
def should_open?(error_count)
|
70
|
+
error_count >= threshold
|
71
|
+
end
|
72
|
+
end
|
data/lib/cb2.rb
ADDED
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cb21
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Talha Shoaib
|
8
|
+
- Rab Nawaz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2021-12-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 4.3.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 4.3.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rr
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.1'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.1'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.1'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.1'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: timecop
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.7'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.7'
|
84
|
+
description: Implementation of the circuit breaker pattern in Ruby
|
85
|
+
email:
|
86
|
+
- talhashoaib27@gmail.com
|
87
|
+
- rabn1993@gmail.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- lib/cb2.rb
|
95
|
+
- lib/cb2/breaker.rb
|
96
|
+
- lib/cb2/error.rb
|
97
|
+
- lib/cb2/strategies/percentage.rb
|
98
|
+
- lib/cb2/strategies/rolling_window.rb
|
99
|
+
- lib/cb2/strategies/stub.rb
|
100
|
+
homepage: https://github.com/talhashoaib/cb2
|
101
|
+
licenses: []
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.7.8
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Circuit breaker with latest redis
|
123
|
+
test_files: []
|