sidekiq-unique-jobs 5.0.11 → 6.0.0.rc1

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -9
  3. data/.gitignore +1 -3
  4. data/.reek +105 -0
  5. data/.rubocop.yml +36 -1
  6. data/.simplecov +7 -2
  7. data/.travis.yml +11 -10
  8. data/Appraisals +3 -7
  9. data/CHANGELOG.md +17 -0
  10. data/Gemfile +16 -13
  11. data/Guardfile +55 -0
  12. data/README.md +85 -73
  13. data/examples/another_unique_job.rb +13 -0
  14. data/examples/custom_queue_job.rb +12 -0
  15. data/examples/custom_queue_job_with_filter_method.rb +13 -0
  16. data/examples/custom_queue_job_with_filter_proc.rb +16 -0
  17. data/examples/expiring_job.rb +12 -0
  18. data/examples/inline_worker.rb +12 -0
  19. data/examples/just_a_worker.rb +13 -0
  20. data/examples/long_running_job.rb +12 -0
  21. data/examples/main_job.rb +13 -0
  22. data/examples/my_job.rb +12 -0
  23. data/examples/my_unique_job.rb +16 -0
  24. data/examples/my_unique_job_with_filter_method.rb +21 -0
  25. data/examples/my_unique_job_with_filter_proc.rb +19 -0
  26. data/examples/notify_worker.rb +14 -0
  27. data/examples/plain_class.rb +13 -0
  28. data/examples/simple_worker.rb +15 -0
  29. data/examples/spawn_simple_worker.rb +12 -0
  30. data/examples/test_class.rb +9 -0
  31. data/examples/unique_across_workers_job.rb +20 -0
  32. data/examples/unique_job_with_conditional_parameter.rb +18 -0
  33. data/examples/unique_job_with_filter_method.rb +18 -0
  34. data/examples/unique_job_with_nil_unique_args.rb +20 -0
  35. data/examples/unique_job_with_no_unique_args_method.rb +16 -0
  36. data/examples/unique_job_withthout_unique_args_parameter.rb +18 -0
  37. data/examples/unique_on_all_queues_job.rb +16 -0
  38. data/examples/until_and_while_executing_job.rb +13 -0
  39. data/examples/until_executed_2_job.rb +24 -0
  40. data/examples/until_executed_job.rb +25 -0
  41. data/examples/until_executing_job.rb +11 -0
  42. data/examples/until_expired_job.rb +12 -0
  43. data/examples/until_global_expired_job.rb +12 -0
  44. data/examples/while_executing_job.rb +15 -0
  45. data/examples/while_executing_reject_job.rb +14 -0
  46. data/examples/without_argument_job.rb +13 -0
  47. data/lib/sidekiq-unique-jobs.rb +1 -91
  48. data/lib/sidekiq/simulator.rb +15 -16
  49. data/lib/sidekiq_unique_jobs.rb +79 -0
  50. data/lib/sidekiq_unique_jobs/cli.rb +9 -18
  51. data/lib/sidekiq_unique_jobs/client/middleware.rb +16 -18
  52. data/lib/sidekiq_unique_jobs/connection.rb +18 -0
  53. data/lib/sidekiq_unique_jobs/constants.rb +13 -17
  54. data/lib/sidekiq_unique_jobs/core_ext.rb +28 -55
  55. data/lib/sidekiq_unique_jobs/exceptions.rb +30 -0
  56. data/lib/sidekiq_unique_jobs/lock/base_lock.rb +84 -0
  57. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +11 -6
  58. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +6 -58
  59. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +6 -5
  60. data/lib/sidekiq_unique_jobs/lock/until_expired.rb +17 -0
  61. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +20 -34
  62. data/lib/sidekiq_unique_jobs/lock/while_executing_reject.rb +78 -0
  63. data/lib/sidekiq_unique_jobs/lock/while_executing_requeue.rb +20 -0
  64. data/lib/sidekiq_unique_jobs/locksmith.rb +149 -0
  65. data/lib/sidekiq_unique_jobs/logging.rb +30 -0
  66. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +27 -41
  67. data/lib/sidekiq_unique_jobs/scripts.rb +25 -24
  68. data/lib/sidekiq_unique_jobs/server/middleware.rb +12 -20
  69. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +5 -5
  70. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +42 -0
  71. data/lib/sidekiq_unique_jobs/testing.rb +40 -50
  72. data/lib/sidekiq_unique_jobs/timeout.rb +15 -0
  73. data/lib/sidekiq_unique_jobs/timeout/calculator.rb +49 -0
  74. data/lib/sidekiq_unique_jobs/unique_args.rb +33 -77
  75. data/lib/sidekiq_unique_jobs/unlockable.rb +5 -14
  76. data/lib/sidekiq_unique_jobs/util.rb +28 -90
  77. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  78. data/redis/acquire_lock.lua +5 -3
  79. data/redis/create.lua +58 -0
  80. data/redis/delete.lua +11 -0
  81. data/redis/release_stale_locks.lua +90 -0
  82. data/redis/signal.lua +21 -0
  83. data/sidekiq-unique-jobs.gemspec +15 -8
  84. metadata +108 -32
  85. data/lib/sidekiq_unique_jobs/config.rb +0 -17
  86. data/lib/sidekiq_unique_jobs/lock.rb +0 -12
  87. data/lib/sidekiq_unique_jobs/lock/until_timeout.rb +0 -17
  88. data/lib/sidekiq_unique_jobs/run_lock_failed.rb +0 -3
  89. data/lib/sidekiq_unique_jobs/script_mock.rb +0 -66
  90. data/lib/sidekiq_unique_jobs/scripts/acquire_lock.rb +0 -47
  91. data/lib/sidekiq_unique_jobs/scripts/release_lock.rb +0 -49
  92. data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +0 -50
  93. data/lib/sidekiq_unique_jobs/timeout_calculator.rb +0 -67
  94. data/redis/synchronize.lua +0 -16
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'sidekiq/cli'
3
4
  require 'sidekiq/launcher'
