sidekiq-unique-jobs 7.1.8 → 8.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +331 -2
  3. data/README.md +28 -25
  4. data/lib/sidekiq_unique_jobs/batch_delete.rb +7 -10
  5. data/lib/sidekiq_unique_jobs/changelog.rb +7 -17
  6. data/lib/sidekiq_unique_jobs/cli.rb +33 -8
  7. data/lib/sidekiq_unique_jobs/config.rb +5 -0
  8. data/lib/sidekiq_unique_jobs/connection.rb +4 -7
  9. data/lib/sidekiq_unique_jobs/constants.rb +1 -0
  10. data/lib/sidekiq_unique_jobs/core_ext.rb +1 -1
  11. data/lib/sidekiq_unique_jobs/digests.rb +7 -17
  12. data/lib/sidekiq_unique_jobs/exceptions.rb +3 -3
  13. data/lib/sidekiq_unique_jobs/expiring_digests.rb +14 -0
  14. data/lib/sidekiq_unique_jobs/job.rb +6 -1
  15. data/lib/sidekiq_unique_jobs/key.rb +13 -8
  16. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +6 -1
  17. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +4 -0
  18. data/lib/sidekiq_unique_jobs/lock/until_expired.rb +3 -1
  19. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +5 -3
  20. data/lib/sidekiq_unique_jobs/lock.rb +32 -12
  21. data/lib/sidekiq_unique_jobs/lock_args.rb +19 -15
  22. data/lib/sidekiq_unique_jobs/lock_config.rb +6 -6
  23. data/lib/sidekiq_unique_jobs/lock_digest.rb +7 -7
  24. data/lib/sidekiq_unique_jobs/lock_info.rb +2 -2
  25. data/lib/sidekiq_unique_jobs/lock_timeout.rb +4 -4
  26. data/lib/sidekiq_unique_jobs/lock_ttl.rb +4 -4
  27. data/lib/sidekiq_unique_jobs/lock_type.rb +37 -0
  28. data/lib/sidekiq_unique_jobs/locksmith.rb +36 -13
  29. data/lib/sidekiq_unique_jobs/logging.rb +14 -0
  30. data/lib/sidekiq_unique_jobs/lua/delete.lua +3 -6
  31. data/lib/sidekiq_unique_jobs/lua/delete_by_digest.lua +3 -6
  32. data/lib/sidekiq_unique_jobs/lua/delete_job_by_digest.lua +1 -1
  33. data/lib/sidekiq_unique_jobs/lua/find_digest_in_queues.lua +1 -1
  34. data/lib/sidekiq_unique_jobs/lua/lock.lua +16 -10
  35. data/lib/sidekiq_unique_jobs/lua/lock_until_expired.lua +92 -0
  36. data/lib/sidekiq_unique_jobs/lua/locked.lua +1 -1
  37. data/lib/sidekiq_unique_jobs/lua/queue.lua +1 -1
  38. data/lib/sidekiq_unique_jobs/lua/reap_orphans.lua +33 -8
  39. data/lib/sidekiq_unique_jobs/lua/shared/_common.lua +1 -6
  40. data/lib/sidekiq_unique_jobs/lua/shared/_delete_from_sorted_set.lua +1 -0
  41. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_process_set.lua +1 -1
  42. data/lib/sidekiq_unique_jobs/lua/unlock.lua +16 -15
  43. data/lib/sidekiq_unique_jobs/lua/update_version.lua +1 -1
  44. data/lib/sidekiq_unique_jobs/lua/upgrade.lua +1 -3
  45. data/lib/sidekiq_unique_jobs/middleware/client.rb +2 -0
  46. data/lib/sidekiq_unique_jobs/middleware/server.rb +2 -0
  47. data/lib/sidekiq_unique_jobs/middleware.rb +4 -4
  48. data/lib/sidekiq_unique_jobs/on_conflict/reject.rb +0 -43
  49. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +3 -3
  50. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +4 -4
  51. data/lib/sidekiq_unique_jobs/orphans/lua_reaper.rb +1 -1
  52. data/lib/sidekiq_unique_jobs/orphans/manager.rb +6 -13
  53. data/lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb +95 -16
  54. data/lib/sidekiq_unique_jobs/redis/sorted_set.rb +9 -2
  55. data/lib/sidekiq_unique_jobs/redis/string.rb +3 -1
  56. data/lib/sidekiq_unique_jobs/reflections.rb +1 -1
  57. data/lib/sidekiq_unique_jobs/script/caller.rb +14 -8
  58. data/lib/sidekiq_unique_jobs/server.rb +0 -1
  59. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +1 -1
  60. data/lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb +14 -4
  61. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +40 -21
  62. data/lib/sidekiq_unique_jobs/testing.rb +53 -21
  63. data/lib/sidekiq_unique_jobs/timer_task.rb +266 -45
  64. data/lib/sidekiq_unique_jobs/timing.rb +1 -1
  65. data/lib/sidekiq_unique_jobs/upgrade_locks.rb +11 -14
  66. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  67. data/lib/sidekiq_unique_jobs/web/helpers.rb +15 -3
  68. data/lib/sidekiq_unique_jobs/web/views/changelogs.erb +44 -38
  69. data/lib/sidekiq_unique_jobs/web/views/lock.erb +5 -3
  70. data/lib/sidekiq_unique_jobs/web/views/locks.erb +42 -37
  71. data/lib/sidekiq_unique_jobs/web.rb +26 -8
  72. data/lib/sidekiq_unique_jobs.rb +2 -0
  73. data/lib/tasks/changelog.rake +1 -1
  74. metadata +16 -43
