sidekiq-limit_fetch 2.4.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1edc411f8e5b3ab852b0b005548a28fafd0167e4
4
- data.tar.gz: df6b938724aa978eab9757d1c969c9577c9e58c3
3
+ metadata.gz: 76501c38471c02fe380102202fbd2afb9799976e
4
+ data.tar.gz: 20d1ed4d5d36ca62b7dcff40e09842378ba1d3a1
5
5
  SHA512:
6
- metadata.gz: 4cb020c997e1dc814969e949ae497f49ee048f4d8b0b18833dd8eca51de0a1b943cae09d2e34e523e9d9c29b251101bf707cbab67d7a5ff270cda43be69ffbbd
7
- data.tar.gz: 29eb71b1e70bde0da6dee13798e2fae39c2900abca6cbcf25d22ab2466028525ef84ff4abb3c00b02b48524b4ffef09bb8a98f432761d9e5081bf3a656c7d24e
6
+ metadata.gz: b14c94fad7ef434afc4b16e58f3517a426bf1f95f31aac2b12b279e4f72e70ebb11746b58ca1873d582a32568c5d27cc6fe05daf28f5a3e0f299361f7ebdc015
7
+ data.tar.gz: 030fe7be744ff109d6ff17740e71576f9011f432255ce00ab5423dcd5486e75d2a76cf9e0c06825f5fbc962aab6fbe6a4d3a4e46931698006e3e9e39467ae3c7
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  Gemfile.lock
2
2
  pkg/
3
+ .bundle/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper --color
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
- gem 'sidekiq', github: 'mperham/sidekiq'
4
+ gem 'sidekiq', github: 'mperham/sidekiq', branch: 'internal_rewrite'
data/README.md CHANGED
@@ -18,7 +18,10 @@ Add this line to your application's Gemfile:
18
18
 
19
19
  redis 2.6 or newer
20
20
 
21
- **Important note:** At this moment, `sidekiq-limit_fetch` is incompatible with Sidekiq Pro's `reliable_fetch`.
21
+ **Important note:** At this moment, `sidekiq-limit_fetch` is incompatible with
22
+ - sidekiq pro's `reliable_fetch`
23
+ - `sidekiq-rate-limiter`
24
+ - any other plugin that rewrites fetch strategy of sidekiq.
22
25
 
23
26
  https://github.com/mperham/sidekiq/issues/1508 — a sidekiq admin busy page is unreliable, so if you need to check how many workers are running a queue use a `#busy` method described below.
24
27
 
@@ -146,6 +149,9 @@ To use this mode you need to specify a following line in sidekiq.yml:
146
149
 
147
150
  Dynamic queues will be ran at the lowest priority.
148
151
 
152
+ ### Maintenance
153
+
154
+ If you use ```flushdb```, restart the sidekiq process to re-populate the dynamic configuration.
149
155
 
150
156
  ### Thanks
151
157
 
@@ -0,0 +1,16 @@
1
+ class Sidekiq::Manager
2
+ module InitLimitFetch
3
+ def initialize(options={})
4
+ options[:fetch] = Sidekiq::LimitFetch
5
+ super
6
+ end
7
+
8
+ def start
9
+ Sidekiq::LimitFetch::Queues.start options
10
+ Global::Monitor.start!
11
+ super
12
+ end
13
+ end
14
+
15
+ prepend InitLimitFetch
16
+ end
@@ -1,6 +1,6 @@
1
1
  module Sidekiq
2
2
  class Queue
3
- extend LimitFetch::Singleton, Forwardable
3
+ extend LimitFetch::Instances, Forwardable
4
4
  attr_reader :rname
5
5
 
6
6
  def_delegators :lock,
@@ -1,6 +1,5 @@
1
1
  module Sidekiq::LimitFetch::Global
2
2
  module Monitor
3
- include Sidekiq::LimitFetch::Redis
4
3
  extend self
5
4
 
6
5
  HEARTBEAT_PREFIX = 'limit:heartbeat:'
@@ -8,10 +7,10 @@ module Sidekiq::LimitFetch::Global
8
7
  HEARTBEAT_TTL = 20
9
8
  REFRESH_TIMEOUT = 5
10
9
 
11
- def start!(queues, ttl=HEARTBEAT_TTL, timeout=REFRESH_TIMEOUT)
10
+ def start!(ttl=HEARTBEAT_TTL, timeout=REFRESH_TIMEOUT)
12
11
  Thread.new do
13
12
  loop do
14
- add_dynamic queues if queues.dynamic?
13
+ add_dynamic_queues
15
14
  update_heartbeat ttl