4
- require 'timeout'
5
+
6
+ require 'sidekiq_unique_jobs/timeout'
5
7
 
6
8
  module Sidekiq
7
9
  class Simulator
8
- extend Forwardable
9
- def_delegator SidekiqUniqueJobs, :logger
10
+ include SidekiqUniqueJobs::Logging
11
+ include SidekiqUniqueJobs::Timeout
10
12
 
11
13
  attr_reader :queues, :launcher
12
14
 
@@ -15,12 +17,13 @@ module Sidekiq
15
17
  end
16
18
 
17
19
  def initialize(queue)
18
- @queues = [queue].flatten.uniq
20
+ @queues = Array(queue).uniq
19
21
  @launcher = Sidekiq::Launcher.new(sidekiq_options(queues))
20
22
  end
21
23
 
22
24
  def process_queue
23
- run_launcher { yield }
25
+ run_launcher
26
+ yield
24
27
  ensure
25
28
  terminate_launcher
26
29
  end
@@ -28,15 +31,17 @@ module Sidekiq
28
31
  private
29
32
 
30
33
  def run_launcher
34
+ run_launcher!
35
+ rescue Timeout::Error => exception
36
+ log_warn('Timeout while starting Sidekiq')
37
+ log_warn(exception)
38
+ end
39
+
40
+ def run_launcher!
31
41
  using_timeout(15) do
32
42
  launcher.run
33
43
  sleep 0.001 until alive?
34
44
  end
35
- rescue Timeout::Error => e
36
- logger.warn { "Timeout while running #{__method__}" }
37
- logger.warn { e }
38
- ensure
39
- yield
40
45
  end
41
46
 
42
47
  def terminate_launcher
@@ -51,12 +56,6 @@ module Sidekiq
51
56
  !alive?
52
57
  end
53
58
 
54
- def using_timeout(value)
55
- Timeout.timeout(value) do
56
- yield
57
- end
58
- end
59
-
60
59
  def sidekiq_options(queues = [])
