berater 0.13.1 → 0.15.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: 1270edae5d2c019ae92b623ff4c26e588cf8d5413c8f7b6c549d3d3ec4e16766
4
- data.tar.gz: a9a659acde5a04263b1ca516a2fb1ecdd760d9133f3603b4fa7ec336c06f59df
3
+ metadata.gz: 81f3c362a1360d2b3f3b4a2829b374dd38f0f9de6809737efe7d9bb44ffbd88c
4
+ data.tar.gz: cbad555e49e5ac86d63f99486139e6df13b9da4289832b2d8cf5034bd7140018
5
5
  SHA512:
6
- metadata.gz: 07fc3784c2e6d41648f97878e3a0c62e3a089c1ef69315e88141642350180634c65a89deda082ed5933a90666c620111329756d0699d3fde5238b97d0ad37e82
7
- data.tar.gz: 6f8dc42b5b694a45c286b1d66a0b8efbf17371b3fd6bfd8936098b4155b85ae6523146fecfc6b7c46e3b0a8df1ddb03158f1fd181c9e6a1e13042232089e1a41
6
+ metadata.gz: ed26b29a67a30db075ec18ab64698b6ae76dbe7d4d8aee64c290dc449a6867566ea3c8155eb6ec6c5fe440208c19345bfc02c30adcddb4356b591f784678df99
7
+ data.tar.gz: 56143daeb1069e5d1162bea25c9892f146009168f8ef42c23cdc78774e6148602ef40364423c94e5248d2cff99793e6e5ef6a676e918ec4cf23d2686a35b5927
@@ -62,12 +62,12 @@ module Berater
62
62
  lock = limit(cost: 0)
63
63
 
64
64
  if lock.capacity == 0
65
- 1.0
65
+ 100.0
66
66
  else
67
- lock.contention.to_f / lock.capacity
67
+ lock.contention.to_f / lock.capacity * 100
68
68
  end
69
69
  rescue Berater::Overloaded
70
- 1.0
70
+ 100.0
71
71
  end
72
72
 
73
73
  def ==(other)
@@ -124,12 +124,7 @@ module Berater
124
124
  # can only call via subclass
125
125
  raise NoMethodError if self == Berater::Limiter
126
126
 
127
- if RUBY_VERSION < '3' && kwargs.empty?
128
- # avoid ruby 2 problems with empty hashes
129
- super(*args)
130
- else
131
- super
132
- end
127
+ super
133
128
  end
134
129
 
135
130
  def cache_key(key)
@@ -6,7 +6,9 @@ module Berater
6
6
  end
7
7
 
8
8
  def call(limiter, **)
9
- tracer&.trace('Berater') do |span|
9
+ return yield unless tracer
10
+
11
+ tracer.trace('Berater') do |span|
10
12
  begin
11
13
  lock = yield
12
14
  rescue Exception => error
@@ -32,7 +34,7 @@ module Berater
32
34
  private
33
35
 
34
36
  def tracer
35
- @tracer || (defined?(Datadog::Tracing) && Datadog::Tracing) || (defined?(Datadog) && Datadog.tracer)
37
+ @tracer || (defined?(Datadog) && Datadog::Tracing)
36
38
  end
37
39
  end
38
40
  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
@@ -14,7 +14,7 @@ module Berater
14
14
  if res.is_a? Berater::Limiter
15
15
  # eg. expect { Berater.new(...) }.to be_overloaded
16
16
  @limiter = res
17
- @limiter.utilization >= 1
17
+ @limiter.utilization >= 100
18
18
  else
19
19
  # eg. expect { Berater(...) }.to be_overloaded
20
20
  # eg. expect { limiter.limit }.to be_overloaded
@@ -23,7 +23,7 @@ module Berater
23
23
  when Berater::Limiter
24
24
  # eg. expect(Berater.new(...)).to be_overloaded
25
25
  @limiter = obj
26
- @limiter.utilization >= 1
26
+ @limiter.utilization >= 100
27
27
  end
