sidekiq-limit_fetch 0.2 → 0.3

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/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