berater 0.13.0 → 0.14.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
  SHA256:
3
- metadata.gz: defd1ba585ef0f8b5ff8f2f388604c344571fe4fe887f7ca44d03d57c7ce20f8
4
- data.tar.gz: 729865914aa2f72c4b61a16cbcd29f12d86d034a04f9c15f3c48c53a7611c5cf
3
+ metadata.gz: 3780da104c79084b2c3b348363a5f763c42d9d936dc218e68df90cdbbc223cfe
4
+ data.tar.gz: a3b7e7af7b3a764f97c013cb642eccf6d71ad825df459d32722a4e312ca2cdd8
5
5
  SHA512:
6
- metadata.gz: 864cc51b0eaf4c1767a36580331a27dfb20a2e0eeaaff805d2d188078cf9500ccfbf0d08e331cd06efb238b0c58dca9f67cde27025d1e87ebec8ef4ae234c683
7
- data.tar.gz: aa303e6a58f1d79b0471d9d31bb5aa8835bd7f253c26f7930c6253e7801e154b9c26ce977529e70a7de647d9967caa6753963608828e721f7d1cfe5523805f41
6
+ metadata.gz: 364a70d42009b4c80b7235cbbc736f8c074f309abfa529f494f2fd01959e9b5ffb2bbd08ad3a39b12b0565abb8435bf86cd59b774055c4fbd845ba7d2f0c798b
7
+ data.tar.gz: 92271042126ccc8b70a3a95f6fa530a8f36dceddaf7cc8de3ef9eb1ac861fcd9f7d10e066d27ac4d7bb807d5f43741f78aaf4b9b43a970bc159a5b2a3142f73a
@@ -23,13 +23,14 @@ module Berater
23
23
  priority = Float(priority) rescue nil
24
24
  end
25
25
 
26
- unless PRIORITY_RANGE.include?(priority)
27
- return capacity
28
- end
26
+ if PRIORITY_RANGE.include?(priority)
27
+ # priority 1 stays at 100%, 2 scales down to 90%, 5 to 60%
28
+ factor = 1 - (priority - 1) * 0.1
29
29
 
30
- # priority 1 stays at 100%, 2 scales down to 90%, 5 to 60%
31
- factor = 1 - (priority - 1) * 0.1
32
- (capacity * factor).floor
30
+ (capacity * factor).floor
31
+ else
32
+ capacity
33
+ end
33
34
  end
34
35
  end
35
36
  end
@@ -1,5 +1,3 @@
1
- require 'ddtrace'
2
-
3
1
  module Berater
4
2
  module Middleware
5
3
  class Trace
@@ -8,7 +6,7 @@ module Berater
8
6
  end
9
7
 
10
8
  def call(limiter, **)
11
- tracer.trace('Berater.limit') do |span|
9
+ tracer&.trace('Berater') do |span|
12
10
  begin
13
11
  lock = yield
14
12
  rescue Exception => error
@@ -34,7 +32,7 @@ module Berater
34
32
  private
35
33
 
36
34
  def tracer
37
- @tracer || Datadog.tracer
35
+ @tracer || (defined?(Datadog::Tracing) && Datadog::Tracing) || (defined?(Datadog) && Datadog.tracer)
38
36
  end
39
37
  end
40
38
  end
@@ -0,0 +1,28 @@
1
+ module Berater
2
+ module Mutex
3
+ def self.included(base)
4
+ # add class methods
5
+ base.instance_eval do
6
+ def synchronize(subkey = nil, **opts, &block)
7
+ key = [ 'Mutex', name&.delete(':') || object_id, subkey ].compact.join(':')
8
+
9
+ Berater::ConcurrencyLimiter(key, 1, **mutex_options.merge(opts)) do
10
+ yield if block_given?
11
+ end
12
+ end
13
+
14
+ def mutex_options(**kwargs)
15
+ (@mutex_options ||= {}).update(kwargs)
16
+ end
17
+ end
18
+ end
19
+
20
+ def synchronize(...)
21
+ self.class.synchronize(...)
22
+ end
23
+
24
+ def self.extend_object(base)
25
+ included(base)
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Berater
2
- VERSION = "0.13.0"
2
+ VERSION = "0.14.0"
3
3
  end
data/lib/berater.rb CHANGED
@@ -3,11 +3,13 @@ require 'berater/limiter_set'
3
3
  require 'berater/lock'
4
4
  require 'berater/lua_script'
5
5
  require 'berater/middleware'
