circuitbox 0.11.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0fe08dafcf12318b2880700034caf98461a2f3e
4
- data.tar.gz: f136784e4d4d32ffa0b53580fb64eded3bf4d5ba
3
+ metadata.gz: 38c8cdbfef9e769dd2ad65c59905a6c7f1c9bad1
4
+ data.tar.gz: e7ed610ab1231ff949b4549a6cec911b61690431
5
5
  SHA512:
6
- metadata.gz: 0e2870b5f3b3cffd2623f04937be7794fe2ee0085655b65b2533da3ba421e9e7772c4b85593097f300aa4f0984b484f590a8811eb22b667a31e7a8d81c9070d2
7
- data.tar.gz: c98b3b8e64b4def135717ae7eedb833807d4a484cb6772d284c38c08cb992013a8adc64041ffaddb529fdfecda187287160216467940b63c252a5d3ce765bcb7
6
+ metadata.gz: 82ba7ae27fabb672864ba38c4678c0b37a2bb08104103fc67ed3f8eb9f36973105d6fe5eae03d8dcd2e42f297371f7e87775b8ddf409ea35e0ddddcdce57a0b7
7
+ data.tar.gz: b7b4fe13bb21d1978e620b481d188f475b78b4e9809ca59f6f9ef5b12839114b3f358e0136a6ab3e23a2e993358c5701a0d9f962cf010d8bd735d1614aa61956
data/README.md CHANGED
@@ -190,7 +190,7 @@ Circuitbox.circuit :identifier, cache: Moneta.new(:Memory)
190
190
 
191
191
  ### LMDB
192
192
 
193
- An persisted directory backed store, which is thread and multi process save.
193
+ An persisted directory backed store, which is thread and multi process safe.
194
194
  depends on the `lmdb` gem. It is slower than Memory or Daybreak, but can be
195
195
  used in multi thread and multi process environments like like Puma.
196
196
 
@@ -204,7 +204,7 @@ Circuitbox.circuit :identifier, cache: Moneta.new(:LMDB, dir: "./", db: "mydb")
204
204
  An persisted file backed store, which comes with the ruby