28
28
  rescue Berater::Overloaded
29
29
  true
@@ -1,3 +1,3 @@
1
1
  module Berater
2
- VERSION = "0.13.1"
2
+ VERSION = "0.15.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
 
@@ -255,19 +255,19 @@ describe Berater::ConcurrencyLimiter do
255
255
  let(:limiter) { described_class.new(:key, 10, timeout: 30) }
256
256
 
257
257
  it 'works' do
258
- expect(limiter.utilization).to be 0.0
258
+ expect(limiter.utilization).to eq 0
259
259
 
260
260
  2.times { limiter.limit }
261
- expect(limiter.utilization).to be 0.2
261
+ expect(limiter.utilization).to eq 20
262
262
 
263
263
  Timecop.freeze(15)
264
264
 
265
265
  8.times { limiter.limit }
266
- expect(limiter.utilization).to be 1.0
266
+ expect(limiter.utilization).to eq 100
267
267
 
268
268
  Timecop.freeze(15)
269
269
 
270
- expect(limiter.utilization).to be 0.8
270
+ expect(limiter.utilization).to eq 80
271
271
  end
272
272
  end
273
273
 
@@ -65,6 +65,16 @@ describe Berater::Middleware::Trace do
65
65
  }.to raise_error(IOError)
66
66
  end
67
67
  end
68
+
69
+ context 'when tracer is not defined' do
70
+ before do
71
+ allow(instance).to receive(:tracer).and_return(nil)
72
+ end
73
+
74
+ it 'still yields' do
75
+ expect {|b| instance.call(limiter, &b) }.to yield_control
76
+ end
77
+ end
68
78
  end
69
79
 
70
80
  context 'when used as middleware' do
@@ -0,0 +1,145 @@
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
+
111
+ describe 'when used in a counter' do
112
+ subject(:counter) { klass.new }
113
+
114
+ let(:klass) do
115
+ class Counter
116
+ include Berater::Mutex
117
+
118
+ @@count = 0
119
+ @@counts = {}
120
+
121
+ def incr
122
+ synchronize { @@count += 1 }
123
+ end
124
+
125
+ def incr_key(key)
126
+ synchronize(key) do
127
+ @@counts[key] ||= 0
128
+ @@counts[key] += 1
129
+ end
130
+ end
131
+ end
132
+ Counter
133
+ end
134
+
135
+ it { expect(counter.incr).to eq 1 }
136
+ it { expect(counter.incr_key(:a)).to eq 1 }
137
+
138
+ it 'separates keys' do
139
+ res = 3.times.map { counter.incr_key(:a) }
140
+ expect(res).to eq [ 1, 2, 3 ]
141
+
142
+ expect(counter.incr_key(:b)).to eq 1
143
+ end
144
+ end
145
+ end
@@ -237,17 +237,17 @@ describe Berater::RateLimiter do
237
237
  let(:limiter) { described_class.new(:key, 10, :minute) }
238
238
 
239
239
  it do
240
- expect(limiter.utilization).to be 0.0
240
+ expect(limiter.utilization).to eq 0
241
241
 
242
242
  2.times { limiter.limit }
243
- expect(limiter.utilization).to be 0.2
243
+ expect(limiter.utilization).to eq 20
244
244
 
245
245
  8.times { limiter.limit }
246
- expect(limiter.utilization).to be 1.0
246
+ expect(limiter.utilization).to eq 100
247
247
 
248
248
  Timecop.freeze(30)
249
249
 
250
- expect(limiter.utilization).to be 0.5
250
+ expect(limiter.utilization).to eq 50
251
251
  end
252
252
  end
253
253
 
@@ -60,13 +60,13 @@ describe Berater::StaticLimiter do
60
60
  let(:limiter) { described_class.new(:key, 10) }
61
61
 
62
62
  it do
63
- expect(limiter.utilization).to be 0.0
63
+ expect(limiter.utilization).to eq 0
64
64
 
