sidekiq-unique-jobs 4.0.0 → 4.0.7

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.

Potentially problematic release.


This version of sidekiq-unique-jobs might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +1 -1
  3. data/.travis.yml +1 -2
  4. data/Appraisals +1 -1
  5. data/CHANGELOG.md +44 -1
  6. data/Gemfile +1 -2
  7. data/README.md +49 -51
  8. data/circle.yml +3 -1
  9. data/gemfiles/sidekiq_2.17.gemfile +7 -5
  10. data/gemfiles/sidekiq_3.0.gemfile +6 -4
  11. data/gemfiles/sidekiq_3.1.gemfile +6 -4
  12. data/gemfiles/sidekiq_3.2.gemfile +6 -4
  13. data/gemfiles/sidekiq_3.3.gemfile +6 -4
  14. data/gemfiles/sidekiq_develop.gemfile +6 -4
  15. data/lib/sidekiq-unique-jobs.rb +3 -6
  16. data/lib/sidekiq_unique_jobs/config.rb +1 -6
  17. data/lib/sidekiq_unique_jobs/constants.rb +17 -0
  18. data/lib/sidekiq_unique_jobs/core_ext.rb +28 -23
  19. data/lib/sidekiq_unique_jobs/lock.rb +1 -1
  20. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +13 -0
  21. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +17 -3
  22. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +4 -0
  23. data/lib/sidekiq_unique_jobs/lock/until_timeout.rb +9 -2
  24. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +17 -5
  25. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +14 -5
  26. data/lib/sidekiq_unique_jobs/server/middleware.rb +3 -40
  27. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +29 -2
  28. data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +6 -2
  29. data/lib/sidekiq_unique_jobs/timeout_calculator.rb +42 -0
  30. data/lib/sidekiq_unique_jobs/unique_args.rb +31 -29
  31. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  32. data/redis/synchronize.lua +1 -1
  33. data/sidekiq-unique-jobs.gemspec +1 -2
  34. data/spec/{workers/unique_worker.rb → jobs/another_unique_job.rb} +3 -3
  35. data/spec/{workers/queue_worker.rb → jobs/custom_queue_job.rb} +1 -1
  36. data/spec/jobs/custom_queue_job_with_filter_method.rb +7 -0
  37. data/spec/{workers/queue_worker_with_filter_proc.rb → jobs/custom_queue_job_with_filter_proc.rb} +2 -3
  38. data/spec/jobs/expiring_job.rb +4 -0
  39. data/spec/{workers → jobs}/inline_worker.rb +1 -1
  40. data/spec/jobs/just_a_worker.rb +8 -0
  41. data/spec/jobs/main_job.rb +8 -0
  42. data/spec/{workers/my_worker.rb → jobs/my_job.rb} +2 -3
  43. data/spec/jobs/my_unique_job.rb +7 -0
  44. data/spec/{workers → jobs}/plain_class.rb +0 -0
  45. data/spec/{workers → jobs}/test_class.rb +0 -0
  46. data/spec/{workers → jobs}/unique_job_with_filter_method.rb +2 -2
  47. data/spec/{workers/unique_on_all_queues_worker.rb → jobs/unique_on_all_queues_job.rb} +3 -3
  48. data/spec/jobs/until_and_while_executing_job.rb +8 -0
  49. data/spec/{workers/another_unique_worker.rb → jobs/until_executed_job.rb} +6 -2
  50. data/spec/jobs/until_executing_job.rb +8 -0
  51. data/spec/jobs/until_global_timeout_job.rb +8 -0
  52. data/spec/{workers/inline_expiration_worker.rb → jobs/until_timeout_job.rb} +2 -2
  53. data/spec/{workers/after_unlock_worker.rb → jobs/while_executing_job.rb} +2 -3
  54. data/spec/lib/sidekiq_unique_jobs/client/middleware_spec.rb +60 -44
  55. data/spec/lib/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb +39 -0
  56. data/spec/lib/sidekiq_unique_jobs/lock/until_executed_spec.rb +40 -0
  57. data/spec/lib/sidekiq_unique_jobs/lock/while_executing_spec.rb +15 -4
  58. data/spec/lib/sidekiq_unique_jobs/options_with_fallback_spec.rb +25 -0
  59. data/spec/lib/sidekiq_unique_jobs/scripts_spec.rb +3 -4
  60. data/spec/lib/sidekiq_unique_jobs/server/middleware_spec.rb +51 -68
  61. data/spec/lib/sidekiq_unique_jobs/sidekiq_testing_enabled_spec.rb +75 -79
  62. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_ext_spec.rb +2 -2
  63. data/spec/lib/sidekiq_unique_jobs/{lock/time_calculator_spec.rb → timeout_calculator_spec.rb} +10 -11
  64. data/spec/lib/sidekiq_unique_jobs/unique_args_spec.rb +28 -45
  65. data/spec/spec_helper.rb +23 -15
  66. data/spec/support/unique_macros.rb +20 -1
  67. metadata +30 -42
  68. data/lib/sidekiq_unique_jobs/lock/time_calculator.rb +0 -44
  69. data/spec/workers/after_yield_worker.rb +0 -17
  70. data/spec/workers/before_yield_worker.rb +0 -9
  71. data/spec/workers/expiring_worker.rb +0 -4
  72. data/spec/workers/inline_unlock_order_worker.rb +0 -8
  73. data/spec/workers/just_a_worker.rb +0 -8
  74. data/spec/workers/main_job.rb +0 -8
  75. data/spec/workers/my_unique_worker.rb +0 -8
  76. data/spec/workers/queue_worker_with_filter_method.rb +0 -7
  77. data/spec/workers/run_lock_with_retries_worker.rb +0 -12
  78. data/spec/workers/run_lock_worker.rb +0 -7
  79. data/spec/workers/while_executing_worker.rb +0 -13
