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 +4 -4
- data/lib/berater/middleware/load_shedder.rb +7 -6
- data/lib/berater/middleware/trace.rb +2 -4
- data/lib/berater/mutex.rb +28 -0
- data/lib/berater/version.rb +1 -1
- data/lib/berater.rb +2 -6
- data/spec/limiter_spec.rb +3 -7
- data/spec/middleware/trace_spec.rb +59 -21
- data/spec/mutex_spec.rb +110 -0
- data/spec/test_mode_spec.rb +9 -0
- metadata +21 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3780da104c79084b2c3b348363a5f763c42d9d936dc218e68df90cdbbc223cfe
|
4
|
+
data.tar.gz: a3b7e7af7b3a764f97c013cb642eccf6d71ad825df459d32722a4e312ca2cdd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
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
|
data/lib/berater/version.rb
CHANGED
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
|
-
|
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:
|
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
|
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
|
-
|
11
|
-
|
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
|
-
|
15
|
-
|
26
|
+
describe '#tracer' do
|
27
|
+
subject { instance.send(:tracer) }
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/spec/mutex_spec.rb
ADDED
@@ -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
|
data/spec/test_mode_spec.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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/
|
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/
|
248
|
-
- spec/
|
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/
|
258
|
+
- spec/test_mode_spec.rb
|
257
259
|
- spec/unlimiter_spec.rb
|
260
|
+
- spec/utils_spec.rb
|