16
15
  invalidate_old_processes
17
16
  sleep timeout
@@ -20,23 +19,24 @@ module Sidekiq::LimitFetch::Global
20
19
  end
21
20
 
22
21
  def all_processes
23
- redis {|it| it.smembers PROCESS_SET }
22
+ Sidekiq.redis {|it| it.smembers PROCESS_SET }
24
23
  end
25
24
 
26
25
  def old_processes
27
26
  all_processes.reject do |process|
28
- redis {|it| it.get heartbeat_key process }
27
+ Sidekiq.redis {|it| it.get heartbeat_key process }
29
28
  end
30
29
  end
31
30
 
32
31
  def remove_old_processes!
33
- redis do |it|
32
+ Sidekiq.redis do |it|
34
33
  old_processes.each {|process| it.srem PROCESS_SET, process }
35
34
  end
36
35
  end
37
36
 
38
- def add_dynamic(queues)
39
- queues.add Sidekiq::Queue.all.map(&:name)
37
+ def add_dynamic_queues
38
+ queues = Sidekiq::LimitFetch::Queues
39
+ queues.add Sidekiq::Queue.all.map(&:name) if queues.dynamic?
40
40
  end
41
41
 
42
42
  private
@@ -1,7 +1,5 @@
1
1
  module Sidekiq::LimitFetch::Global
2
2
  class Semaphore
3
- include Sidekiq::LimitFetch::Redis
4
-
5
3
  PREFIX = 'limit_fetch'
6
4
 
7
5
  attr_reader :local_busy
@@ -45,7 +43,7 @@ module Sidekiq::LimitFetch::Global
45
43
  end
46
44
 
47
45
  def acquire
48
- Selector.acquire([@name], determine_namespace).size > 0
46
+ Selector.acquire([@name], namespace).size > 0
49
47
  end
50
48
 
51
49
  def release
@@ -163,5 +161,15 @@ module Sidekiq::LimitFetch::Global
163
161
  it.lrem "#{PREFIX}:busy:#@name", 0, process
164
162
  end
165
163
  end
164
+
165
+ private
166
+
167
+ def redis(&block)
168
+ Sidekiq.redis(&block)
169
+ end
170
+
171
+ def namespace
172
+ Sidekiq::LimitFetch::Queues.namespace
173
+ end
166
174
  end
167
175
  end
@@ -1,4 +1,4 @@
1
- module Sidekiq::LimitFetch::Singleton
1
+ module Sidekiq::LimitFetch::Instances
2
2
  def self.extended(klass)
3
3
  klass.instance_variable_set :@instances, {}
4
4
  end
@@ -1,88 +1,97 @@
1
- class Sidekiq::LimitFetch
2
- class Queues
3
- THREAD_KEY = :acquired_queues
1
+ module Sidekiq::LimitFetch::Queues
2
+ extend self
4
3
 
5
- def initialize(options)
6
- @queues = options[:queues]
7
- @namespace = options[:namespace]
8
- @dynamic = options[:dynamic]
4
+ THREAD_KEY = :acquired_queues
9
5
 
10
- options[:strict] ? strict_order! : weighted_order!
6
+ def start(options)
7
+ @queues = options[:queues]
8
+ @dynamic = options[:dynamic]
11
9
 
12
- set :process_limit, options[:process_limits]
13
- set :limit, options[:limits]
14
- set_blocks options[:blocking]
15
- end
10
+ options[:strict] ? strict_order! : weighted_order!
16
11
 
17
- def acquire
18
- selector.acquire(ordered_queues, @namespace)
19
- .tap {|it| save it }
20
- .map {|it| "queue:#{it}" }
21
- end
12
+ set :process_limit, options[:process_limits]
13
+ set :limit, options[:limits]
14
+ set_blocks options[:blocking]
15
+ end
22
16
 
23
- def release_except(full_name)
24
- queues = restore
25
- queues.delete full_name[/queue:(.*)/, 1] if full_name
26
- selector.release queues, @namespace
27
- end
17
+ def acquire
18
+ selector.acquire(ordered_queues, namespace)
19
+ .tap {|it| save it }
20
+ .map {|it| "queue:#{it}" }
21
+ end
28
22
 
29
- def dynamic?
30
- @dynamic
31
- end
23
+ def release_except(full_name)
24
+ queues = restore
25
+ queues.delete full_name[/queue:(.*)/, 1] if full_name
26
+ selector.release queues, namespace
27
+ end
32
28
 