@@ -20,9 +20,11 @@ module SidekiqUniqueJobs
20
20
  option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of digests to return"
21
21
  # :nodoc:
22
22
  def list(pattern = "*")
23
- entries = digests.entries(pattern: pattern, count: options[:count])
24
- say "Found #{entries.size} digests matching '#{pattern}':"
25
- print_in_columns(entries.sort) if entries.any?
23
+ max_count = options[:count]
24
+ say "Searching for regular digests"
25
+ list_entries(digests.entries(pattern: pattern, count: max_count), pattern)
26
+ say "Searching for expiring digests"
27
+ list_entries(expiring_digests.entries(pattern: pattern, count: max_count), pattern)
26
28
  end
27
29
 
28
30
  desc "del PATTERN", "deletes unique digests from redis by pattern"
@@ -32,11 +34,9 @@ module SidekiqUniqueJobs
32
34
  def del(pattern)
33
35
  max_count = options[:count]
34
36
  if options[:dry_run]
35
- result = digests.entries(pattern: pattern, count: max_count)
36
- say "Would delete #{result.size} digests matching '#{pattern}'"
37
+ count_entries_for_del(max_count, pattern)
37
38
  else
38
- deleted_count = digests.delete_by_pattern(pattern, count: max_count)
39
- say "Deleted #{deleted_count} digests matching '#{pattern}'"
39
+ del_entries(max_count, pattern)
40
40
  end
41
41
  end
42
42
 
@@ -51,12 +51,17 @@ module SidekiqUniqueJobs
51
51
  console_class.start
52
52
  end
53
53
 
54
- no_commands do
54
+ no_commands do # rubocop:disable Metrics/BlockLength
55
55
  # :nodoc:
56
56
  def digests
57
57
  @digests ||= SidekiqUniqueJobs::Digests.new
58
58
  end
59
59
 
60
+ # :nodoc:
61
+ def expiring_digests
62
+ @expiring_digests ||= SidekiqUniqueJobs::ExpiringDigests.new
63
+ end
64
+
60
65
  # :nodoc:
61
66
  def console_class
62
67
  require "pry"
@@ -65,6 +70,26 @@ module SidekiqUniqueJobs
65
70
  require "irb"
66
71
  IRB
67
72
  end
73
+
74
+ # :nodoc:
75
+ def list_entries(entries, pattern)
76
+ say "Found #{entries.size} digests matching '#{pattern}':"
77
+ print_in_columns(entries.sort) if entries.any?
78
+ end
79
+
80
+ # :nodoc:
81
+ def count_entries_for_del(max_count, pattern)
82
+ count = digests.entries(pattern: pattern, count: max_count).size +
83
+ expiring_digests.entries(pattern: pattern, count: max_count).size
84
+ say "Would delete #{count} digests matching '#{pattern}'"
85
+ end
86
+
87
+ # :nodoc:
88
+ def del_entries(max_count, pattern)
89
+ deleted_count = digests.delete_by_pattern(pattern, count: max_count).to_i +
90
+ expiring_digests.delete_by_pattern(pattern, count: max_count).to_i
91
+ say "Deleted #{deleted_count} digests matching '#{pattern}'"
92
+ end
68
93
  end