65
65
  2.times { limiter.limit }
66
- expect(limiter.utilization).to be 0.2
66
+ expect(limiter.utilization).to eq 20
67
67
 
68
68
  8.times { limiter.limit }
69
- expect(limiter.utilization).to be 1.0
69
+ expect(limiter.utilization).to eq 100
70
70
  end
71
71
  end
72
72
 
@@ -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.1
4
+ version: 0.15.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-07-13 00:00:00.000000000 Z
11
+ date: 2023-10-13 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
@@ -66,34 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: codecov
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: ddtrace
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - ">="
88
74
  - !ruby/object:Gem::Version
89
- version: '0'
75
+ version: '1'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
80
  - - ">="
95
81
  - !ruby/object:Gem::Version
96
- version: '0'
82
+ version: '1'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: dogstatsd-ruby
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,20 +94,6 @@ dependencies:
108
94
  - - ">="
109
95
  - !ruby/object:Gem::Version
110
96
  version: '4.3'
111
- - !ruby/object:Gem::Dependency
112
- name: rake
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
97
  - !ruby/object:Gem::Dependency
126
98
  name: rspec
127
99
  requirement: !ruby/object:Gem::Requirement
@@ -183,6 +155,7 @@ files:
183
155
  - lib/berater/middleware/load_shedder.rb
184
156
  - lib/berater/middleware/statsd.rb
185
157
  - lib/berater/middleware/trace.rb
158
+ - lib/berater/mutex.rb
186
159
  - lib/berater/rate_limiter.rb
187
160
  - lib/berater/rspec.rb
188
161
  - lib/berater/rspec/matchers.rb
@@ -205,6 +178,7 @@ files:
205
178
  - spec/middleware/statsd_spec.rb
206
179
  - spec/middleware/trace_spec.rb
207
180
  - spec/middleware_spec.rb
181
+ - spec/mutex_spec.rb
208
182
  - spec/rate_limiter_spec.rb
209
183
  - spec/riddle_spec.rb
210
184
  - spec/static_limiter_spec.rb
@@ -223,35 +197,36 @@ required_ruby_version: !ruby/object:Gem::Requirement
223
197
  requirements:
224
198
  - - ">="
225
199
  - !ruby/object:Gem::Version
226
- version: '0'
200
+ version: '3'
227
201
  required_rubygems_version: !ruby/object:Gem::Requirement
228
202
  requirements:
229
203
  - - ">="
230
204
  - !ruby/object:Gem::Version
231
205
  version: '0'
232
206
  requirements: []
233
- rubygems_version: 3.1.6
207
+ rubygems_version: 3.3.7
234
208
  signing_key:
235
209
  specification_version: 4
236
210
  summary: Berater
237
211
  test_files:
238
- - spec/rate_limiter_spec.rb
212
+ - spec/berater_spec.rb
213
+ - spec/concurrency_limiter_spec.rb
214
+ - spec/dsl_refinement_spec.rb
215
+ - spec/dsl_spec.rb
216
+ - spec/inhibitor_spec.rb
217
+ - spec/limiter_set_spec.rb
218
+ - spec/limiter_spec.rb
219
+ - spec/lua_script_spec.rb
220
+ - spec/matchers_spec.rb
221
+ - spec/middleware/fail_open_spec.rb
239
222
  - spec/middleware/load_shedder_spec.rb
240
223
  - spec/middleware/statsd_spec.rb
241
224
  - 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
225
  - spec/middleware_spec.rb
247
- - spec/dsl_spec.rb
248
- - spec/lua_script_spec.rb
249
- - spec/concurrency_limiter_spec.rb
226
+ - spec/mutex_spec.rb
227
+ - spec/rate_limiter_spec.rb
250
228
  - 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
229
  - spec/static_limiter_spec.rb
256
- - spec/inhibitor_spec.rb
230
+ - spec/test_mode_spec.rb
257
231
  - spec/unlimiter_spec.rb
232
+ - spec/utils_spec.rb