6
+ require 'berater/mutex'
6
7
  require 'berater/utils'
7
8
  require 'berater/version'
8
9
  require 'meddleware'
9
10
 
10
11
  module Berater
12
+ include Meddleware
11
13
  extend self
12
14
 
13
15
  class Overloaded < StandardError; end
@@ -22,12 +24,6 @@ module Berater
22
24
  @limiters ||= LimiterSet.new
23
25
  end
24
26
 
25
- def middleware(&block)
26
- (@middleware ||= Meddleware.new).tap do
27
- @middleware.instance_eval(&block) if block_given?
28
- end
29
- end
30
-
31
27
  def new(key, capacity, **opts)
32
28
  args = []
33
29
 
data/spec/limiter_spec.rb CHANGED
@@ -220,9 +220,7 @@ describe Berater::Limiter do
220
220
  end
221
221
 
222
222
  context 'with custom limiter' do
223
- MyLimiter = Class.new(Berater::Unlimiter)
224
-
225
- let(:klass) { MyLimiter }
223
+ let(:klass) { MyLimiter = Class.new(Berater::Unlimiter) }
226
224
 
227
225
  it 'adds Berater prefix' do
228
226
  is_expected.to eq 'Berater:MyLimiter:key'
@@ -242,12 +240,10 @@ describe Berater::Limiter do
242
240
  end
243
241
 
244
242
  context 'with custom limiter' do
245
- MyLimiter = Class.new(Berater::Unlimiter)
246
-
247
- let(:klass) { MyLimiter }
243
+ let(:klass) { MyOtherLimiter = Class.new(Berater::Unlimiter) }
248
244
 
249
245
  it 'adds Berater prefix' do
250
- is_expected.to eq 'Berater:MyLimiter:key'
246
+ is_expected.to eq 'Berater:MyOtherLimiter:key'
251
247
  end
252
248
  end
253
249
  end
@@ -1,30 +1,61 @@
1
+ require 'ddtrace'
2
+
1
3
  describe Berater::Middleware::Trace do
2
- before { Datadog.tracer.enabled = false }
4
+ before do
5
+ allow(tracer).to receive(:trace).and_yield(span)
6
+ end
3
7
 
4
8
  it_behaves_like 'a limiter middleware'
5
9
 
6
10
  let(:limiter) { Berater::Unlimiter.new }
7
- let(:span) { double(Datadog::Span, set_tag: nil) }
8
- let(:tracer) { double(Datadog::Tracer) }
9
11
 
10
- before do
11
- allow(tracer).to receive(:trace) {|&b| b.call(span) }
12
+ if defined?(Datadog::Tracing)
13
+ before { Datadog.configure { |c| c.tracing.enabled = false } }
14
+
15
+ let(:span) { double(Datadog::Tracing::SpanOperation, set_tag: nil) }
16
+ let(:tracer) { double(Datadog::Tracing::Tracer) }
17
+ let(:ddtracer) { Datadog::Tracing }
18
+ else
19
+ before { Datadog.tracer.enabled = false }
20
+
21
+ let(:span) { double(Datadog::Span, set_tag: nil) }
22
+ let(:tracer) { double(Datadog::Tracer) }
23
+ let(:ddtracer) { Datadog.tracer }
12
24
  end
13
25
 
14
- context 'with a provided tracer' do
15
- let(:instance) { described_class.new(tracer: tracer) }
26
+ describe '#tracer' do
27
+ subject { instance.send(:tracer) }
16
28
 
17
- it 'traces' do
18
- expect(tracer).to receive(:trace).with(/Berater/)
19
- expect(span).to receive(:set_tag).with(/capacity/, Numeric)
29
+ let(:instance) { described_class.new }
30
+
31
+ it 'defaults to Datadog.tracer' do
32
+ is_expected.to be ddtracer
33
+ end
34
+
35
+ context 'when provided a tracer' do
36
+ let(:instance) { described_class.new(tracer: tracer) }
20
37
 
21
- instance.call(limiter) {}
38
+ it { is_expected.to be tracer }
22
39
  end
40
+ end
41
+
42
+ describe '#call' do
43
+ let(:instance) { described_class.new(tracer: tracer) }
23
44
 
24
45
  it 'yields' do
25
46
  expect {|b| instance.call(limiter, &b) }.to yield_control
26
47
  end
27
48
 