@@ -3,13 +3,15 @@
3
3
  source "http://rubygems.org"
4
4
 
5
5
  gem "appraisal", "~> 2.0.0"
6
+ gem "rspec-its", :require => false
6
7
  gem "sidekiq", "~> 3.3.0"
7
8
 
8
- platforms :mri do
9
- gem "pry-suite", :require => false
10
- gem "let_it_go", :require => false
11
- gem "memory-profiler", :require => false
9
+ platforms :mri_22 do
10
+ gem "pry", :require => false
11
+ gem "pry-rescue", :require => false
12
+ gem "pry-byebug", :require => false
12
13
  gem "simplecov-json", :require => false
14
+ gem "memory_profiler", :require => false
13
15
  gem "codeclimate-test-reporter", :require => false
14
16
  end
15
17
 
@@ -3,13 +3,15 @@
3
3
  source "http://rubygems.org"
4
4
 
5
5
  gem "appraisal", "~> 2.0.0"
6
+ gem "rspec-its", :require => false
6
7
  gem "sidekiq", :github => "mperham/sidekiq"
7
8
 
8
- platforms :mri do
9
- gem "pry-suite", :require => false
10
- gem "let_it_go", :require => false
11
- gem "memory-profiler", :require => false
9
+ platforms :mri_22 do
10
+ gem "pry", :require => false
11
+ gem "pry-rescue", :require => false
12
+ gem "pry-byebug", :require => false
12
13
  gem "simplecov-json", :require => false
14
+ gem "memory_profiler", :require => false
13
15
  gem "codeclimate-test-reporter", :require => false
14
16
  end
15
17
 
@@ -1,5 +1,7 @@
1
1
  require 'yaml' if RUBY_VERSION.include?('2.0.0') # rubocop:disable FileName
2
+ require 'sidekiq_unique_jobs/constants'
2
3
  require 'sidekiq_unique_jobs/core_ext'
4
+ require 'sidekiq_unique_jobs/timeout_calculator'
3
5
  require 'sidekiq_unique_jobs/options_with_fallback'
4
6
  require 'sidekiq_unique_jobs/scripts'
5
7
  require 'sidekiq_unique_jobs/unique_args'