69
94
  end
70
95
  end
@@ -8,6 +8,7 @@ module SidekiqUniqueJobs
8
8
  :enabled,
9
9
  :lock_prefix,
10
10
  :logger,
11
+ :logger_enabled,
11
12
  :locks,
12
13
  :strategies,
13
14
  :debug_lua,
@@ -91,6 +92,9 @@ module SidekiqUniqueJobs
91
92
  # @return [nil]
92
93
  LOCK_TTL = nil
93
94
  #
95
+ # @return [true,false] by default false (don't disable logger)
96
+ LOGGER_ENABLED = true
97
+ #
94
98
  # @return [true] by default the gem is enabled
95
99
  ENABLED = true
96
100
  #
@@ -180,6 +184,7 @@ module SidekiqUniqueJobs
180
184
  ENABLED,
181
185
  PREFIX,
182
186
  Sidekiq.logger,
187
+ LOGGER_ENABLED,
183
188
  LOCKS,
184
189
  STRATEGIES,
185
190
  DEBUG_LUA,
@@ -10,13 +10,10 @@ module SidekiqUniqueJobs
10
10
  end
11
11
 
12
12
  # Creates a connection to redis
13
- # @return [Sidekiq::RedisConnection, ConnectionPool] a connection to redis
14
- def redis(r_pool = nil, &block)
15
- r_pool ||= defined?(redis_pool) ? redis_pool : r_pool
16
- if r_pool
17
- r_pool.with(&block)
18
- else
19
- Sidekiq.redis(&block)
13
+ # @return [Sidekiq::RedisConnection] a connection to redis
14
+ def redis(_r_pool = nil, &block)
15
+ Sidekiq.redis do |conn|
16
+ conn.with(&block)
20
17
  end
21
18
  end
22
19
  end
@@ -14,6 +14,7 @@ module SidekiqUniqueJobs
14
14
  CREATED_AT = "created_at"
15
15
  DEAD_VERSION = "uniquejobs:dead"
16
16
  DIGESTS = "uniquejobs:digests"
17
+ EXPIRING_DIGESTS = "uniquejobs:expiring_digests"
17
18
  ERRORS = "errors"
18
19
  JID = "jid"
19
20
  LIMIT = "limit"
@@ -98,7 +98,7 @@ class Hash
98
98
  def _deep_transform_keys_in_object(object, &block)
99
99
  case object
100
100
  when Hash
101
- object.each_with_object({}) do |(key, value), result|
101
+ object.each_with_object(self.class.new) do |(key, value), result|
102
102
  result[yield(key)] = _deep_transform_keys_in_object(value, &block)
103
103
  end
104
104
  when Array
@@ -7,15 +7,8 @@ module SidekiqUniqueJobs
7
7
  # @author Mikael Henriksson <mikael@mhenrixon.com>
8
8
  #
9
9
  class Digests < Redis::SortedSet
10
- #
11
- # @return [Integer] the number of matches to return by default
12
- DEFAULT_COUNT = 1_000
13
- #
14
- # @return [String] the default pattern to use for matching
15
- SCAN_PATTERN = "*"
16
-
17
- def initialize
18
- super(DIGESTS)
10
+ def initialize(digests_key = DIGESTS)
11
+ super(digests_key)
19
12
  end
20
13
 
21
14
  #
@@ -77,11 +70,7 @@ module SidekiqUniqueJobs
77
70
  # @return [Array<String>] an array of digests matching the given pattern
78
71
  #
79
72
  def entries(pattern: SCAN_PATTERN, count: DEFAULT_COUNT)
80
- options = {}
81
- options[:match] = pattern
82
- options[:count] = count
83
-
84
- redis { |conn| conn.zscan_each(key, **options).to_a }.to_h
73
+ redis { |conn| conn.zscan(key, match: pattern, count: count).to_a }.to_h
85
74
  end
86
75
 
