sidekiq-queue-throttled 1.0.0 → 1.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f04395444525e11488352d423c291bbefa3d15cd0415cea21a6a248fe9ec11f1
4
- data.tar.gz: 97e5ee1c7501c66e598fa6638a30a2cb1ba39801dbef610456aa590e2300685c
3
+ metadata.gz: b658041cc8f4b7cb1cd5055c8371e3c672c4f74f9151faf5a67dda68f014d6ac
4
+ data.tar.gz: 36afa33c4f2250d70bf55c52318fbedbe2a10e6a629c557d676b81bd1fadf485
5
5
  SHA512:
6
- metadata.gz: c8e536136b78764fe9fa29ac5666839fdac9c81f53d6edf23a0725abb9459966f6a5e0b35c3b47099d665a569e28fc222a6bbc5e49f4b4597c8bce0cd7d51db8
7
- data.tar.gz: 8dfae89423e246c267efa76eda12237e10885917cb519aea643f6e2868d100dd1db518a6f9d5fe5b27bcb01f41c9bb9b4ca80277ae289a97426a4e6e6556ad28
6
+ metadata.gz: 2f6c4dc62bbd48be8646598d3e8aca5cb3baa2572bd80082c121fe140a7e13369694ef5420ecafd169fceb46344efec250d02a6398b8b7ac955b952faf33b710
7
+ data.tar.gz: deb6e902be8ea36f1b63a7bf630bcd8c28800360713349fa18a8f4ab07be142638d8437fb8e6e5ec5b7c0501aaab14be23f0a19b06b692fc53d87ba5e6fa995c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.3] - 2024-12-19
9
+
10
+ ### Changed
11
+ - Re-enabled MFA requirement for RubyGems deployment for enhanced security
12
+
13
+ ## [1.1.2] - 2024-12-19
14
+
15
+ ### Changed
16
+ - Disabled MFA requirement for RubyGems deployment to simplify release process
17
+
8
18
  ## [Unreleased]
9
19
 
10
20
  ### Added
data/README.md CHANGED
@@ -25,6 +25,18 @@ And then execute:
25
25
  $ bundle install
26
26
  ```
27
27
 
28
+ ### Rails Applications
29
+
30
+ For Rails applications, the gem will be automatically loaded when your application starts. No additional configuration is required.
31
+
32
+ ### Non-Rails Applications
33
+
34
+ For non-Rails applications, you need to explicitly require the gem:
35
+
36
+ ```ruby
37
+ require 'sidekiq/queue_throttled'
38
+ ```
39
+
28
40
  ## Configuration
29
41
 
30
42
  ### Queue Limits
@@ -203,7 +215,7 @@ Sidekiq::QueueThrottled.redis = Redis.new(url: ENV['REDIS_URL'])
203
215
  limiter = Sidekiq::QueueThrottled::QueueLimiter.new(queue_name, limit)
204
216
 
205
217
  # Acquire a lock (returns lock_id or false)
206
- lock_id = limiter.acquire_lock
218
+ lock_id = limiter.acquire_lock?
207
219
 
208
220
  # Release a lock
209
221
  limiter.release_lock(lock_id)
@@ -272,9 +284,9 @@ RSpec.describe "Queue Limiting" do
272
284
  it "respects queue limits" do
273
285
  limiter = Sidekiq::QueueThrottled::QueueLimiter.new("test_queue", 2)
274
286
 
275
- expect(limiter.acquire_lock).to be_truthy
276
- expect(limiter.acquire_lock).to be_truthy
277
- expect(limiter.acquire_lock).to be_falsey # Limit reached
287
+ expect(limiter.acquire_lock?).to be_truthy
288
+ expect(limiter.acquire_lock?).to be_truthy
289
+ expect(limiter.acquire_lock?).to be_falsey # Limit reached
278
290
  end
279
291
  end
280
292
  ```
@@ -286,6 +298,30 @@ end
286
298
  - **Network Latency**: Consider Redis network latency when setting TTL values
287
299
  - **Concurrent Access**: The gem uses thread-safe primitives for concurrent access
288
300
 
301
+ ## Troubleshooting
302
+
303
+ ### "uninitialized constant Sidekiq::QueueThrottled" Error
304
+
305
+ If you encounter this error when trying to use the gem:
306
+
307
+ ```
308
+ uninitialized constant Sidekiq::QueueThrottled (NameError)
309
+ ```
310
+
311
+ **For Rails applications:**
312
+ - Make sure the gem is properly added to your Gemfile
313
+ - Restart your Rails server after adding the gem
314
+ - The gem should auto-load when your Rails application starts
315
+
316
+ **For non-Rails applications:**
317
+ - Explicitly require the gem at the top of your file:
318
+ ```ruby
319
+ require 'sidekiq/queue_throttled'
320
+ ```
321
+
322
+ **For IRB/Console:**
323
+ - If you're testing in IRB or Rails console, make sure to restart the console after adding the gem to your Gemfile
324
+
289
325
  ## Contributing
290
326
 
291
327
  1. Fork the repository
@@ -5,12 +5,24 @@ module Sidekiq
5
5
  module Job
6
6
  def self.included(base)
7
7
  base.extend(ClassMethods)
8
+ setup_class_attributes(base)
9
+ end
10
+
11
+ def self.setup_class_attributes(base)
12
+ setup_attr_accessor(base)
13
+ setup_class_methods(base)
14
+ end
15
+
16
+ def self.setup_attr_accessor(base)
8
17
  base.class_eval do
9
- # Simple class attribute implementation
10
18
  class << self
11
19
  attr_accessor :sidekiq_throttle_config
12
20
  end
21
+ end
22
+ end
13
23
 
24
+ def self.setup_class_methods(base)
25
+ base.class_eval do
14
26
  def self.sidekiq_throttle_config
15
27
  @sidekiq_throttle_config
16
28
  end
@@ -31,50 +43,39 @@ module Sidekiq
31
43
 
32
44
  def validate_throttle_options!(options)
33
45
  return if options.empty?
46
+ raise ArgumentError, 'Cannot specify both concurrency and rate limits' if both_limits?(options)
34
47
 
35
- if options[:concurrency] && options[:rate]
36
- raise ArgumentError, 'Cannot specify both concurrency and rate limits'
37
- end
38
-
39
- if options[:concurrency]
40
- validate_concurrency_options!(options[:concurrency])
41
- end
48
+ validate_concurrency_options!(options[:concurrency]) if options[:concurrency]
49
+ validate_rate_options!(options[:rate]) if options[:rate]
50
+ end
42
51
 