@@ -18,17 +20,12 @@ module SidekiqUniqueJobs
18
20
  def config
19
21
  @config ||= Config.new(
20
22
  unique_prefix: 'uniquejobs',
21
- unique_args_enabled: true,
22
23
  default_expiration: 30 * 60,
23
24
  default_lock: :while_executing,
24
25
  redis_test_mode: :redis # :mock
25
26
  )
26
27
  end
27
28
 
28
- def unique_args_enabled?
29
- config.unique_args_enabled
30
- end
31
-
32
29
  def default_lock
33
30
  config.default_lock
34
31
  end
@@ -38,7 +35,7 @@ module SidekiqUniqueJobs
38
35
  yield config
39
36
  else
40
37
  options.each do |key, val|
41
- config[key] = val
38
+ config.send("#{key}=", val)
42
39
  end
43
40
  end
44
41
  end
@@ -1,6 +1,5 @@
1
1
  module SidekiqUniqueJobs
2
2
  class Config < OpenStruct
3
- TESTING_CONSTANT ||= 'Testing'.freeze
4
3
  CONFIG_ACCESSORS = [
5
4
  :unique_prefix,
6
5
  :default_expiration,
@@ -17,11 +16,7 @@ module SidekiqUniqueJobs
17
16
  end
18
17
 
19
18
  def testing_enabled?
20
- Sidekiq.const_defined?(TESTING_CONSTANT) && Sidekiq::Testing.enabled?
21
- end
22
-
23
- def unique_args_enabled?
24
- config.unique_args_enabled
19
+ Sidekiq.const_defined?(TESTING_CONSTANT, false) && Sidekiq::Testing.enabled?
25
20
  end
26
21
  end
27
22
  end
@@ -0,0 +1,17 @@
1
+ module SidekiqUniqueJobs
2
+ ARGS_KEY ||= 'args'.freeze
3
+ AT_KEY ||= 'at'.freeze
4
+ CLASS_KEY ||= 'class'.freeze
5
+ JID_KEY ||= 'jid'.freeze
6
+ LOG_DUPLICATE_KEY ||= 'log_duplicate_payload'.freeze
7
+ QUEUE_KEY ||= 'queue'.freeze
8
+ TESTING_CONSTANT ||= 'Testing'.freeze
9
+ UNIQUE_KEY ||= 'unique'.freeze
10
+ UNIQUE_LOCK_KEY ||= 'unique_lock'.freeze
11
+ UNIQUE_ARGS_KEY ||= 'unique_args'.freeze
12
+ UNIQUE_PREFIX_KEY ||= 'unique_prefix'.freeze
13
+ UNIQUE_DIGEST_KEY ||= 'unique_digest'.freeze
14
+ UNIQUE_ON_ALL_QUEUES_KEY ||= 'unique_on_all_queues'.freeze
15
+ UNIQUE_TIMEOUT_KEY ||= 'unique_expiration'.freeze
16
+ UNIQUE_ARGS_ENABLED_KEY ||= 'unique_args_enabled'.freeze
17
+ end
@@ -19,28 +19,33 @@ rescue LoadError
19
19
  end unless {}.respond_to?(:slice!)
20
20
  end
21
21
  end
22
- begin
23
- require 'active_support/core_ext/string/inflections'
24
- rescue LoadError
25
- class String
26
- # File activesupport/lib/active_support/inflector/methods.rb, line 178
27
- def classify
28
- camelize(singularize(sub(/.*\./, '')))
29
- end unless ''.respond_to?(:classify)
30
22
 
31
- # File activesupport/lib/active_support/inflector/methods.rb, line 67
32
- def camelize(uppercase_first_letter = true)
33
- string = self
34
- if uppercase_first_letter
35
- string = string.sub(/^[a-z\d]*/) { $&.capitalize }
36
- else
37
- string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
38
- end
39
- string.gsub!(%r{(?:_|(\/))([a-z\d]*)}i) do
40
- "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}"
41
- end
42
- string.gsub!(%r{/}, '::')
43
- string
44
- end unless ''.respond_to?(:camelize)
45
- end
23
+ class Array
24
+ def extract_options!
25
+ if last.is_a?(Hash) && last.instance_of?(Hash)
26
+ pop
27
+ else
28
+ {}
29
+ end
30
+ end unless [].respond_to?(:extract_options!)
31
+ end
32
+
33
+ class String
34
+ def classify
35
+ camelize(sub(/.*\./, ''))
36
+ end unless ''.respond_to?(:classify)
37
+
38
+ def camelize(uppercase_first_letter = true)
39
+ string = self
40
+ if uppercase_first_letter
41
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
42
+ else
43
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
44
+ end
45
+ string.gsub!(%r{(?:_|(\/))([a-z\d]*)}i) do
46
+ "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}"
47
+ end
48
+ string.gsub!(%r{/}, '::')
49
+ string
50
+ end unless ''.respond_to?(:camelize)
46
51
  end
@@ -1,8 +1,8 @@
1
- require 'sidekiq_unique_jobs/lock/time_calculator'
2
1
  require 'sidekiq_unique_jobs/lock/until_executed'
3
2
  require 'sidekiq_unique_jobs/lock/until_executing'
4
3
  require 'sidekiq_unique_jobs/lock/while_executing'
5
4
  require 'sidekiq_unique_jobs/lock/until_timeout'
5
+ require 'sidekiq_unique_jobs/lock/until_and_while_executing'
6
6
 
7
7
  module SidekiqUniqueJobs
8
8
  module Lock
@@ -0,0 +1,13 @@
1
+ module SidekiqUniqueJobs
2
+ module Lock
3
+ class UntilAndWhileExecuting < UntilExecuting
4
+ def execute(callback)
5
+ lock = WhileExecuting.new(item, redis_pool)
6
+ lock.synchronize do
7
+ callback.call if unlock(:server)
8
+ yield
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -11,11 +11,21 @@ module SidekiqUniqueJobs
11
11
  @redis_pool = redis_pool
12
12
  end
13
13
 
14
+ def execute(callback, &blk)
15
+ operative = true
16
+ send(:after_yield_yield, &blk)
17
+ rescue Sidekiq::Shutdown
18
+ operative = false
19
+ raise
20
+ ensure
21
+ callback.call if operative && unlock(:server)
22
+ end
23
+
14
24
  def unlock(scope)
15
25
  unless [:server, :api, :test].include?(scope)
16
26
  fail ArgumentError, "#{scope} middleware can't #{__method__} #{unique_key}"
17
27
  end
18
- SidekiqUniqueJobs::Unlockable.unlock(unique_key, item['jid'.freeze], redis_pool)
28
+ SidekiqUniqueJobs::Unlockable.unlock(unique_key, item[JID_KEY], redis_pool)
19
29
  end
20
30
 
21
31
  # rubocop:disable MethodLength
@@ -26,7 +36,7 @@ module SidekiqUniqueJobs
26
36
 
27
37
  result = Scripts.call(:aquire_lock, redis_pool,
28
38
  keys: [unique_key],
29
- argv: [item['jid'.freeze], max_lock_time])
39
+ argv: [item[JID_KEY], max_lock_time])
30
40
  case result