87
76
  #
@@ -95,11 +84,12 @@ module SidekiqUniqueJobs
95
84
  #
96
85
  def page(cursor: 0, pattern: SCAN_PATTERN, page_size: 100)
97
86
  redis do |conn|
98
- total_size, digests = conn.multi do
99
- conn.zcard(key)
100
- conn.zscan(key, cursor, match: pattern, count: page_size)
87
+ total_size, digests = conn.multi do |pipeline|
88
+ pipeline.zcard(key)
89
+ pipeline.zscan(key, cursor, match: pattern, count: page_size)
101
90
  end
102
91
 
92
+ # NOTE: When debugging, check the last item in the returned array.
103
93
  [
104
94
  total_size.to_i,
105
95
  digests[0].to_i, # next_cursor
@@ -72,14 +72,14 @@ module SidekiqUniqueJobs
72
72
  class InvalidUniqueArguments < UniqueJobsError
73
73
  def initialize(options)
74
74
  given = options[:given]
75
- worker_class = options[:worker_class]
75
+ job_class = options[:job_class]
76
76
  lock_args_method = options[:lock_args_method]
77
- lock_args_meth = worker_class.method(lock_args_method)
77
+ lock_args_meth = job_class.method(lock_args_method)
78
78
  num_args = lock_args_meth.arity
79
79
  source_location = lock_args_meth.source_location
80
80
 
81
81
  super(
82
- "#{worker_class}##{lock_args_method} takes #{num_args} arguments, received #{given.inspect}" \
82
+ "#{job_class}##{lock_args_method} takes #{num_args} arguments, received #{given.inspect}" \
83
83
  "\n\n" \
84
84
  " #{source_location.join(':')}"
85
85
  )
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ #
5
+ # Class ExpiringDigests provides access to the expiring digests used by until_expired locks
6
+ #
7
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
8
+ #
9
+ class ExpiringDigests < Digests
10
+ def initialize
11
+ super(EXPIRING_DIGESTS)
12
+ end
13
+ end
14
+ end
@@ -7,10 +7,11 @@ module SidekiqUniqueJobs
7
7
  module Job
8
8
  extend self
9
9
 
10
- # Adds timeout, expiration, lock_args, lock_prefix and lock_digest to the sidekiq job hash
10
+ # Adds lock, timeout, expiration, lock_args, lock_prefix, and lock_digest to the sidekiq job hash
11
11
  # @return [Hash] the job hash
12
12
  def prepare(item)
13
13
  stringify_on_conflict_hash(item)
14
+ add_lock_type(item)
14
15
  add_lock_timeout(item)
15
16
  add_lock_ttl(item)
16
17
  add_digest(item)
@@ -54,5 +55,9 @@ module SidekiqUniqueJobs
54
55
  def add_lock_prefix(item)
55
56
  item[LOCK_PREFIX] ||= SidekiqUniqueJobs.config.lock_prefix
56
57
  end
58
+
59
+ def add_lock_type(item)
60
+ item[LOCK] ||= SidekiqUniqueJobs::LockType.call(item)
61
+ end
57
62
  end
58
63
  end
@@ -33,6 +33,10 @@ module SidekiqUniqueJobs
33
33
  # @!attribute [r] digests
34
34
  # @return [String] the zset with locked digests
35
35
  attr_reader :digests
36
+ #
37
+ # @!attribute [r] expiring_digests
38
+ # @return [String] the zset with locked expiring_digests
39
+ attr_reader :expiring_digests
36
40
 
37
41
  #
38
42
  # Initialize a new Key
@@ -40,13 +44,14 @@ module SidekiqUniqueJobs
40
44
  # @param [String] digest the digest to use as key
41
45
  #
42
46
  def initialize(digest)
43
- @digest = digest
44
- @queued = suffixed_key("QUEUED")
45
- @primed = suffixed_key("PRIMED")
46
- @locked = suffixed_key("LOCKED")
47
- @info = suffixed_key("INFO")
48
- @changelog = CHANGELOGS
49
- @digests = DIGESTS
47
+ @digest = digest
48
+ @queued = suffixed_key("QUEUED")
49
+ @primed = suffixed_key("PRIMED")
50
+ @locked = suffixed_key("LOCKED")
51
+ @info = suffixed_key("INFO")
52
+ @changelog = CHANGELOGS
53
+ @digests = DIGESTS
54
+ @expiring_digests = EXPIRING_DIGESTS
50
55
  end
51
56
 
52
57
  #
@@ -81,7 +86,7 @@ module SidekiqUniqueJobs
81
86
  # @return [Array] an ordered array with all keys
82
87
  #
83
88
  def to_a
84
- [digest, queued, primed, locked, info, changelog, digests]
89
+ [digest, queued, primed, locked, info, changelog, digests, expiring_digests]
85
90
  end
86
91
 
87
92
  private
@@ -33,10 +33,15 @@ module SidekiqUniqueJobs
33
33
  # Executes in the Sidekiq server process
34
34
  # @yield to the worker class perform method
35
35
  def execute
36
- locksmith.execute do
36
+ executed = locksmith.execute do
37
37
  yield
38
+ ensure
38
39
  unlock_and_callback
39
40
  end
41
+
42
+ reflect(:execution_failed, item) unless executed
43
+
44
+ nil
40
45
  end
41
46
  end
42
47
  end
@@ -33,6 +33,10 @@ module SidekiqUniqueJobs
33
33
  def execute
34
34
  callback_safely if locksmith.unlock
35
35
  yield
36
+ rescue StandardError => ex
37
+ reflect(:execution_failed, item, ex)
38
+ locksmith.lock(wait: 1)
39
+ raise
36
40
  end
37
41
  end
38
42
  end
@@ -33,7 +33,9 @@ module SidekiqUniqueJobs
33
33
  # Executes in the Sidekiq server process
34
34
  # @yield to the worker class perform method
35
35
  def execute(&block)
36
- locksmith.execute(&block)
36
+ executed = locksmith.execute(&block)
37
+
38
+ reflect(:execution_failed, item) unless executed
37
39
  end
38
40
  end
39
41
  end
@@ -42,12 +42,14 @@ module SidekiqUniqueJobs
42
42
  with_logging_context do
43
43
  executed = locksmith.execute do
44
44
  yield
45
- callback_safely if locksmith.unlock
46
45
  ensure
47
- locksmith.unlock
46
+ unlock_and_callback
48
47
  end
49
48
 
50
- call_strategy(origin: :server, &block) unless executed
49
+ unless executed
50
+ reflect(:execution_failed, item)
51
+ call_strategy(origin: :server, &block)
52
+ end
51
53
  end
52
54
  end
53
55
 
@@ -46,8 +46,11 @@ module SidekiqUniqueJobs
46
46
  # @param [Timstamp, Float] time nil optional timestamp to initiate this lock with
47
47
  #
48
48
  def initialize(key, time: nil)
49
- @key = get_key(key)
50
- @created_at = time.is_a?(Float) ? time : time.to_f
49
+ @key = get_key(key)
50
+ time = time.is_a?(Float) ? time : time.to_f
51
+ return unless time.nonzero?
52
+
53
+ @created_at = time
51
54
  end
52
55
 
53
56
  #
@@ -62,13 +65,13 @@ module SidekiqUniqueJobs
62
65
  #
63
66
  def lock(job_id, lock_info = {})
64
67
  redis do |conn|
65
- conn.multi do
66
- conn.set(key.digest, job_id)
67
- conn.hset(key.locked, job_id, now_f)
68
- info.set(lock_info)
69
- conn.zadd(key.digests, now_f, key.digest)
70
- conn.zadd(key.changelog, now_f, changelog_json(job_id, "queue.lua", "Queued"))
71
- conn.zadd(key.changelog, now_f, changelog_json(job_id, "lock.lua", "Locked"))
68
+ conn.multi do |pipeline|
69
+ pipeline.set(key.digest, job_id)
70
+ pipeline.hset(key.locked, job_id, now_f)
71
+ info.set(lock_info, pipeline)
72
+ add_digest_to_set(pipeline, lock_info)
73
+ pipeline.zadd(key.changelog, now_f, changelog_json(job_id, "queue.lua", "Queued"))
74
+ pipeline.zadd(key.changelog, now_f, changelog_json(job_id, "lock.lua", "Locked"))
72
75
  end
73
76
  end
74
77
  end
@@ -123,9 +126,9 @@ module SidekiqUniqueJobs
123
126
  #
124
127
  def del
125
128
  redis do |conn|
126
- conn.multi do
127
- conn.zrem(DIGESTS, key.digest)
128
- conn.del(key.digest, key.queued, key.primed, key.locked, key.info)
129
+ conn.multi do |pipeline|
130
+ pipeline.zrem(DIGESTS, key.digest)
131
+ pipeline.del(key.digest, key.queued, key.primed, key.locked, key.info)
129
132
  end
130
133
  end
131
134
  end
@@ -321,5 +324,22 @@ module SidekiqUniqueJobs
321
324
  time: now_f,
322
325
  )