33
- def add(queues)
34
- queues.each do |queue|
35
- @queues.push queue unless @queues.include? queue
36
- end
37
- end
29
+ def dynamic?
30
+ @dynamic
31
+ end
38
32
 
39
- def strict_order!
40
- @queues.uniq!
41
- def ordered_queues; @queues end
33
+ def add(queues)
34
+ queues.each do |queue|
35
+ @queues.push queue unless @queues.include? queue
42
36
  end
37
+ end
43
38
 
44
- def weighted_order!
45
- def ordered_queues; @queues.shuffle.uniq end
46
- end
39
+ def strict_order!
40
+ @queues.uniq!
41
+ def ordered_queues; @queues end
42
+ end
47
43
 
48
- private
44
+ def weighted_order!
45
+ def ordered_queues; @queues.shuffle.uniq end
46
+ end
49
47
 
50
- def selector
51
- Global::Selector
48
+ def namespace
49
+ @namespace ||= Sidekiq.redis do |it|
50
+ if it.respond_to?(:namespace) and it.namespace
51
+ it.namespace + ':'
52
+ else
53
+ ''
54
+ end
52
55
  end
56
+ end
53
57
 
54
- def set(limit_type, limits)
55
- limits ||= {}
56
- each_queue do |queue|
57
- limit = limits[queue.name.to_s] || limits[queue.name.to_sym]
58
- queue.send "#{limit_type}=", limit unless queue.limit_changed?
59
- end
58
+ private
59
+
60
+ def selector
61
+ Sidekiq::LimitFetch::Global::Selector
62
+ end
63
+
64
+ def set(limit_type, limits)
65
+ limits ||= {}
66
+ each_queue do |queue|
67
+ limit = limits[queue.name.to_s] || limits[queue.name.to_sym]
68
+ queue.send "#{limit_type}=", limit unless queue.limit_changed?
60
69
  end
70
+ end
61
71
 
62
- def set_blocks(blocks)
63
- each_queue(&:unblock)
72
+ def set_blocks(blocks)
73
+ each_queue(&:unblock)
64
74
 
65
- blocks.to_a.each do |it|
66
- if it.is_a? Array
67
- it.each {|name| Sidekiq::Queue[name].block_except it }
68
- else
69
- Sidekiq::Queue[it].block
70
- end
75
+ blocks.to_a.each do |it|
76
+ if it.is_a? Array
77
+ it.each {|name| Sidekiq::Queue[name].block_except it }
78
+ else
79
+ Sidekiq::Queue[it].block
71
80
  end
72
81
  end
82
+ end
73
83
 
74
- def save(queues)
75
- Thread.current[THREAD_KEY] = queues
76
- end
84
+ def save(queues)
85
+ Thread.current[THREAD_KEY] = queues
86
+ end
77
87
 
78
- def restore
79
- Thread.current[THREAD_KEY] || []
80
- ensure
81
- Thread.current[THREAD_KEY] = nil
82
- end
88
+ def restore
89
+ Thread.current[THREAD_KEY] || []
90
+ ensure
91
+ Thread.current[THREAD_KEY] = nil
92
+ end
83
93
 
84
- def each_queue
85
- @queues.uniq.each {|it| yield Sidekiq::Queue[it] }
86
- end
94
+ def each_queue
95
+ @queues.uniq.each {|it| yield Sidekiq::Queue[it] }
87
96
  end
88
97
  end
@@ -1,56 +1,39 @@
1
+ require 'forwardable'
1
2
  require 'sidekiq'
2
- require 'sidekiq/util'
3
+ require 'sidekiq/manager'
3
4
  require 'sidekiq/api'
4
- require 'forwardable'
5
5
 
6
- class Sidekiq::LimitFetch
6
+ module Sidekiq::LimitFetch
7
7
  autoload :UnitOfWork, 'sidekiq/limit_fetch/unit_of_work'
8
8
 
9
- require_relative 'limit_fetch/redis'
10
- require_relative 'limit_fetch/singleton'
9
+ require_relative 'limit_fetch/instances'
11
10
  require_relative 'limit_fetch/queues'
12
11
  require_relative 'limit_fetch/global/semaphore'
13
12
  require_relative 'limit_fetch/global/selector'
14
13
  require_relative 'limit_fetch/global/monitor'
15
14
  require_relative 'extensions/queue'
15
+ require_relative 'extensions/manager'
16
16
 
17
- include Redis
18
- Sidekiq.options[:fetch] = self
19
-
20
- def self.bulk_requeue(*args)
21
- Sidekiq::BasicFetch.bulk_requeue *args
22
- end
17
+ extend self
23
18
 