31
41
  when 1
32
42
  logger.debug { "successfully locked #{unique_key} for #{max_lock_time} seconds" }
@@ -45,7 +55,11 @@ module SidekiqUniqueJobs
45
55
  end
46
56
 
47
57
  def max_lock_time
48
- @max_lock_time ||= TimeCalculator.for_item(item).seconds
58
+ @max_lock_time ||= TimeoutCalculator.for_item(item).seconds
59
+ end
60
+
61
+ def after_yield_yield
62
+ yield
49
63
  end
50
64
 
51
65
  private
@@ -1,6 +1,10 @@
1
1
  module SidekiqUniqueJobs
2
2
  module Lock
3
3
  class UntilExecuting < UntilExecuted
4
+ def execute(callback, &_block)
5
+ callback.call if unlock(:server)
6
+ yield
7
+ end
4
8
  end
5
9
  end
6
10
  end
@@ -2,8 +2,15 @@ module SidekiqUniqueJobs
2
2
  module Lock
3
3
  class UntilTimeout < UntilExecuted
4
4
  def unlock(scope)
5
- return true if scope.to_sym == :server
6
- fail ArgumentError, "#{scope} middleware can't #{__method__} #{unique_key}"
5
+ if scope.to_sym == :server
6
+ return true
7
+ else
8
+ fail ArgumentError, "#{scope} middleware can't #{__method__} #{unique_key}"
9
+ end
10
+ end
11
+
12
+ def execute(_callback)
13
+ yield if block_given?
7
14
  end
