sidekiq-limit_fetch 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ services:
8
+ - redis-server
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: 2.0.0
12
+ - rvm: rbx-19mode
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
+ [![Build
7
+ Status](https://secure.travis-ci.org/brainopia/sidekiq-limit_fetch.png)](http://travis-ci.org/brainopia/sidekiq-limit_fetch)
8
+ [![Gem
9
+ Version](https://badge.fury.io/rb/sidekiq-limit_fetch.png)](http://badge.fury.io/rb/sidekiq-limit_fetch)
10
+ [![Dependency
11
+ Status](https://gemnasium.com/brainopia/sidekiq-limit_fetch.png)](https://gemnasium.com/brainopia/sidekiq-limit_fetch)
12
+ [![Code
13
+ Climate](https://codeclimate.com/badge.png)](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
- queue_name1: 5
19
- queue_name2: 10
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'].continue # allows workers to use the queue with the same limit
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
@@ -1 +1,5 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task default: :spec
@@ -5,7 +5,9 @@ module Sidekiq
5
5
  def_delegators :lock,
6
6
  :limit, :limit=,
7
7
  :acquire, :release,
8
- :pause, :continue,
8
+ :pause, :unpause,
9
+ :block, :unblock,
10
+ :paused?, :blocking?,
9
11
  :busy
10
12
 
11
13
  def lock
@@ -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
- local busy_key = namespace..'busy:'..queue
54
- local pause_key = namespace..'pause:'..queue
55
- local paused = redis.call('get', pause_key)
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
- if not paused then
58
- local limit_key = namespace..'limit:'..queue
59
- local queue_limit = tonumber(redis.call('get', limit_key))
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
- if queue_limit then
62
- local queue_locks = redis.call('llen', busy_key)
64
+ local block_key = namespace..'block:'..queue
65
+ local can_block = redis.call('get', block_key)
63
66
 
64
- if queue_limit > queue_locks then
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 = Sidekiq.redis {|it| it.get "#{PREFIX}:limit:#@name" }
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
- Sidekiq.redis {|it| it.set "#{PREFIX}:limit:#@name", value }
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
- Sidekiq.redis {|it| it.llen "#{PREFIX}:busy:#@name" }
30
+ redis {|it| it.llen "#{PREFIX}:busy:#@name" }
28
31
  end
29
32
 
30
33
  def pause
31
- Sidekiq.redis {|it| it.set "#{PREFIX}:pause:#@name", true }
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 continue
35
- Sidekiq.redis {|it| it.del "#{PREFIX}:pause:#@name" }
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
- queues(names).select(&:acquire).map(&:name)
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 continue
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'sidekiq-limit_fetch'
3
- gem.version = '0.7'
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.continue
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 2
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
@@ -40,7 +40,7 @@ describe 'semaphore' do
40
40
  it 'should unpause tasks' do
41
41
  subject.pause
42
42
  3.times { subject.acquire }
43
- subject.continue
43
+ subject.unpause
44
44
  2.times { subject.acquire }
45
45
  subject.busy.should == 2
46
46
  end
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.7'
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-24 00:00:00.000000000 Z
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