61
60
  { queues: queues,
62
61
  concurrency: 3,
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml' if RUBY_VERSION.include?('2.0.0')
4
+ require 'forwardable'
5
+ require 'concurrent/mutable_struct'
6
+ require 'ostruct'
7
+
8
+ require 'sidekiq_unique_jobs/version'
9
+ require 'sidekiq_unique_jobs/constants'
10
+ require 'sidekiq_unique_jobs/logging'
11
+ require 'sidekiq_unique_jobs/sidekiq_worker_methods'
12
+ require 'sidekiq_unique_jobs/connection'
13
+ require 'sidekiq_unique_jobs/exceptions'
14
+ require 'sidekiq_unique_jobs/util'
15
+ require 'sidekiq_unique_jobs/cli'
16
+ require 'sidekiq_unique_jobs/core_ext'
17
+ require 'sidekiq_unique_jobs/timeout'
18
+ require 'sidekiq_unique_jobs/scripts'
19
+ require 'sidekiq_unique_jobs/unique_args'
20
+ require 'sidekiq_unique_jobs/unlockable'
21
+ require 'sidekiq_unique_jobs/locksmith'
22
+ require 'sidekiq_unique_jobs/options_with_fallback'
23
+ require 'sidekiq_unique_jobs/middleware'
24
+ require 'sidekiq_unique_jobs/sidekiq_unique_ext'
25
+
26
+ module SidekiqUniqueJobs
27
+ include SidekiqUniqueJobs::Connection
28
+
29
+ module_function
30
+
31
+ Concurrent::MutableStruct.new(
32
+ 'Config',
33
+ :default_lock_timeout,
34
+ :enabled,
35
+ :unique_prefix,
36
+ :logger,
37
+ )
38
+
39
+ def config
40
+ # Arguments here need to match the definition of the new class (see above)
41
+ @config ||= Concurrent::MutableStruct::Config.new(
42
+ 0,
43
+ true,
44
+ 'uniquejobs',
45
+ Sidekiq.logger,
46
+ )
47
+ end
48
+
49
+ def logger
50
+ config.logger
51
+ end
52
+
53
+ def logger=(other)
54
+ config.logger = other
55
+ end
56
+
57
+ def use_config(tmp_config)
58
+ fail ::ArgumentError, "#{name}.#{__method__} needs a block" unless block_given?
59
+
60
+ old_config = config.to_h
61
+ configure(tmp_config)
62
+ yield
63
+ configure(old_config)
64
+ end
65
+
66
+ def configure(options = {})
67
+ if block_given?
68
+ yield config
69
+ else
70
+ options.each do |key, val|
71
+ config.send("#{key}=", val)
72
+ end
73
+ end
74
+ end
75
+
76
+ def redis_version
77
+ @redis_version ||= redis { |conn| conn.info('server')['redis_version'] }
78
+ end
79
+ end
@@ -4,17 +4,13 @@ require 'thor'
4
4
 
5
5
  module SidekiqUniqueJobs
6
6
  class Cli < Thor
7
- # def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
8
- # @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
9
- # end
10
-
11
7
  def self.banner(command, _namespace = nil, _subcommand = false)
12
8
  "jobs #{@package_name} #{command.usage}"
13
9
  end
14
10
 
15
11
  desc 'keys PATTERN', 'list all unique keys and their expiry time'
16
12
  option :count, aliases: :c, type: :numeric, default: 1000, desc: 'The max number of keys to return'
17
- def keys(pattern)
13
+ def keys(pattern = '*')
18
14
  keys = Util.keys(pattern, options[:count])
19
15
  say "Found #{keys.size} keys matching '#{pattern}':"
20
16
  print_in_columns(keys.sort) if keys.any?
@@ -24,15 +20,14 @@ module SidekiqUniqueJobs
24
20
  option :dry_run, aliases: :d, type: :boolean, desc: 'set to false to perform deletion'
25
21
  option :count, aliases: :c, type: :numeric, default: 1000, desc: 'The max number of keys to return'
26
22
  def del(pattern)
27
- deleted_count = Util.del(pattern, options[:count], options[:dry_run])
28
- say "Deleted #{deleted_count} keys matching '#{pattern}'"
29
- end
30
-
31
- desc 'expire', 'removes all expired unique keys from the hash in redis'
32
- def expire
33
- expired = Util.expire
34
- say "Removed #{expired.values.size} left overs from redis."
35
- print_in_columns(expired.values)
23
+ max_count = options[:count]
24
+ if options[:dry_run]
25
+ keys = Util.keys(pattern, max_count)
26
+ say "Would delete #{keys.size} keys matching '#{pattern}'"
27
+ else
28
+ deleted_count = Util.del(pattern, max_count)
29
+ say "Deleted #{deleted_count} keys matching '#{pattern}'"
30
+ end
36
31
  end
37
32
 
38
33
  desc 'console', 'drop into a console with easy access to helper methods'
@@ -45,10 +40,6 @@ module SidekiqUniqueJobs
45
40
  end
46
41
 
47
42
  no_commands do
48
- def logger
49
- SidekiqUniqueJobs.logger
50
- end
51
-
52
43
  def console_class
53
44
  require 'pry'
54
45
  Pry
@@ -5,38 +5,36 @@ require 'sidekiq_unique_jobs/server/middleware'
5
5
  module SidekiqUniqueJobs
6
6
  module Client
7
7
  class Middleware
8
- extend Forwardable
9
- def_delegators :SidekiqUniqueJobs, :connection, :config, :worker_class_constantize
10
- def_delegators :Sidekiq, :logger
11
-
8
+ include SidekiqUniqueJobs::Logging
12
9
  include OptionsWithFallback
13
10
 
11
+ # :reek:LongParameterList { max_params: 4 }
14
12
  def call(worker_class, item, queue, redis_pool = nil)
15
- @worker_class = worker_class_constantize(worker_class)
16
- @item = item
17
- @queue = queue
18
- @redis_pool = redis_pool
13
+ @worker_class = worker_class
14
+ @item = item
15
+ @queue = queue
16
+ @redis_pool = redis_pool
19
17
 
20
- yield if disabled_or_successfully_locked?
18
+ yield if success?
21
19
  end
22
20
 
23
21
  private
24
22
 
25
- attr_reader :item, :worker_class, :redis_pool, :queue
23
+ attr_reader :item
26
24
 
27
- def disabled_or_successfully_locked?
28
- unique_disabled? || acquire_lock
25
+ def success?
26
+ unique_disabled? || locked?
29
27
  end
30
28
 
31
- def acquire_lock
32
- return true unless lock.respond_to?(:lock)
33
- locked = lock.lock(:client)
34
- warn_about_duplicate(item) unless locked
29
+ def locked?
30
+ locked = lock.lock
31
+ warn_about_duplicate unless locked
35
32
  locked
36
33
  end
37
34
 
38
- def warn_about_duplicate(item)
39
- logger.warn "payload is not unique #{item}" if log_duplicate_payload?
35
+ def warn_about_duplicate
36
+ return unless log_duplicate_payload?
37
+ log_warn "payload is not unique #{item}"
40
38
  end
41
39
  end
42
40
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ module Connection
5
+ def self.included(base)
6
+ base.send(:extend, self)
7
+ end
8
+
9
+ # :reek:UtilityFunction { enabled: false }
10
+ def redis(redis_pool = nil)
11
+ if redis_pool
12
+ redis_pool.with { |conn| yield conn }
13
+ else
14
+ Sidekiq.redis { |conn| yield conn }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,22 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SidekiqUniqueJobs
4
- ARGS_KEY ||= 'args'
5
- AT_KEY ||= 'at'
6
- CLASS_KEY ||= 'class'
7
- JID_KEY ||= 'jid'
8
- LOG_DUPLICATE_KEY ||= 'log_duplicate_payload'
9
- QUEUE_KEY ||= 'queue'
10
- HASH_KEY ||= 'uniquejobs'
11
- QUEUE_LOCK_TIMEOUT_KEY ||= 'unique_expiration'
12
- RUN_LOCK_TIMEOUT_KEY ||= 'run_lock_expiration'
13
- TESTING_CONSTANT ||= 'Testing'
14
- UNIQUE_KEY ||= 'unique'
15
- UNIQUE_LOCK_KEY ||= 'unique_lock'
16
- UNIQUE_ARGS_KEY ||= 'unique_args'
17
- UNIQUE_PREFIX_KEY ||= 'unique_prefix'
18
- UNIQUE_DIGEST_KEY ||= 'unique_digest'
19
- UNIQUE_ON_ALL_QUEUES_KEY ||= 'unique_on_all_queues'
4
+ ARGS_KEY ||= 'args'
5
+ AT_KEY ||= 'at'
6
+ CLASS_KEY ||= 'class'
7
+ JID_KEY ||= 'jid'
8
+ LOCK_EXPIRATION_KEY ||= 'lock_expiration'
9
+ LOCK_TIMEOUT_KEY ||= 'lock_timeout'
10
+ LOG_DUPLICATE_KEY ||= 'log_duplicate_payload'
11
+ QUEUE_KEY ||= 'queue'
20
12
  UNIQUE_ACROSS_WORKERS_KEY ||= 'unique_across_workers'
21
- UNIQUE_ARGS_ENABLED_KEY ||= 'unique_args_enabled'
13
+ UNIQUE_ARGS_KEY ||= 'unique_args'
14
+ UNIQUE_DIGEST_KEY ||= 'unique_digest'
15
+ UNIQUE_KEY ||= 'unique'
16
+ UNIQUE_ON_ALL_QUEUES_KEY ||= 'unique_on_all_queues'
17
+ UNIQUE_PREFIX_KEY ||= 'unique_prefix'
22
18
  end
@@ -1,43 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require 'active_support/core_ext/hash/keys'
5
- require 'active_support/core_ext/hash/deep_merge'
6
- rescue LoadError
7
- class Hash
8
- unless {}.respond_to?(:slice)
9
- def slice(*keys)
10
- keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
11
- keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if key?(k) }
12
- end
3
+ # :nocov:
4
+
5
+ class Hash
6
+ unless {}.respond_to?(:slice)
7
+ def slice(*keys)
8
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
9
+ keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if key?(k) }
13
10
  end