323
326
  end
327
+
328
+ #
329
+ # Add the digest to the correct sorted set
330
+ #
331
+ # @param [Object] pipeline a redis pipeline object for issue commands
332
+ # @param [Hash] lock_info the lock info relevant to the digest
333
+ #
334
+ # @return [nil]
335
+ #
336
+ def add_digest_to_set(pipeline, lock_info)
337
+ digest_string = key.digest
338
+ if lock_info["lock"] == :until_expired
339
+ pipeline.zadd(key.expiring_digests, now_f + lock_info["ttl"], digest_string)
340
+ else
341
+ pipeline.zadd(key.digests, now_f, digest_string)
342
+ end
343
+ end
324
344
  end
325
345
  end
@@ -26,15 +26,15 @@ module SidekiqUniqueJobs
26
26
 
27
27
  # @param [Hash] item a Sidekiq job hash
28
28
  def initialize(item)
29
- @item = item
30
- @worker_class = item[CLASS]
31
- @args = item[ARGS]
29
+ @item = item
30
+ @args = item[ARGS]
31
+ self.job_class = item[CLASS]
32
32
  end
33
33
 
34
34
  # The unique arguments to use for creating a lock
35
35
  # @return [Array] the arguments filters by the {#filtered_args} method if {#lock_args_enabled?}
