stoplight 3.0.2 → 4.0.0
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 +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
|