11
+ end
14
12
 
15
- unless {}.respond_to?(:stringify_keys)
16
- def stringify_keys
17
- transform_keys(&:to_s)
18
- end
13
+ unless {}.respond_to?(:stringify_keys)
14
+ def stringify_keys
15
+ transform_keys(&:to_s)
19
16
  end
17
+ end
20
18
 
21
- unless {}.respond_to?(:transform_keys)
22
- def transform_keys
23
- result = {}
24
- each_key do |key|
25
- result[yield(key)] = self[key]
26
- end
27
- result
19
+ unless {}.respond_to?(:transform_keys)
20
+ def transform_keys
21
+ result = {}
22
+ each_key do |key|
23
+ result[yield(key)] = self[key]
28
24
  end
25
+ result
29
26
  end
27
+ end
30
28
 
31
- unless {}.respond_to?(:slice!)
32
- def slice!(*keys)
33
- keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
34
- omit = slice(*self.keys - keys)
35
- hash = slice(*keys)
36
- hash.default = default
37
- hash.default_proc = default_proc if default_proc
38
- replace(hash)
39
- omit
40
- end
29
+ unless {}.respond_to?(:slice!)
30
+ def slice!(*keys)
31
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
32
+ omit = slice(*self.keys - keys)
33
+ hash = slice(*keys)
34
+ hash.default = default
35
+ hash.default_proc = default_proc if default_proc
36
+ replace(hash)
37
+ omit
41
38
  end