8
15
  end
9
16
  end
@@ -6,25 +6,37 @@ module SidekiqUniqueJobs
6
6
  end
7
7
 
8
8
  def initialize(item, redis_pool = nil)
9
- @unique_digest = item['unique_digest'.freeze]
10
- @run_key = "#{@unique_digest}:run"
9
+ @item = item
11
10
  @mutex = Mutex.new
12
11
  @redis_pool = redis_pool
12
+ @unique_digest = "#{create_digest}:run"
13
+ yield self if block_given?
13
14
  end
14
15
 
15
- def synchronize(&_block)
16
+ def synchronize
16
17
  @mutex.lock
17
18
  sleep 0.001 until locked?
18
19
 
19
20
  yield
20
21
 
21
22
  ensure
22
- SidekiqUniqueJobs.connection(@redis_pool) { |c| c.del @run_key }
23
+ SidekiqUniqueJobs.connection(@redis_pool) { |c| c.del @unique_digest }
23
24
  @mutex.unlock
24
25
  end
25
26
 
26
27
  def locked?
27
- Scripts.call(:synchronize, @redis_pool, keys: [@run_key], argv: [Time.now.to_i]) == 1
28
+ Scripts.call(:synchronize, @redis_pool, keys: [@unique_digest], argv: [Time.now.to_i]) == 1
29
+ end
30
+
31
+ def execute(_callback)
32
+ synchronize do
33
+ yield
34
+ end
35
+ end
36
+
37
+ def create_digest
38
+ @unique_digest ||= @item[UNIQUE_DIGEST_KEY]
39
+ @unique_digest ||= SidekiqUniqueJobs::UniqueArgs.digest(@item)
28
40
  end
29
41
  end
30
42
  end
@@ -1,8 +1,9 @@
1
1
  module SidekiqUniqueJobs
2
2
  module OptionsWithFallback
3
- UNIQUE_KEY ||= 'unique'.freeze
4
- UNIQUE_LOCK_KEY ||= 'unique_lock'.freeze
5
- LOG_DUPLICATE_KEY ||= 'log_duplicate_payload'.freeze
3
+ def self.included(base)
4
+ base.class_attribute :lock_cache unless base.respond_to?(:lock_cache)
5
+ base.lock_cache ||= {}
6
+ end
6
7
 
7
8
  def unique_enabled?
8
9
  options[UNIQUE_KEY] || item[UNIQUE_KEY]
@@ -21,16 +22,24 @@ module SidekiqUniqueJobs
21
22
  end
22
23
 
23
24
  def lock_class
24
- "SidekiqUniqueJobs::Lock::#{unique_lock.to_s.classify}".constantize
25
+ lock_cache[unique_lock.to_sym] ||= "SidekiqUniqueJobs::Lock::#{unique_lock.to_s.classify}".constantize
25
26
  end
26
27
 
27
28
  def unique_lock
28
- options[UNIQUE_LOCK_KEY] || item[UNIQUE_LOCK_KEY] || SidekiqUniqueJobs.default_lock
29
+ if options.key?(UNIQUE_KEY) && options[UNIQUE_KEY] == true
30
+ warn('unique: true is no longer valid. Please set it to the type of lock required like: ' \
31
+ '`unique: :until_executed`')
32
+ options[UNIQUE_LOCK_KEY] || SidekiqUniqueJobs.default_lock
33
+ else
34
+ options[UNIQUE_KEY] || item[UNIQUE_KEY] || SidekiqUniqueJobs.default_lock
35
+ end
29
36
  end