43
- if options[:rate]
44
- validate_rate_options!(options[:rate])
45
- end
52
+ def both_limits?(options)
53
+ options[:concurrency] && options[:rate]
46
54
  end
47
55
 
48
56
  def validate_concurrency_options!(concurrency)
49
- unless concurrency.is_a?(Hash)
50
- raise ArgumentError, 'Concurrency must be a hash'
51
- end
57
+ raise ArgumentError, 'Concurrency must be a hash' unless concurrency.is_a?(Hash)
52
58
 
53
59
  unless concurrency[:limit].is_a?(Integer) && concurrency[:limit].positive?
54
- raise ArgumentError, 'Concurrency limit must be a positive integer'
55
- end
56
-
57
- unless concurrency[:key_suffix]
58
- raise ArgumentError, 'Concurrency key_suffix is required'
60
+ raise ArgumentError,
61
+ 'Concurrency limit must be a positive integer'
59
62
  end
63
+ raise ArgumentError, 'Concurrency key_suffix is required' unless concurrency[:key_suffix]
60
64
  end
61
65
 
62
66
  def validate_rate_options!(rate)
63
- unless rate.is_a?(Hash)
64
- raise ArgumentError, 'Rate must be a hash'
65
- end
66
-
67
- unless rate[:limit].is_a?(Integer) && rate[:limit].positive?
68
- raise ArgumentError, 'Rate limit must be a positive integer'
69
- end
67
+ raise ArgumentError, 'Rate must be a hash' unless rate.is_a?(Hash)
68
+ raise ArgumentError, 'Rate limit must be a positive integer' unless valid_rate_limit?(rate)
69
+ raise ArgumentError, 'Rate period must be a positive integer' unless valid_rate_period?(rate)
70
+ raise ArgumentError, 'Rate key_suffix is required' unless rate[:key_suffix]
71
+ end
70
72
 
71
- unless rate[:period].nil? || (rate[:period].is_a?(Integer) && rate[:period].positive?)
72
- raise ArgumentError, 'Rate period must be a positive integer'
73
- end
73
+ def valid_rate_limit?(rate)
74
+ rate[:limit].is_a?(Integer) && rate[:limit].positive?
75
+ end
74
76
 
75
- unless rate[:key_suffix]
76
- raise ArgumentError, 'Rate key_suffix is required'
77
- end
77
+ def valid_rate_period?(rate)
78
+ rate[:period].nil? || (rate[:period].is_a?(Integer) && rate[:period].positive?)
78
79
  end
79
80
  end
80
81
  end
@@ -2,7 +2,33 @@
2
2
 
3
3
  module Sidekiq
4
4
  module QueueThrottled
5
+ module RedisKeyManager
6
+ def concurrency_key(key_suffix)
7
+ "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:concurrency:#{@job_class}:#{key_suffix}"
8
+ end
9
+
10
+ def rate_key(key_suffix, period)
11
+ window = Time.now.to_i / period
12
+ "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:rate:#{@job_class}:#{key_suffix}:#{window}"
13
+ end
14
+
15
+ def resolve_key_suffix(key_suffix, args)
16
+ case key_suffix
17
+ when Proc
18
+ key_suffix.call(*args)
19
+ when Symbol
20
+ args.first.send(key_suffix) if args.first.respond_to?(key_suffix)
21
+ when String
22
+ key_suffix
23
+ else
24
+ 'default'
25
+ end.to_s
26
+ end
27
+ end
28
+
5
29
  class JobThrottler
30
+ include RedisKeyManager
31
+
6
32
  attr_reader :job_class, :throttle_config, :redis
7
33
 
8
34
  def initialize(job_class, throttle_config, redis = nil)
@@ -15,9 +41,7 @@ module Sidekiq
15
41
  def can_process?(args)
16
42
  return true unless @throttle_config
17
43
 
18
- @mutex.with_read_lock do
19
- check_concurrency_limit(args) && check_rate_limit(args)
20
- end
44
+ @mutex.with_read_lock { concurrency_allowed?(args) && rate_allowed?(args) }
21
45
  end
22
46
 
23
47
  def acquire_slot(args)
@@ -25,14 +49,10 @@ module Sidekiq
25
49
 
26
50
  @mutex.with_write_lock do
27
51
  return false unless can_process?(args)
52
+ return acquire_concurrency_slot?(args) if @throttle_config[:concurrency]
53
+ return acquire_rate_slot?(args) if @throttle_config[:rate]
28
54
 
29
- if @throttle_config[:concurrency]
30
- acquire_concurrency_slot(args)
31
- elsif @throttle_config[:rate]
32
- acquire_rate_slot(args)
33
- else
34
- true
35
- end
55
+ true
36
56
  end
37
57
  end
38
58
 
@@ -40,12 +60,8 @@ module Sidekiq
40
60
  return true unless @throttle_config
41
61
 
42
62
  @mutex.with_write_lock do
43
- if @throttle_config[:concurrency]
44
- release_concurrency_slot(args)
45
- end
46
- # Rate limiting slots are not released - they expire naturally
63
+ release_concurrency_slot?(args) if @throttle_config[:concurrency]
47
64
  end
48
-
49
65
  true
50
66
  rescue StandardError => e
51
67
  Sidekiq::QueueThrottled.logger.error "Failed to release slot for job #{@job_class}: #{e.message}"
@@ -54,102 +70,72 @@ module Sidekiq
54
70
 
55
71
  private
56
72
 
57
- def check_concurrency_limit(args)
73
+ def concurrency_allowed?(args)
58
74
  return true unless @throttle_config[:concurrency]
59
75
 
60
76
  config = @throttle_config[:concurrency]
61
77
  limit = config[:limit]
62
78
  key_suffix = resolve_key_suffix(config[:key_suffix], args)
63
- current_count = get_concurrency_count(key_suffix)
64
-
79
+ current_count = concurrency_count(key_suffix)
65
80
  current_count < limit
66
81
  end
67
82
 
68
- def check_rate_limit(args)
83
+ def rate_allowed?(args)
69
84
  return true unless @throttle_config[:rate]
70
85
 
71
86
  config = @throttle_config[:rate]
72
87
  limit = config[:limit]
73
88
  period = config[:period] || 60
