sidekiq-limit_fetch 3.4.0 → 4.3.2

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.
@@ -0,0 +1,53 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ sidekiq-limit_fetch (4.3.2)
5
+ redis (>= 4.6.0)
6
+ sidekiq (>= 4)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ appraisal (2.4.1)
12
+ bundler
13
+ rake
14
+ thor (>= 0.14.0)
15
+ connection_pool (2.2.5)
16
+ diff-lcs (1.5.0)
17
+ rack (2.2.3.1)
18
+ rake (13.0.6)
19
+ redis (4.6.0)
20
+ redis-namespace (1.8.2)
21
+ redis (>= 3.0.4)
22
+ rspec (3.11.0)
23
+ rspec-core (~> 3.11.0)
24
+ rspec-expectations (~> 3.11.0)
25
+ rspec-mocks (~> 3.11.0)
26
+ rspec-core (3.11.0)
27
+ rspec-support (~> 3.11.0)
28
+ rspec-expectations (3.11.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.11.0)
31
+ rspec-mocks (3.11.1)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.11.0)
34
+ rspec-support (3.11.0)
35
+ sidekiq (6.5.0)
36
+ connection_pool (>= 2.2.2)
37
+ rack (~> 2.0)
38
+ redis (>= 4.2.0)
39
+ thor (1.2.1)
40
+
41
+ PLATFORMS
42
+ x86_64-linux
43
+
44
+ DEPENDENCIES
45
+ appraisal
46
+ rake
47
+ redis-namespace (~> 1.5, >= 1.5.2)
48
+ rspec
49
+ sidekiq (~> 6.5.0)
50
+ sidekiq-limit_fetch!
51
+
52
+ BUNDLED WITH
53
+ 2.3.21
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", github: "mperham/sidekiq"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,58 @@
1
+ GIT
2
+ remote: https://github.com/mperham/sidekiq.git
3
+ revision: 277ea1888f25917e9dde8d00bdb866f86a55691f
4
+ specs:
5
+ sidekiq (6.5.0)
6
+ connection_pool (>= 2.2.2)
7
+ rack (~> 2.0)
8
+ redis (>= 4.2.0)
9
+
10
+ PATH
11
+ remote: ..
12
+ specs:
13
+ sidekiq-limit_fetch (4.3.2)
14
+ redis (>= 4.6.0)
15
+ sidekiq (>= 4)
16
+
17
+ GEM
18
+ remote: https://rubygems.org/
19
+ specs:
20
+ appraisal (2.4.1)
21
+ bundler
22
+ rake
23
+ thor (>= 0.14.0)
24
+ connection_pool (2.2.5)
25
+ diff-lcs (1.5.0)
26
+ rack (2.2.3.1)
27
+ rake (13.0.6)
28
+ redis (4.6.0)
29
+ redis-namespace (1.8.2)
30
+ redis (>= 3.0.4)
31
+ rspec (3.11.0)
32
+ rspec-core (~> 3.11.0)
33
+ rspec-expectations (~> 3.11.0)
34
+ rspec-mocks (~> 3.11.0)
35
+ rspec-core (3.11.0)
36
+ rspec-support (~> 3.11.0)
37
+ rspec-expectations (3.11.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.11.0)
40
+ rspec-mocks (3.11.1)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.11.0)
43
+ rspec-support (3.11.0)
44
+ thor (1.2.1)
45
+
46
+ PLATFORMS
47
+ x86_64-linux
48
+
49
+ DEPENDENCIES
50
+ appraisal
51
+ rake
52
+ redis-namespace (~> 1.5, >= 1.5.2)
53
+ rspec
54
+ sidekiq!
55
+ sidekiq-limit_fetch!
56
+
57
+ BUNDLED WITH
58
+ 2.3.21
@@ -6,7 +6,8 @@ class Sidekiq::Manager
6
6
  end
7
7
 
8
8
  def start
9
- Sidekiq::LimitFetch::Queues.start options
9
+ # In sidekiq 6.5.0 the variable @options has been renamed to @config
10
+ Sidekiq::LimitFetch::Queues.start @options || @config
10
11
  Sidekiq::LimitFetch::Global::Monitor.start!