36
36
  def lock_args
37
- @lock_args ||= filtered_args
37
+ @lock_args ||= filtered_args || []
38
38
  end
39
39
 
40
40
  # Checks if the worker class has enabled lock_args
@@ -83,31 +83,31 @@ module SidekiqUniqueJobs
83
83
 
84
84
  # Filters unique arguments by method configured in the sidekiq worker
85
85
  # @param [Array] args the arguments passed to the sidekiq worker
86
- # @return [Array] unfiltered unless {#worker_method_defined?}
86
+ # @return [Array] unfiltered unless {#job_method_defined?}
87
87
  # @return [Array] with the filtered arguments
88
88
  def filter_by_symbol(args)
89
- return args unless worker_method_defined?(lock_args_method)
89
+ return args unless job_method_defined?(lock_args_method)
90
90
 
91
- worker_class.send(lock_args_method, args)
91
+ job_class.send(lock_args_method, args)
92
92
  rescue ArgumentError
93
93
  raise SidekiqUniqueJobs::InvalidUniqueArguments,
94
94
  given: args,
95
- worker_class: worker_class,
95
+ job_class: job_class,
96
96
  lock_args_method: lock_args_method
97
97
  end
98
98
 
99
99
  # The method to use for filtering unique arguments
100
100
  def lock_args_method
101
- @lock_args_method ||= worker_options.slice(LOCK_ARGS_METHOD, UNIQUE_ARGS_METHOD).values.first
102
- @lock_args_method ||= :lock_args if worker_method_defined?(:lock_args)
103
- @lock_args_method ||= :unique_args if worker_method_defined?(:unique_args)
101
+ @lock_args_method ||= job_options.slice(LOCK_ARGS_METHOD, UNIQUE_ARGS_METHOD).values.first
102
+ @lock_args_method ||= :lock_args if job_method_defined?(:lock_args)
103
+ @lock_args_method ||= :unique_args if job_method_defined?(:unique_args)
104
104
  @lock_args_method ||= default_lock_args_method
105
105
  end
106
106
 
107
107
  # The global worker options defined in Sidekiq directly
108
108
  def default_lock_args_method