74
89
  key_suffix = resolve_key_suffix(config[:key_suffix], args)
75
-
76
- current_count = get_rate_count(key_suffix, period)
90
+ current_count = rate_count(key_suffix, period)
77
91
  current_count < limit
78
92
  end
79
93
 
80
- def acquire_concurrency_slot(args)
94
+ def acquire_concurrency_slot?(args)
81
95
  config = @throttle_config[:concurrency]
82
96
  key_suffix = resolve_key_suffix(config[:key_suffix], args)
83
97
  key = concurrency_key(key_suffix)
84
-
85
98
  @redis.multi do |multi|
86
99
  multi.incr(key)
87
100
  multi.expire(key, Sidekiq::QueueThrottled.configuration.throttle_ttl)
88
101
  end
89
-
90
102
  true
91
103
  end
92
104
 
93
- def release_concurrency_slot(args)
105
+ def release_concurrency_slot?(args)
94
106
  config = @throttle_config[:concurrency]
95
107
  key_suffix = resolve_key_suffix(config[:key_suffix], args)
96
108
  key = concurrency_key(key_suffix)
97
-
98
109
  @redis.multi do |multi|
99
110
  multi.decr(key)
100
111
  multi.expire(key, Sidekiq::QueueThrottled.configuration.throttle_ttl)
101
112
  end
102
-
103
113
  true
104
114
  end
105
115
 
106
- def acquire_rate_slot(args)
116
+ def acquire_rate_slot?(args)
107
117
  config = @throttle_config[:rate]
108
118
  period = config[:period] || 60
109
119
  key_suffix = resolve_key_suffix(config[:key_suffix], args)
110
120
  key = rate_key(key_suffix, period)
111
-
112
121
  @redis.multi do |multi|
113
122
  multi.incr(key)
114
123
  multi.expire(key, period)
115
124
  end
116
-
117
125
  true
118
126
  end
119
127
 
120
- def get_concurrency_count(key_suffix)
128
+ def concurrency_count(key_suffix)
121
129
  key = concurrency_key(key_suffix)
122
130
  count = @redis.get(key)
123
131
  count ? count.to_i : 0
124
132
  end
125
133
 
126
- def get_rate_count(key_suffix, period)
134
+ def rate_count(key_suffix, period)
127
135
  key = rate_key(key_suffix, period)
128
136
  count = @redis.get(key)
129
137
  count ? count.to_i : 0
130
138
  end
131
-
132
- def concurrency_key(key_suffix)
133
- "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:concurrency:#{@job_class}:#{key_suffix}"
134
- end
135
-
136
- def rate_key(key_suffix, period)
137
- window = Time.now.to_i / period
138
- "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:rate:#{@job_class}:#{key_suffix}:#{window}"
139
- end
140
-
141
- def resolve_key_suffix(key_suffix, args)
142
- case key_suffix
143
- when Proc
144
- key_suffix.call(*args)
145
- when Symbol
146
- args.first.send(key_suffix) if args.first.respond_to?(key_suffix)
147
- when String
148
- key_suffix
149
- else
150
- 'default'
151
- end.to_s
152
- end
153
139
  end
154
140
  end
155
141
  end
@@ -8,84 +8,90 @@ module Sidekiq
8
8
  @job_throttlers = Concurrent::Map.new
9
9
  end
10
10
 
11
- def call(worker, job, queue)
11
+ def call(worker, job, queue, &block)
12
12
  queue_name = job['queue'] || queue
13
13
  job_class = worker.class.name
14
14
 
15
- # Check queue-level limits
15
+ return reschedule_and_return(job, queue_name, 'queue') unless queue_slot_available?(queue_name, job)
16
+ return reschedule_and_return(job, queue_name, 'job') unless job_slot_available?(job_class, job['args'], job)
17
+
18
+ process_job(job, queue_name, job_class, &block)
19
+ end
20
+
21
+ private
22
+
23
+ def queue_slot_available?(queue_name, job)
16
24
  queue_limiter = get_queue_limiter(queue_name)
17
- if queue_limiter
18
- lock_id = queue_limiter.acquire_lock
19
- unless lock_id
20
- Sidekiq::QueueThrottled.logger.info "Queue limit reached for #{queue_name}, rescheduling job"
21
- reschedule_job(job, queue_name)
22
- return nil
23
- end
24
- end
25
+ return true unless queue_limiter
25
26
 
26
- # Check job-level throttling
27
+ lock_id = queue_limiter.acquire_lock?
28
+ job['lock_id'] = lock_id if lock_id
29
+ !!lock_id
30
+ end
31
+
32
+ def job_slot_available?(job_class, args, job)
27
33
  job_throttler = get_job_throttler(job_class)
28
- if job_throttler && !job_throttler.acquire_slot(job['args'])
29
- Sidekiq::QueueThrottled.logger.info "Job throttling limit reached for #{job_class}, rescheduling job"
30
- reschedule_job(job, queue_name)
31
- return nil
32
- end
34
+ return true unless job_throttler
33
35
 
34
- # Process the job
36
+ acquired = job_throttler.acquire_slot(args)
37
+ job['job_throttle_acquired'] = acquired
38
+ acquired
39
+ end
40
+
41
+ def process_job(job, queue_name, job_class, &_block)
42
+ queue_limiter = get_queue_limiter(queue_name)
43
+ job_throttler = get_job_throttler(job_class)
44
+ lock_id = job['lock_id']
35
45
  begin
36
46
  yield
37
47
  ensure
38
- # Release locks
39
48
  queue_limiter&.release_lock(lock_id)
40
49
  job_throttler&.release_slot(job['args'])
41
50
  end
42
51
  end
43
52
 
44
- private
53
+ def reschedule_and_return(job, queue_name, type)
54
+ msg = type == 'queue' ? 'Queue limit reached' : 'Job throttling limit reached'
55
+ Sidekiq::QueueThrottled.logger.info "#{msg} for #{queue_name}, rescheduling job"
56
+ reschedule_job(job, queue_name)
57
+ nil
58
+ end
45
59
 
46
60
  def get_queue_limiter(queue_name)
47
61
  limit = Sidekiq::QueueThrottled.configuration.queue_limit(queue_name)
48
62
  return nil unless limit
49
63
 
50
- @queue_limiters.compute_if_absent(queue_name) do
51
- QueueLimiter.new(queue_name, limit)
52
- end
64
+ @queue_limiters.compute_if_absent(queue_name) { QueueLimiter.new(queue_name, limit) }
53
65
  end