11
12
  super
12
13
  end
@@ -14,7 +14,8 @@ module Sidekiq
14
14
  :probed, :busy,
15
15
  :increase_busy, :decrease_busy,
16
16
  :local_busy?, :explain,
17
- :remove_locks_except!
17
+ :remove_locks_except!,
18
+ :clear_limits
18
19
 
19
20
  def lock
20
21
  @lock ||= LimitFetch::Global::Semaphore.new name
@@ -11,7 +11,7 @@ module Sidekiq::LimitFetch::Global
11
11
  Thread.new do
12
12
  loop do
13
13
  Sidekiq::LimitFetch.redis_retryable do
14
- add_dynamic_queues
14
+ handle_dynamic_queues
15
15
  update_heartbeat ttl
16
16
  invalidate_old_processes
17
17
  end
@@ -33,23 +33,23 @@ module Sidekiq::LimitFetch::Global
33
33
 
34
34
  def remove_old_processes!
35
35
  Sidekiq.redis do |it|
36
- old_processes.each {|process| it.srem PROCESS_SET, process }
36
+ old_processes.each {|process| it.srem PROCESS_SET, [process] }
37
37
  end
38
38
  end
39
39
 
40
- def add_dynamic_queues
40
+ def handle_dynamic_queues
41
41
  queues = Sidekiq::LimitFetch::Queues
42
- queues.add Sidekiq::Queue.all.map(&:name) if queues.dynamic?
42
+ queues.handle Sidekiq::Queue.all.map(&:name) if queues.dynamic?
43
43
  end
44
44
 
45
45
  private
46
46
 
47
47
  def update_heartbeat(ttl)
48
48
  Sidekiq.redis do |it|
49
- it.multi do
50
- it.set heartbeat_key, true
51
- it.sadd PROCESS_SET, Selector.uuid
52
- it.expire heartbeat_key, ttl
49
+ it.multi do |pipeline|
50
+ pipeline.set heartbeat_key, true
51
+ pipeline.sadd PROCESS_SET, [Selector.uuid]
52
+ pipeline.expire heartbeat_key, ttl
53
53
  end
54
54
  end
55
55
  end
@@ -109,6 +109,14 @@ module Sidekiq::LimitFetch::Global
109
109
  redis {|it| it.get "#{PREFIX}:block:#@name" }
110
110
  end
111
111
 
112
+ def clear_limits
113
+ redis do |it|
114
+ %w(block busy limit pause probed process_limit).each do |key|
115
+ it.del "#{PREFIX}:#{key}:#@name"
116
+ end
117
+ end
118
+ end
119
+
112
120
  def increase_local_busy
113
121
  @lock.synchronize { @local_busy += 1 }
114
122
  end
@@ -16,4 +16,8 @@ module Sidekiq::LimitFetch::Instances
16
16
  def reset_instances!
17
17
  @instances = {}
18
18
  end
19
+
20
+ def delete_instance(name)
21
+ @instances.delete [name]
22
+ end
19
23
  end
@@ -5,6 +5,7 @@ module Sidekiq::LimitFetch::Queues
5
5
 
6
6
  def start(options)
7
7
  @queues = options[:queues]
8
+ @startup_queues = options[:queues].dup
8
9
  @dynamic = options[:dynamic]
9
10
 
10
11
  @limits = options[:limits] || {}
@@ -19,15 +20,17 @@ module Sidekiq::LimitFetch::Queues
19
20
  end
20
21
 
21
22
  def acquire
22
- selector.acquire(ordered_queues, namespace)
23
- .tap {|it| save it }
24
- .map {|it| "queue:#{it}" }
23
+ queues = saved
24
+ queues ||= Sidekiq::LimitFetch.redis_retryable do
25
+ selector.acquire(ordered_queues, namespace)
26
+ end
27
+ save queues
28
+ queues.map { |it| "queue:#{it}" }
25
29
  end
26
30
 
27
31
  def release_except(full_name)
28
32
  queues = restore
29
33
  queues.delete full_name[/queue:(.*)/, 1] if full_name
30
-
31
34
  Sidekiq::LimitFetch.redis_retryable do