49
+ context 'when a Berater::Overloaded exception is raised' do
50
+ it 'tags the span as overloaded and raises' do
51
+ expect(span).to receive(:set_tag).with('overloaded', true)
52
+
53
+ expect {
54
+ instance.call(limiter) { raise Berater::Overloaded }
55
+ }.to be_overloaded
56
+ end
57
+ end
58
+
28
59
  context 'when an exception is raised' do
29
60
  it 'tags the span and raises' do
30
61
  expect(span).to receive(:set_tag).with('error', 'IOError')
@@ -34,25 +65,32 @@ describe Berater::Middleware::Trace do
34
65
  }.to raise_error(IOError)
35
66
  end
36
67
  end
68
+ end
69
+
70
+ context 'when used as middleware' do
71
+ before do
72
+ Berater.middleware.use described_class, tracer: tracer
73
+ end
37
74
 
38
- context 'when an Overloaded exception is raised' do
75
+ it 'traces' do
76
+ expect(tracer).to receive(:trace).with('Berater')
77
+ expect(span).to receive(:set_tag).with('key', limiter.key)
78
+ expect(span).to receive(:set_tag).with('capacity', Numeric)
79
+ expect(span).to receive(:set_tag).with('contention', Numeric)
80
+
81
+ limiter.limit
82
+ end
83
+
84
+ context 'when limiter is overloaded' do
39
85
  let(:limiter) { Berater::Inhibitor.new }
40
86
 
41
87
  it 'tags the span as overloaded and raises' do
42
88
  expect(span).to receive(:set_tag).with('overloaded', true)
43
89
 
44
90
  expect {
45
- instance.call(limiter) { raise Berater::Overloaded }
91
+ limiter.limit
46
92
  }.to be_overloaded
47
93
  end
48
94
  end
49
95
  end
50
-
51
- context 'with the default tracer' do
52
- it 'uses Datadog.tracer' do
53
- expect(Datadog).to receive(:tracer).and_return(tracer)
54
-
55
- described_class.new.call(limiter) {}
56
- end
57
- end
58
96
  end
@@ -0,0 +1,110 @@
1
+ describe Berater::Mutex do
2
+ let(:klass) do
3
+ Class.new do
4
+ include Berater::Mutex
5
+ end
6
+ end
7
+
8
+ describe 'synchronize' do
9
+ it { expect(klass).to respond_to(:synchronize) }
10
+ it { expect(klass.new).to respond_to(:synchronize) }
11
+
12
+ it { expect { |block| klass.synchronize(&block) }.to yield_control }
13
+
14
+ it 'returns the blocks value' do
15
+ res = klass.synchronize { 123 }
16
+ expect(res).to be 123
17
+ end
18
+
19
+ it 'still works and returns nil without a block' do
20
+ expect(klass.synchronize).to be nil
21
+ end
22
+
23
+ it 'does not allow simultaneous calls' do
24
+ expect {
25
+ klass.synchronize do
26
+ klass.synchronize
27
+ end
28
+ }.to be_overloaded
29
+ end
30
+
31
+ it 'allows simultaneous calls with different sub-keys' do
32
+ expect {
33
+ klass.synchronize(:a) do
34
+ klass.synchronize(:b)
35
+ end
36
+ }.not_to raise_error
37
+ end
38
+
39
+ it 'allows consecutive calls' do
40
+ expect {
41
+ 3.times { klass.synchronize }
42
+ }.not_to raise_error
43
+ end
44
+
45
+ describe 'the instance method' do
46
+ it 'is a pass through to the class method' do
47
+ expect(klass).to receive(:synchronize)
48
+ klass.new.synchronize
49
+ end
50
+
51
+ it 'works with arguments' do
52
+ key = 'key'
53
+ opts = { timeout: 1 }
54
+ block = ->{}
55
+
56
+ expect(klass).to receive(:synchronize) do |this_key, **these_opts, &this_block|
57
+ expect(this_key).to be key
58
+ expect(these_opts).to eq opts
59
+ expect(this_block).to be block
60
+ end
61
+
62
+ klass.new.synchronize(key, **opts, &block)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '.mutex_options' do
68
+ subject { klass.mutex_options }
69
+
70
+ it { expect(klass).to respond_to(:mutex_options) }
71
+ it { is_expected.to be_a Hash }
72
+ it { is_expected.to be_empty }
73
+ it { expect(klass.new).not_to respond_to(:mutex_options) }
74
+
75
+ context 'when mutex_options are set' do
76
+ let(:klass) do
77
+ Class.new do
78
+ include Berater::Mutex
79
+
80
+ mutex_options timeout: 1
81
+ end
82
+ end
83
+
84
+ it { is_expected.to eq(timeout: 1) }
85
+
86
+ it 'uses mutex_options during synchronize' do
87
+ expect(Berater::ConcurrencyLimiter).to receive(:new).and_wrap_original do |original, *args, **kwargs|
88
+ expect(kwargs).to eq(subject)
89
+ original.call(*args, **kwargs)
90
+ end
91
+
92
+ klass.synchronize
93
+ end
94
+ end
95
+ end
96
+
97
+ describe 'when extended rather than included' do
98
+ let(:klass) do
99
+ Class.new do
100
+ extend Berater::Mutex
101
+ end
102
+ end
103
+
104
+ it { expect(klass).to respond_to(:synchronize) }
105
+ it { expect(klass).to respond_to(:mutex_options) }
106
+
107
+ it { expect(klass.new).not_to respond_to(:synchronize) }
108
+ it { expect(klass.new).not_to respond_to(:mutex_options) }
109
+ end
110
+ end
@@ -171,4 +171,13 @@ describe Berater::TestMode do
171
171
  it_behaves_like 'it supports test_mode'