54
66
 
55
67
  def get_job_throttler(job_class)
56
- throttle_config = get_throttle_config(job_class)
68
+ throttle_config = throttle_config_for(job_class)
57
69
  return nil unless throttle_config
58
70
 
59
- @job_throttlers.compute_if_absent(job_class) do
60
- JobThrottler.new(job_class, throttle_config)
61
- end
71
+ @job_throttlers.compute_if_absent(job_class) { JobThrottler.new(job_class, throttle_config) }
62
72
  end
63
73
 
64
- def get_throttle_config(job_class)
65
- # Handle string class names
74
+ def throttle_config_for(job_class)
66
75
  if job_class.is_a?(String)
67
- begin
68
- klass = Object.const_get(job_class)
69
- return klass.sidekiq_throttle_config if klass.respond_to?(:sidekiq_throttle_config)
70
- rescue NameError
71
- # For test classes that don't have proper constant names
72
- return nil
73
- end
76
+ klass = safe_const_get(job_class)
77
+ return klass.sidekiq_throttle_config if klass.respond_to?(:sidekiq_throttle_config)
74
78
  elsif job_class.respond_to?(:sidekiq_throttle_config)
75
- # Handle actual class objects
76
79
  return job_class.sidekiq_throttle_config
77
80
  end
78
81
  nil
79
82
  end
80
83
 
84
+ def safe_const_get(class_name)
85
+ Object.const_get(class_name)
86
+ rescue NameError
87
+ nil
88
+ end
89
+
81
90
  def reschedule_job(job, queue_name)
82
91
  delay = Sidekiq::QueueThrottled.configuration.retry_delay
83
92
  job['at'] = Time.now.to_f + delay
84
93
  job['queue'] = queue_name
85
-
86
- Sidekiq.redis do |conn|
87
- conn.zadd('schedule', job['at'], job.to_json)
88
- end
94
+ Sidekiq.redis { |conn| conn.zadd('schedule', job['at'], job.to_json) }
89
95
  end
90
96
  end
91
97
  end
@@ -14,42 +14,28 @@ module Sidekiq
14
14
  @mutex = Concurrent::ReentrantReadWriteLock.new
15
15
  end
16
16
 
17
- def acquire_lock(worker_id = nil)
17
+ def acquire_lock?(worker_id = nil)
18
18
  worker_id ||= SecureRandom.uuid
19
19
  lock_id = "#{worker_id}:#{Time.now.to_f}"
20
-
21
20
  @mutex.with_write_lock do
22
- current_count = get_current_count
23
- puts "DEBUG: QueueLimiter - current_count: #{current_count}, limit: #{@limit}"
24
- return false if current_count >= @limit
21
+ return false if limit_reached?
25
22
 
26
- # Increment the counter first
27
23
  increment_counter
28
- puts "DEBUG: QueueLimiter - acquired lock: #{lock_id}"
29
- return lock_id
24
+ lock_id
30
25
  end
31
-
32
- false
33
26
  end
34
27
 
35
28
  def release_lock(lock_id)
36
29
  return false unless lock_id
37
30
 
38
- @mutex.with_write_lock do
39
- # For time-based limiting, we don't immediately decrement the counter
40
- # The counter will expire naturally after the TTL period
41
- # This prevents immediate reuse of slots
42
- true
43
- end
31
+ @mutex.with_write_lock { true }
44
32
  rescue StandardError => e
45
33
  Sidekiq::QueueThrottled.logger.error "Failed to release lock #{lock_id} for queue #{@queue_name}: #{e.message}"
46
34
  false
47
35
  end
48
36
 
49
37
  def current_count
50
- @mutex.with_read_lock do
51
- get_current_count
52
- end
38
+ @mutex.with_read_lock { fetch_current_count }
53
39
  end
54
40
 
55
41
  def available_slots
@@ -59,7 +45,6 @@ module Sidekiq
59
45
  def reset!
60
46
  @mutex.with_write_lock do
61
47
  @redis.del(@counter_key)
62
- # Clear all locks for this queue
63
48
  pattern = "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:queue:#{@queue_name}:lock:*"
64
49
  keys = @redis.keys(pattern)
65
50
  @redis.del(*keys) unless keys.empty?
@@ -68,7 +53,11 @@ module Sidekiq
68
53
 
69
54
  private
70
55
 
71
- def get_current_count
56
+ def limit_reached?
57
+ fetch_current_count >= @limit
58
+ end
59
+
60
+ def fetch_current_count
72
61
  count = @redis.get(@counter_key)
73
62
  result = count ? count.to_i : 0
74
63
  puts "DEBUG: get_current_count - key: #{@counter_key}, count: #{result}"
@@ -81,7 +70,7 @@ module Sidekiq
81
70
  multi.incr(@counter_key)
82
71
  multi.expire(@counter_key, Sidekiq::QueueThrottled.configuration.throttle_ttl)
83
72
  end
84
- puts "DEBUG: increment_counter - after increment, count: #{get_current_count}"
73
+ puts "DEBUG: increment_counter - after increment, count: #{fetch_current_count}"
85
74
  end
86
75
 
87
76
  def decrement_counter
@@ -89,11 +78,8 @@ module Sidekiq
89
78
  multi.decr(@counter_key)
90
79
  multi.expire(@counter_key, Sidekiq::QueueThrottled.configuration.throttle_ttl)
91
80
  end
92
- # Ensure counter doesn't go below 0
93
- current = get_current_count
94
- if current.negative?
95
- @redis.set(@counter_key, 0)
96
- end
81
+ current = fetch_current_count
82
+ @redis.set(@counter_key, 0) if current.negative?
97
83
  end
98
84
 
99
85
  def acquire_redis_lock(lock_id)
@@ -101,7 +87,7 @@ module Sidekiq
101
87
  @redis.set(lock_key, '1', nx: true, ex: Sidekiq::QueueThrottled.configuration.lock_ttl)
102
88
  end
103
89
 
104
- def release_redis_lock(lock_id)
90
+ def release_redis_lock?(lock_id)
105
91
  lock_key = "#{@lock_key}:#{lock_id}"
106
92
  @redis.del(lock_key).positive?
107
93
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module Sidekiq
6
+ module QueueThrottled
7
+ class Railtie < Rails::Railtie
8
+ initializer 'sidekiq.queue_throttled' do
9
+ require 'sidekiq/queue_throttled'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module QueueThrottled
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.4'
6
6
  end