109
- default_worker_options[LOCK_ARGS_METHOD] ||
110
- default_worker_options[UNIQUE_ARGS_METHOD]
109
+ default_job_options[LOCK_ARGS_METHOD] ||
110
+ default_job_options[UNIQUE_ARGS_METHOD]
111
111
  end
112
112
 
113
113
  #
@@ -116,8 +116,12 @@ module SidekiqUniqueJobs
116
116
  #
117
117
  # @return [Hash<String, Object>]
118
118
  #
119
- def default_worker_options
120
- @default_worker_options ||= Sidekiq.default_worker_options.stringify_keys
119
+ def default_job_options
120
+ @default_job_options ||= if Sidekiq.respond_to?(:default_job_options)
121
+ Sidekiq.default_job_options.stringify_keys
122
+ else
123
+ Sidekiq.default_worker_options.stringify_keys
124
+ end
121
125
  end
122
126
  end
123
127
  end
@@ -13,9 +13,9 @@ module SidekiqUniqueJobs
13
13
  # @return [Symbol] the type of lock
14
14
  attr_reader :type
15
15
  #
16
- # @!attribute [r] worker
17
- # @return [Symbol] the worker class
18
- attr_reader :worker
16
+ # @!attribute [r] job
17
+ # @return [Symbol] the job class
18
+ attr_reader :job
19
19
  #
20
20
  # @!attribute [r] limit
21
21
  # @return [Integer] the number of simultaneous locks
@@ -58,7 +58,7 @@ module SidekiqUniqueJobs
58
58
 
59
59
  def initialize(job_hash = {})
60
60
  @type = job_hash[LOCK]&.to_sym
61
- @worker = SidekiqUniqueJobs.safe_constantize(job_hash[CLASS])
61
+ @job = SidekiqUniqueJobs.safe_constantize(job_hash[CLASS])
62
62
  @limit = job_hash.fetch(LOCK_LIMIT, 1)&.to_i
63
63
  @timeout = job_hash.fetch(LOCK_TIMEOUT, 0)&.to_i
64
64
  @ttl = job_hash.fetch(LOCK_TTL) { job_hash.fetch(LOCK_EXPIRATION, nil) }.to_i
@@ -113,13 +113,13 @@ module SidekiqUniqueJobs
113
113
 
114
114
  # the strategy to use as conflict resolution from sidekiq client
115
115
  def on_client_conflict
116
- @on_client_conflict ||= on_conflict["client"] if on_conflict.is_a?(Hash)
116
+ @on_client_conflict ||= on_conflict["client"] || on_conflict[:client] if on_conflict.is_a?(Hash)
117
117
  @on_client_conflict ||= on_conflict
118
118
  end
119
119
 
120
120
  # the strategy to use as conflict resolution from sidekiq server
121
121
  def on_server_conflict
122
- @on_server_conflict ||= on_conflict["server"] if on_conflict.is_a?(Hash)
122
+ @on_server_conflict ||= on_conflict["server"] || on_conflict[:server] if on_conflict.is_a?(Hash)
123
123
  @on_server_conflict ||= on_conflict
124
124
  end
125
125
  end
@@ -36,10 +36,10 @@ module SidekiqUniqueJobs
36
36
 
37
37
  # @param [Hash] item a Sidekiq job hash
38
38
  def initialize(item)
39
- @item = item
40
- @worker_class = item[CLASS]
41
- @lock_args = item.slice(LOCK_ARGS, UNIQUE_ARGS).values.first # TODO: Deprecate UNIQUE_ARGS
42
- @lock_prefix = item.slice(LOCK_PREFIX, UNIQUE_PREFIX).values.first # TODO: Deprecate UNIQUE_PREFIX
39
+ @item = item
40
+ @lock_args = item[LOCK_ARGS] || item[UNIQUE_ARGS] # TODO: Deprecate UNIQUE_ARGS
41
+ @lock_prefix = item[LOCK_PREFIX] || item[UNIQUE_PREFIX] # TODO: Deprecate UNIQUE_PREFIX
42
+ self.job_class = item[CLASS]
43
43
  end
44
44
 
45
45
  # Memoized lock_digest
@@ -51,7 +51,7 @@ module SidekiqUniqueJobs
51
51
  # Creates a namespaced unique digest based on the {#digestable_hash} and the {#lock_prefix}
