faulty 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  module Events
5
5
  # The interface required to implement a event listener
6
6
  #
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  module Events
5
5
  # A default listener that logs Faulty events
6
6
  class LogListener
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
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 { |l| l.handle(event, payload) }
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  # A struct that cannot be modified after initialization
5
5
  module ImmutableOptions
6
6
  # @param hash [Hash] A hash of attributes to initialize with
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
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) # rubocop:disable Naming/MethodParameterName
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  # The status of a circuit
5
5
  #
6
6
  # Includes information like the state and locks. Also calculates
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  # The namespace for Faulty storage
5
5
  module Storage
6
6
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  module Storage
5
5
  # A wrapper for storage backends that may raise errors
6
6
  #
7
- # {Scope} automatically wraps all non-fault-tolerant storage backends with
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
- cool_down: circuit.options.cool_down,
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
- module Faulty
3
+ class Faulty
4
4
  module Storage
5
5
  # The interface required for a storage backend implementation
6
6
  #
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
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.pop if runs.size > options.max_sample_size
86
+ runs.shift if runs.size > options.max_sample_size
87
87
  end
88
88
  memory.status(circuit.options)
89
89
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
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
- result = redis.watch(key) do
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
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Faulty
3
+ class Faulty
4
4
  # The current Faulty version
5
5
  def self.version
6
- Gem::Version.new('0.1.0')
6
+ Gem::Version.new('0.2.0')
7
7
  end
8
8
  end
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.1.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-08-27 00:00:00.000000000 Z
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: irb
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: '3.5'
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: '3.5'
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
- - !ruby/object:Gem::Dependency
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.1.4
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: []