7
7
  end
@@ -13,6 +13,14 @@ require_relative 'queue_throttled/job_throttler'
13
13
  require_relative 'queue_throttled/middleware'
14
14
  require_relative 'queue_throttled/job'
15
15
 
16
+ # Auto-load Rails integration if Rails is available
17
+ begin
18
+ require 'rails'
19
+ require_relative 'queue_throttled/railtie'
20
+ rescue LoadError
21
+ # Rails is not available, which is fine for non-Rails applications
22
+ end
23
+
16
24
  module Sidekiq
17
25
  module QueueThrottled
18
26
  class << self
data/spec/examples.txt CHANGED
@@ -1,110 +1,115 @@
1
1
  example_id | status | run_time |
2
2
  ------------------------------------------------------------- | ------ | --------------- |
3
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:1] | passed | 0.0001 seconds |
4
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:2] | passed | 0.0001 seconds |
5
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:3] | passed | 0.00009 seconds |
3
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:1] | passed | 0.0019 seconds |
4
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:2] | passed | 0.00016 seconds |
5
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:1:3] | passed | 0.00017 seconds |
6
6
  ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:2:1] | passed | 0.0001 seconds |
7
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:2:2] | passed | 0.0001 seconds |
8
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:2:3] | passed | 0.0001 seconds |
9
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:1] | passed | 0.00019 seconds |
10
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:2] | passed | 0.0002 seconds |
11
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:3] | passed | 0.00354 seconds |
12
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:1] | passed | 0.00031 seconds |
13
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:2] | passed | 0.00437 seconds |
14
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:3] | passed | 0.01693 seconds |
15
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:1] | passed | 0.0001 seconds |
16
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:2] | passed | 0.0001 seconds |
17
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:3] | passed | 0.0001 seconds |
18
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:4] | passed | 0.00011 seconds |
19
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:1] | passed | 0.0001 seconds |
20
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:2] | passed | 0.00009 seconds |
21
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:3] | passed | 0.0001 seconds |
22
- ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:4] | passed | 0.00009 seconds |
23
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:1:1] | passed | 0.00014 seconds |
24
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:1:2] | passed | 0.00013 seconds |
25
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:1] | passed | 0.00015 seconds |
26
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:2] | passed | 0.00019 seconds |
27
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:3] | passed | 0.00012 seconds |
28
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:4] | passed | 0.00015 seconds |
29
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:5] | passed | 0.00017 seconds |
30
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:6] | passed | 0.00015 seconds |
31
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:7] | passed | 0.00015 seconds |
32
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:8] | passed | 0.00016 seconds |
33
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:9] | passed | 0.00016 seconds |
34
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:10] | passed | 0.00016 seconds |
35
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:11] | passed | 0.00015 seconds |
36
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:12] | passed | 0.00016 seconds |
37
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:13] | passed | 0.00016 seconds |
38
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:3:1] | passed | 0.00072 seconds |
39
- ./spec/sidekiq/queue_throttled/job_spec.rb[1:3:2] | passed | 0.00139 seconds |
40
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:1:1] | passed | 0.00024 seconds |
41
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:1:2] | passed | 0.00018 seconds |
42
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:1:1] | passed | 0.00011 seconds |
43
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:1] | passed | 0.00022 seconds |
44
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:2] | passed | 0.00065 seconds |
45
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:3] | passed | 0.00053 seconds |
46
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:1] | passed | 0.00015 seconds |
47
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:2] | passed | 0.00047 seconds |
48
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:3] | passed | 0.00072 seconds |
49
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:4:1] | passed | 0.0003 seconds |
50
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:1:1] | passed | 0.0001 seconds |
51
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:1] | passed | 0.00037 seconds |
52
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:2] | passed | 0.00066 seconds |
53
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:3] | passed | 0.00079 seconds |
54
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:3:1] | passed | 0.00022 seconds |
55
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:3:2] | passed | 0.00043 seconds |
56
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:1:1] | passed | 0.0045 seconds |
57
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:2:1] | passed | 0.00214 seconds |
58
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:2:2] | passed | 0.00612 seconds |
59
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:3:1] | passed | 0.00015 seconds |
60
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:1:1] | passed | 0.00066 seconds |
61
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:2:1] | passed | 0.00079 seconds |
7
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:2:2] | passed | 0.00017 seconds |
8
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:2:3] | passed | 0.00057 seconds |
9
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:1] | passed | 0.00016 seconds |
10
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:2] | passed | 0.0001 seconds |
11
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:3:3] | passed | 0.00098 seconds |
12
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:1] | passed | 0.00046 seconds |
13
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:2] | passed | 0.00045 seconds |
14
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:4:3] | passed | 0.0121 seconds |
15
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:1] | passed | 0.00082 seconds |
16
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:2] | passed | 0.00016 seconds |
17
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:3] | passed | 0.00021 seconds |
18
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:5:4] | passed | 0.00016 seconds |
19
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:1] | passed | 0.00014 seconds |
20
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:2] | passed | 0.00016 seconds |
21
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:3] | passed | 0.00013 seconds |
22
+ ./spec/sidekiq/queue_throttled/configuration_spec.rb[1:6:4] | passed | 0.0001 seconds |
23
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:1:1] | passed | 0.00056 seconds |
24
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:1:2] | passed | 0.0002 seconds |
25
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:1] | passed | 0.00014 seconds |
26
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:2] | passed | 0.00013 seconds |
27
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:3] | passed | 0.00013 seconds |
28
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:4] | passed | 0.00019 seconds |
29
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:5] | passed | 0.00012 seconds |
30
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:6] | passed | 0.00012 seconds |
31
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:7] | passed | 0.00011 seconds |
32
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:8] | passed | 0.00014 seconds |
33
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:9] | passed | 0.00011 seconds |
34
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:10] | passed | 0.00013 seconds |
35
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:11] | passed | 0.00011 seconds |
36
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:12] | passed | 0.00013 seconds |
37
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:2:13] | passed | 0.00013 seconds |
38
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:3:1] | passed | 0.00073 seconds |
39
+ ./spec/sidekiq/queue_throttled/job_spec.rb[1:3:2] | passed | 0.0015 seconds |
40
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:1:1] | passed | 0.00014 seconds |
41
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:1:2] | passed | 0.00016 seconds |
42
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:1:1] | passed | 0.00017 seconds |
43
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:1] | passed | 0.00024 seconds |
44
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:2] | passed | 0.0009 seconds |
45
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:2:3] | passed | 0.0007 seconds |
46
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:1] | passed | 0.00026 seconds |
47
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:2] | passed | 0.00076 seconds |
48
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:3:3] | passed | 0.00075 seconds |
49
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:2:4:1] | passed | 0.00061 seconds |
50
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:1:1] | passed | 0.00013 seconds |
51
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:1] | passed | 0.00046 seconds |
52
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:2] | passed | 0.00071 seconds |
53
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:2:3] | passed | 0.00078 seconds |
54
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:3:1] | passed | 0.00037 seconds |
55
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:3:3:2] | passed | 0.00068 seconds |
56
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:1:1] | passed | 0.00012 seconds |
57
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:2:1] | passed | 0.0006 seconds |
58
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:2:2] | passed | 0.0051 seconds |
59
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:4:3:1] | passed | 0.00011 seconds |
60
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:1:1] | passed | 0.0006 seconds |
61
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:2:1] | passed | 0.00056 seconds |
62
62
  ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:2:2] | passed | 0.00024 seconds |