205
205
  [stdlib](http://ruby-doc.org/stdlib-2.3.0/libdoc/pstore/rdoc/PStore.html). It
206
206
  has no external dependecies and works on every ruby implementation. Due to it
207
- being file backed it is multi process save, good for development using Unicorn.
207
+ being file backed it is multi process safe, good for development using Unicorn.
208
208
 
209
209
  ```ruby
210
210
  Circuitbox.circuit :identifier, cache: Moneta.new(:PStore, file: "db.pstore")
@@ -212,7 +212,7 @@ Circuitbox.circuit :identifier, cache: Moneta.new(:PStore, file: "db.pstore")
212
212
 
213
213
  ### Daybreak
214
214
 
215
- Persisted, file backed key value store in pure ruby. It is process save and
215
+ Persisted, file backed key value store in pure ruby. It is process safe and
216
216
  outperforms most other stores in circuitbox. This is recommended for production
217
217
  use with Unicorn. It depends on the `daybreak` gem.
218
218
 
@@ -295,8 +295,9 @@ c.use Circuitbox::FaradayMiddleware, open_circuit: lambda { |response| response.
295
295
  ```
296
296
 
297
297
  ## CHANGELOG
298
-
299
298
  ### version next
299
+
300
+ ### v0.11.0
300
301
  - fix URI require missing (https://github.com/yammer/circuitbox/pull/42 @gottfrois)
301
302
  - configurable circuitbox store backend via Moneta supporting multi process circuits
302
303
 
@@ -27,9 +27,7 @@ def service
27
27
  end
28
28
 
29
29
  def run_flip_flopping circuit
30
- before = circuit.open?
31
30
  circuit.run { service }
32
- after = circuit.open?
33
31
  circuit.try_close_next_time if circuit.open?
34
32
  end
35
33
 
@@ -16,7 +16,7 @@ require 'circuitbox/errors/open_circuit_error'
16
16
  require 'circuitbox/errors/service_failure_error'
17
17
 
18
18
  class Circuitbox
19
- attr_accessor :circuits, :circuit_store, :stat_store
19
+ attr_accessor :circuits, :circuit_store
20
20
  cattr_accessor :configure
21
21
 
22
22
  def self.instance
@@ -44,14 +44,6 @@ class Circuitbox
44
44
  self.instance.circuit_store = store
45
45
  end
46
46
 
47
- def self.stat_store
48
- self.instance.stat_store
49
- end
50
-
51
- def self.stat_store=(store)
52
- self.instance.stat_store = store
53
- end
54
-
55
47
  def self.[](service_identifier, options = {})
56
48
  self.circuit(service_identifier, options)
57
49
  end
@@ -1,7 +1,7 @@
1
1
  class Circuitbox
2
2
  class CircuitBreaker
3
3
  attr_accessor :service, :circuit_options, :exceptions, :partition,
4
- :logger, :stat_store, :circuit_store, :notifier
4
+ :logger, :circuit_store, :notifier
5
5
 
6
6
  DEFAULTS = {
7
7
  sleep_window: 300,
@@ -31,7 +31,6 @@ class Circuitbox
31
31
  @exceptions = [Timeout::Error] if @exceptions.blank?
32
32
 
33
33
  @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
34
- @stat_store = options.fetch(:stat_store) { Circuitbox.stat_store }
35
34
  @time_class = options.fetch(:time_class) { Time }
36
35
  sanitize_options
37
36
  end
@@ -91,26 +90,6 @@ class Circuitbox
91
90
  end
92
91
  end
93
92
 
94
- def stats(partition)
95
- @partition = partition
96
- options = { without_partition: @partition.blank? }
97
-
98
- stats = []
99
- end_time = Time.now
100
- hour = 48.hours.ago.change(min: 0, sec: 0)
101
- while hour <= end_time
102
- time_object = hour
103
-
104
- 60.times do |i|
105
- time = time_object.change(min: i, sec: 0).to_i
106
- stats << stats_for_time(time, options) unless time > Time.now.to_i
107
- end
108
-
109
- hour += 3600
110
- end
111
- stats
112
- end
113
-
114
93
  def error_rate(failures = failure_count, success = success_count)
115
94
  all_count = failures + success
116
95
  return 0.0 unless all_count > 0
@@ -194,11 +173,6 @@ class Circuitbox
194
173
  def log_event(event)
195
174
  notifier.new(service,partition).notify(event)
196
175
  log_event_to_process(event)
197
-
198
- if stat_store.present?
199
- log_event_to_stat_store(stat_storage_key(event))
200
- log_event_to_stat_store(stat_storage_key(event, without_partition: true))
201
- end
202
176
  end
203
177
 
204
178
  def log_metrics(error_rate, failures, successes)
@@ -217,7 +191,7 @@ class Circuitbox
217
191
  end
218
192
  end
219
193
 
220
- # When there is a successful response within a stat interval, clear the failures.
194
+ # When there is a successful response within a count interval, clear the failures.
221
195
  def clear_failures!
222
196
  circuit_store.store(stat_storage_key(:failure), 0, raw: true)
223
197
  end
@@ -251,6 +225,7 @@ class Circuitbox
251
225
  storage_key(:stats, align_time_on_minute, event, options)
252
226
  end
253
227
 
228
+
254
229
  # return time representation in seconds
255
230
  def align_time_on_minute(time=nil)
256
231
  time ||= @time_class.now.to_i
@@ -278,12 +253,5 @@ class Circuitbox
278
253
  Circuitbox.reset
279
254
  end
280
255
 
281
- def stats_for_time(time, options = {})
282
- stats = { time: align_time_on_minute(time) }
283
- [:success, :failure, :open].each do |event|
284
- stats[event] = stat_store.read(storage_key(:stats, time, event, options), raw: true) || 0
285
- end
286
- stats
287
- end
288
256
  end
289
257
  end
@@ -21,10 +21,18 @@ class Circuitbox
21
21
 
22
22
  attr_reader :opts
23
23
 
24
+ DEFAULT_CIRCUITBOX_OPTIONS = {
25
+ open_circuit: lambda do |response|
26
+ # response.status:
27
+ # nil -> connection could not be established, or failed very hard
28
+ # 5xx -> non recoverable server error, oposed to 4xx which are client errors
29
+ response.status.nil? || (500 <= response.status && response.status <= 599)
30
+ end
31
+ }
32
+
24
33
  def initialize(app, opts = {})
25
34
  @app = app
26
- default_options = { open_circuit: lambda { |response| !response.success? } }
27
- @opts = default_options.merge(opts)
35
+ @opts = DEFAULT_CIRCUITBOX_OPTIONS.merge(opts)
28
36
  super(app)
29
37
  end
30
38
 
@@ -101,5 +109,6 @@ class Circuitbox
101
109
  id = identifier.respond_to?(:call) ? identifier.call(env) : identifier
102
110
  circuitbox.circuit id, circuit_breaker_options
103
111
  end
112
+
104
113
  end
105
114
  end
@@ -1,3 +1,3 @@
1
1
  class Circuitbox
2
- VERSION='0.11.0'
2
+ VERSION='1.0.0'
3
3
  end
@@ -6,16 +6,6 @@ describe Circuitbox do
6
6
  before { Circuitbox.reset }
7
7
  after { Circuitbox.reset }
8
8
 
9
- describe "Circuitbox.configure" do
10
- it "configures instance variables on init" do
11
- Circuitbox.configure do
12
- self.stat_store = "hello"
13
- end
14
-
15
- assert_equal "hello", Circuitbox.stat_store
16
- end
17
- end
18
-
19
9
  describe "Circuitbox.circuit_store" do
20
10
  it "is configurable" do
21
11
  example_store = Circuitbox::ExampleStore.new
@@ -24,14 +14,6 @@ describe Circuitbox do
24
14
  end
25
15
  end
26
16
 
27
- describe "Circuitbox.stat_store" do
28
- it "is configurable" do
29
- example_store = Circuitbox::ExampleStore.new
30
- Circuitbox.stat_store = example_store
31
- assert_equal example_store, Circuitbox[:yammer].stat_store
32
- end
33
- end
34
-
35
17
  describe "Circuitbox[:service]" do
36
18
  it "delegates to #circuit" do
37
19
  Circuitbox.expects(:circuit).with(:yammer, {})
@@ -64,21 +64,43 @@ class Circuitbox
64
64
  def test_overridde_success_response
65
65
  env = { url: "url" }
66
66
  app = gimme
67
- give(app).call(anything) { Faraday::Response.new(status: 400) }
68
- error_response = lambda { |response| response.status >= 500 }
67
+ give(app).call(anything) { Faraday::Response.new(status: 500) }
68
+ error_response = lambda { |response| false }
69
69
  response = FaradayMiddleware.new(app, open_circuit: error_response).call(env)
70
70
  assert_kind_of Faraday::Response, response
71
- assert_equal response.status, 400
71
+ assert_equal response.status, 500
72
72
  assert response.finished?
73
73
  refute response.success?
74
74
  end
75
75
 
76
76
  def test_default_success_response
77
+ env = { url: "url" }
78
+ app = gimme
79
+ give(app).call(anything) { Faraday::Response.new(status: 500) }
80
+ response = FaradayMiddleware.new(app).call(env)
81
+ assert_kind_of Faraday::Response, response
82
+ assert_equal response.status, 503
83
+ assert response.finished?
84
+ refute response.success?
85
+ end
86
+
87
+ def test_default_open_circuit_does_not_trip_on_400
77
88
  env = { url: "url" }
78
89
  app = gimme
79
90
  give(app).call(anything) { Faraday::Response.new(status: 400) }
80
91
  response = FaradayMiddleware.new(app).call(env)
81
92
  assert_kind_of Faraday::Response, response
93
+ assert_equal response.status, 400
94
+ assert response.finished?
95
+ refute response.success?
96
+ end
97
+
98
+ def test_default_open_circuit_does_trip_on_nil
99
+ env = { url: "url" }
100
+ app = gimme
101
+ give(app).call(anything) { Faraday::Response.new(status: nil) }
102
+ response = FaradayMiddleware.new(app).call(env)
103
+ assert_kind_of Faraday::Response, response
82
104
  assert_equal response.status, 503
83
105
  assert response.finished?
84
106
  refute response.success?
@@ -4,7 +4,6 @@ require "typhoeus/adapters/faraday"
4
4
  require "pstore"
5
5
 
6
6
  class Circuitbox
7
-
8
7
  class CrossProcessTest < Minitest::Test
9
8
  include IntegrationHelpers
10
9
 
@@ -21,16 +20,17 @@ class Circuitbox
21
20
  circuit_breaker_options: { cache: Moneta.new(:PStore, file: dbfile) }
22
21
  c.adapter :typhoeus # support in_parallel
23
22
  end
24
- @failure_url = "http://localhost:4713"
23
+ @failure_url = "http://127.0.0.1:4713"
25
24
 
26
25
  if !@@only_once
27
- thread = Thread.new do
28
- Rack::Handler::WEBrick.run(Proc.new { |env| ["Failure"] },
26
+ pid = fork do
27
+ Rack::Handler::WEBrick.run(lambda { |env| [500, {}, ["Failure"]] },
29
28
  Port: 4713,
30
29
  AccessLog: [],
31
30
  Logger: WEBrick::Log.new(DEV_NULL))
32
31
  end
33
- Minitest.after_run { thread.exit }
32
+ sleep 0.5
33
+ Minitest.after_run { Process.kill "KILL", pid }
34
34
  end
35
35
  end
36
36
 
@@ -44,6 +44,7 @@ class Circuitbox
44
44
  con = Faraday.new do |c|
45
45
  c.use FaradayMiddleware, identifier: "circuitbox_test_cross_process",
46
46
  circuit_breaker_options: { cache: Moneta.new(:PStore, file: dbfile) }
47
+ c.adapter :typhoeus
47
48
  end
48
49
  open_circuit(con)
49
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circuitbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fahim Ferdous
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-22 00:00:00.000000000 Z
11
+ date: 2016-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -261,7 +261,6 @@ files:
261
261
  - lib/circuitbox/notifier.rb
262
262
  - lib/circuitbox/railtie.rb
263
263
  - lib/circuitbox/version.rb
264
- - lib/tasks/circuits.rake
265
264
  - test/circuit_breaker_test.rb
266
265
  - test/circuitbox_test.rb
267
266
  - test/excon_middleware_test.rb
@@ -1,12 +0,0 @@
1
- namespace :circuits do
2
- task :stats => :environment do
3
- service = ENV['SERVICE']
4
- partition_key = ENV['PARTITION']
5
-
6
- if service.blank?
7
- raise "You must specify a SERVICE env variable, eg. `bundle exec rake circuits:stats SERVICE=yammer`"
8
- else
9
- pp Circuitbox::CircuitBreaker.new(service).stats(partition_key)
10
- end
11
- end
12
- end