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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -9
- data/.gitignore +1 -3
- data/.reek +105 -0
- data/.rubocop.yml +36 -1
- data/.simplecov +7 -2
- data/.travis.yml +11 -10
- data/Appraisals +3 -7
- data/CHANGELOG.md +17 -0
- data/Gemfile +16 -13
- data/Guardfile +55 -0
- data/README.md +85 -73
- data/examples/another_unique_job.rb +13 -0
- data/examples/custom_queue_job.rb +12 -0
- data/examples/custom_queue_job_with_filter_method.rb +13 -0
- data/examples/custom_queue_job_with_filter_proc.rb +16 -0
- data/examples/expiring_job.rb +12 -0
- data/examples/inline_worker.rb +12 -0
- data/examples/just_a_worker.rb +13 -0
- data/examples/long_running_job.rb +12 -0
- data/examples/main_job.rb +13 -0
- data/examples/my_job.rb +12 -0
- data/examples/my_unique_job.rb +16 -0
- data/examples/my_unique_job_with_filter_method.rb +21 -0
- data/examples/my_unique_job_with_filter_proc.rb +19 -0
- data/examples/notify_worker.rb +14 -0
- data/examples/plain_class.rb +13 -0
- data/examples/simple_worker.rb +15 -0
- data/examples/spawn_simple_worker.rb +12 -0
- data/examples/test_class.rb +9 -0
- data/examples/unique_across_workers_job.rb +20 -0
- data/examples/unique_job_with_conditional_parameter.rb +18 -0
- data/examples/unique_job_with_filter_method.rb +18 -0
- data/examples/unique_job_with_nil_unique_args.rb +20 -0
- data/examples/unique_job_with_no_unique_args_method.rb +16 -0
- data/examples/unique_job_withthout_unique_args_parameter.rb +18 -0
- data/examples/unique_on_all_queues_job.rb +16 -0
- data/examples/until_and_while_executing_job.rb +13 -0
- data/examples/until_executed_2_job.rb +24 -0
- data/examples/until_executed_job.rb +25 -0
- data/examples/until_executing_job.rb +11 -0
- data/examples/until_expired_job.rb +12 -0
- data/examples/until_global_expired_job.rb +12 -0
- data/examples/while_executing_job.rb +15 -0
- data/examples/while_executing_reject_job.rb +14 -0
- data/examples/without_argument_job.rb +13 -0
- data/lib/sidekiq-unique-jobs.rb +1 -91
- data/lib/sidekiq/simulator.rb +15 -16
- data/lib/sidekiq_unique_jobs.rb +79 -0
- data/lib/sidekiq_unique_jobs/cli.rb +9 -18
- data/lib/sidekiq_unique_jobs/client/middleware.rb +16 -18
- data/lib/sidekiq_unique_jobs/connection.rb +18 -0
- data/lib/sidekiq_unique_jobs/constants.rb +13 -17
- data/lib/sidekiq_unique_jobs/core_ext.rb +28 -55
- data/lib/sidekiq_unique_jobs/exceptions.rb +30 -0
- data/lib/sidekiq_unique_jobs/lock/base_lock.rb +84 -0
- data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +11 -6
- data/lib/sidekiq_unique_jobs/lock/until_executed.rb +6 -58
- data/lib/sidekiq_unique_jobs/lock/until_executing.rb +6 -5
- data/lib/sidekiq_unique_jobs/lock/until_expired.rb +17 -0
- data/lib/sidekiq_unique_jobs/lock/while_executing.rb +20 -34
- data/lib/sidekiq_unique_jobs/lock/while_executing_reject.rb +78 -0
- data/lib/sidekiq_unique_jobs/lock/while_executing_requeue.rb +20 -0
- data/lib/sidekiq_unique_jobs/locksmith.rb +149 -0
- data/lib/sidekiq_unique_jobs/logging.rb +30 -0
- data/lib/sidekiq_unique_jobs/options_with_fallback.rb +27 -41
- data/lib/sidekiq_unique_jobs/scripts.rb +25 -24
- data/lib/sidekiq_unique_jobs/server/middleware.rb +12 -20
- data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +5 -5
- data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +42 -0
- data/lib/sidekiq_unique_jobs/testing.rb +40 -50
- data/lib/sidekiq_unique_jobs/timeout.rb +15 -0
- data/lib/sidekiq_unique_jobs/timeout/calculator.rb +49 -0
- data/lib/sidekiq_unique_jobs/unique_args.rb +33 -77
- data/lib/sidekiq_unique_jobs/unlockable.rb +5 -14
- data/lib/sidekiq_unique_jobs/util.rb +28 -90
- data/lib/sidekiq_unique_jobs/version.rb +1 -1
- data/redis/acquire_lock.lua +5 -3
- data/redis/create.lua +58 -0
- data/redis/delete.lua +11 -0
- data/redis/release_stale_locks.lua +90 -0
- data/redis/signal.lua +21 -0
- data/sidekiq-unique-jobs.gemspec +15 -8
- metadata +108 -32
- data/lib/sidekiq_unique_jobs/config.rb +0 -17
- data/lib/sidekiq_unique_jobs/lock.rb +0 -12
- data/lib/sidekiq_unique_jobs/lock/until_timeout.rb +0 -17
- data/lib/sidekiq_unique_jobs/run_lock_failed.rb +0 -3
- data/lib/sidekiq_unique_jobs/script_mock.rb +0 -66
- data/lib/sidekiq_unique_jobs/scripts/acquire_lock.rb +0 -47
- data/lib/sidekiq_unique_jobs/scripts/release_lock.rb +0 -49
- data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +0 -50
- data/lib/sidekiq_unique_jobs/timeout_calculator.rb +0 -67
- data/redis/synchronize.lua +0 -16
data/lib/sidekiq/simulator.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'sidekiq/cli'
|
3
4
|
require 'sidekiq/launcher'
|
4
|
-
|
5
|
+
|
6
|
+
require 'sidekiq_unique_jobs/timeout'
|
5
7
|
|
6
8
|
module Sidekiq
|
7
9
|
class Simulator
|
8
|
-
|
9
|
-
|
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 =
|
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
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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 =
|
16
|
-
@item
|
17
|
-
@queue
|
18
|
-
@redis_pool
|
13
|
+
@worker_class = worker_class
|
14
|
+
@item = item
|
15
|
+
@queue = queue
|
16
|
+
@redis_pool = redis_pool
|
19
17
|
|
20
|
-
yield if
|
18
|
+
yield if success?
|
21
19
|
end
|
22
20
|
|
23
21
|
private
|
24
22
|
|
25
|
-
attr_reader :item
|
23
|
+
attr_reader :item
|
26
24
|
|
27
|
-
def
|
28
|
-
unique_disabled? ||
|
25
|
+
def success?
|
26
|
+
unique_disabled? || locked?
|
29
27
|
end
|
30
28
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
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
|
39
|
-
|
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
|
5
|
-
AT_KEY
|
6
|
-
CLASS_KEY
|
7
|
-
JID_KEY
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|