stoplight 3.0.2 → 4.0.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 +176 -180
- data/lib/stoplight/builder.rb +68 -0
- data/lib/stoplight/circuit_breaker.rb +92 -0
- data/lib/stoplight/configurable.rb +95 -0
- data/lib/stoplight/configuration.rb +126 -0
- data/lib/stoplight/data_store/memory.rb +20 -5
- data/lib/stoplight/data_store/redis.rb +37 -5
- data/lib/stoplight/default.rb +2 -0
- data/lib/stoplight/error.rb +1 -0
- data/lib/stoplight/light/deprecated.rb +44 -0
- data/lib/stoplight/light/lockable.rb +45 -0
- data/lib/stoplight/light/runnable.rb +25 -24
- data/lib/stoplight/light.rb +69 -63
- data/lib/stoplight/rspec/generic_notifier.rb +42 -0
- data/lib/stoplight/rspec.rb +3 -0
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight.rb +32 -8
- data/spec/spec_helper.rb +7 -0
- data/spec/stoplight/builder_spec.rb +165 -0
- data/spec/stoplight/circuit_breaker_spec.rb +35 -0
- data/spec/stoplight/configurable_spec.rb +25 -0
- data/spec/stoplight/data_store/memory_spec.rb +12 -149
- data/spec/stoplight/data_store/redis_spec.rb +26 -158
- data/spec/stoplight/error_spec.rb +10 -0
- data/spec/stoplight/light/lockable_spec.rb +93 -0
- data/spec/stoplight/light/runnable_spec.rb +12 -273
- data/spec/stoplight/light_spec.rb +4 -28
- data/spec/stoplight/notifier/generic_spec.rb +35 -35
- data/spec/stoplight/notifier/io_spec.rb +1 -0
- data/spec/stoplight/notifier/logger_spec.rb +3 -0
- data/spec/stoplight_spec.rb +17 -6
- data/spec/support/configurable.rb +69 -0
- data/spec/support/data_store/base/clear_failures.rb +18 -0
- data/spec/support/data_store/base/clear_state.rb +20 -0
- data/spec/support/data_store/base/get_all.rb +44 -0
- data/spec/support/data_store/base/get_failures.rb +30 -0
- data/spec/support/data_store/base/get_state.rb +7 -0
- data/spec/support/data_store/base/names.rb +29 -0
- data/spec/support/data_store/base/record_failures.rb +70 -0
- data/spec/support/data_store/base/set_state.rb +15 -0
- data/spec/support/data_store/base/with_notification_lock.rb +27 -0
- data/spec/support/data_store/base.rb +21 -0
- data/spec/support/database_cleaner.rb +26 -0
- data/spec/support/exception_helpers.rb +9 -0
- data/spec/support/light/runnable/color.rb +79 -0
- data/spec/support/light/runnable/run.rb +247 -0
- data/spec/support/light/runnable.rb +4 -0
- metadata +51 -231
- data/lib/stoplight/notifier/bugsnag.rb +0 -37
- data/lib/stoplight/notifier/honeybadger.rb +0 -44
- data/lib/stoplight/notifier/pagerduty.rb +0 -21
- data/lib/stoplight/notifier/raven.rb +0 -40
- data/lib/stoplight/notifier/rollbar.rb +0 -39
- data/lib/stoplight/notifier/slack.rb +0 -21
- data/spec/stoplight/notifier/bugsnag_spec.rb +0 -90
- data/spec/stoplight/notifier/honeybadger_spec.rb +0 -88
- data/spec/stoplight/notifier/pagerduty_spec.rb +0 -40
- data/spec/stoplight/notifier/raven_spec.rb +0 -90
- data/spec/stoplight/notifier/rollbar_spec.rb +0 -90
- data/spec/stoplight/notifier/slack_spec.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0194283983ed6568bca930b6640a7d397dd06e779d921e8bd169cb6a8e743448'
|
4
|
+
data.tar.gz: 9b68b81b05c15cbed7885d8f10a042c0e0ecacafacb0fe6e621e3c124007d74e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64c7f5d19bb00b46f9908d938a120c475f1fd60e481e58400c49360dae260bd6c83690e61f34362a20dea0b3b955f4f9b9d76030b8ccbb7c788fd7a531ad395b
|
7
|
+
data.tar.gz: e48e470e7f72d811852df5576b132a11f6f69dedf711cf371c9cfbc4dc1a4600f217b147ba53c9313a8863a06aaef88e279d3cd5f7e8ff51bd69c731123b1093
|
data/README.md
CHANGED
@@ -9,6 +9,8 @@ Stoplight is traffic control for code. It's an implementation of the circuit
|
|
9
9
|
breaker pattern in Ruby.
|
10
10
|
|
11
11
|
---
|
12
|
+
:warning: You're browsing the documentation of the not realized version. You can find the documentation
|
13
|
+
for the most recent version 3.0.1 [here](https://github.com/bolshakov/stoplight/tree/v3.0.1).
|
12
14
|
|
13
15
|
Does your code use unreliable systems, like a flaky database or a spotty web
|
14
16
|
service? Wrap calls to those up in stoplights to prevent them from affecting
|
@@ -17,97 +19,88 @@ the rest of your application.
|
|
17
19
|
Check out [stoplight-admin][] for controlling your stoplights.
|
18
20
|
|
19
21
|
- [Installation](#installation)
|
20
|
-
- [Basic
|
21
|
-
- [Custom
|
22
|
-
- [Custom
|
23
|
-
- [Custom
|
24
|
-
- [Custom
|
22
|
+
- [Basic Usage](#basic-usage)
|
23
|
+
- [Custom Errors](#custom-errors)
|
24
|
+
- [Custom Fallback](#custom-fallback)
|
25
|
+
- [Custom Threshold](#custom-threshold)
|
26
|
+
- [Custom Window Size](#custom-window-size)
|
27
|
+
- [Custom Cool Off Time](#custom-cool-off-time)
|
25
28
|
- [Rails](#rails)
|
26
29
|
- [Setup](#setup)
|
27
|
-
- [Data
|
30
|
+
- [Data Store](#data-store)
|
28
31
|
- [Redis](#redis)
|
29
32
|
- [Notifiers](#notifiers)
|
30
|
-
- [
|
31
|
-
- [Honeybadger](#honeybadger)
|
33
|
+
- [IO](#io)
|
32
34
|
- [Logger](#logger)
|
33
|
-
- [
|
34
|
-
- [
|
35
|
-
- [Sentry](#sentry)
|
36
|
-
- [Slack](#slack)
|
35
|
+
- [Community-supported Notifiers](#community-supported-notifiers)
|
36
|
+
- [How to Implement Your Own Notifier?](#how-to-implement-your-own-notifier)
|
37
37
|
- [Rails](#rails-1)
|
38
|
-
- [Advanced
|
38
|
+
- [Advanced Usage](#advanced-usage)
|
39
39
|
- [Locking](#locking)
|
40
40
|
- [Testing](#testing)
|
41
|
+
- [Maintenance Policy][#maintenance-policy]
|
41
42
|
- [Credits](#credits)
|
42
43
|
|
43
44
|
## Installation
|
44
45
|
|
45
46
|
Add it to your Gemfile:
|
46
47
|
|
47
|
-
```
|
48
|
+
```ruby
|
48
49
|
gem 'stoplight'
|
49
50
|
```
|
50
51
|
|
51
52
|
Or install it manually:
|
52
53
|
|
53
|
-
```
|
54
|
+
```sh
|
54
55
|
$ gem install stoplight
|
55
56
|
```
|
56
57
|
|
57
58
|
Stoplight uses [Semantic Versioning][]. Check out [the change log][] for a
|
58
59
|
detailed list of changes.
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
## Basic usage
|
61
|
+
## Basic Usage
|
63
62
|
|
64
63
|
To get started, create a stoplight:
|
65
64
|
|
66
|
-
```
|
67
|
-
light = Stoplight('example-pi')
|
68
|
-
# => #<Stoplight::Light:...>
|
65
|
+
```ruby
|
66
|
+
light = Stoplight('example-pi')
|
69
67
|
```
|
70
68
|
|
71
|
-
Then you can run it and it will return the result of calling the block. This is
|
72
|
-
the green state. (The green state corresponds to the closed state for circuit
|
73
|
-
breakers.)
|
69
|
+
Then you can run it with a block of code and it will return the result of calling the block. This is
|
70
|
+
the green state. (The green state corresponds to the closed state for circuit breakers.)
|
74
71
|
|
75
|
-
```
|
76
|
-
light.run
|
72
|
+
```ruby
|
73
|
+
light.run { 22.0 / 7 }
|
77
74
|
# => 3.142857142857143
|
78
75
|
light.color
|
79
76
|
# => "green"
|
80
77
|
```
|
81
78
|
|
82
79
|
If everything goes well, you shouldn't even be able to tell that you're using a
|
83
|
-
stoplight. That's not very interesting though, so let's
|
84
|
-
stoplight:
|
80
|
+
stoplight. That's not very interesting though, so let's make stoplight fail.
|
85
81
|
|
86
|
-
|
87
|
-
light = Stoplight('example-zero') { 1 / 0 }
|
88
|
-
# => #<Stoplight::Light:...>
|
89
|
-
```
|
90
|
-
|
91
|
-
Now when you run it, the error will be recorded and passed through. After
|
82
|
+
When you run it, the error will be recorded and passed through. After
|
92
83
|
running it a few times, the stoplight will stop trying and fail fast. This is
|
93
84
|
the red state. (The red state corresponds to the open state for circuit
|
94
|
-
|
85
|
+
breakers.)
|
95
86
|
|
96
|
-
```
|
97
|
-
light
|
87
|
+
```ruby
|
88
|
+
light = Stoplight('example-zero')
|
89
|
+
# => #<Stoplight::CircuitBreaker:...>
|
90
|
+
light.run { 1 / 0 }
|
98
91
|
# ZeroDivisionError: divided by 0
|
99
|
-
light.run
|
92
|
+
light.run { 1 / 0 }
|
100
93
|
# ZeroDivisionError: divided by 0
|
101
|
-
light.run
|
94
|
+
light.run { 1 / 0 }
|
102
95
|
# Switching example-zero from green to red because ZeroDivisionError divided by 0
|
103
96
|
# ZeroDivisionError: divided by 0
|
104
|
-
light.run
|
97
|
+
light.run { 1 / 0 }
|
105
98
|
# Stoplight::Error::RedLight: example-zero
|
106
99
|
light.color
|
107
100
|
# => "red"
|
108
101
|
```
|
109
102
|
|
110
|
-
When the
|
103
|
+
When the Stoplight changes from green to red, it will notify every configured
|
111
104
|
notifier. See [the notifiers section][] to learn more about notifiers.
|
112
105
|
|
113
106
|
The stoplight will move into the yellow state after being in the red state for
|
@@ -117,7 +110,7 @@ check out [the cool off time section][] When stoplights are yellow, they will
|
|
117
110
|
try to run their code. If it fails, they'll switch back to red. If it succeeds,
|
118
111
|
they'll switch to green.
|
119
112
|
|
120
|
-
### Custom
|
113
|
+
### Custom Errors
|
121
114
|
|
122
115
|
Some errors shouldn't cause your stoplight to move into the red state. Usually
|
123
116
|
these are handled elsewhere in your stack and don't represent real failures. A
|
@@ -140,89 +133,126 @@ provide a custom block that will be called with the error and a handler
|
|
140
133
|
never ignore the error. That means a `NoMemoryError` could change the color
|
141
134
|
of your stoplights.
|
142
135
|
|
143
|
-
```
|
144
|
-
light = Stoplight('example-not-found')
|
136
|
+
```ruby
|
137
|
+
light = Stoplight('example-not-found')
|
145
138
|
.with_error_handler do |error, handle|
|
146
|
-
|
147
|
-
|
139
|
+
if error.is_a?(ActiveRecord::RecordNotFound)
|
140
|
+
raise error
|
141
|
+
else
|
142
|
+
handle.call(error)
|
143
|
+
end
|
148
144
|
end
|
149
|
-
# => #<Stoplight::
|
150
|
-
light.run
|
145
|
+
# => #<Stoplight::CircuitBreaker:...>
|
146
|
+
light.run { User.find(123) }
|
151
147
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
152
|
-
light.run
|
148
|
+
light.run { User.find(123) }
|
153
149
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
154
|
-
light.run
|
150
|
+
light.run { User.find(123) }
|
155
151
|
# ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
156
152
|
light.color
|
157
153
|
# => "green"
|
158
154
|
```
|
159
155
|
|
160
|
-
### Custom
|
156
|
+
### Custom Fallback
|
161
157
|
|
162
158
|
By default, stoplights will re-raise errors when they're green. When they're
|
163
159
|
red, they'll raise a `Stoplight::Error::RedLight` error. You can provide a
|
164
160
|
fallback that will be called in both of these cases. It will be passed the
|
165
161
|
error if the light was green.
|
166
162
|
|
167
|
-
```
|
168
|
-
light = Stoplight('example-fallback')
|
163
|
+
```ruby
|
164
|
+
light = Stoplight('example-fallback')
|
169
165
|
.with_fallback { |e| p e; 'default' }
|
170
|
-
# => #<Stoplight::
|
171
|
-
light.run
|
166
|
+
# => #<Stoplight::CircuitBreaker:..>
|
167
|
+
light.run { 1 / 0 }
|
172
168
|
# #<ZeroDivisionError: divided by 0>
|
173
169
|
# => "default"
|
174
|
-
light.run
|
170
|
+
light.run { 1 / 0 }
|
175
171
|
# #<ZeroDivisionError: divided by 0>
|
176
172
|
# => "default"
|
177
|
-
light.run
|
173
|
+
light.run { 1 / 0 }
|
178
174
|
# Switching example-fallback from green to red because ZeroDivisionError divided by 0
|
179
175
|
# #<ZeroDivisionError: divided by 0>
|
180
176
|
# => "default"
|
181
|
-
light.run
|
177
|
+
light.run { 1 / 0 }
|
182
178
|
# nil
|
183
179
|
# => "default"
|
184
180
|
```
|
185
181
|
|
186
|
-
### Custom
|
182
|
+
### Custom Threshold
|
187
183
|
|
188
184
|
Some bits of code might be allowed to fail more or less frequently than others.
|
189
185
|
You can configure this by setting a custom threshold.
|
190
186
|
|
191
|
-
```
|
192
|
-
light = Stoplight('example-threshold')
|
187
|
+
```ruby
|
188
|
+
light = Stoplight('example-threshold')
|
193
189
|
.with_threshold(1)
|
194
|
-
# => #<Stoplight::
|
195
|
-
light.run
|
190
|
+
# => #<Stoplight::CircuitBreaker:...>
|
191
|
+
light.run { fail }
|
196
192
|
# Switching example-threshold from green to red because RuntimeError
|
197
193
|
# RuntimeError:
|
198
|
-
light.run
|
194
|
+
light.run { fail }
|
199
195
|
# Stoplight::Error::RedLight: example-threshold
|
200
196
|
```
|
201
197
|
|
202
198
|
The default threshold is `3`.
|
203
199
|
|
204
|
-
### Custom
|
200
|
+
### Custom Window Size
|
201
|
+
|
202
|
+
By default, all recorded failures, regardless of the time these happen, will count to reach
|
203
|
+
the threshold (hence turning the light to red). If needed, a window size can be set,
|
204
|
+
meaning you can control how many errors per period of time will count to reach the red
|
205
|
+
state.
|
206
|
+
|
207
|
+
By default, every recorded failure contributes to reaching the threshold, regardless of when it occurs,
|
208
|
+
causing the stoplight to turn red. By configuring a custom window size, you control how errors are
|
209
|
+
counted within a specified time frame. Here's how it works:
|
210
|
+
|
211
|
+
Let's say you set the window size to 2 seconds:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
window_size_in_seconds = 2
|
215
|
+
|
216
|
+
light = Stoplight('example-threshold')
|
217
|
+
.with_window_size(window_size_in_seconds)
|
218
|
+
.with_threshold(1) #=> #<Stoplight::CircuitBreaker:...>
|
219
|
+
|
220
|
+
light.run { 1 / 0 } #=> #<ZeroDivisionError: divided by 0>
|
221
|
+
sleep(3)
|
222
|
+
light.run { 1 / 0 }
|
223
|
+
```
|
224
|
+
|
225
|
+
Without the window size configuration, the second `light.run { 1 / 0 }` call will result in a
|
226
|
+
`Stoplight::Error::RedLight` exception being raised, as the stoplight transitions to the red state
|
227
|
+
after the first call. With a sliding window of 2 seconds, only the errors that occur within the latest
|
228
|
+
2 seconds are considered. The first error causes the stoplight to turn red, but after 3 seconds
|
229
|
+
(when the second error occurs), the window has shifted, and the stoplight switches to green state
|
230
|
+
causing the error to raise again. This provides a way to focus on the most recent errors.
|
231
|
+
|
232
|
+
The default window size is infinity, so all failures counts.
|
233
|
+
|
234
|
+
### Custom Cool Off Time
|
205
235
|
|
206
236
|
Stoplights will automatically attempt to recover after a certain amount of
|
207
237
|
time. A light in the red state for longer than the cool off period will
|
208
238
|
transition to the yellow state. This cool off time is customizable.
|
209
239
|
|
210
|
-
```
|
211
|
-
light = Stoplight('example-cool-off')
|
240
|
+
```ruby
|
241
|
+
light = Stoplight('example-cool-off')
|
212
242
|
.with_cool_off_time(1)
|
213
|
-
# => #<Stoplight::
|
214
|
-
light.run
|
243
|
+
# => #<Stoplight::CircuitBreaker:...>
|
244
|
+
light.run { fail }
|
215
245
|
# RuntimeError:
|
216
|
-
light.run
|
246
|
+
light.run { fail }
|
217
247
|
# RuntimeError:
|
218
|
-
light.run
|
248
|
+
light.run { fail }
|
219
249
|
# Switching example-cool-off from green to red because RuntimeError
|
220
250
|
# RuntimeError:
|
221
251
|
sleep(1)
|
222
252
|
# => 1
|
223
253
|
light.color
|
224
254
|
# => "yellow"
|
225
|
-
light.run
|
255
|
+
light.run { fail }
|
226
256
|
# RuntimeError:
|
227
257
|
```
|
228
258
|
|
@@ -236,19 +266,19 @@ effectively replaces the red state with yellow.
|
|
236
266
|
Stoplight was designed to wrap Rails actions with minimal effort. Here's an
|
237
267
|
example configuration:
|
238
268
|
|
239
|
-
```
|
269
|
+
```ruby
|
240
270
|
class ApplicationController < ActionController::Base
|
241
271
|
around_action :stoplight
|
242
272
|
|
243
273
|
private
|
244
274
|
|
245
275
|
def stoplight(&block)
|
246
|
-
Stoplight("#{params[:controller]}##{params[:action]}"
|
276
|
+
Stoplight("#{params[:controller]}##{params[:action]}")
|
247
277
|
.with_fallback do |error|
|
248
278
|
Rails.logger.error(error)
|
249
279
|
render(nothing: true, status: :service_unavailable)
|
250
280
|
end
|
251
|
-
.run
|
281
|
+
.run(&block)
|
252
282
|
end
|
253
283
|
end
|
254
284
|
```
|
@@ -259,10 +289,10 @@ end
|
|
259
289
|
|
260
290
|
Stoplight uses an in-memory data store out of the box.
|
261
291
|
|
262
|
-
```
|
292
|
+
```ruby
|
263
293
|
require 'stoplight'
|
264
294
|
# => true
|
265
|
-
Stoplight
|
295
|
+
Stoplight.default_data_store
|
266
296
|
# => #<Stoplight::DataStore::Memory:...>
|
267
297
|
```
|
268
298
|
|
@@ -271,17 +301,16 @@ the only supported persistent data store is Redis.
|
|
271
301
|
|
272
302
|
#### Redis
|
273
303
|
|
274
|
-
Make sure you have [the Redis gem][]
|
275
|
-
Stoplight.
|
304
|
+
Make sure you have [the Redis gem][] installed before configuring Stoplight.
|
276
305
|
|
277
|
-
```
|
306
|
+
```ruby
|
278
307
|
require 'redis'
|
279
308
|
# => true
|
280
309
|
redis = Redis.new
|
281
310
|
# => #<Redis client ...>
|
282
311
|
data_store = Stoplight::DataStore::Redis.new(redis)
|
283
312
|
# => #<Stoplight::DataStore::Redis:...>
|
284
|
-
Stoplight
|
313
|
+
Stoplight.default_data_store = data_store
|
285
314
|
# => #<Stoplight::DataStore::Redis:...>
|
286
315
|
```
|
287
316
|
|
@@ -290,38 +319,26 @@ Stoplight::Light.default_data_store = data_store
|
|
290
319
|
Stoplight sends notifications to standard error by default.
|
291
320
|
|
292
321
|
``` rb
|
293
|
-
Stoplight
|
322
|
+
Stoplight.default_notifiers
|
294
323
|
# => [#<Stoplight::Notifier::IO:...>]
|
295
324
|
```
|
296
325
|
|
297
326
|
If you want to send notifications elsewhere, you'll have to set them up.
|
298
327
|
|
299
|
-
####
|
300
|
-
|
301
|
-
Make sure you have [the Bugsnag gem][] (`~> 4.0`) installed before configuring
|
302
|
-
Stoplight.
|
303
|
-
|
304
|
-
``` rb
|
305
|
-
require 'bugsnag'
|
306
|
-
# => true
|
307
|
-
notifier = Stoplight::Notifier::Bugsnag.new(Bugsnag)
|
308
|
-
# => #<Stoplight::Notifier::Bugsnag:...>
|
309
|
-
Stoplight::Light.default_notifiers += [notifier]
|
310
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Bugsnag:...>]
|
311
|
-
```
|
328
|
+
#### IO
|
312
329
|
|
313
|
-
|
330
|
+
Stoplight can notify not only into STDOUT, but into any IO object. You can configure
|
331
|
+
the `Stoplight::Notifier::IO` notifier for that.
|
314
332
|
|
315
|
-
|
316
|
-
|
333
|
+
```ruby
|
334
|
+
require 'stringio'
|
317
335
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
Stoplight::
|
324
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Honeybadger:...>]
|
336
|
+
io = StringIO.new
|
337
|
+
# => #<StringIO:...>
|
338
|
+
notifier = Stoplight::Notifier::IO.new(io)
|
339
|
+
# => #<Stoplight::Notifier::IO:...>
|
340
|
+
Stoplight.default_notifiers += [notifier]
|
341
|
+
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::IO:...>]
|
325
342
|
```
|
326
343
|
|
327
344
|
#### Logger
|
@@ -329,77 +346,48 @@ Stoplight::Light.default_notifiers += [notifier]
|
|
329
346
|
Stoplight can be configured to use [the Logger class][] from the standard
|
330
347
|
library.
|
331
348
|
|
332
|
-
```
|
349
|
+
```ruby
|
333
350
|
require 'logger'
|
334
351
|
# => true
|
335
352
|
logger = Logger.new(STDERR)
|
336
353
|
# => #<Logger:...>
|
337
354
|
notifier = Stoplight::Notifier::Logger.new(logger)
|
338
355
|
# => #<Stoplight::Notifier::Logger:...>
|
339
|
-
Stoplight
|
356
|
+
Stoplight.default_notifiers += [notifier]
|
340
357
|
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Logger:...>]
|
341
358
|
```
|
342
359
|
|
343
|
-
####
|
344
|
-
|
345
|
-
Make sure you have [the Pagerduty gem][] (`~> 2.1`) installed before configuring
|
346
|
-
Stoplight.
|
347
|
-
|
348
|
-
``` rb
|
349
|
-
require 'pagerduty'
|
350
|
-
# => true
|
351
|
-
pagerduty = Pagerduty.new('http://www.example.com/webhook-url')
|
352
|
-
# => #<Pagerduty:...>
|
353
|
-
notifier = Stoplight::Notifier::Pagerduty.new(pagerduty)
|
354
|
-
# => #<Stoplight::Notifier::Pagerduty:...>
|
355
|
-
Stoplight::Light.default_notifiers += [notifier]
|
356
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Pagerduty:...>]
|
357
|
-
```
|
360
|
+
#### Community-supported Notifiers
|
358
361
|
|
359
|
-
|
362
|
+
* [stoplight-sentry]
|
360
363
|
|
361
|
-
|
362
|
-
Stoplight.
|
364
|
+
You you want to implement your own notifier, the following section contains all the required information.
|
363
365
|
|
364
|
-
|
365
|
-
require 'rollbar'
|
366
|
-
# => true
|
367
|
-
notifier = Stoplight::Notifier::Rollbar.new(Rollbar)
|
368
|
-
# => #<Stoplight::Notifier::Rollbar:...>
|
369
|
-
Stoplight::Light.default_notifiers += [notifier]
|
370
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Rollbar:...>]
|
371
|
-
```
|
366
|
+
Pull requests to update this section are welcome.
|
372
367
|
|
373
|
-
####
|
368
|
+
#### How to implement your own notifier?
|
374
369
|
|
375
|
-
|
376
|
-
Stoplight.
|
370
|
+
A notifier has to implement the `Stoplight::Notifier::Base` interface:
|
377
371
|
|
378
|
-
```
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
# => #<Raven::Configuration:...>
|
383
|
-
notifier = Stoplight::Notifier::Raven.new(configuration)
|
384
|
-
# => #<Stoplight::Notifier::Raven:...>
|
385
|
-
Stoplight::Light.default_notifiers += [notifier]
|
386
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Raven:...>]
|
372
|
+
```ruby
|
373
|
+
def notify(light, from_color, to_color, error)
|
374
|
+
raise NotImplementedError
|
375
|
+
end
|
387
376
|
```
|
388
377
|
|
389
|
-
|
378
|
+
For convenience, you can use the `Stoplight::Notifier::Generic` module. It takes care of
|
379
|
+
the message formatting, and you have to implement only the `put` method, which takes message sting as an argument:
|
390
380
|
|
391
|
-
|
392
|
-
Stoplight
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
Stoplight::Light.default_notifiers += [notifier]
|
402
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Slack:...>]
|
381
|
+
```ruby
|
382
|
+
class IO < Stoplight::Notifier::Base
|
383
|
+
include Generic
|
384
|
+
|
385
|
+
private
|
386
|
+
|
387
|
+
def put(message)
|
388
|
+
@object.puts(message)
|
389
|
+
end
|
390
|
+
end
|
403
391
|
```
|
404
392
|
|
405
393
|
### Rails
|
@@ -409,11 +397,11 @@ in-memory data store, you don't need to do anything special. If you want to use
|
|
409
397
|
a persistent data store, you'll need to configure it. Create an initializer for
|
410
398
|
Stoplight:
|
411
399
|
|
412
|
-
```
|
400
|
+
```ruby
|
413
401
|
# config/initializers/stoplight.rb
|
414
402
|
require 'stoplight'
|
415
|
-
Stoplight
|
416
|
-
Stoplight
|
403
|
+
Stoplight.default_data_store = Stoplight::DataStore::Redis.new(...)
|
404
|
+
Stoplight.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.logger)]
|
417
405
|
```
|
418
406
|
|
419
407
|
## Advanced usage
|
@@ -421,17 +409,17 @@ Stoplight::Light.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.log
|
|
421
409
|
### Locking
|
422
410
|
|
423
411
|
Although stoplights can operate on their own, occasionally you may want to
|
424
|
-
override the default behavior. You can lock a light
|
425
|
-
|
412
|
+
override the default behavior. You can lock a light using `#lock(color)` method.
|
413
|
+
Color should be either `Stoplight::Color::GREEN` or ``Stoplight::Color::RED``.
|
426
414
|
|
427
|
-
```
|
428
|
-
light = Stoplight('example-locked')
|
429
|
-
# => #<Stoplight::
|
430
|
-
light.run
|
415
|
+
```ruby
|
416
|
+
light = Stoplight('example-locked')
|
417
|
+
# => #<Stoplight::CircuitBreaker:..>
|
418
|
+
light.run { true }
|
431
419
|
# => true
|
432
|
-
light.
|
433
|
-
# =>
|
434
|
-
light.run
|
420
|
+
light.lock(Stoplight::Color::RED)
|
421
|
+
# => #<Stoplight::CircuitBreaker:..>
|
422
|
+
light.run { true }
|
435
423
|
# Stoplight::Error::RedLight: example-locked
|
436
424
|
```
|
437
425
|
|
@@ -440,10 +428,11 @@ have configured a custom data store and that data store fails, Stoplight will
|
|
440
428
|
switch over to using a blank in-memory data store. That means you will lose the
|
441
429
|
locked state of any stoplights.
|
442
430
|
|
443
|
-
You can go back to using the default behavior by unlocking the stoplight
|
431
|
+
You can go back to using the default behavior by unlocking the stoplight using `#unlock`.
|
444
432
|
|
445
|
-
```
|
446
|
-
light.
|
433
|
+
```ruby
|
434
|
+
light.unlock
|
435
|
+
# => #<Stoplight::CircuitBreaker:..>
|
447
436
|
```
|
448
437
|
|
449
438
|
### Testing
|
@@ -453,28 +442,34 @@ However there are a few things you can do to make them behave better. If your
|
|
453
442
|
stoplights are spewing messages into your test output, you can silence them
|
454
443
|
with a couple configuration changes.
|
455
444
|
|
456
|
-
```
|
457
|
-
Stoplight
|
458
|
-
Stoplight
|
445
|
+
```ruby
|
446
|
+
Stoplight.default_error_notifier = -> _ {}
|
447
|
+
Stoplight.default_notifiers = []
|
459
448
|
```
|
460
449
|
|
461
450
|
If your tests mysteriously fail because stoplights are the wrong color, you can
|
462
451
|
try resetting the data store before each test case. For example, this would
|
463
452
|
give each test case a fresh data store with RSpec.
|
464
453
|
|
465
|
-
```
|
454
|
+
```ruby
|
466
455
|
before(:each) do
|
467
|
-
Stoplight
|
456
|
+
Stoplight.default_data_store = Stoplight::DataStore::Memory.new
|
468
457
|
end
|
469
458
|
```
|
470
459
|
|
471
460
|
Sometimes you may want to test stoplights directly. You can avoid resetting the
|
472
461
|
data store by giving each stoplight a unique name.
|
473
462
|
|
474
|
-
```
|
475
|
-
stoplight = Stoplight("test-#{rand}")
|
463
|
+
```ruby
|
464
|
+
stoplight = Stoplight("test-#{rand}")
|
476
465
|
```
|
477
466
|
|
467
|
+
## Maintenance Policy
|
468
|
+
|
469
|
+
Stoplight supports the latest three minor versions of Ruby, which currently are: `3.0.x`, `3.1.x`, and `3.2.x`. Changing
|
470
|
+
the minimum supported Ruby version is not considered a breaking change.
|
471
|
+
We support the current stable Redis version (`7.2`) and the latest release of the previous major version (`6.2.9`)
|
472
|
+
|
478
473
|
## Credits
|
479
474
|
|
480
475
|
Stoplight is brought to you by [@camdez][] and [@tfausak][] from [@OrgSync][]. [@bolshakov][] is the current
|
@@ -512,3 +507,4 @@ Stoplight is licensed under [the MIT License][].
|
|
512
507
|
[complete list of contributors]: https://github.com/bolshakov/stoplight/graphs/contributors
|
513
508
|
[CircuitBreaker]: http://martinfowler.com/bliki/CircuitBreaker.html
|
514
509
|
[the MIT license]: LICENSE.md
|
510
|
+
[stoplight-sentry]: https://github.com/bolshakov/stoplight-sentry
|