42
39
  end
43
40
  end
@@ -53,27 +50,3 @@ class Array
53
50
  end
54
51
  end
55
52
  end
56
-
57
- class String
58
- unless ''.respond_to?(:classify)
59
- def classify
60
- camelize(sub(/.*\./, ''))
61
- end
62
- end
63
-
64
- unless ''.respond_to?(:camelize)
65
- def camelize(uppercase_first_letter = true)
66
- string = self
67
- string = if uppercase_first_letter
68
- string.sub(/^[a-z\d]*/) { $&.capitalize }
69
- else
70
- string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
71
- end
72
- string.gsub!(%r{(?:_|(\/))([a-z\d]*)}i) do
73
- "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}"
74
- end
75
- string.gsub!(%r{/}, '::')
76
- string
77
- end
78
- end
79
- end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ class LockTimeout < StandardError
5
+ end
6
+
7
+ class RunLockFailed < StandardError
8
+ end
9
+
10
+ class ScriptError < StandardError
11
+ def initialize(file_name:, source_exception:)
12
+ super("Problem compiling #{file_name}. Message: #{source_exception.message}")
13
+ end
14
+ end
15
+
16
+ class UniqueKeyMissing < ArgumentError
17
+ end
18
+
19
+ class JidMissing < ArgumentError
20
+ end
21
+
22
+ class MaxLockTimeMissing < ArgumentError
23
+ end
24
+
25
+ class UnexpectedValue < StandardError
26
+ end
27
+
28
+ class UnknownLock < StandardError
29
+ end
30
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ class Lock
5
+ class BaseLock
6
+ include SidekiqUniqueJobs::Logging
7
+
8
+ def initialize(item, redis_pool = nil)
9
+ @item = prepare_item(item)
10
+ @redis_pool = redis_pool
11
+ end
12
+
13
+ def lock
14
+ locksmith.lock(item[LOCK_TIMEOUT_KEY])
15
+ end
16
+
17
+ def execute(_callback = nil)
18
+ raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
19
+ end
20
+
21
+ def unlock
22
+ locksmith.signal(item[JID_KEY]) # Only signal to release the lock
23
+ end
24
+
25
+ def delete
26
+ locksmith.delete # Soft delete (don't forcefully remove when expiration is set)
27
+ end
28
+
29
+ def delete!
30
+ locksmith.delete! # Force delete the lock
31
+ end
32
+
33
+ def locked?
34
+ locksmith.locked?(item[JID_KEY])
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :item, :redis_pool, :operative
40
+
41
+ def locksmith
42
+ @locksmith ||= SidekiqUniqueJobs::Locksmith.new(item, redis_pool)
43
+ end
44
+
45
+ def using_protection(callback)
46
+ @operative = true
47
+ yield
48
+ rescue Sidekiq::Shutdown
49
+ @operative = false
50
+ raise
51
+ ensure
52
+ unlock_and_callback(callback)
53
+ end
54
+
55
+ def prepare_item(item)
56
+ calculator = SidekiqUniqueJobs::Timeout::Calculator.new(item)
57
+ item[LOCK_TIMEOUT_KEY] = calculator.lock_timeout
58
+ item[LOCK_EXPIRATION_KEY] = calculator.lock_expiration
59
+ SidekiqUniqueJobs::UniqueArgs.digest(item)
60
+ item
61
+ end
62
+
63
+ def unlock_and_callback(callback)
64
+ return notify_about_manual_unlock unless operative
65
+ unlock
66
+ delete
67
+
68
+ return notify_about_manual_unlock if locked?
69
+ callback_safely(callback)
70
+ end
71
+
72
+ def notify_about_manual_unlock
73
+ log_fatal("the unique_key: #{item[UNIQUE_DIGEST_KEY]} needs to be unlocked manually")
74
+ end
75
+
76
+ def callback_safely(callback)
77
+ callback.call
78
+ rescue StandardError
79
+ log_warn("the callback for unique_key: #{item[UNIQUE_DIGEST_KEY]} failed!")
80
+ raise
81
+ end
82
+ end
83
+ end
84
+ end