63
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:3:1] | passed | 0.00054 seconds |
64
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:4:1] | passed | 0.00068 seconds |
65
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:1] | passed | 0.00155 seconds |
66
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:2] | passed | 0.00058 seconds |
67
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:3] | passed | 0.0007 seconds |
68
- ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:7:1] | passed | 0.0013 seconds |
69
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:1:1] | passed | 0.00033 seconds |
70
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:2:1] | passed | 0.00045 seconds |
71
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:2:2] | passed | 0.00296 seconds |
72
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:1] | passed | 0.00073 seconds |
73
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:2] | passed | 0.00098 seconds |
74
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:3] | passed | 0.00104 seconds |
75
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:4:1] | passed | 0.00105 seconds |
76
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:5:1] | passed | 0.00068 seconds |
77
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:5:2] | passed | 0.00223 seconds |
78
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:1] | passed | 0.00009 seconds |
79
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:2] | passed | 0.00013 seconds |
80
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:3] | passed | 0.0001 seconds |
81
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:1] | passed | 0.00015 seconds |
82
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:2] | passed | 0.00058 seconds |
83
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:3] | passed | 0.00023 seconds |
84
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:4:1] | passed | 0.0005 seconds |
85
- ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:4:2] | passed | 0.00098 seconds |
86
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:1] | passed | 0.00013 seconds |
87
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:2] | passed | 0.00014 seconds |
88
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:3] | passed | 0.00016 seconds |
89
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:1] | passed | 0.0004 seconds |
90
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:2] | passed | 0.0011 seconds |
91
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:3] | passed | 0.00088 seconds |
92
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:4] | passed | 0.00046 seconds |
93
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:5] | passed | 0.00058 seconds |
94
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:1] | passed | 0.00058 seconds |
95
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:2] | passed | 0.00014 seconds |
63
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:3:1] | passed | 0.00048 seconds |
64
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:5:4:1] | passed | 0.00051 seconds |
65
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:1] | passed | 0.00092 seconds |
66
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:2] | passed | 0.00057 seconds |
67
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:6:3] | passed | 0.00045 seconds |
68
+ ./spec/sidekiq/queue_throttled/job_throttler_spec.rb[1:7:1] | passed | 0.00121 seconds |
69
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:1:1] | passed | 0.00052 seconds |
70
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:2:1] | passed | 0.00054 seconds |
71
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:2:2] | passed | 0.0011 seconds |
72
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:1] | passed | 0.00114 seconds |
73
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:2] | passed | 0.0028 seconds |
74
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:3:3] | passed | 0.0016 seconds |
75
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:4:1] | passed | 0.0012 seconds |
76
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:5:1] | passed | 0.00116 seconds |
77
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:1:5:2] | passed | 0.00146 seconds |
78
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:1] | passed | 0.00013 seconds |
79
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:2] | passed | 0.00014 seconds |
80
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:2:3] | passed | 0.00013 seconds |
81
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:1] | passed | 0.00013 seconds |
82
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:2] | passed | 0.00018 seconds |
83
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:3:3] | passed | 0.00018 seconds |
84
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:4:1] | passed | 0.00052 seconds |
85
+ ./spec/sidekiq/queue_throttled/middleware_spec.rb[1:4:2] | passed | 0.00094 seconds |
86
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:1] | passed | 0.00012 seconds |
87
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:2] | passed | 0.0001 seconds |
88
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:1:3] | passed | 0.0002 seconds |
89
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:1] | passed | 0.00067 seconds |
90
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:2] | passed | 0.001 seconds |
91
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:3] | passed | 0.0013 seconds |
92
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:4] | passed | 0.00281 seconds |
93
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:2:5] | passed | 0.00075 seconds |
94
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:1] | passed | 0.00038 seconds |
95
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:2] | passed | 0.00011 seconds |
96
96
  ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:3] | passed | 0.00016 seconds |
97
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:4] | passed | 0.00038 seconds |
98
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:1] | passed | 0.0002 seconds |
99
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:2] | passed | 0.00098 seconds |
100
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:3] | passed | 0.00095 seconds |
101
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:1] | passed | 0.00021 seconds |
102
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:2] | passed | 0.00045 seconds |
103
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:3] | passed | 0.00112 seconds |
104
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:4] | passed | 0.00023 seconds |
105
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:6:1] | passed | 0.00081 seconds |
106
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:6:2] | passed | 0.00152 seconds |
107
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:7:1] | passed | 0.00151 seconds |
108
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:7:2] | passed | 0.00136 seconds |
109
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:8:1] | passed | 0.00066 seconds |
110
- ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:8:2] | passed | 0.00093 seconds |
97
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:3:4] | passed | 0.00041 seconds |
98
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:1] | passed | 0.00016 seconds |
99
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:2] | passed | 0.00058 seconds |
100
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:4:3] | passed | 0.00063 seconds |
101
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:1] | passed | 0.00023 seconds |
102
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:2] | passed | 0.00067 seconds |
103
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:3] | passed | 0.0013 seconds |
104
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:5:4] | passed | 0.00024 seconds |
105
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:6:1] | passed | 0.00069 seconds |
106
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:6:2] | passed | 0.00158 seconds |
107
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:7:1] | passed | 0.00158 seconds |
108
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:7:2] | passed | 0.00131 seconds |
109
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:8:1] | passed | 0.00045 seconds |
110
+ ./spec/sidekiq/queue_throttled/queue_limiter_spec.rb[1:8:2] | passed | 0.0008 seconds |
111
+ ./spec/sidekiq/queue_throttled/railtie_spec.rb[1:1:1] | failed | 0.00014 seconds |
112
+ ./spec/sidekiq/queue_throttled/railtie_spec.rb[1:1:2] | failed | 0.00011 seconds |
113
+ ./spec/sidekiq/queue_throttled/railtie_spec.rb[1:1:3] | failed | 0.00011 seconds |
114
+ ./spec/sidekiq/queue_throttled/railtie_spec.rb[1:2:1] | failed | 0.00128 seconds |
115
+ ./spec/sidekiq/queue_throttled/railtie_spec.rb[1:2:2] | failed | 0.00021 seconds |
@@ -25,17 +25,17 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
25
25
  end
