sidekiq-limit_fetch 0.7 → 0.8
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.
- data/.travis.yml +12 -0
- data/README.md +51 -6
- data/Rakefile +5 -1
- data/lib/sidekiq/extensions/queue.rb +3 -1
- data/lib/sidekiq/limit_fetch/global/selector.rb +18 -12
- data/lib/sidekiq/limit_fetch/global/semaphore.rb +25 -6
- data/lib/sidekiq/limit_fetch/local/selector.rb +6 -1
- data/lib/sidekiq/limit_fetch/local/semaphore.rb +17 -1
- data/sidekiq-limit_fetch.gemspec +3 -2
- data/spec/sidekiq/extensions/queue_spec.rb +17 -1
- data/spec/sidekiq/limit_fetch/global/monitor_spec.rb +1 -1
- data/spec/sidekiq/limit_fetch/queues_spec.rb +12 -0
- data/spec/sidekiq/limit_fetch/semaphore_spec.rb +1 -1
- metadata +31 -2
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -3,6 +3,15 @@
|
|
3
3
|
Sidekiq strategy to restrict number of workers
|
4
4
|
which are able to run specified queues simultaneously.
|
5
5
|
|
6
|
+
[](http://travis-ci.org/brainopia/sidekiq-limit_fetch)
|
8
|
+
[](http://badge.fury.io/rb/sidekiq-limit_fetch)
|
10
|
+
[](https://gemnasium.com/brainopia/sidekiq-limit_fetch)
|
12
|
+
[](https://codeclimate.com/github/brainopia/sidekiq-limit_fetch)
|
14
|
+
|
6
15
|
## Installation
|
7
16
|
|
8
17
|
Add this line to your application's Gemfile:
|
@@ -14,9 +23,9 @@ Add this line to your application's Gemfile:
|
|
14
23
|
Specify limits which you want to place on queues inside sidekiq.yml:
|
15
24
|
|
16
25
|
```yaml
|
17
|
-
:limits:
|
18
|
-
|
19
|
-
|
26
|
+
:limits:
|
27
|
+
queue_name1: 5
|
28
|
+
queue_name2: 10
|
20
29
|
```
|
21
30
|
|
22
31
|
Or set it dynamically in your code:
|
@@ -32,22 +41,58 @@ workers simultaneously.
|
|
32
41
|
Ability to set limits dynamically allows you to resize worker
|
33
42
|
distribution among queues any time you want.
|
34
43
|
|
44
|
+
|
35
45
|
You can also pause your queues temporarely. Upon continuing their limits
|
36
46
|
will be preserved.
|
37
47
|
|
38
48
|
```ruby
|
39
49
|
Sidekiq::Queue['name'].pause # prevents workers from running tasks from this queue
|
40
|
-
|
41
|
-
Sidekiq::Queue['name'].
|
50
|
+
Sidekiq::Queue['name'].paused? # => true
|
51
|
+
Sidekiq::Queue['name'].unpause # allows workers to use the queue
|
52
|
+
```
|
53
|
+
|
54
|
+
|
55
|
+
You can see how many workers currently handling a queue:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
Sidekiq::Queue['name'].busy # number of busy workers
|
42
59
|
```
|
43
60
|
|
61
|
+
|
44
62
|
Limits are applied per process. In case you have several worker
|
45
63
|
processes and want to have global locks between them, you'll need to
|
46
64
|
enable global mode by setting global option, eg:
|
47
65
|
|
48
66
|
```yaml
|
49
|
-
:global: true
|
67
|
+
:global: true
|
50
68
|
```
|
51
69
|
|
70
|
+
|
71
|
+
If you use strict queue ordering (it will be used if you don't specify queue weights)
|
72
|
+
then you can set blocking status for queues. It means if a blocking
|
73
|
+
queue task is executing then no new task from lesser priority queues will
|
74
|
+
be ran. Eg,
|
75
|
+
|
76
|
+
```yaml
|
77
|
+
:queues:
|
78
|
+
- a
|
79
|
+
- b
|
80
|
+
- c
|
81
|
+
:limits:
|
82
|
+
- b
|
83
|
+
```
|
84
|
+
|
85
|
+
In this case when a task for `b` queue is ran no new task from `c` queue
|
86
|
+
will be started.
|
87
|
+
|
88
|
+
You can also enable and disable blocking mode for queues on the fly:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
Sidekiq::Queue['name'].block
|
92
|
+
Sidekiq::Queue['name'].blocking? # => true
|
93
|
+
Sidekiq::Queue['name'].unblock
|
94
|
+
```
|
95
|
+
|
96
|
+
|
52
97
|
Sponsored by [Evil Martians].
|
53
98
|
[Evil Martians]: http://evilmartians.com/
|
data/Rakefile
CHANGED
@@ -48,26 +48,32 @@ module Sidekiq::LimitFetch::Global
|
|
48
48
|
local worker_name = table.remove(ARGV, 1)
|
49
49
|
local queues = ARGV
|
50
50
|
local available = {}
|
51
|
+
local queue_locks
|
52
|
+
local blocked
|
51
53
|
|
52
54
|
for _, queue in ipairs(queues) do
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
if not blocked then
|
56
|
+
local busy_key = namespace..'busy:'..queue
|
57
|
+
local pause_key = namespace..'pause:'..queue
|
58
|
+
local paused = redis.call('get', pause_key)
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
if not paused then
|
61
|
+
local limit_key = namespace..'limit:'..queue
|
62
|
+
local queue_limit = tonumber(redis.call('get', limit_key))
|
60
63
|
|
61
|
-
|
62
|
-
local
|
64
|
+
local block_key = namespace..'block:'..queue
|
65
|
+
local can_block = redis.call('get', block_key)
|
63
66
|
|
64
|
-
if
|
67
|
+
if can_block or queue_limit then
|
68
|
+
queue_locks = redis.call('llen', busy_key)
|
69
|
+
end
|
70
|
+
|
71
|
+
blocked = can_block and queue_locks > 0
|
72
|
+
|
73
|
+
if not queue_limit or queue_limit > queue_locks then
|
65
74
|
redis.call('rpush', busy_key, worker_name)
|
66
75
|
table.insert(available, queue)
|
67
76
|
end
|
68
|
-
else
|
69
|
-
redis.call('rpush', busy_key, worker_name)
|
70
|
-
table.insert(available, queue)
|
71
77
|
end
|
72
78
|
end
|
73
79
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module Sidekiq::LimitFetch::Global
|
2
2
|
class Semaphore
|
3
|
+
extend Forwardable
|
4
|
+
def_delegator Sidekiq, :redis
|
5
|
+
|
3
6
|
PREFIX = 'limit_fetch'
|
4
7
|
|
5
8
|
def initialize(name)
|
@@ -7,12 +10,12 @@ module Sidekiq::LimitFetch::Global
|
|
7
10
|
end
|
8
11
|
|
9
12
|
def limit
|
10
|
-
value =
|
13
|
+
value = redis {|it| it.get "#{PREFIX}:limit:#@name" }
|
11
14
|
value.to_i if value
|
12
15
|
end
|
13
16
|
|
14
17
|
def limit=(value)
|
15
|
-
|
18
|
+
redis {|it| it.set "#{PREFIX}:limit:#@name", value }
|
16
19
|
end
|
17
20
|
|
18
21
|
def acquire
|
@@ -24,15 +27,31 @@ module Sidekiq::LimitFetch::Global
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def busy
|
27
|
-
|
30
|
+
redis {|it| it.llen "#{PREFIX}:busy:#@name" }
|
28
31
|
end
|
29
32
|
|
30
33
|
def pause
|
31
|
-
|
34
|
+
redis {|it| it.set "#{PREFIX}:pause:#@name", true }
|
35
|
+
end
|
36
|
+
|
37
|
+
def unpause
|
38
|
+
redis {|it| it.del "#{PREFIX}:pause:#@name" }
|
39
|
+
end
|
40
|
+
|
41
|
+
def paused?
|
42
|
+
redis {|it| it.get "#{PREFIX}:pause:#@name" }
|
43
|
+
end
|
44
|
+
|
45
|
+
def block
|
46
|
+
redis {|it| it.set "#{PREFIX}:block:#@name", true }
|
47
|
+
end
|
48
|
+
|
49
|
+
def unblock
|
50
|
+
redis {|it| it.del "#{PREFIX}:block:#@name" }
|
32
51
|
end
|
33
52
|
|
34
|
-
def
|
35
|
-
|
53
|
+
def blocking?
|
54
|
+
redis {|it| it.get "#{PREFIX}:block:#@name" }
|
36
55
|
end
|
37
56
|
end
|
38
57
|
end
|
@@ -3,7 +3,12 @@ module Sidekiq::LimitFetch::Local
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def acquire(names)
|
6
|
-
|
6
|
+
blocked = false
|
7
|
+
queues(names).select {|queue|
|
8
|
+
next false if blocked
|
9
|
+
blocked = true if not queue.paused? and queue.blocking? and queue.busy > 0
|
10
|
+
queue.acquire
|
11
|
+
}.map(&:name)
|
7
12
|
end
|
8
13
|
|
9
14
|
def release(names)
|
@@ -32,8 +32,24 @@ module Sidekiq::LimitFetch::Local
|
|
32
32
|
@paused = true
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def unpause
|
36
36
|
@paused = false
|
37
37
|
end
|
38
|
+
|
39
|
+
def paused?
|
40
|
+
@paused
|
41
|
+
end
|
42
|
+
|
43
|
+
def block
|
44
|
+
@block = true
|
45
|
+
end
|
46
|
+
|
47
|
+
def unblock
|
48
|
+
@block = false
|
49
|
+
end
|
50
|
+
|
51
|
+
def blocking?
|
52
|
+
@block
|
53
|
+
end
|
38
54
|
end
|
39
55
|
end
|
data/sidekiq-limit_fetch.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = 'sidekiq-limit_fetch'
|
3
|
-
gem.version = '0.
|
3
|
+
gem.version = '0.8'
|
4
4
|
gem.authors = 'brainopia'
|
5
5
|
gem.email = 'brainopia@evilmartians.com'
|
6
6
|
gem.summary = 'Sidekiq strategy to support queue limits'
|
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.test_files = gem.files.grep %r{^spec/}
|
15
15
|
gem.require_paths = %w(lib)
|
16
16
|
|
17
|
-
gem.add_dependency 'sidekiq', '>= 2.6.5'
|
17
|
+
gem.add_dependency 'sidekiq', '>= 2.6.5', '< 3'
|
18
18
|
gem.add_development_dependency 'rspec'
|
19
|
+
gem.add_development_dependency 'rake'
|
19
20
|
end
|
@@ -40,7 +40,7 @@ describe Sidekiq::Queue do
|
|
40
40
|
|
41
41
|
it 'should be continuable' do
|
42
42
|
queue.pause
|
43
|
-
queue.
|
43
|
+
queue.unpause
|
44
44
|
queue.acquire.should be
|
45
45
|
end
|
46
46
|
|
@@ -69,6 +69,22 @@ describe Sidekiq::Queue do
|
|
69
69
|
queue.release
|
70
70
|
queue.busy.should == 0
|
71
71
|
end
|
72
|
+
|
73
|
+
it 'should tell if paused' do
|
74
|
+
queue.should_not be_paused
|
75
|
+
queue.pause
|
76
|
+
queue.should be_paused
|
77
|
+
queue.unpause
|
78
|
+
queue.should_not be_paused
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should tell if blocking' do
|
82
|
+
queue.should_not be_blocking
|
83
|
+
queue.block
|
84
|
+
queue.should be_blocking
|
85
|
+
queue.unblock
|
86
|
+
queue.should_not be_blocking
|
87
|
+
end
|
72
88
|
end
|
73
89
|
|
74
90
|
context 'global' do
|
@@ -22,7 +22,7 @@ describe Sidekiq::LimitFetch::Global::Monitor do
|
|
22
22
|
2.times { queue.acquire }
|
23
23
|
described_class.send(:invalidate_old_processors)
|
24
24
|
queue.busy.should == 2
|
25
|
-
sleep
|
25
|
+
sleep 4
|
26
26
|
described_class.send(:invalidate_old_processors)
|
27
27
|
queue.busy.should == 0
|
28
28
|
end
|
@@ -23,6 +23,18 @@ describe Sidekiq::LimitFetch::Queues do
|
|
23
23
|
Sidekiq::Queue['queue2'].busy.should == 1
|
24
24
|
end
|
25
25
|
|
26
|
+
it 'should acquire blocking queues' do
|
27
|
+
subject.acquire
|
28
|
+
Sidekiq::Queue['queue1'].busy.should == 1
|
29
|
+
Sidekiq::Queue['queue2'].busy.should == 1
|
30
|
+
|
31
|
+
Sidekiq::Queue['queue1'].block
|
32
|
+
|
33
|
+
subject.acquire
|
34
|
+
Sidekiq::Queue['queue1'].busy.should == 2
|
35
|
+
Sidekiq::Queue['queue2'].busy.should == 1
|
36
|
+
end
|
37
|
+
|
26
38
|
it 'should release queues' do
|
27
39
|
subject.acquire
|
28
40
|
subject.release_except nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-limit_fetch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.8'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -19,6 +19,9 @@ dependencies:
|
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 2.6.5
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '3'
|
22
25
|
type: :runtime
|
23
26
|
prerelease: false
|
24
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,6 +30,9 @@ dependencies:
|
|
27
30
|
- - ! '>='
|
28
31
|
- !ruby/object:Gem::Version
|
29
32
|
version: 2.6.5
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '3'
|
30
36
|
- !ruby/object:Gem::Dependency
|
31
37
|
name: rspec
|
32
38
|
requirement: !ruby/object:Gem::Requirement
|
@@ -43,6 +49,22 @@ dependencies:
|
|
43
49
|
- - ! '>='
|
44
50
|
- !ruby/object:Gem::Version
|
45
51
|
version: '0'
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rake
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
46
68
|
description: ! " Sidekiq strategy to restrict number of workers\n which are
|
47
69
|
able to run specified queues simultaneously.\n"
|
48
70
|
email: brainopia@evilmartians.com
|
@@ -51,6 +73,7 @@ extensions: []
|
|
51
73
|
extra_rdoc_files: []
|
52
74
|
files:
|
53
75
|
- .gitignore
|
76
|
+
- .travis.yml
|
54
77
|
- Gemfile
|
55
78
|
- LICENSE.txt
|
56
79
|
- README.md
|
@@ -84,12 +107,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
107
|
- - ! '>='
|
85
108
|
- !ruby/object:Gem::Version
|
86
109
|
version: '0'
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
hash: 4297700822520772168
|
87
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
114
|
none: false
|
89
115
|
requirements:
|
90
116
|
- - ! '>='
|
91
117
|
- !ruby/object:Gem::Version
|
92
118
|
version: '0'
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
hash: 4297700822520772168
|
93
122
|
requirements: []
|
94
123
|
rubyforge_project:
|
95
124
|
rubygems_version: 1.8.24
|