sidekiq-limit_fetch 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,9 +19,20 @@ Specify limits which you want to place on queues inside sidekiq.yml:
19
19
  queue_name2: 10
20
20
  ```
21
21
 
22
- In this example, tasks for the first restricted queue will be run by at most 5
23
- workers at the same time and the second queue will have no more than 10
22
+ Or set it dynamically in your code:
23
+ ```ruby
24
+ Sidekiq::Queue.new('queue_name1').limit = 5
25
+ Sidekiq::Queue['queue_name2'].limit = 10
26
+ ```
27
+
28
+ In these examples, tasks for the ```queue_name1``` will be run by at most 5
29
+ workers at the same time and the ```queue_name2``` will have no more than 10
24
30
  workers simultaneously.
25
31
 
32
+ Ability to set limits dynamically allows you to resize worker
33
+ distribution among queues any time you want.
34
+
35
+ Limits are applied strictly for current process.
36
+
26
37
  Sponsored by [Evil Martians].
27
38
  [Evil Martians]: http://evilmartians.com/
@@ -3,11 +3,16 @@ require 'sidekiq/fetch'
3
3
 
4
4
  class Sidekiq::LimitFetch
5
5
  require_relative 'limit_fetch/semaphore'
6
- require_relative 'limit_fetch/queue'
7
6
  require_relative 'limit_fetch/unit_of_work'
7
+ require_relative 'limit_fetch/singleton'
8
+ require_relative 'limit_fetch/queue'
8
9
 
9
10
  Sidekiq.options[:fetch] = self
10
11
 
12
+ def self.bulk_requeue(jobs)
13
+ Sidekiq::BasicFetch.bulk_requeue jobs
14
+ end
15
+
11
16
  def initialize(options)
12
17
  prepare_queues options
13
18
  options[:strict] ? define_strict_queues : define_weighted_queues
@@ -19,14 +24,18 @@ class Sidekiq::LimitFetch
19
24
 
20
25
  def retrieve_work
21
26
  queues = available_queues
27
+
28
+ if queues.empty?
29
+ sleep Sidekiq::Fetcher::TIMEOUT
30
+ return
31
+ end
32
+
22
33
  queue_name, message = Sidekiq.redis do |it|
23
34
  it.brpop *queues.map(&:full_name), Sidekiq::Fetcher::TIMEOUT
24
35
  end
25
36
 
26
37
  if message
27
- queue = queues.find {|it| it.full_name == queue_name }
28
- queues.delete queue
29
-
38
+ queue = queues.delete queues.find {|it| it.full_name == queue_name }
30
39
  UnitOfWork.new queue, message
31
40
  end
32
41
  ensure
@@ -36,11 +45,11 @@ class Sidekiq::LimitFetch
36
45
  private
37
46
 
38
47
  def prepare_queues(options)
39
- cache = {}
40
48
  limits = options[:limits] || {}
41
-
42
49
  @queues = options[:queues].map do |name|
43
- cache[name] ||= Queue.new name, limits[name]
50
+ Sidekiq::Queue.new(name).tap do |it|
51
+ it.limit = limits[name] if limits[name]
52
+ end
44
53
  end
45
54
  end
46
55
 
@@ -1,14 +1,14 @@
1
- class Sidekiq::LimitFetch
1
+ module Sidekiq
2
2
  class Queue
3
- extend Forwardable
3
+ extend LimitFetch::Singleton, Forwardable
4
+ def_delegators :lock, :limit, :limit=, :acquire, :release, :busy
4
5
 
5
- attr_reader :name, :full_name
6
- def_delegators :@lock, :acquire, :release
6
+ def full_name
7
+ @rname
8
+ end
7
9
 
8
- def initialize(name, limit)
9
- @name = name
10
- @full_name = "queue:#{name}"
11
- @lock = Semaphore.for limit
10
+ def lock
11
+ @lock ||= LimitFetch::Semaphore.new
12
12
  end
13
13
  end
14
14
  end
@@ -1,28 +1,26 @@
1
1
  class Sidekiq::LimitFetch::Semaphore
2
- Stub = Struct.new(:acquire, :release)
2
+ attr_reader :limit, :busy
3
3
 
4
- def self.for(limit)
5
- limit ? new(limit) : stub
4
+ def initialize
5
+ @lock = Mutex.new
6
+ @busy = 0
6
7
  end
7
8
 
8
- def self.stub
9
- @stub ||= Stub.new(true, true)
10
- end
11
-
12
- def initialize(limit)
13
- @lock = Mutex.new
14
- @limit = limit
9
+ def limit=(value)
10
+ @lock.synchronize do
11
+ @limit = value
12
+ end
15
13
  end
16
14
 
17
15
  def acquire
18
16
  @lock.synchronize do
19
- @limit -= 1 if @limit > 0
17
+ @busy += 1 if not @limit or @limit > @busy
20
18
  end
21
19
  end
22
20
 
23
21
  def release
24
22
  @lock.synchronize do
25
- @limit += 1
23
+ @busy -= 1
26
24
  end
27
25
  end
28
26
  end
@@ -0,0 +1,11 @@
1
+ module Sidekiq::LimitFetch::Singleton
2
+ def self.extended(klass)
3
+ klass.instance_variable_set :@instances, {}
4
+ end
5
+
6
+ def new(*args)
7
+ @instances[args] ||= super
8
+ end
9
+
10
+ alias [] new
11
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'sidekiq-limit_fetch'
3
- gem.version = '0.2'
3
+ gem.version = '0.3'
4
4
  gem.authors = 'brainopia'
5
5
  gem.email = 'brainopia@evilmartians.com'
6
6
  gem.summary = 'Sidekig strategy to support queue limits'
@@ -1,11 +1,11 @@
1
- require 'sidekiq/limit_fetch'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Sidekiq::LimitFetch do
4
4
  before :each do
5
5
  Sidekiq.redis do |it|
6
- it.del 'queue:example'
7
- it.rpush 'queue:example', 'task'
8
- it.expire 'queue:example', 30
6
+ it.del 'queue:example1'
7
+ it.rpush 'queue:example1', 'task'
8
+ it.expire 'queue:example1', 30
9
9
  end
10
10
  end
11
11
 
@@ -14,21 +14,21 @@ describe Sidekiq::LimitFetch do
14
14
  end
15
15
 
16
16
  def new_fetcher(options={})
17
- described_class.new options.merge queues: %w(example example example2 example2)
17
+ described_class.new options.merge queues: %w(example1 example1 example2 example2)
18
18
  end
19
19
 
20
20
  it 'should retrieve weighted queues' do
21
21
  fetcher = new_fetcher
22
- queues(fetcher).should =~ %w(queue:example queue:example2)
22
+ queues(fetcher).should =~ %w(queue:example1 queue:example2)
23
23
  end
24
24
 
25
25
  it 'should retrieve strictly ordered queues' do
26
26
  fetcher = new_fetcher strict: true
27
- queues(fetcher).should == %w(queue:example queue:example2)
27
+ queues(fetcher).should == %w(queue:example1 queue:example2)
28
28
  end
29
29
 
30
- it 'should retrieve limited queues' do
31
- fetcher = new_fetcher strict: true, limits: { 'example' => 2 }
30
+ it 'should retrieve only available queues' do
31
+ fetcher = new_fetcher strict: true, limits: { 'example1' => 2 }
32
32
  queues = -> { fetcher.available_queues }
33
33
 
34
34
  queues1 = queues.call
@@ -47,11 +47,11 @@ describe Sidekiq::LimitFetch do
47
47
  end
48
48
 
49
49
  it 'should acquire lock on queue for excecution' do
50
- fetcher = new_fetcher limits: { 'example' => 1, 'example2' => 1 }
50
+ fetcher = new_fetcher limits: { 'example1' => 1, 'example2' => 1 }
51
51
  work = fetcher.retrieve_work
52
52
  work.message.should == 'task'
53
- work.queue.should == 'queue:example'
54
- work.queue_name.should == 'example'
53
+ work.queue.should == 'queue:example1'
54
+ work.queue_name.should == 'example1'
55
55
 
56
56
  queues = fetcher.available_queues
57
57
  queues.should have(1).item
@@ -64,4 +64,18 @@ describe Sidekiq::LimitFetch do
64
64
 
65
65
  fetcher.available_queues.should have(2).items
66
66
  end
67
+
68
+ it 'should set queue limits on the fly' do
69
+ Sidekiq::Queue['example1'].limit = 1
70
+ Sidekiq::Queue['example2'].limit = 2
71
+
72
+ fetcher = new_fetcher
73
+
74
+ fetcher.available_queues.should have(2).item
75
+ fetcher.available_queues.should have(1).item
76
+ fetcher.available_queues.should have(0).item
77
+
78
+ Sidekiq::Queue['example1'].limit = 2
79
+ fetcher.available_queues.should have(1).item
80
+ end
67
81
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Queue do
4
+ context 'singleton' do
5
+ shared_examples :constructor do
6
+ it 'with default name' do
7
+ new_object = -> { described_class.send constructor }
8
+ new_object.call.should == new_object.call
9
+ end
10
+
11
+ it 'with given name' do
12
+ new_object = ->(name) { described_class.send constructor, name }
13
+ new_object.call('name').should == new_object.call('name')
14
+ end
15
+ end
16
+
17
+ context '.new' do
18
+ let(:constructor) { :new }
19
+ it_behaves_like :constructor
20
+ end
21
+
22
+ context '.[]' do
23
+ let(:constructor) { :[] }
24
+ it_behaves_like :constructor
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ require 'sidekiq/limit_fetch'
2
+
3
+ RSpec.configure do |config|
4
+ config.before :each do
5
+ Sidekiq::Queue.instance_variable_set :@instances, {}
6
+ end
7
+ 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.2'
4
+ version: '0.3'
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-14 00:00:00.000000000 Z
12
+ date: 2013-01-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -58,9 +58,12 @@ files:
58
58
  - lib/sidekiq/limit_fetch.rb
59
59
  - lib/sidekiq/limit_fetch/queue.rb
60
60
  - lib/sidekiq/limit_fetch/semaphore.rb
61
+ - lib/sidekiq/limit_fetch/singleton.rb
61
62
  - lib/sidekiq/limit_fetch/unit_of_work.rb
62
63
  - sidekiq-limit_fetch.gemspec
63
- - spec/integration_spec.rb
64
+ - spec/limit_fetch_spec.rb
65
+ - spec/sidekiq/limit_fetch/queue_spec.rb
66
+ - spec/spec_helper.rb
64
67
  homepage: https://github.com/brainopia/sidekiq-limit_fetch
65
68
  licenses: []
66
69
  post_install_message:
@@ -86,4 +89,6 @@ signing_key:
86
89
  specification_version: 3
87
90
  summary: Sidekig strategy to support queue limits
88
91
  test_files:
89
- - spec/integration_spec.rb
92
+ - spec/limit_fetch_spec.rb
93
+ - spec/sidekiq/limit_fetch/queue_spec.rb
94
+ - spec/spec_helper.rb