32
35
  selector.release queues, namespace
33
36
  end
@@ -37,17 +40,40 @@ module Sidekiq::LimitFetch::Queues
37
40
  @dynamic
38
41
  end
39
42
 
43
+ def startup_queue?(queue)
44
+ @startup_queues.include?(queue)
45
+ end
46
+
40
47
  def add(queues)
48
+ return unless queues
41
49
  queues.each do |queue|
42
50
  unless @queues.include? queue
43
- apply_process_limit_to_queue(queue)
44
- apply_limit_to_queue(queue)
51
+ if startup_queue?(queue)
52
+ apply_process_limit_to_queue(queue)
53
+ apply_limit_to_queue(queue)
54
+ end
45
55
 
46
56
  @queues.push queue
47
57
  end
48
58
  end
49
59
  end
50
60
 
61
+ def remove(queues)
62
+ return unless queues
63
+ queues.each do |queue|
64
+ if @queues.include? queue
65
+ clear_limits_for_queue(queue)
66
+ @queues.delete queue
67
+ Sidekiq::Queue.delete_instance(queue)
68
+ end
69
+ end
70
+ end
71
+
72
+ def handle(queues)
73
+ add(queues - @queues)
74
+ remove(@queues - queues)
75
+ end
76
+
51
77
  def strict_order!
52
78
  @queues.uniq!
53
79
  def ordered_queues; @queues end
@@ -108,17 +134,26 @@ module Sidekiq::LimitFetch::Queues
108
134
  end
109
135
  end
110
136
 
137
+ def clear_limits_for_queue(queue_name)
138
+ queue = Sidekiq::Queue[queue_name]
139
+ queue.clear_limits
140
+ end
141
+
111
142
  def selector
112
143
  Sidekiq::LimitFetch::Global::Selector
113
144
  end
114
145
 
146
+ def saved
147
+ Thread.current[THREAD_KEY]
148
+ end
149
+
115
150
  def save(queues)
116
151
  Thread.current[THREAD_KEY] = queues
117
152
  end
118
153
 
119
154
  def restore
120
- Thread.current[THREAD_KEY] || []
155
+ saved || []
121
156
  ensure
122
- Thread.current[THREAD_KEY] = nil
157
+ save nil
123
158
  end
124
159
  end
@@ -1,7 +1,11 @@
1
1
  module Sidekiq
2
2
  class LimitFetch::UnitOfWork < BasicFetch::UnitOfWork
3
3
  def initialize(queue, job)
4
- super
4
+ if post_6_5?
5
+ super(queue, job, Sidekiq)
6
+ else
7
+ super
8
+ end
5
9
  redis_retryable { Queue[queue_name].increase_busy }
6
10
  end
7
11
 
@@ -17,6 +21,10 @@ module Sidekiq
17
21
 
18
22
  private
19
23
 
24
+ def post_6_5?
25
+ Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('6.5.0')
26
+ end
27
+
20
28
  def redis_retryable(&block)
21
29
  Sidekiq::LimitFetch.redis_retryable(&block)
22
30
  end
@@ -14,6 +14,8 @@ module Sidekiq::LimitFetch
14
14
  require_relative 'extensions/queue'
15
15
  require_relative 'extensions/manager'
16
16
 
17
+ TIMEOUT = Sidekiq::BasicFetch::TIMEOUT
18
+
17
19
  extend self
18
20
 
19
21
  def new(_)
@@ -26,27 +28,49 @@ module Sidekiq::LimitFetch
26
28
  UnitOfWork.new(queue, job) if job
27
29
  end
28
30
 
31
+ def config
32
+ # Post 6.5, Sidekiq.options is deprecated and replaced with passing Sidekiq directly
33
+ post_6_5? ? Sidekiq : Sidekiq.options
34
+ end
35
+
36
+ # Backwards compatibility for sidekiq v6.1.0
37
+ # @see https://github.com/mperham/sidekiq/pull/4602
29
38
  def bulk_requeue(*args)
