switch_gear 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +7 -4
- data/docs/SwitchGear.html +127 -0
- data/docs/SwitchGear/CircuitBreaker.html +1004 -0
- data/docs/SwitchGear/CircuitBreaker/Failure.html +448 -0
- data/docs/SwitchGear/CircuitBreaker/Memory.html +929 -0
- data/docs/SwitchGear/CircuitBreaker/OpenError.html +124 -0
- data/docs/SwitchGear/CircuitBreaker/Redis.html +1032 -0
- data/docs/_config.yml +1 -0
- data/docs/_index.html +182 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +492 -0
- data/docs/file.README.html +205 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +205 -0
- data/docs/js/app.js +243 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +315 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/switch_gear.rb +1 -0
- data/lib/switch_gear/circuit_breaker.rb +9 -4
- data/lib/switch_gear/circuit_breaker/failure.rb +2 -2
- data/lib/switch_gear/circuit_breaker/memory.rb +4 -4
- data/lib/switch_gear/circuit_breaker/redis.rb +15 -5
- data/lib/switch_gear/version.rb +1 -1
- metadata +23 -2
@@ -0,0 +1,205 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
File: README
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.8
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
pathId = "README";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="file_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
<span class="title">File: README</span>
|
41
|
+
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div id="search">
|
45
|
+
|
46
|
+
<a class="full_list_link" id="class_list_link"
|
47
|
+
href="class_list.html">
|
48
|
+
|
49
|
+
<svg width="24" height="24">
|
50
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
51
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
52
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
</svg>
|
54
|
+
</a>
|
55
|
+
|
56
|
+
</div>
|
57
|
+
<div class="clear"></div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div id="content"><div id='filecontents'><h1 id="switchgear">SwitchGear</h1>
|
61
|
+
|
62
|
+
<blockquote>
|
63
|
+
<p>In an electric power system, switchgear is the combination of electrical disconnect switches, fuses or circuit breakers used to control, protect and isolate electrical equipment. Switchgears are used both to de-energize equipment to allow work to be done and to clear faults downstream. This type of equipment is directly linked to the reliability of the electricity supply.</p>
|
64
|
+
</blockquote>
|
65
|
+
|
66
|
+
<p>SwitchGear is a module that will implement various failover protection layers for deploying apps at scale. The first module is a lightweight implementation of the famous <a href="https://www.martinfowler.com/bliki/CircuitBreaker.html">Michael Nygard</a> circuit breaker pattern.</p>
|
67
|
+
|
68
|
+
<h2 id="installation">Installation</h2>
|
69
|
+
|
70
|
+
<p>This gem is in alpha and is on RubyGems.org. I’m still finalizing the API, but if you wish to help me get to it’s first stable release, please do!</p>
|
71
|
+
|
72
|
+
<p>Add this line to your application’s Gemfile:</p>
|
73
|
+
|
74
|
+
<p><code>ruby
|
75
|
+
gem 'switch_gear'
|
76
|
+
</code></p>
|
77
|
+
|
78
|
+
<p>And then execute:</p>
|
79
|
+
|
80
|
+
<pre class="code ruby"><code class="ruby">$ bundle install
|
81
|
+
</code></pre>
|
82
|
+
|
83
|
+
<h2 id="usage">Usage</h2>
|
84
|
+
|
85
|
+
<h3 id="circuitbreaker">CircuitBreaker</h3>
|
86
|
+
|
87
|
+
<h4 id="in-memory">In Memory</h4>
|
88
|
+
|
89
|
+
<p>Here is an example of how you could use the breaker while making routine calls to a third party service such as Twitter:</p>
|
90
|
+
|
91
|
+
<p>```ruby
|
92
|
+
require ‘switch_gear/circuit_breaker’
|
93
|
+
require ‘logger’</p>
|
94
|
+
|
95
|
+
<p>@logger = Logger.new(STDOUT)</p>
|
96
|
+
|
97
|
+
<p>handles = [“joe”, “jane”, “mary”, “steve”]</p>
|
98
|
+
|
99
|
+
<p>def get_tweets(twitter_handle)
|
100
|
+
http_result = [“Success!”, “Fail”].sample
|
101
|
+
raise RuntimeError.new(“Failed to fetch tweets for #twitter_handle”) if http_result == “Fail”
|
102
|
+
@logger.info “#http_result getting tweets for #twitter_handle”
|
103
|
+
end</p>
|
104
|
+
|
105
|
+
<p>breaker = SwitchGear::CircuitBreaker::Memory.new do |cb|
|
106
|
+
cb.circuit = -> (twitter_handle) { get_tweets(twitter_handle) }
|
107
|
+
cb.failure_limit = 2
|
108
|
+
cb.reset_timeout = 5
|
109
|
+
end</p>
|
110
|
+
|
111
|
+
<p>handles.each do |handle|
|
112
|
+
begin
|
113
|
+
breaker.call(handle)
|
114
|
+
rescue SwitchGear::CircuitBreaker::OpenError
|
115
|
+
@logger.warn “Circuit is open - unable to make calls for #handle”
|
116
|
+
sleep breaker.reset_timeout
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```</p>
|
120
|
+
|
121
|
+
<p>You will see output similar to:
|
122
|
+
<code>
|
123
|
+
W, [2017-02-12T20:49:12.374971 #85900] WARN -- : [RuntimeError] - Failed to fetch tweets for joe
|
124
|
+
W, [2017-02-12T20:49:12.375049 #85900] WARN -- : [RuntimeError] - Failed to fetch tweets for jane
|
125
|
+
I, [2017-02-12T20:49:17.380771 #85900] INFO -- : Success! getting tweets for steve
|
126
|
+
I, [2017-02-12T20:49:17.380865 #85900] INFO -- : Circuit closed
|
127
|
+
</code></p>
|
128
|
+
|
129
|
+
<p>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 <code>failuire_limit</code> method. If another call comes in after two failures, it will raise a <code>SwitchGear::CircuitBreaker::OpenError</code> error. The only way the circuit breaker will be closed again is if the <code>reset_timeout</code> period has lapsed. In our loop we catch the <code>SwitchGear::CircuitBreaker::OpenError</code> 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,</p>
|
130
|
+
|
131
|
+
<p><code>
|
132
|
+
I, [2017-02-12T20:49:17.380771 #85900] INFO -- : Success! getting tweets for steve
|
133
|
+
</code>
|
134
|
+
is 5+ seconds after the last error which exceeds the <code>reset_timeout</code> - that’s why the breaker allowed the method invocation to go get steve’s tweets.</p>
|
135
|
+
|
136
|
+
<h4 id="redis">Redis</h4>
|
137
|
+
|
138
|
+
<p>In an distributed environment the in memory solution of the circuit breaker creates quite a bit of unnecessary work. If you can imagine 5 servers all running their own circuit breakers, the <code>failure_limit</code> has just increased by a factor of 5. Ideally, we want server1’s failures and server2’s failures to be included for similar breakers. We do this by using redis where the state of the breaker and the failures are persisted. Redis is a great choice for this especially since most distributed systems have a redis instance in use.</p>
|
139
|
+
|
140
|
+
<p>You can visualize a few servers that were originally in a closed state moving to open upon failures as such:</p>
|
141
|
+
|
142
|
+
<p><img src="https://s3.postimg.org/stxckap03/ezgif_com_video_to_gif.gif" alt="img" /></p>
|
143
|
+
|
144
|
+
<p>You can set up the <code>CircuitBreaker</code> to use the redis adapter like this:</p>
|
145
|
+
|
146
|
+
<p><code>ruby
|
147
|
+
breaker = SwitchGear::CircuitBreaker::Redis.new do |cb|
|
148
|
+
cb.circuit = -> (twitter_handle) { get_tweets(twitter_handle) }
|
149
|
+
cb.client = redis
|
150
|
+
cb.namespace = "get_tweets"
|
151
|
+
cb.failure_limit = 2
|
152
|
+
cb.reset_timeout = 5
|
153
|
+
end
|
154
|
+
</code></p>
|
155
|
+
|
156
|
+
<p>You need 2 additional parameters(compared to the <code>Memory</code> adapter), they are defined as such:</p>
|
157
|
+
|
158
|
+
<ul>
|
159
|
+
<li><code>client</code> - an instance of a <code>Redis</code> client. This gem does not have a hard dependency on a particular redis client but for testing I’ve used <a href="https://github.com/redis/redis-rb">redis-rb</a>. Whatever you pass in here simply has to implement a few redis commands such as <code>sadd</code>, <code>del</code>, <code>smembers</code>, <code>get</code> and <code>set</code>. The client will ensure these exist before the breaker can be instantiated.</li>
|
160
|
+
<li><code>namespace</code> - A unique name that will be used across servers to sync <code>state</code> and <code>failures</code>. I’d recommend <code>class_name:some_method</code> or whatever is special about what’s being invoked in the <code>circuit</code>.</li>
|
161
|
+
</ul>
|
162
|
+
|
163
|
+
<h4 id="roll-your-own-circuit-breaker">Roll Your Own Circuit Breaker</h4>
|
164
|
+
|
165
|
+
<p>The goal of this project is to help you implement a circuit breaker pattern and be agnostic to the persistence layer. I did it in memory and in redis both as working implementations to make the gem usable out of the box. There are other in memory data stores that would work really well with this and so you can easily implement your own.</p>
|
166
|
+
|
167
|
+
<p><code>ruby
|
168
|
+
class MyPreferredAdapter
|
169
|
+
include SwitchGear::CircuitBreaker
|
170
|
+
end
|
171
|
+
</code></p>
|
172
|
+
|
173
|
+
<h2 id="forthcoming">Forthcoming</h2>
|
174
|
+
|
175
|
+
<ol>
|
176
|
+
<li>A middleware in Sidekiq using this gem</li>
|
177
|
+
<li>Better in memory support for async tasks</li>
|
178
|
+
<li>More examples</li>
|
179
|
+
<li>More documentation</li>
|
180
|
+
</ol>
|
181
|
+
|
182
|
+
<h2 id="development">Development</h2>
|
183
|
+
|
184
|
+
<p>After checking out the repo, run <code>bin/setup</code> to install dependencies. Then, run <code>rake spec</code> to run the tests. You can also run <code>bin/console</code> for an interactive prompt that will allow you to experiment.</p>
|
185
|
+
|
186
|
+
<p>To install this gem onto your local machine, run <code>bundle exec rake install</code>. To release a new version, update the version number in <code>version.rb</code>, and then run <code>bundle exec rake release</code>, which will create a git tag for the version, push git commits and tags, and push the <code>.gem</code> file to <a href="https://rubygems.org">rubygems.org</a>.</p>
|
187
|
+
|
188
|
+
<h2 id="contributing">Contributing</h2>
|
189
|
+
|
190
|
+
<p>Bug reports and pull requests are welcome on GitHub at https://github.com/allcentury/circuit_breaker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the <a href="http://contributor-covenant.org">Contributor Covenant</a> code of conduct.</p>
|
191
|
+
|
192
|
+
<h2 id="license">License</h2>
|
193
|
+
|
194
|
+
<p>The gem is available as open source under the terms of the <a href="http://opensource.org/licenses/MIT">MIT License</a>.</p>
|
195
|
+
</div></div>
|
196
|
+
|
197
|
+
<div id="footer">
|
198
|
+
Generated on Fri May 26 06:53:24 2017 by
|
199
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
200
|
+
0.9.8 (ruby-2.4.0).
|
201
|
+
</div>
|
202
|
+
|
203
|
+
</div>
|
204
|
+
</body>
|
205
|
+
</html>
|
data/docs/file_list.html
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
5
|
+
<meta charset="utf-8" />
|
6
|
+
|
7
|
+
<link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" charset="utf-8" />
|
8
|
+
|
9
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
14
|
+
|
15
|
+
<script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
|
16
|
+
|
17
|
+
|
18
|
+
<title>File List</title>
|
19
|
+
<base id="base_target" target="_parent" />
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
<div id="content">
|
23
|
+
<div class="fixed_header">
|
24
|
+
<h1 id="full_list_header">File List</h1>
|
25
|
+
<div id="full_list_nav">
|
26
|
+
|
27
|
+
<span><a target="_self" href="class_list.html">
|
28
|
+
Classes
|
29
|
+
</a></span>
|
30
|
+
|
31
|
+
<span><a target="_self" href="method_list.html">
|
32
|
+
Methods
|
33
|
+
</a></span>
|
34
|
+
|
35
|
+
<span><a target="_self" href="file_list.html">
|
36
|
+
Files
|
37
|
+
</a></span>
|
38
|
+
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div id="search">Search: <input type="text" /></div>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<ul id="full_list" class="file">
|
45
|
+
|
46
|
+
|
47
|
+
<li id="object_README" class="odd">
|
48
|
+
<div class="item"><span class="object_link"><a href="index.html" title="README">README</a></span></div>
|
49
|
+
</li>
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
</ul>
|
54
|
+
</div>
|
55
|
+
</body>
|
56
|
+
</html>
|
data/docs/frames.html
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Documentation by YARD 0.9.8</title>
|
6
|
+
</head>
|
7
|
+
<script type="text/javascript" charset="utf-8">
|
8
|
+
var match = unescape(window.location.hash).match(/^#!(.+)/);
|
9
|
+
var name = match ? match[1] : 'index.html';
|
10
|
+
name = name.replace(/^(\w+):\/\//, '').replace(/^\/\//, '');
|
11
|
+
window.top.location = name;
|
12
|
+
</script>
|
13
|
+
<noscript>
|
14
|
+
<h1>Oops!</h1>
|
15
|
+
<h2>YARD requires JavaScript!</h2>
|
16
|
+
</noscript>
|
17
|
+
</html>
|
data/docs/index.html
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
File: README
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.8
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
pathId = "README";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
<span class="title">File: README</span>
|
41
|
+
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div id="search">
|
45
|
+
|
46
|
+
<a class="full_list_link" id="class_list_link"
|
47
|
+
href="class_list.html">
|
48
|
+
|
49
|
+
<svg width="24" height="24">
|
50
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
51
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
52
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
</svg>
|
54
|
+
</a>
|
55
|
+
|
56
|
+
</div>
|
57
|
+
<div class="clear"></div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div id="content"><div id='filecontents'><h1 id="switchgear">SwitchGear</h1>
|
61
|
+
|
62
|
+
<blockquote>
|
63
|
+
<p>In an electric power system, switchgear is the combination of electrical disconnect switches, fuses or circuit breakers used to control, protect and isolate electrical equipment. Switchgears are used both to de-energize equipment to allow work to be done and to clear faults downstream. This type of equipment is directly linked to the reliability of the electricity supply.</p>
|
64
|
+
</blockquote>
|
65
|
+
|
66
|
+
<p>SwitchGear is a module that will implement various failover protection layers for deploying apps at scale. The first module is a lightweight implementation of the famous <a href="https://www.martinfowler.com/bliki/CircuitBreaker.html">Michael Nygard</a> circuit breaker pattern.</p>
|
67
|
+
|
68
|
+
<h2 id="installation">Installation</h2>
|
69
|
+
|
70
|
+
<p>This gem is in alpha and is on RubyGems.org. I’m still finalizing the API, but if you wish to help me get to it’s first stable release, please do!</p>
|
71
|
+
|
72
|
+
<p>Add this line to your application’s Gemfile:</p>
|
73
|
+
|
74
|
+
<p><code>ruby
|
75
|
+
gem 'switch_gear'
|
76
|
+
</code></p>
|
77
|
+
|
78
|
+
<p>And then execute:</p>
|
79
|
+
|
80
|
+
<pre class="code ruby"><code class="ruby">$ bundle install
|
81
|
+
</code></pre>
|
82
|
+
|
83
|
+
<h2 id="usage">Usage</h2>
|
84
|
+
|
85
|
+
<h3 id="circuitbreaker">CircuitBreaker</h3>
|
86
|
+
|
87
|
+
<h4 id="in-memory">In Memory</h4>
|
88
|
+
|
89
|
+
<p>Here is an example of how you could use the breaker while making routine calls to a third party service such as Twitter:</p>
|
90
|
+
|
91
|
+
<p>```ruby
|
92
|
+
require ‘switch_gear/circuit_breaker’
|
93
|
+
require ‘logger’</p>
|
94
|
+
|
95
|
+
<p>@logger = Logger.new(STDOUT)</p>
|
96
|
+
|
97
|
+
<p>handles = [“joe”, “jane”, “mary”, “steve”]</p>
|
98
|
+
|
99
|
+
<p>def get_tweets(twitter_handle)
|
100
|
+
http_result = [“Success!”, “Fail”].sample
|
101
|
+
raise RuntimeError.new(“Failed to fetch tweets for #twitter_handle”) if http_result == “Fail”
|
102
|
+
@logger.info “#http_result getting tweets for #twitter_handle”
|
103
|
+
end</p>
|
104
|
+
|
105
|
+
<p>breaker = SwitchGear::CircuitBreaker::Memory.new do |cb|
|
106
|
+
cb.circuit = -> (twitter_handle) { get_tweets(twitter_handle) }
|
107
|
+
cb.failure_limit = 2
|
108
|
+
cb.reset_timeout = 5
|
109
|
+
end</p>
|
110
|
+
|
111
|
+
<p>handles.each do |handle|
|
112
|
+
begin
|
113
|
+
breaker.call(handle)
|
114
|
+
rescue SwitchGear::CircuitBreaker::OpenError
|
115
|
+
@logger.warn “Circuit is open - unable to make calls for #handle”
|
116
|
+
sleep breaker.reset_timeout
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```</p>
|
120
|
+
|
121
|
+
<p>You will see output similar to:
|
122
|
+
<code>
|
123
|
+
W, [2017-02-12T20:49:12.374971 #85900] WARN -- : [RuntimeError] - Failed to fetch tweets for joe
|
124
|
+
W, [2017-02-12T20:49:12.375049 #85900] WARN -- : [RuntimeError] - Failed to fetch tweets for jane
|
125
|
+
I, [2017-02-12T20:49:17.380771 #85900] INFO -- : Success! getting tweets for steve
|
126
|
+
I, [2017-02-12T20:49:17.380865 #85900] INFO -- : Circuit closed
|
127
|
+
</code></p>
|
128
|
+
|
129
|
+
<p>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 <code>failuire_limit</code> method. If another call comes in after two failures, it will raise a <code>SwitchGear::CircuitBreaker::OpenError</code> error. The only way the circuit breaker will be closed again is if the <code>reset_timeout</code> period has lapsed. In our loop we catch the <code>SwitchGear::CircuitBreaker::OpenError</code> 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,</p>
|
130
|
+
|
131
|
+
<p><code>
|
132
|
+
I, [2017-02-12T20:49:17.380771 #85900] INFO -- : Success! getting tweets for steve
|
133
|
+
</code>
|
134
|
+
is 5+ seconds after the last error which exceeds the <code>reset_timeout</code> - that’s why the breaker allowed the method invocation to go get steve’s tweets.</p>
|
135
|
+
|
136
|
+
<h4 id="redis">Redis</h4>
|
137
|
+
|
138
|
+
<p>In an distributed environment the in memory solution of the circuit breaker creates quite a bit of unnecessary work. If you can imagine 5 servers all running their own circuit breakers, the <code>failure_limit</code> has just increased by a factor of 5. Ideally, we want server1’s failures and server2’s failures to be included for similar breakers. We do this by using redis where the state of the breaker and the failures are persisted. Redis is a great choice for this especially since most distributed systems have a redis instance in use.</p>
|
139
|
+
|
140
|
+
<p>You can visualize a few servers that were originally in a closed state moving to open upon failures as such:</p>
|
141
|
+
|
142
|
+
<p><img src="https://s3.postimg.org/stxckap03/ezgif_com_video_to_gif.gif" alt="img" /></p>
|
143
|
+
|
144
|
+
<p>You can set up the <code>CircuitBreaker</code> to use the redis adapter like this:</p>
|
145
|
+
|
146
|
+
<p><code>ruby
|
147
|
+
breaker = SwitchGear::CircuitBreaker::Redis.new do |cb|
|
148
|
+
cb.circuit = -> (twitter_handle) { get_tweets(twitter_handle) }
|
149
|
+
cb.client = redis
|
150
|
+
cb.namespace = "get_tweets"
|
151
|
+
cb.failure_limit = 2
|
152
|
+
cb.reset_timeout = 5
|
153
|
+
end
|
154
|
+
</code></p>
|
155
|
+
|
156
|
+
<p>You need 2 additional parameters(compared to the <code>Memory</code> adapter), they are defined as such:</p>
|
157
|
+
|
158
|
+
<ul>
|
159
|
+
<li><code>client</code> - an instance of a <code>Redis</code> client. This gem does not have a hard dependency on a particular redis client but for testing I’ve used <a href="https://github.com/redis/redis-rb">redis-rb</a>. Whatever you pass in here simply has to implement a few redis commands such as <code>sadd</code>, <code>del</code>, <code>smembers</code>, <code>get</code> and <code>set</code>. The client will ensure these exist before the breaker can be instantiated.</li>
|
160
|
+
<li><code>namespace</code> - A unique name that will be used across servers to sync <code>state</code> and <code>failures</code>. I’d recommend <code>class_name:some_method</code> or whatever is special about what’s being invoked in the <code>circuit</code>.</li>
|
161
|
+
</ul>
|
162
|
+
|
163
|
+
<h4 id="roll-your-own-circuit-breaker">Roll Your Own Circuit Breaker</h4>
|
164
|
+
|
165
|
+
<p>The goal of this project is to help you implement a circuit breaker pattern and be agnostic to the persistence layer. I did it in memory and in redis both as working implementations to make the gem usable out of the box. There are other in memory data stores that would work really well with this and so you can easily implement your own.</p>
|
166
|
+
|
167
|
+
<p><code>ruby
|
168
|
+
class MyPreferredAdapter
|
169
|
+
include SwitchGear::CircuitBreaker
|
170
|
+
end
|
171
|
+
</code></p>
|
172
|
+
|
173
|
+
<h2 id="forthcoming">Forthcoming</h2>
|
174
|
+
|
175
|
+
<ol>
|
176
|
+
<li>A middleware in Sidekiq using this gem</li>
|
177
|
+
<li>Better in memory support for async tasks</li>
|
178
|
+
<li>More examples</li>
|
179
|
+
<li>More documentation</li>
|
180
|
+
</ol>
|
181
|
+
|
182
|
+
<h2 id="development">Development</h2>
|
183
|
+
|
184
|
+
<p>After checking out the repo, run <code>bin/setup</code> to install dependencies. Then, run <code>rake spec</code> to run the tests. You can also run <code>bin/console</code> for an interactive prompt that will allow you to experiment.</p>
|
185
|
+
|
186
|
+
<p>To install this gem onto your local machine, run <code>bundle exec rake install</code>. To release a new version, update the version number in <code>version.rb</code>, and then run <code>bundle exec rake release</code>, which will create a git tag for the version, push git commits and tags, and push the <code>.gem</code> file to <a href="https://rubygems.org">rubygems.org</a>.</p>
|
187
|
+
|
188
|
+
<h2 id="contributing">Contributing</h2>
|
189
|
+
|
190
|
+
<p>Bug reports and pull requests are welcome on GitHub at https://github.com/allcentury/circuit_breaker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the <a href="http://contributor-covenant.org">Contributor Covenant</a> code of conduct.</p>
|
191
|
+
|
192
|
+
<h2 id="license">License</h2>
|
193
|
+
|
194
|
+
<p>The gem is available as open source under the terms of the <a href="http://opensource.org/licenses/MIT">MIT License</a>.</p>
|
195
|
+
</div></div>
|
196
|
+
|
197
|
+
<div id="footer">
|
198
|
+
Generated on Fri May 26 06:53:24 2017 by
|
199
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
200
|
+
0.9.8 (ruby-2.4.0).
|
201
|
+
</div>
|
202
|
+
|
203
|
+
</div>
|
204
|
+
</body>
|
205
|
+
</html>
|