faulty 0.1.0 → 0.2.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/.gitignore +1 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +4 -2
- data/CHANGELOG.md +34 -0
- data/Gemfile +17 -0
- data/README.md +177 -47
- data/bin/console +1 -1
- data/faulty.gemspec +3 -10
- data/lib/faulty.rb +155 -43
- data/lib/faulty/cache.rb +1 -1
- data/lib/faulty/cache/default.rb +1 -1
- data/lib/faulty/cache/fault_tolerant_proxy.rb +2 -2
- data/lib/faulty/cache/interface.rb +1 -1
- data/lib/faulty/cache/mock.rb +1 -1
- data/lib/faulty/cache/null.rb +1 -1
- data/lib/faulty/cache/rails.rb +1 -1
- data/lib/faulty/circuit.rb +1 -1
- data/lib/faulty/error.rb +4 -4
- data/lib/faulty/events.rb +3 -2
- data/lib/faulty/events/callback_listener.rb +1 -1
- data/lib/faulty/events/honeybadger_listener.rb +53 -0
- data/lib/faulty/events/listener_interface.rb +1 -1
- data/lib/faulty/events/log_listener.rb +1 -1
- data/lib/faulty/events/notifier.rb +11 -2
- data/lib/faulty/immutable_options.rb +1 -1
- data/lib/faulty/result.rb +2 -2
- data/lib/faulty/status.rb +1 -1
- data/lib/faulty/storage.rb +1 -1
- data/lib/faulty/storage/fault_tolerant_proxy.rb +8 -10
- data/lib/faulty/storage/interface.rb +1 -1
- data/lib/faulty/storage/memory.rb +2 -2
- data/lib/faulty/storage/redis.rb +9 -9
- data/lib/faulty/version.rb +2 -2
- metadata +14 -123
- data/lib/faulty/scope.rb +0 -117
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Faulty
|
4
4
|
module Events
|
5
5
|
# The default event dispatcher for Faulty
|
6
6
|
class Notifier
|
@@ -11,6 +11,9 @@ module Faulty
|
|
11
11
|
|
12
12
|
# Notify all listeners of an event
|
13
13
|
#
|
14
|
+
# If a listener raises an error while handling an event, that error will
|
15
|
+
# be captured and written to STDERR.
|
16
|
+
#
|
14
17
|
# @param event [Symbol] The event name
|
15
18
|
# @param payload [Hash] A hash of event payload data. The payload keys
|
16
19
|
# differ between events, but should be consistent across calls for a
|
@@ -18,7 +21,13 @@ module Faulty
|
|
18
21
|
def notify(event, payload)
|
19
22
|
raise ArgumentError, "Unknown event #{event}" unless EVENTS.include?(event)
|
20
23
|
|
21
|
-
@listeners.each
|
24
|
+
@listeners.each do |listener|
|
25
|
+
begin
|
26
|
+
listener.handle(event, payload)
|
27
|
+
rescue StandardError => e
|
28
|
+
warn "Faulty listener #{listener.class.name} crashed: #{e.message}"
|
29
|
+
end
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
24
33
|
end
|
data/lib/faulty/result.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Faulty
|
4
4
|
# An approximation of the `Result` type from some strongly-typed languages.
|
5
5
|
#
|
6
6
|
# F#: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/results
|
@@ -53,7 +53,7 @@ module Faulty
|
|
53
53
|
#
|
54
54
|
# @param ok An ok value
|
55
55
|
# @param error [Error] An error instance
|
56
|
-
def initialize(ok: NOTHING, error: NOTHING)
|
56
|
+
def initialize(ok: NOTHING, error: NOTHING)
|
57
57
|
if ok.equal?(NOTHING) && error.equal?(NOTHING)
|
58
58
|
raise ArgumentError, 'Result must have an ok or error value'
|
59
59
|
end
|
data/lib/faulty/status.rb
CHANGED
data/lib/faulty/storage.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Faulty
|
4
4
|
module Storage
|
5
5
|
# A wrapper for storage backends that may raise errors
|
6
6
|
#
|
7
|
-
# {
|
7
|
+
# {Faulty#initialize} automatically wraps all non-fault-tolerant storage backends with
|
8
8
|
# this class.
|
9
9
|
#
|
10
10
|
# If the storage backend raises a `StandardError`, it will be captured and
|
@@ -53,8 +53,8 @@ module Faulty
|
|
53
53
|
# @see Interface#open
|
54
54
|
# @param (see Interface#open)
|
55
55
|
# @return (see Interface#open)
|
56
|
-
def open(circuit)
|
57
|
-
@storage.open(circuit)
|
56
|
+
def open(circuit, opened_at)
|
57
|
+
@storage.open(circuit, opened_at)
|
58
58
|
rescue StandardError => e
|
59
59
|
options.notifier.notify(:storage_failure, circuit: circuit, action: :open, error: e)
|
60
60
|
false
|
@@ -65,8 +65,8 @@ module Faulty
|
|
65
65
|
# @see Interface#reopen
|
66
66
|
# @param (see Interface#reopen)
|
67
67
|
# @return (see Interface#reopen)
|
68
|
-
def reopen(circuit)
|
69
|
-
@storage.reopen(circuit)
|
68
|
+
def reopen(circuit, opened_at, previous_opened_at)
|
69
|
+
@storage.reopen(circuit, opened_at, previous_opened_at)
|
70
70
|
rescue StandardError => e
|
71
71
|
options.notifier.notify(:storage_failure, circuit: circuit, action: :reopen, error: e)
|
72
72
|
false
|
@@ -167,10 +167,8 @@ module Faulty
|
|
167
167
|
# @return [Status] The stub status
|
168
168
|
def stub_status(circuit)
|
169
169
|
Faulty::Status.new(
|
170
|
-
|
171
|
-
stub: true
|
172
|
-
sample_threshold: circuit.options.sample_threshold,
|
173
|
-
rate_threshold: circuit.options.rate_threshold
|
170
|
+
options: circuit.options,
|
171
|
+
stub: true
|
174
172
|
)
|
175
173
|
end
|
176
174
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Faulty
|
4
4
|
module Storage
|
5
5
|
# The default in-memory storage for circuits
|
6
6
|
#
|
@@ -83,7 +83,7 @@ module Faulty
|
|
83
83
|
memory = fetch(circuit)
|
84
84
|
memory.runs.borrow do |runs|
|
85
85
|
runs.push([time, success])
|
86
|
-
runs.
|
86
|
+
runs.shift if runs.size > options.max_sample_size
|
87
87
|
end
|
88
88
|
memory.status(circuit.options)
|
89
89
|
end
|
data/lib/faulty/storage/redis.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
class Faulty
|
4
4
|
module Storage
|
5
5
|
class Redis # rubocop:disable Metrics/ClassLength
|
6
6
|
# Separates the time/status for history entry strings
|
@@ -97,7 +97,7 @@ module Faulty
|
|
97
97
|
# @return (see Interface#open)
|
98
98
|
def open(circuit, opened_at)
|
99
99
|
redis do |r|
|
100
|
-
opened = compare_and_set(r, state_key(circuit), ['closed', nil], 'open')
|
100
|
+
opened = compare_and_set(r, state_key(circuit), ['closed', nil], 'open', ex: options.circuit_ttl)
|
101
101
|
r.set(opened_at_key(circuit), opened_at, ex: options.circuit_ttl) if opened
|
102
102
|
opened
|
103
103
|
end
|
@@ -110,7 +110,7 @@ module Faulty
|
|
110
110
|
# @return (see Interface#reopen)
|
111
111
|
def reopen(circuit, opened_at, previous_opened_at)
|
112
112
|
redis do |r|
|
113
|
-
compare_and_set(r, opened_at_key(circuit), [previous_opened_at.to_s], opened_at)
|
113
|
+
compare_and_set(r, opened_at_key(circuit), [previous_opened_at.to_s], opened_at, ex: options.circuit_ttl)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
@@ -121,7 +121,7 @@ module Faulty
|
|
121
121
|
# @return (see Interface#close)
|
122
122
|
def close(circuit)
|
123
123
|
redis do |r|
|
124
|
-
closed = compare_and_set(r, state_key(circuit), ['open'], 'closed')
|
124
|
+
closed = compare_and_set(r, state_key(circuit), ['open'], 'closed', ex: options.circuit_ttl)
|
125
125
|
r.del(entries_key(circuit)) if closed
|
126
126
|
closed
|
127
127
|
end
|
@@ -287,16 +287,16 @@ module Faulty
|
|
287
287
|
# @param new [String] The new value to set if the compare passes
|
288
288
|
# @return [Boolean] True if the value was set to `new`, false if the CAS
|
289
289
|
# failed
|
290
|
-
def compare_and_set(redis, key, old, new)
|
291
|
-
|
290
|
+
def compare_and_set(redis, key, old, new, ex:)
|
291
|
+
redis.watch(key) do
|
292
292
|
if old.include?(redis.get(key))
|
293
|
-
redis.multi { |m| m.set(key, new) }
|
293
|
+
result = redis.multi { |m| m.set(key, new, ex: ex) }
|
294
|
+
result && result[0] == 'OK'
|
294
295
|
else
|
295
296
|
redis.unwatch
|
297
|
+
false
|
296
298
|
end
|
297
299
|
end
|
298
|
-
|
299
|
-
result[0] == 'OK'
|
300
300
|
end
|
301
301
|
|
302
302
|
# Yield a Redis connection
|
data/lib/faulty/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faulty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Howard
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -24,54 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: activesupport
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '4.2'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '4.2'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.17'
|
48
|
-
- - "<"
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version: '3'
|
51
|
-
type: :development
|
52
|
-
prerelease: false
|
53
|
-
version_requirements: !ruby/object:Gem::Requirement
|
54
|
-
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '1.17'
|
58
|
-
- - "<"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '3'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: byebug
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '11.0'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '11.0'
|
75
27
|
- !ruby/object:Gem::Dependency
|
76
28
|
name: connection_pool
|
77
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,33 +39,19 @@ dependencies:
|
|
87
39
|
- !ruby/object:Gem::Version
|
88
40
|
version: '2.0'
|
89
41
|
- !ruby/object:Gem::Dependency
|
90
|
-
name:
|
42
|
+
name: honeybadger
|
91
43
|
requirement: !ruby/object:Gem::Requirement
|
92
44
|
requirements:
|
93
|
-
- - "
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '1.0'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '1.0'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: redcarpet
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
45
|
+
- - ">="
|
108
46
|
- !ruby/object:Gem::Version
|
109
|
-
version: '
|
47
|
+
version: '2.0'
|
110
48
|
type: :development
|
111
49
|
prerelease: false
|
112
50
|
version_requirements: !ruby/object:Gem::Requirement
|
113
51
|
requirements:
|
114
|
-
- - "
|
52
|
+
- - ">="
|
115
53
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
54
|
+
version: '2.0'
|
117
55
|
- !ruby/object:Gem::Dependency
|
118
56
|
name: redis
|
119
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,20 +80,6 @@ dependencies:
|
|
142
80
|
- - "~>"
|
143
81
|
- !ruby/object:Gem::Version
|
144
82
|
version: '3.8'
|
145
|
-
- !ruby/object:Gem::Dependency
|
146
|
-
name: rspec_junit_formatter
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
148
|
-
requirements:
|
149
|
-
- - "~>"
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '0.4'
|
152
|
-
type: :development
|
153
|
-
prerelease: false
|
154
|
-
version_requirements: !ruby/object:Gem::Requirement
|
155
|
-
requirements:
|
156
|
-
- - "~>"
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '0.4'
|
159
83
|
- !ruby/object:Gem::Dependency
|
160
84
|
name: rubocop
|
161
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,26 +108,6 @@ dependencies:
|
|
184
108
|
- - '='
|
185
109
|
- !ruby/object:Gem::Version
|
186
110
|
version: 1.38.1
|
187
|
-
- !ruby/object:Gem::Dependency
|
188
|
-
name: simplecov
|
189
|
-
requirement: !ruby/object:Gem::Requirement
|
190
|
-
requirements:
|
191
|
-
- - ">="
|
192
|
-
- !ruby/object:Gem::Version
|
193
|
-
version: 0.17.1
|
194
|
-
- - "<"
|
195
|
-
- !ruby/object:Gem::Version
|
196
|
-
version: '0.18'
|
197
|
-
type: :development
|
198
|
-
prerelease: false
|
199
|
-
version_requirements: !ruby/object:Gem::Requirement
|
200
|
-
requirements:
|
201
|
-
- - ">="
|
202
|
-
- !ruby/object:Gem::Version
|
203
|
-
version: 0.17.1
|
204
|
-
- - "<"
|
205
|
-
- !ruby/object:Gem::Version
|
206
|
-
version: '0.18'
|
207
111
|
- !ruby/object:Gem::Dependency
|
208
112
|
name: timecop
|
209
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -218,21 +122,7 @@ dependencies:
|
|
218
122
|
- - ">="
|
219
123
|
- !ruby/object:Gem::Version
|
220
124
|
version: '0.9'
|
221
|
-
|
222
|
-
name: yard
|
223
|
-
requirement: !ruby/object:Gem::Requirement
|
224
|
-
requirements:
|
225
|
-
- - "~>"
|
226
|
-
- !ruby/object:Gem::Version
|
227
|
-
version: 0.9.25
|
228
|
-
type: :development
|
229
|
-
prerelease: false
|
230
|
-
version_requirements: !ruby/object:Gem::Requirement
|
231
|
-
requirements:
|
232
|
-
- - "~>"
|
233
|
-
- !ruby/object:Gem::Version
|
234
|
-
version: 0.9.25
|
235
|
-
description:
|
125
|
+
description:
|
236
126
|
email:
|
237
127
|
- jmhoward0@gmail.com
|
238
128
|
executables: []
|
@@ -244,6 +134,7 @@ files:
|
|
244
134
|
- ".rubocop.yml"
|
245
135
|
- ".travis.yml"
|
246
136
|
- ".yardopts"
|
137
|
+
- CHANGELOG.md
|
247
138
|
- Gemfile
|
248
139
|
- LICENSE.txt
|
249
140
|
- README.md
|
@@ -267,12 +158,12 @@ files:
|
|
267
158
|
- lib/faulty/error.rb
|
268
159
|
- lib/faulty/events.rb
|
269
160
|
- lib/faulty/events/callback_listener.rb
|
161
|
+
- lib/faulty/events/honeybadger_listener.rb
|
270
162
|
- lib/faulty/events/listener_interface.rb
|
271
163
|
- lib/faulty/events/log_listener.rb
|
272
164
|
- lib/faulty/events/notifier.rb
|
273
165
|
- lib/faulty/immutable_options.rb
|
274
166
|
- lib/faulty/result.rb
|
275
|
-
- lib/faulty/scope.rb
|
276
167
|
- lib/faulty/status.rb
|
277
168
|
- lib/faulty/storage.rb
|
278
169
|
- lib/faulty/storage/fault_tolerant_proxy.rb
|
@@ -284,7 +175,7 @@ homepage: https://github.com/ParentSquare/faulty
|
|
284
175
|
licenses:
|
285
176
|
- MIT
|
286
177
|
metadata: {}
|
287
|
-
post_install_message:
|
178
|
+
post_install_message:
|
288
179
|
rdoc_options: []
|
289
180
|
require_paths:
|
290
181
|
- lib
|
@@ -299,8 +190,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
299
190
|
- !ruby/object:Gem::Version
|
300
191
|
version: '0'
|
301
192
|
requirements: []
|
302
|
-
rubygems_version: 3.
|
303
|
-
signing_key:
|
193
|
+
rubygems_version: 3.0.8
|
194
|
+
signing_key:
|
304
195
|
specification_version: 4
|
305
196
|
summary: Fault-tolerance tools for ruby based on circuit-breakers
|
306
197
|
test_files: []
|