30
- Sidekiq::BasicFetch.bulk_requeue(*args)
39
+ if Sidekiq::BasicFetch.respond_to?(:bulk_requeue) # < 6.1.0
40
+ Sidekiq::BasicFetch.bulk_requeue(*args)
41
+ else # 6.1.0+
42
+ Sidekiq::BasicFetch.new(config).bulk_requeue(*args)
43
+ end
31
44
  end
32
45
 
33
46
  def redis_retryable
34
47
  yield
35
48
  rescue Redis::BaseConnectionError
36
- sleep 1
49
+ sleep TIMEOUT
37
50
  retry
51
+ rescue Redis::CommandError => error
52
+ # If Redis was restarted and is still loading its snapshot,
53
+ # then we should treat this as a temporary connection error too.
54
+ if error.message =~ /^LOADING/
55
+ sleep TIMEOUT
56
+ retry
57
+ else
58
+ raise
59
+ end
38
60
  end
39
61
 
40
62
  private
41
63
 
42
- TIMEOUT = Sidekiq::BasicFetch::TIMEOUT
64
+ def post_6_5?
65
+ @post_6_5 ||= Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('6.5.0')
66
+ end
43
67
 
44
68
  def redis_brpop(queues)
45
69
  if queues.empty?
46
70
  sleep TIMEOUT # there are no queues to handle, so lets sleep
47
71
  [] # and return nothing
48
72
  else
49
- redis_retryable { Sidekiq.redis { |it| it.brpop *queues, TIMEOUT } }
73
+ redis_retryable { Sidekiq.redis { |it| it.brpop *queues, timeout: TIMEOUT } }
50
74
  end
51
75
  end
52
76
  end
@@ -1,22 +1,25 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'sidekiq-limit_fetch'
3
- gem.version = '3.4.0'
3
+ gem.version = '4.3.2'
4
4
  gem.license = 'MIT'
5
- gem.authors = 'brainopia'
6
- gem.email = 'brainopia@evilmartians.com'
5
+ gem.authors = ['Dean Perry', 'brainopia']
6
+ gem.email = 'dean@deanpcmad.com'
7
7
  gem.summary = 'Sidekiq strategy to support queue limits'
8
- gem.homepage = 'https://github.com/brainopia/sidekiq-limit_fetch'
9
- gem.description = <<-DESCRIPTION
10
- Sidekiq strategy to restrict number of workers
11
- which are able to run specified queues simultaneously.
12
- DESCRIPTION
8
+ gem.homepage = 'https://github.com/deanpcmad/sidekiq-limit_fetch'
9
+ gem.description = "Sidekiq strategy to restrict number of workers which are able to run specified queues simultaneously."
10
+
11
+ gem.metadata["homepage_uri"] = gem.homepage
12
+ gem.metadata["source_code_uri"] = "https://github.com/deanpcmad/sidekiq-limit_fetch"
13
+ gem.metadata["changelog_uri"] = "https://github.com/deanpcmad/sidekiq-limit_fetch/blob/master/CHANGELOG.md"
13
14
 
14
15
  gem.files = `git ls-files`.split($/)
15
16
  gem.test_files = gem.files.grep %r{^spec/}
16
17
  gem.require_paths = %w(lib)
17
18
 
18
19
  gem.add_dependency 'sidekiq', '>= 4'
20
+ gem.add_dependency 'redis', '>= 4.6.0'
19
21
  gem.add_development_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
22
+ gem.add_development_dependency 'appraisal'
20
23
  gem.add_development_dependency 'rspec'
21
24
  gem.add_development_dependency 'rake'
22
25
  end
@@ -15,61 +15,72 @@ RSpec.describe Sidekiq::LimitFetch::Queues do
15
15
 
16
16
  before { subject.start options }
17
17
 
18
+ def in_thread(&block)
19
+ thr = Thread.new(&block)
20
+ thr.join
21
+ end
22
+
18
23
  it 'should acquire queues' do
19
- subject.acquire
24
+ in_thread { subject.acquire }
20
25
  expect(Sidekiq::Queue['queue1'].probed).to eq 1
21
26
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
22
27
  end
23
28
 
24
29
  it 'should acquire dynamically blocking queues' do
25
- subject.acquire
30
+ in_thread { subject.acquire }
26
31
  expect(Sidekiq::Queue['queue1'].probed).to eq 1