52
52
  # @return [String] a unique digest
53
53
  def create_digest
54
- digest = OpenSSL::Digest::MD5.hexdigest(dump_json(digestable_hash))
54
+ digest = OpenSSL::Digest::MD5.hexdigest(dump_json(digestable_hash.sort))
55
55
  "#{lock_prefix}:#{digest}"
56
56
  end
57
57
 
@@ -67,13 +67,13 @@ module SidekiqUniqueJobs
67
67
  # Checks if we should disregard the queue when creating the unique digest
68
68
  # @return [true, false]
69
69
  def unique_across_queues?
70
- item[UNIQUE_ACROSS_QUEUES] || worker_options[UNIQUE_ACROSS_QUEUES]
70
+ item[UNIQUE_ACROSS_QUEUES] || job_options[UNIQUE_ACROSS_QUEUES]
71
71
  end
72
72
 
73
73
  # Checks if we should disregard the worker when creating the unique digest
74
74
  # @return [true, false]
75
75
  def unique_across_workers?
76
- item[UNIQUE_ACROSS_WORKERS] || worker_options[UNIQUE_ACROSS_WORKERS]
76
+ item[UNIQUE_ACROSS_WORKERS] || job_options[UNIQUE_ACROSS_WORKERS]
77
77
  end
78
78
  end
79
79
  end
@@ -55,13 +55,13 @@ module SidekiqUniqueJobs
55
55
  #
56
56
  # @return [Hash]
57
57
  #
58
- def set(obj)
58
+ def set(obj, pipeline = nil)
59
59
  return unless SidekiqUniqueJobs.config.lock_info
60
60
  raise InvalidArgument, "argument `obj` (#{obj}) needs to be a hash" unless obj.is_a?(Hash)
61
61
 
62
62
  json = dump_json(obj)
63
63
  @value = load_json(json)
64
- super(json)
64
+ super(json, pipeline)
65
65
  value
66
66
  end
67
67
  end
@@ -30,8 +30,8 @@ module SidekiqUniqueJobs
30
30
  # @option item [String] :class the class of the sidekiq worker
31
31
  # @option item [Float] :at the unix time the job is scheduled at
32
32
  def initialize(item)
33
- @item = item
34
- @worker_class = item[CLASS]
33
+ @item = item
34
+ self.job_class = item[CLASS]
35
35
  end
36
36
 
37
37
  #
@@ -42,9 +42,9 @@ module SidekiqUniqueJobs
42
42
  # @return [Integer, nil]
43
43
  #
44
44
  def calculate
45
- timeout = default_worker_options[LOCK_TIMEOUT]
45
+ timeout = default_job_options[LOCK_TIMEOUT]
46
46
  timeout = default_lock_timeout if default_lock_timeout
47
- timeout = worker_options[LOCK_TIMEOUT] if worker_options.key?(LOCK_TIMEOUT)
47
+ timeout = job_options[LOCK_TIMEOUT] if job_options.key?(LOCK_TIMEOUT)
48
48
  timeout
49
49
  end
50
50
 
@@ -33,8 +33,8 @@ module SidekiqUniqueJobs
33
33
  # @option item [String] :class the class of the sidekiq worker
34
34
  # @option item [Float] :at the unix time the job is scheduled at
35
35
  def initialize(item)
36
- @item = item
37
- @worker_class = item[CLASS]
36
+ @item = item
37
+ self.job_class = item[CLASS]
38
38
  end
39
39
 
40
40
  #
@@ -67,9 +67,9 @@ module SidekiqUniqueJobs
67
67
  #
68
68
  def calculate
69
69
  ttl = item[LOCK_TTL]
70
- ttl ||= worker_options[LOCK_TTL]
70
+ ttl ||= job_options[LOCK_TTL]
71
71
  ttl ||= item[LOCK_EXPIRATION] # TODO: Deprecate at some point
72
- ttl ||= worker_options[LOCK_EXPIRATION] # TODO: Deprecate at some point
72
+ ttl ||= job_options[LOCK_EXPIRATION] # TODO: Deprecate at some point
73
73
  ttl ||= SidekiqUniqueJobs.config.lock_ttl
74
74
  ttl && (ttl.to_i + time_until_scheduled)
75
75
  end