172
172
  end
173
173
 
174
+ describe 'Mutex' do
175
+ let(:klass) { Class.new { include Berater::Mutex } }
176
+
177
+ context 'when test_mode = :fail' do
178
+ before { Berater.test_mode = :fail }
179
+
180
+ it { expect { klass.synchronize }.to be_overloaded }
181
+ end
182
+ end
174
183
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Pepper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-04 00:00:00.000000000 Z
11
+ date: 2022-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: meddleware
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.2'
19
+ version: '0.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0.2'
26
+ version: '0.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: redis
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -183,6 +183,7 @@ files:
183
183
  - lib/berater/middleware/load_shedder.rb
184
184
  - lib/berater/middleware/statsd.rb
185
185
  - lib/berater/middleware/trace.rb
186
+ - lib/berater/mutex.rb
186
187
  - lib/berater/rate_limiter.rb
187
188
  - lib/berater/rspec.rb
188
189
  - lib/berater/rspec/matchers.rb
@@ -205,6 +206,7 @@ files:
205
206
  - spec/middleware/statsd_spec.rb
206
207
  - spec/middleware/trace_spec.rb
207
208
  - spec/middleware_spec.rb
209
+ - spec/mutex_spec.rb
208
210
  - spec/rate_limiter_spec.rb
209
211
  - spec/riddle_spec.rb
210
212
  - spec/static_limiter_spec.rb
@@ -230,28 +232,29 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
232
  - !ruby/object:Gem::Version
231
233
  version: '0'
232
234
  requirements: []
233
- rubygems_version: 3.1.6
235
+ rubygems_version: 3.3.7
234
236
  signing_key:
235
237
  specification_version: 4
236
238
  summary: Berater
237
239
  test_files:
238
- - spec/rate_limiter_spec.rb
240
+ - spec/berater_spec.rb
241
+ - spec/concurrency_limiter_spec.rb
242
+ - spec/dsl_refinement_spec.rb
243
+ - spec/dsl_spec.rb
244
+ - spec/inhibitor_spec.rb
245
+ - spec/limiter_set_spec.rb
246
+ - spec/limiter_spec.rb
247
+ - spec/lua_script_spec.rb
248
+ - spec/matchers_spec.rb
249
+ - spec/middleware/fail_open_spec.rb
239
250
  - spec/middleware/load_shedder_spec.rb
240
251
  - spec/middleware/statsd_spec.rb
241
252
  - spec/middleware/trace_spec.rb
242
- - spec/middleware/fail_open_spec.rb
243
- - spec/matchers_spec.rb
244
- - spec/dsl_refinement_spec.rb
245
- - spec/test_mode_spec.rb
246
253
  - spec/middleware_spec.rb
247
- - spec/dsl_spec.rb
248
- - spec/lua_script_spec.rb
249
- - spec/concurrency_limiter_spec.rb
254
+ - spec/mutex_spec.rb
255
+ - spec/rate_limiter_spec.rb
250
256
  - spec/riddle_spec.rb
251
- - spec/limiter_set_spec.rb
252
- - spec/utils_spec.rb
253
- - spec/berater_spec.rb
254
- - spec/limiter_spec.rb
255
257
  - spec/static_limiter_spec.rb
256
- - spec/inhibitor_spec.rb
258
+ - spec/test_mode_spec.rb
257
259
  - spec/unlimiter_spec.rb
260
+ - spec/utils_spec.rb