sidekiq-rate-limiter 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +11 -0
- data/README.md +28 -7
- data/lib/sidekiq-rate-limiter/fetch.rb +60 -8
- data/lib/sidekiq-rate-limiter/version.rb +1 -1
- data/sidekiq-rate-limiter.gemspec +3 -3
- data/spec/sidekiq-rate-limiter/fetch_spec.rb +22 -0
- metadata +34 -46
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eed802823e01b40545973fe5e2ddd21c5fa85002
|
4
|
+
data.tar.gz: 33ffa14b502ca8872d1573752c0e3eab5fc9799e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ae5a1738821693e8a9bde19b9495cdbf4e87fd9ec7d20457460ec42a76c327cac522db118032178e833b2c83ec7328b89d0f75d19bac2f0fd6c34c0d4bbd30a3
|
7
|
+
data.tar.gz: 79e48fc5d5b222bdb3e1bc0cedb0b4a9b32372508ef284b4f246fd8c235c5cbaedf1d80f3d9e9048241bbcf2e3f3ade04756284a8169e6d4f49f508d30706e7a
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -3,8 +3,7 @@ sidekiq-rate-limiter
|
|
3
3
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/enova/sidekiq-rate-limiter.png)](http://travis-ci.org/enova/sidekiq-rate-limiter)
|
5
5
|
|
6
|
-
|
7
|
-
a redis-backed fashion.
|
6
|
+
Redis-backed, per-worker rate limits for job processing.
|
8
7
|
|
9
8
|
## Compatibility
|
10
9
|
|
@@ -12,10 +11,10 @@ sidekiq-rate-limiter is actively tested against MRI versions 2.0.0 and 1.9.3.
|
|
12
11
|
|
13
12
|
sidekiq-rate-limiter works by using a custom fetch class, the class responsible
|
14
13
|
for pulling work from the queue stored in redis. Consequently you'll want to be
|
15
|
-
careful about using other gems that use a same strategy
|
14
|
+
careful about using other gems that use a same strategy, [sidekiq-priority](https://github.com/socialpandas/sidekiq-priority)
|
16
15
|
being one example.
|
17
16
|
|
18
|
-
I've attempted to support the same options as used by [sidekiq-throttler](/gevans/sidekiq-throttler). So, if
|
17
|
+
I've attempted to support the same options as used by [sidekiq-throttler](https://github.com/gevans/sidekiq-throttler). So, if
|
19
18
|
your worker already looks like this example I lifted from the sidekiq-throttler wiki:
|
20
19
|
|
21
20
|
```ruby
|
@@ -87,15 +86,37 @@ The configuration above would result in any jobs beyond the first 50 in a one
|
|
87
86
|
hour period being delayed. The server will continue to fetch items from redis, &
|
88
87
|
will place any items that are beyond the threshold at the back of their queue.
|
89
88
|
|
89
|
+
### Dynamic Configuration
|
90
|
+
|
91
|
+
The simplest way to set the rate-limiting options (`:name`, `:limit`, and `:period`) is to assign them each a static value (as above). In some cases, you may wish to calculate values for these options for each specific job. You can do this by supplying a `Proc` for any or all of these options.
|
92
|
+
|
93
|
+
The `Proc` may receive as its arguments the same values that will be passed to `perform` when the job is finally performed.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class Job
|
97
|
+
include Sidekiq::Worker
|
98
|
+
|
99
|
+
sidekiq_options :queue => "my_queue",
|
100
|
+
:rate => {
|
101
|
+
:name => ->(user_id, rate_limit) { user_id },
|
102
|
+
:limit => ->(user_id, rate_limit) { rate_limit },
|
103
|
+
:period => ->{ Date.today.monday? ? 2.hours : 4.hours }, # can ignore arguments
|
104
|
+
}
|
105
|
+
|
106
|
+
def perform(user_id, rate_limit)
|
107
|
+
## do something
|
108
|
+
```
|
109
|
+
|
110
|
+
**Caveat**: Normally, Sidekiq stores the `sidekiq_options` with the job on your Redis server at the time the job is enqueued, and it is these stored values that are used for rate-limiting. This means that if you deploy a new version of your code with different `sidekiq_options`, the already-queued jobs will continue to behave according to the options that were in place when they were created. When you supply a `Proc` for one or more of your configuration options, your rate-limiting options can no longer be stored in Redis, but must instead be calculated when the job is fetched by your Sidekiq server for potential execution. If your application code changes while a job is in the queue, it may run with different `sidekiq_options` than existed when it was first enqueued.
|
111
|
+
|
90
112
|
## Motivation
|
91
113
|
|
92
114
|
Sidekiq::Throttler is great for smaller quantities of jobs, but falls down a bit
|
93
|
-
for larger queues (see [issue #8](/gevans/sidekiq-throttler/issues/8). In addition, jobs that are
|
94
|
-
limited multiple times are counted as 'processed' each time, so the stats
|
115
|
+
for larger queues (see [issue #8](https://github.com/gevans/sidekiq-throttler/issues/8)). In addition, jobs that are
|
116
|
+
limited multiple times are counted as 'processed' each time, so the stats balloon quickly.
|
95
117
|
|
96
118
|
## TODO
|
97
119
|
|
98
|
-
* Most or all of the configuration options should support procs
|
99
120
|
* While it subclasses instead of monkey patching, setting Sidekiq.options[:fetch]
|
100
121
|
is still asking for interaction issues. It would be better for this to be directly
|
101
122
|
in sidekiq or to use some other means to accomplish this goal.
|
@@ -14,18 +14,20 @@ module Sidekiq::RateLimiter
|
|
14
14
|
def limit(work)
|
15
15
|
message = JSON.parse(work.message) rescue {}
|
16
16
|
|
17
|
+
args = message['args']
|
17
18
|
klass = message['class']
|
18
|
-
rate = message
|
19
|
-
limit = rate['limit'] || rate['threshold']
|
20
|
-
interval = rate['period'] || rate['interval']
|
21
|
-
name = rate['name'] || DEFAULT_LIMIT_NAME
|
19
|
+
rate = Rate.new(message)
|
22
20
|
|
23
|
-
return work unless !!(klass &&
|
21
|
+
return work unless !!(klass && rate.valid?)
|
22
|
+
|
23
|
+
limit = rate.limit
|
24
|
+
interval = rate.interval
|
25
|
+
name = rate.name
|
24
26
|
|
25
27
|
options = {
|
26
|
-
:limit => limit,
|
27
|
-
:interval => interval,
|
28
|
-
:name => name,
|
28
|
+
:limit => (limit.respond_to?(:call) ? limit.call(*args) : limit).to_i,
|
29
|
+
:interval => (interval.respond_to?(:call) ? interval.call(*args) : interval).to_f,
|
30
|
+
:name => (name.respond_to?(:call) ? name.call(*args) : name).to_s,
|
29
31
|
}
|
30
32
|
|
31
33
|
Sidekiq.redis do |conn|
|
@@ -39,6 +41,56 @@ module Sidekiq::RateLimiter
|
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class Rate
|
48
|
+
def initialize(message)
|
49
|
+
@message = message
|
50
|
+
end
|
51
|
+
|
52
|
+
def limit
|
53
|
+
rate['limit'] || rate['threshold']
|
54
|
+
end
|
55
|
+
|
56
|
+
def interval
|
57
|
+
rate['interval'] || rate['period']
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
rate['name'] || DEFAULT_LIMIT_NAME
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid?
|
65
|
+
!!(limit && interval)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def rate
|
71
|
+
use_server_rate? ? server_rate : client_rate
|
72
|
+
end
|
73
|
+
|
74
|
+
def use_server_rate?
|
75
|
+
server_rate['limit'] && server_rate['limit'].respond_to?(:call) ||
|
76
|
+
server_rate['threshold'] && server_rate['threshold'].respond_to?(:call) ||
|
77
|
+
server_rate['period'] && server_rate['period'].respond_to?(:call) ||
|
78
|
+
server_rate['interval'] && server_rate['interval'].respond_to?(:call) ||
|
79
|
+
server_rate['name'] && server_rate['name'].respond_to?(:call)
|
80
|
+
end
|
81
|
+
|
82
|
+
def client_rate
|
83
|
+
@client_rate ||= @message['rate'] || @message['throttle'] || {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def server_rate
|
87
|
+
return @server_rate if @server_rate
|
88
|
+
|
89
|
+
worker_class = @message['class']
|
90
|
+
options = Object.const_get(worker_class).get_sidekiq_options rescue {}
|
91
|
+
server_rate = options['rate'] || options['throttle'] || {}
|
92
|
+
@server_rate = server_rate.stringify_keys
|
93
|
+
end
|
42
94
|
end
|
43
95
|
|
44
96
|
class Limit < RedisRateLimiter
|
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.authors = ["Blake Thomas", "Enova"]
|
12
12
|
s.email = ["bwthomas@gmail.com"]
|
13
13
|
s.homepage = "https://github.com/enova/sidekiq-rate-limiter"
|
14
|
-
s.summary = %q{
|
15
|
-
s.description = %q{
|
14
|
+
s.summary = %q{Redis-backed, per-worker rate limits for job processing}
|
15
|
+
s.description = %q{Redis-backed, per-worker rate limits for job processing}
|
16
16
|
s.rubyforge_project = "nowarning"
|
17
17
|
|
18
18
|
s.files = `git ls-files`.split("\n")
|
@@ -27,6 +27,6 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_development_dependency "simplecov-rcov"
|
28
28
|
|
29
29
|
s.add_dependency "redis"
|
30
|
-
s.add_dependency "sidekiq"
|
30
|
+
s.add_dependency "sidekiq", ">= 2.0", "< 4.0"
|
31
31
|
s.add_dependency "redis_rate_limiter"
|
32
32
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'sidekiq'
|
3
|
+
require 'sidekiq/api'
|
3
4
|
|
4
5
|
describe Sidekiq::RateLimiter::Fetch do
|
5
6
|
before(:all) do
|
@@ -13,6 +14,17 @@ describe Sidekiq::RateLimiter::Fetch do
|
|
13
14
|
}
|
14
15
|
def perform(*args); end
|
15
16
|
end
|
17
|
+
class ProcJob
|
18
|
+
include Sidekiq::Worker
|
19
|
+
sidekiq_options 'queue' => 'basic',
|
20
|
+
'retry' => false,
|
21
|
+
'rate' => {
|
22
|
+
'limit' => ->(arg1, arg2) { arg2 },
|
23
|
+
'name' => ->(arg1, arg2) { arg2 },
|
24
|
+
'period' => ->(arg1, arg2) { arg2 }
|
25
|
+
}
|
26
|
+
def perform(arg1, arg2); end
|
27
|
+
end
|
16
28
|
end
|
17
29
|
|
18
30
|
let(:options) { { queues: [queue, another_queue, another_queue] } }
|
@@ -20,6 +32,7 @@ describe Sidekiq::RateLimiter::Fetch do
|
|
20
32
|
let(:another_queue) { 'some_other_queue' }
|
21
33
|
let(:args) { ['I am some args'] }
|
22
34
|
let(:worker) { Job }
|
35
|
+
let(:proc_worker) { ProcJob }
|
23
36
|
let(:redis_class) { Sidekiq.redis { |conn| conn.class } }
|
24
37
|
|
25
38
|
it 'should inherit from Sidekiq::BasicFetch' do
|
@@ -61,4 +74,13 @@ describe Sidekiq::RateLimiter::Fetch do
|
|
61
74
|
q.size.should == 1
|
62
75
|
end
|
63
76
|
|
77
|
+
it 'should accept procs for limit, name, and period config keys', queuing: true do
|
78
|
+
proc_worker.perform_async(1,2)
|
79
|
+
|
80
|
+
Sidekiq::RateLimiter::Limit.should_receive(:new).with(anything(), {:limit => 2, :interval => 2, :name => "2"}).and_call_original
|
81
|
+
|
82
|
+
fetch = described_class.new(options)
|
83
|
+
work = fetch.retrieve_work
|
84
|
+
end
|
85
|
+
|
64
86
|
end
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-rate-limiter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Blake Thomas
|
@@ -10,137 +9,127 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2014-05-30 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: pry
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- -
|
18
|
+
- - '>='
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '0'
|
23
21
|
type: :development
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- -
|
25
|
+
- - '>='
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '0'
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: rake
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - '>='
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :development
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - '>='
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: rspec
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
|
-
- -
|
46
|
+
- - '>='
|
53
47
|
- !ruby/object:Gem::Version
|
54
48
|
version: '0'
|
55
49
|
type: :development
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
|
-
- -
|
53
|
+
- - '>='
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '0'
|
63
56
|
- !ruby/object:Gem::Dependency
|
64
57
|
name: simplecov
|
65
58
|
requirement: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
59
|
requirements:
|
68
|
-
- -
|
60
|
+
- - '>='
|
69
61
|
- !ruby/object:Gem::Version
|
70
62
|
version: '0'
|
71
63
|
type: :development
|
72
64
|
prerelease: false
|
73
65
|
version_requirements: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
66
|
requirements:
|
76
|
-
- -
|
67
|
+
- - '>='
|
77
68
|
- !ruby/object:Gem::Version
|
78
69
|
version: '0'
|
79
70
|
- !ruby/object:Gem::Dependency
|
80
71
|
name: simplecov-rcov
|
81
72
|
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
73
|
requirements:
|
84
|
-
- -
|
74
|
+
- - '>='
|
85
75
|
- !ruby/object:Gem::Version
|
86
76
|
version: '0'
|
87
77
|
type: :development
|
88
78
|
prerelease: false
|
89
79
|
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
80
|
requirements:
|
92
|
-
- -
|
81
|
+
- - '>='
|
93
82
|
- !ruby/object:Gem::Version
|
94
83
|
version: '0'
|
95
84
|
- !ruby/object:Gem::Dependency
|
96
85
|
name: redis
|
97
86
|
requirement: !ruby/object:Gem::Requirement
|
98
|
-
none: false
|
99
87
|
requirements:
|
100
|
-
- -
|
88
|
+
- - '>='
|
101
89
|
- !ruby/object:Gem::Version
|
102
90
|
version: '0'
|
103
91
|
type: :runtime
|
104
92
|
prerelease: false
|
105
93
|
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
94
|
requirements:
|
108
|
-
- -
|
95
|
+
- - '>='
|
109
96
|
- !ruby/object:Gem::Version
|
110
97
|
version: '0'
|
111
98
|
- !ruby/object:Gem::Dependency
|
112
99
|
name: sidekiq
|
113
100
|
requirement: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
101
|
requirements:
|
116
|
-
- -
|
102
|
+
- - '>='
|
117
103
|
- !ruby/object:Gem::Version
|
118
|
-
version: '0'
|
104
|
+
version: '2.0'
|
105
|
+
- - <
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '4.0'
|
119
108
|
type: :runtime
|
120
109
|
prerelease: false
|
121
110
|
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
111
|
requirements:
|
124
|
-
- -
|
112
|
+
- - '>='
|
125
113
|
- !ruby/object:Gem::Version
|
126
|
-
version: '0'
|
114
|
+
version: '2.0'
|
115
|
+
- - <
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '4.0'
|
127
118
|
- !ruby/object:Gem::Dependency
|
128
119
|
name: redis_rate_limiter
|
129
120
|
requirement: !ruby/object:Gem::Requirement
|
130
|
-
none: false
|
131
121
|
requirements:
|
132
|
-
- -
|
122
|
+
- - '>='
|
133
123
|
- !ruby/object:Gem::Version
|
134
124
|
version: '0'
|
135
125
|
type: :runtime
|
136
126
|
prerelease: false
|
137
127
|
version_requirements: !ruby/object:Gem::Requirement
|
138
|
-
none: false
|
139
128
|
requirements:
|
140
|
-
- -
|
129
|
+
- - '>='
|
141
130
|
- !ruby/object:Gem::Version
|
142
131
|
version: '0'
|
143
|
-
description:
|
132
|
+
description: Redis-backed, per-worker rate limits for job processing
|
144
133
|
email:
|
145
134
|
- bwthomas@gmail.com
|
146
135
|
executables: []
|
@@ -151,6 +140,7 @@ files:
|
|
151
140
|
- .rspec
|
152
141
|
- .simplecov
|
153
142
|
- .travis.yml
|
143
|
+
- CHANGELOG.md
|
154
144
|
- Gemfile
|
155
145
|
- LICENSE
|
156
146
|
- README.md
|
@@ -167,31 +157,29 @@ files:
|
|
167
157
|
homepage: https://github.com/enova/sidekiq-rate-limiter
|
168
158
|
licenses:
|
169
159
|
- MIT
|
160
|
+
metadata: {}
|
170
161
|
post_install_message:
|
171
162
|
rdoc_options: []
|
172
163
|
require_paths:
|
173
164
|
- lib
|
174
165
|
required_ruby_version: !ruby/object:Gem::Requirement
|
175
|
-
none: false
|
176
166
|
requirements:
|
177
|
-
- -
|
167
|
+
- - '>='
|
178
168
|
- !ruby/object:Gem::Version
|
179
169
|
version: '0'
|
180
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
-
none: false
|
182
171
|
requirements:
|
183
|
-
- -
|
172
|
+
- - '>='
|
184
173
|
- !ruby/object:Gem::Version
|
185
174
|
version: '0'
|
186
175
|
requirements: []
|
187
176
|
rubyforge_project: nowarning
|
188
|
-
rubygems_version:
|
177
|
+
rubygems_version: 2.0.14
|
189
178
|
signing_key:
|
190
|
-
specification_version:
|
191
|
-
summary:
|
179
|
+
specification_version: 4
|
180
|
+
summary: Redis-backed, per-worker rate limits for job processing
|
192
181
|
test_files:
|
193
182
|
- spec/sidekiq-rate-limiter/fetch_spec.rb
|
194
183
|
- spec/sidekiq-rate-limiter/server_spec.rb
|
195
184
|
- spec/spec_helper.rb
|
196
185
|
- spec/support/redis/.keep
|
197
|
-
has_rdoc:
|