berater 0.13.0 → 0.14.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 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