27
32
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
28
33
 
29
34
  Sidekiq::Queue['queue1'].block
30
35
 
31
- subject.acquire
36
+ in_thread { subject.acquire }
32
37
  expect(Sidekiq::Queue['queue1'].probed).to eq 2
33
38
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
34
39
  end
35
40
 
36
41
  it 'should block except given queues' do
37
42
  Sidekiq::Queue['queue1'].block_except 'queue2'
38
- subject.acquire
43
+ in_thread { subject.acquire }
39
44
  expect(Sidekiq::Queue['queue1'].probed).to eq 1
40
45
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
41
46
 
42
47
  Sidekiq::Queue['queue1'].block_except 'queue404'
43
- subject.acquire
48
+ in_thread { subject.acquire }
44
49
  expect(Sidekiq::Queue['queue1'].probed).to eq 2
45
50
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
46
51
  end
47
52
 
48
53
  it 'should release queues' do
49
- subject.acquire
50
- subject.release_except nil
54
+ in_thread {
55
+ subject.acquire
56
+ subject.release_except nil
57
+ }
51
58
  expect(Sidekiq::Queue['queue1'].probed).to eq 0
52
59
  expect(Sidekiq::Queue['queue2'].probed).to eq 0
53
60
  end
54
61
 
55
62
  it 'should release queues except selected' do
56
- subject.acquire
57
- subject.release_except 'queue:queue1'
63
+ in_thread {
64
+ subject.acquire
65
+ subject.release_except 'queue:queue1'
66
+ }
58
67
  expect(Sidekiq::Queue['queue1'].probed).to eq 1
59
68
  expect(Sidekiq::Queue['queue2'].probed).to eq 0
60
69
  end
61
70
 
62
71
  it 'should release when no queues was acquired' do
63
72
  queues.each {|name| Sidekiq::Queue[name].pause }
64
- subject.acquire
65
- expect { subject.release_except nil }.not_to raise_exception
73
+ in_thread {
74
+ subject.acquire
75
+ expect { subject.release_except nil }.not_to raise_exception
76
+ }
66
77
  end
67
78
 
68
79
  context 'blocking' do
69
80
  let(:blocking) { %w(queue1) }
70
81
 
71
82
  it 'should acquire blocking queues' do
72
- 3.times { subject.acquire }
83
+ 3.times { in_thread { subject.acquire } }
73
84
  expect(Sidekiq::Queue['queue1'].probed).to eq 3
74
85
  expect(Sidekiq::Queue['queue2'].probed).to eq 1
75
86
  end
@@ -6,7 +6,7 @@ RSpec.describe Sidekiq::LimitFetch do
6
6
  let(:limits) {{ 'queue1' => 1, 'queue2' => 2 }}
7
7
 
8
8
  before do
9
- subject::Queues.start options
9
+ subject::Queues.start options
10
10
 
11
11
  Sidekiq.redis do |it|
12
12
  it.del 'queue:queue1'
data/spec/spec_helper.rb CHANGED
@@ -11,13 +11,13 @@ RSpec.configure do |config|
11
11
  Sidekiq::Queue.reset_instances!
12
12
  Sidekiq.redis do |it|
13
13
  clean_redis = ->(queue) do
14
- it.pipelined do
15
- it.del "limit_fetch:limit:#{queue}"
16
- it.del "limit_fetch:process_limit:#{queue}"
17
- it.del "limit_fetch:busy:#{queue}"
18
- it.del "limit_fetch:probed:#{queue}"
19
- it.del "limit_fetch:pause:#{queue}"
20
- it.del "limit_fetch:block:#{queue}"
14
+ it.pipelined do |pipeline|
15
+ pipeline.del "limit_fetch:limit:#{queue}"
16
+ pipeline.del "limit_fetch:process_limit:#{queue}"
17
+ pipeline.del "limit_fetch:busy:#{queue}"
18
+ pipeline.del "limit_fetch:probed:#{queue}"
19
+ pipeline.del "limit_fetch:pause:#{queue}"
20
+ pipeline.del "limit_fetch:block:#{queue}"
21
21
  end
22
22
  end
23
23