24
- def initialize(options)
25
- @queues = Queues.new options.merge(namespace: determine_namespace)
26
- Global::Monitor.start! @queues
19
+ def new(_)
20
+ self
27
21
  end
28
22
 
29
23
  def retrieve_work
30
- queue, message = fetch_message
24
+ queue, message = redis_brpop *Queues.acquire, Sidekiq::BasicFetch::TIMEOUT
25
+ Queues.release_except queue
31
26
  UnitOfWork.new queue, message if message
32
27
  end
33
28
 
34
- private
35
-
36
- def fetch_message
37
- queue, _ = redis_brpop *@queues.acquire, Sidekiq::Fetcher::TIMEOUT
38
- ensure
39
- @queues.release_except queue
29
+ def bulk_requeue(*args)
30
+ Sidekiq::BasicFetch.bulk_requeue(*args)
40
31
  end
41
32
 
33
+ private
34
+
42
35
  def redis_brpop(*args)
43
36
  return if args.size < 2
44
- query = -> redis { redis.brpop *args }
45
-
46
- if busy_local_queues.any? {|queue| not args.include? queue.rname }
47
- nonblocking_redis(&query)
48
- else
49
- redis(&query)
50
- end
51
- end
52
-
53
- def busy_local_queues
54
- Sidekiq::Queue.instances.select(&:local_busy?)
37
+ Sidekiq.redis {|it| it.brpop *args }
55
38
  end
56
39
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'sidekiq-limit_fetch'
3
- gem.version = '2.4.2'
3
+ gem.version = '3.0.0'
4
4
  gem.license = 'MIT'
5
5
  gem.authors = 'brainopia'
6
6
  gem.email = 'brainopia@evilmartians.com'
@@ -13,9 +13,9 @@ Gem::Specification.new do |gem|
13
13
 
14
14
  gem.files = `git ls-files`.split($/)
15
15
  gem.test_files = gem.files.grep %r{^spec/}
16
- gem.require_paths = %w(lib)
16
+ gem.require_paths = 'lib'
17
17
 
18
- gem.add_dependency 'sidekiq', '>= 2.6.5', '< 4.0'
19
- gem.add_development_dependency 'rspec', '~> 3.2.0'
18
+ gem.add_dependency 'sidekiq', '>= 4'
19
+ gem.add_development_dependency 'rspec'
20
20
  gem.add_development_dependency 'rake'
21
21
  end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Sidekiq::Queue do
4
2
  context 'singleton' do
5
3
  shared_examples :constructor do
@@ -1,21 +1,11 @@
1
- require 'spec_helper'
2
-
3
- Thread.abort_on_exception = true
4
-
5
1
  RSpec.describe Sidekiq::LimitFetch::Global::Monitor do
6
- let(:queues) { double dynamic?: false }
7
- let(:monitor) { described_class.start! queues, ttl, timeout }
2
+ let(:monitor) { described_class.start! ttl, timeout }
8
3
  let(:ttl) { 1 }
9
4
  let(:queue) { Sidekiq::Queue[name] }
10
5
  let(:name) { 'default' }
11
6
 
12
- before :each do
13
- monitor
14
- end
15
-
16
- after :each do
17
- monitor.kill
18
- end
7
+ before { monitor }
8
+ after { monitor.kill }
19
9
 
20
10
  context 'old locks' do
21
11
  let(:timeout) { 0.5 }
@@ -1,8 +1,4 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Sidekiq::LimitFetch::Queues do
4
- subject { described_class.new options }
5
-
6
2
  let(:queues) { %w[queue1 queue2] }
7
3
  let(:limits) {{ 'queue1' => 3 }}
8
4
  let(:strict) { true }
@@ -14,10 +10,11 @@ RSpec.describe Sidekiq::LimitFetch::Queues do
14
10
  limits: limits,
15
11
  strict: strict,
16
12
  blocking: blocking,
17
- process_limits: process_limits,
18
- namespace: Sidekiq::LimitFetch::Redis.determine_namespace }
13
+ process_limits: process_limits }
19
14
  end
20
15
 
16
+ before { subject.start options }
17
+
21
18
  it 'should acquire queues' do
22
19
  subject.acquire
23
20
  expect(Sidekiq::Queue['queue1'].probed).to eq 1
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe 'semaphore' do
4
2
  let(:name) { 'default' }
5
3
  subject { Sidekiq::LimitFetch::Global::Semaphore.new name }
@@ -1,7 +1,7 @@
1
- require 'spec_helper'
1
+ Thread.abort_on_exception = true
2
2
 