26
26
  end
27
27
 
28
- describe '#acquire_lock' do
28
+ describe '#acquire_lock?' do
29
29
  it 'acquires lock when under limit' do
30
- lock_id = limiter.acquire_lock
30
+ lock_id = limiter.acquire_lock?
31
31
  expect(lock_id).to be_truthy
32
32
  expect(limiter.current_count).to eq(1)
33
33
  end
34
34
 
35
35
  it 'acquires multiple locks up to limit' do
36
- lock1 = limiter.acquire_lock
37
- lock2 = limiter.acquire_lock
38
- lock3 = limiter.acquire_lock
36
+ lock1 = limiter.acquire_lock?
37
+ lock2 = limiter.acquire_lock?
38
+ lock3 = limiter.acquire_lock?
39
39
 
40
40
  expect(lock1).to be_truthy
41
41
  expect(lock2).to be_truthy
@@ -44,31 +44,31 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
44
44
  end
45
45
 
46
46
  it 'fails to acquire lock when at limit' do
47
- limiter.acquire_lock
48
- limiter.acquire_lock
49
- limiter.acquire_lock
47
+ limiter.acquire_lock?
48
+ limiter.acquire_lock?
49
+ limiter.acquire_lock?
50
50
 
51
- lock4 = limiter.acquire_lock
51
+ lock4 = limiter.acquire_lock?
52
52
  expect(lock4).to be_falsey
53
53
  expect(limiter.current_count).to eq(3)
54
54
  end
55
55
 
56
56
  it 'uses provided worker_id' do
57
57
  worker_id = 'worker_123'
58
- lock_id = limiter.acquire_lock(worker_id)
58
+ lock_id = limiter.acquire_lock?(worker_id)
59
59
  expect(lock_id).to include(worker_id)
60
60
  end
61
61
 
62
62
  it 'generates unique lock_id for each call' do
63
- lock1 = limiter.acquire_lock
64
- lock2 = limiter.acquire_lock
63
+ lock1 = limiter.acquire_lock?
64
+ lock2 = limiter.acquire_lock?
65
65
  expect(lock1).not_to eq(lock2)
66
66
  end
67
67
  end
68
68
 
69
69
  describe '#release_lock' do
70
70
  it 'releases lock and decrements counter' do
71
- lock_id = limiter.acquire_lock
71
+ lock_id = limiter.acquire_lock?
72
72
  expect(limiter.current_count).to eq(1)
73
73
 
74
74
  result = limiter.release_lock(lock_id)
@@ -90,7 +90,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
90
90
  it 'handles redis errors gracefully' do
91
91
  allow(limiter.redis).to receive(:del).and_raise(Redis::BaseError.new('Connection error'))
92
92
 
93
- lock_id = limiter.acquire_lock
93
+ lock_id = limiter.acquire_lock?
94
94
  result = limiter.release_lock(lock_id)
95
95
  expect(result).to be_truthy
96
96
  end
@@ -102,14 +102,14 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
102
102
  end
103
103
 
104
104
  it 'returns correct count after acquiring locks' do
105
- limiter.acquire_lock
106
- limiter.acquire_lock
105
+ limiter.acquire_lock?
106
+ limiter.acquire_lock?
107
107
  expect(limiter.current_count).to eq(2)
108
108
  end
109
109
 
110
110
  it 'returns correct count after releasing locks' do
111
- lock1 = limiter.acquire_lock
112
- limiter.acquire_lock
111
+ lock1 = limiter.acquire_lock?
112
+ limiter.acquire_lock?
113
113
  limiter.release_lock(lock1)
114
114
  expect(limiter.current_count).to eq(2)
115
115
  end
@@ -121,14 +121,14 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
121
121
  end
122
122
 
123
123
  it 'returns correct available slots after acquiring locks' do
124
- limiter.acquire_lock
124
+ limiter.acquire_lock?
125
125
  expect(limiter.available_slots).to eq(2)
126
126
  end
127
127
 
128
128
  it 'returns 0 when at limit' do
129
- limiter.acquire_lock
130
- limiter.acquire_lock
131
- limiter.acquire_lock
129
+ limiter.acquire_lock?
130
+ limiter.acquire_lock?
131
+ limiter.acquire_lock?
132
132
  expect(limiter.available_slots).to eq(0)
133
133
  end
134
134
 
@@ -141,7 +141,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
141
141
 
142
142
  describe '#reset!' do
143
143
  it 'resets counter and lock' do
144
- limiter.acquire_lock
144
+ limiter.acquire_lock?
145
145
  expect(limiter.current_count).to eq(1)
146
146
 
147
147
  limiter.reset!
@@ -149,13 +149,13 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
149
149
  end
150
150
 
151
151
  it 'allows acquiring locks after reset' do
152
- limiter.acquire_lock
153
- limiter.acquire_lock
154
- limiter.acquire_lock
152
+ limiter.acquire_lock?
153
+ limiter.acquire_lock?
154
+ limiter.acquire_lock?
155
155
  expect(limiter.current_count).to eq(3)
156
156
 
157
157
  limiter.reset!
158
- lock_id = limiter.acquire_lock
158
+ lock_id = limiter.acquire_lock?
159
159
  expect(lock_id).to be_truthy
160
160
  expect(limiter.current_count).to eq(1)
161
161
  end
@@ -168,7 +168,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
168
168
 
169
169
  5.times do
170
170
  threads << Thread.new do