30
37
 
31
38
  def options
32
39
  @options ||= worker_class.get_sidekiq_options if worker_class.respond_to?(:get_sidekiq_options)
40
+ @options ||= Sidekiq.default_worker_options
33
41
  @options ||= {}
42
+ @options &&= @options.stringify_keys
34
43
  end
35
44
  end
36
45
  end
@@ -1,4 +1,3 @@
1
- require 'digest'
2
1
  require 'forwardable'
3
2
 
4
3
  module SidekiqUniqueJobs
@@ -15,54 +14,18 @@ module SidekiqUniqueJobs
15
14
  @redis_pool = redis_pool
16
15
  @queue = queue
17
16
  @item = item
18
-
19
- send(unique_lock, &blk)
17
+ return yield unless unique_enabled?
18
+ lock.send(:execute, after_unlock_hook, &blk)
20
19
  end
21
20
 
22
21
  private
23
22
 
24
23
  attr_reader :redis_pool, :worker, :item, :worker_class
25
24
 
26
- def until_executing
27
- unlock
28
- yield
29
- end
30
-
31
- def until_executed(&block)
32
- operative = true
33
- after_yield_yield(&block)
34
- rescue Sidekiq::Shutdown
35
- operative = false
36
- raise
37
- ensure
38
- unlock if operative
39
- end
40
-
41
- def after_yield_yield
42
- yield
43
- end
44
-
45
- def while_executing
46
- lock.synchronize do
47
- yield
48
- end
49
- rescue SidekiqUniqueJobs::RunLockFailed
50
- return reschedule if reschedule_on_lock_fail
51
- raise
52
- end
53
-
54
- def until_timeout
55
- yield if block_given?
56
- end
57
-
58
25
  protected
59
26
 
60
- def unlock
61
- after_unlock_hook if lock.unlock(:server)
62
- end
63
-
64
27
  def after_unlock_hook
65
- worker.after_unlock if worker.respond_to?(:after_unlock)
28
+ -> { worker.after_unlock if worker.respond_to?(:after_unlock) }
66
29
  end
67
30
 
68
31
  def reschedule
@@ -37,7 +37,30 @@ module Sidekiq
37
37
  end
38
38
 
39
39
  class ScheduledSet
40
- module UniqueExtension
40
+ # rubocop:disable Style/ClassAndModuleCamelCase
41
+ module UniqueExtension3_0
42
+ def self.included(base)
43
+ base.class_eval do
44
+ include UnlockMethod
45
+ alias_method :delete_orig, :delete
46
+ alias_method :delete, :delete_ext
47
+ end
48
+ end
49
+
50
+ def delete_ext(score, jid = nil)
51
+ item = find_job(jid)
52
+ unlock(item) if delete_orig(score, jid)
53
+ end
54
+
55
+ def remove_job_ext
56
+ remove_job_orig do |message|
57
+ unlock(Sidekiq.load_json(message))
58
+ yield message
59
+ end
60
+ end
61
+ end
62
+
63
+ module UniqueExtension3_4
41
64
  def self.included(base)
42
65
  base.class_eval do
43
66
  include UnlockMethod
@@ -57,7 +80,11 @@ module Sidekiq
57
80
  end
58
81
  end
59
82
  end
60
- include UniqueExtension if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('3.1')
83
+ sidekiq_version = Gem::Version.new(Sidekiq::VERSION)
84
+ include UniqueExtension3_4 if sidekiq_version >= Gem::Version.new('3.4')
85
+ include UniqueExtension3_0 if sidekiq_version >= Gem::Version.new('3.0') &&
86
+ sidekiq_version < Gem::Version.new('3.4')
87
+ # rubocop:enable Style/ClassAndModuleCamelCase
61
88
  end
62
89
 
63
90
  class Job