3
3
  RSpec.describe Sidekiq::LimitFetch do
4
- before :each do
4
+ before do
5
5
  Sidekiq.redis do |it|
6
6
  it.del 'queue:queue1'
7
7
  it.lpush 'queue:queue1', 'task1'
@@ -10,11 +10,12 @@ RSpec.describe Sidekiq::LimitFetch do
10
10
  end
11
11
  end
12
12
 
13
- subject { described_class.new options }
14
13
  let(:options) {{ queues: queues, limits: limits }}
15
14
  let(:queues) { %w(queue1 queue1 queue2 queue2) }
16
15
  let(:limits) {{ 'queue1' => 1, 'queue2' => 2 }}
17
16
 
17
+ before { subject::Queues.start options }
18
+
18
19
  it 'should acquire lock on queue for execution' do
19
20
  work = subject.retrieve_work
20
21
  expect(work.queue_name).to eq 'queue1'
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  require 'sidekiq/limit_fetch'
2
- require 'celluloid/autostart'
3
- require 'sidekiq/fetch'
4
2
 
5
3
  Sidekiq.logger = nil
6
4
  Sidekiq.redis = { namespace: ENV['namespace'] }
@@ -9,7 +7,7 @@ RSpec.configure do |config|
9
7
  config.order = :random
10
8
  config.disable_monkey_patching!
11
9
  config.raise_errors_for_deprecations!
12
- config.before :each do
10
+ config.before do
13
11
  Sidekiq::Queue.reset_instances!
14
12
  Sidekiq.redis do |it|
15
13
  clean_redis = ->(queue) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-limit_fetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - brainopia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-09 00:00:00.000000000 Z
11
+ date: 2015-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -16,34 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.6.5
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '4.0'
19
+ version: '4'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: 2.6.5
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '4.0'
26
+ version: '4'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rspec
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
31
+ - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: 3.2.0
33
+ version: '0'
40
34
  type: :development
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
- - - "~>"
38
+ - - ">="
45
39
  - !ruby/object:Gem::Version
46
- version: 3.2.0
40
+ version: '0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -67,6 +61,7 @@ extensions: []
67
61
  extra_rdoc_files: []
68
62
  files:
69
63
  - ".gitignore"
64
+ - ".rspec"
70
65
  - ".travis.yml"
71
66
  - Gemfile
72
67
  - LICENSE.txt
@@ -86,14 +81,14 @@ files:
86
81
  - demo/config/environment.rb
87
82
  - demo/config/environments/development.rb
88
83
  - lib/sidekiq-limit_fetch.rb
84
+ - lib/sidekiq/extensions/manager.rb
89
85
  - lib/sidekiq/extensions/queue.rb
90
86
  - lib/sidekiq/limit_fetch.rb
91
87
  - lib/sidekiq/limit_fetch/global/monitor.rb
92
88
  - lib/sidekiq/limit_fetch/global/selector.rb
93
89
  - lib/sidekiq/limit_fetch/global/semaphore.rb
90
+ - lib/sidekiq/limit_fetch/instances.rb
94
91
  - lib/sidekiq/limit_fetch/queues.rb
95
- - lib/sidekiq/limit_fetch/redis.rb
96
- - lib/sidekiq/limit_fetch/singleton.rb
97
92
  - lib/sidekiq/limit_fetch/unit_of_work.rb
98
93
  - sidekiq-limit_fetch.gemspec
99
94
  - spec/sidekiq/extensions/queue_spec.rb
@@ -1,35 +0,0 @@
1
- module Sidekiq::LimitFetch::Redis
2
- extend self
3
-
4
- def nonblocking_redis
5
- redis do |redis|
6
- # Celluloid 0.16 broke this method
7
- if Celluloid::VERSION.to_f >= 0.16
8
- yield redis
9
- else
10
- # prevent blocking of fetcher
11
- # more bullet-proof and faster (O_O)
12
- # than using Celluloid::IO
13
- #
14
- # https://github.com/brainopia/sidekiq-limit_fetch/issues/41
15
- # explanation of why Future#value is beneficial here
16
- begin
17
- Celluloid::Future.new { yield redis }.value
18
- rescue Celluloid::Task::TerminatedError
19
- end
20
- end
21
- end
22
- end
23
-
24
- def redis
25
- Sidekiq.redis {|it| yield it }
26
- end
27
-
28
- def determine_namespace
29
- redis do |it|
30
- if it.respond_to?(:namespace) and it.namespace
31
- it.namespace + ':'
32
- end
33
- end
34
- end
35
- end