171
- lock_id = limiter.acquire_lock
171
+ lock_id = limiter.acquire_lock?
172
172
  lock_ids << lock_id if lock_id
173
173
  end
174
174
  end
@@ -183,7 +183,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
183
183
 
184
184
  it 'handles concurrent releases' do
185
185
  lock_ids = []
186
- 3.times { lock_ids << limiter.acquire_lock }
186
+ 3.times { lock_ids << limiter.acquire_lock? }
187
187
 
188
188
  threads = lock_ids.map do |lock_id|
189
189
  Thread.new do
@@ -199,7 +199,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
199
199
 
200
200
  describe 'redis key management' do
201
201
  it 'uses correct redis key prefix' do
202
- limiter.acquire_lock
202
+ limiter.acquire_lock?
203
203
 
204
204
  # Check that the counter key exists with correct prefix
205
205
  keys = limiter.redis.keys("sidekiq:queue_throttled:queue:#{queue_name}:*")
@@ -207,7 +207,7 @@ RSpec.describe Sidekiq::QueueThrottled::QueueLimiter do
207
207
  end
208
208
 
209
209
  it 'sets TTL on counter keys' do
210
- limiter.acquire_lock
210
+ limiter.acquire_lock?
211
211
 
212
212
  counter_key = "#{Sidekiq::QueueThrottled.configuration.redis_key_prefix}:queue:#{queue_name}:counter"
213
213
  ttl = limiter.redis.ttl(counter_key)
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Rails Integration' do
6
+ describe 'when Rails is available' do
7
+ before do
8
+ # Mock Rails to simulate Rails environment
9
+ stub_const('Rails', double('Rails'))
10
+ stub_const('Rails::Railtie', Class.new)
11
+
12
+ # Reload the main file to trigger Rails detection
13
+ load File.expand_path('../../lib/sidekiq/queue_throttled.rb', __dir__)
14
+ end
15
+
16
+ it 'defines the Sidekiq::QueueThrottled module' do
17
+ expect(defined?(Sidekiq::QueueThrottled)).to be_truthy
18
+ end
19
+
20
+ it 'defines the Sidekiq::QueueThrottled::Job module' do
21
+ expect(defined?(Sidekiq::QueueThrottled::Job)).to be_truthy
22
+ end
23
+
24
+ it 'defines the Railtie class' do
25
+ expect(defined?(Sidekiq::QueueThrottled::Railtie)).to be_truthy
26
+ end
27
+ end
28
+
29
+ describe 'when Rails is not available' do
30
+ before do
31
+ # Remove Rails constants to simulate non-Rails environment
32
+ Object.send(:remove_const, 'Rails') if defined?(Rails)
33
+
34
+ # Reload the main file to trigger Rails detection
35
+ load File.expand_path('../../lib/sidekiq/queue_throttled.rb', __dir__)
36
+ end
37
+
38
+ it 'still defines the Sidekiq::QueueThrottled module' do
39
+ expect(defined?(Sidekiq::QueueThrottled)).to be_truthy
40
+ end
41
+
42
+ it 'still defines the Sidekiq::QueueThrottled::Job module' do
43
+ expect(defined?(Sidekiq::QueueThrottled::Job)).to be_truthy
44
+ end
45
+ end
46
+ end
data/spec/spec_helper.rb CHANGED
@@ -6,10 +6,10 @@ require 'rspec'
6
6
  require 'timecop'
7
7
  require 'pry'
8
8
 
9
- # Set up real Redis with password for tests
10
- redis_url = ENV['REDIS_URL'] || 'redis://:123123@localhost:6379/0'
9
+ # Set up Redis for tests - use passwordless Redis for CI, passworded for local dev
10
+ redis_url = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
11
11
 
12
- # Configure Sidekiq to use Redis with password
12
+ # Configure Sidekiq to use Redis
13
13
  Sidekiq.configure_client { |c| c.redis = { url: redis_url } }
14
14
  Sidekiq.configure_server { |c| c.redis = { url: redis_url } }
15
15
  Sidekiq::QueueThrottled.redis = Redis.new(url: redis_url)
@@ -63,7 +63,7 @@ module TestHelpers
63
63
  klass
64
64
  end
65
65
 
66
- def wait_for_condition(timeout = 5, &condition)
66
+ def wait_for_condition?(timeout = 5, &condition)
67
67
  start_time = Time.now
68
68
  while Time.now - start_time < timeout
69
69
  return true if condition.call
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-queue-throttled
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Farid Mohammadi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-30 00:00:00.000000000 Z
11
+ date: 2025-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -70,6 +70,7 @@ files:
70
70
  - lib/sidekiq/queue_throttled/job_throttler.rb
71
71
  - lib/sidekiq/queue_throttled/middleware.rb
72
72
  - lib/sidekiq/queue_throttled/queue_limiter.rb
73
+ - lib/sidekiq/queue_throttled/railtie.rb
73
74
  - lib/sidekiq/queue_throttled/version.rb
74
75
  - spec/examples.txt
75
76
  - spec/sidekiq/queue_throttled/configuration_spec.rb
@@ -77,14 +78,15 @@ files:
77
78
  - spec/sidekiq/queue_throttled/job_throttler_spec.rb
78
79
  - spec/sidekiq/queue_throttled/middleware_spec.rb
79
80
  - spec/sidekiq/queue_throttled/queue_limiter_spec.rb
81
+ - spec/sidekiq/queue_throttled/railtie_spec.rb
80
82
  - spec/spec_helper.rb
81
- homepage: https://github.com/faridmohammadi/sidekiq-queue-throttled
83
+ homepage: https://github.com/zilnex/sidekiq-queue-throttled
82
84
  licenses:
83
85
  - MIT
84
86
  metadata:
85
- homepage_uri: https://github.com/faridmohammadi/sidekiq-queue-throttled
86
- source_code_uri: https://github.com/faridmohammadi/sidekiq-queue-throttled
87
- changelog_uri: https://github.com/faridmohammadi/sidekiq-queue-throttled/blob/main/CHANGELOG.md
87
+ homepage_uri: https://github.com/zilnex/sidekiq-queue-throttled
88
+ source_code_uri: https://github.com/zilnex/sidekiq-queue-throttled
89
+ changelog_uri: https://github.com/zilnex/sidekiq-queue-throttled/blob/main/CHANGELOG.md
88
90
  rubygems_mfa_required: 'true'
89
91
  post_install_message:
90
92
  rdoc_options: []