sidekiq-unique-jobs 6.0.0.rc6 → 6.0.0.rc7
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 +6 -7
- data/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- data/.reek.yml +17 -48
- data/.rubocop.yml +3 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +2 -0
- data/README.md +65 -23
- data/assets/unique_digests_1.png +0 -0
- data/assets/unique_digests_2.png +0 -0
- data/examples/another_unique_job.rb +4 -2
- data/examples/custom_queue_job_with_filter_method.rb +1 -1
- data/examples/custom_queue_job_with_filter_proc.rb +1 -1
- data/examples/expiring_job.rb +1 -1
- data/examples/inline_worker.rb +1 -1
- data/examples/just_a_worker.rb +1 -1
- data/examples/long_running_job.rb +4 -2
- data/examples/main_job.rb +3 -2
- data/examples/my_unique_job.rb +4 -5
- data/examples/my_unique_job_with_filter_method.rb +3 -3
- data/examples/my_unique_job_with_filter_proc.rb +3 -3
- data/examples/notify_worker.rb +2 -2
- data/examples/simple_worker.rb +2 -2
- data/examples/unique_across_workers_job.rb +1 -1
- data/examples/unique_job_on_conflict_raise.rb +14 -0
- data/examples/unique_job_on_conflict_reject.rb +14 -0
- data/examples/unique_job_on_conflict_reschedule.rb +14 -0
- data/examples/unique_job_with_conditional_parameter.rb +3 -3
- data/examples/unique_job_with_filter_method.rb +5 -2
- data/examples/unique_job_with_nil_unique_args.rb +3 -3
- data/examples/unique_job_with_no_unique_args_method.rb +3 -3
- data/examples/unique_job_withthout_unique_args_parameter.rb +3 -3
- data/examples/unique_on_all_queues_job.rb +1 -1
- data/examples/until_and_while_executing_job.rb +4 -1
- data/examples/until_executed_2_job.rb +5 -5
- data/examples/until_executed_job.rb +5 -5
- data/examples/until_executing_job.rb +1 -1
- data/examples/until_expired_job.rb +1 -1
- data/examples/until_global_expired_job.rb +1 -1
- data/examples/while_executing_job.rb +2 -2
- data/examples/while_executing_reject_job.rb +2 -2
- data/examples/without_argument_job.rb +1 -1
- data/lib/sidekiq_unique_jobs.rb +30 -0
- data/lib/sidekiq_unique_jobs/client/middleware.rb +12 -1
- data/lib/sidekiq_unique_jobs/connection.rb +5 -1
- data/lib/sidekiq_unique_jobs/constants.rb +3 -0
- data/lib/sidekiq_unique_jobs/digests.rb +111 -0
- data/lib/sidekiq_unique_jobs/exceptions.rb +15 -16
- data/lib/sidekiq_unique_jobs/lock/base_lock.rb +44 -3
- data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +13 -3
- data/lib/sidekiq_unique_jobs/lock/until_executed.rb +8 -1
- data/lib/sidekiq_unique_jobs/lock/until_executing.rb +8 -1
- data/lib/sidekiq_unique_jobs/lock/until_expired.rb +14 -2
- data/lib/sidekiq_unique_jobs/lock/while_executing.rb +19 -5
- data/lib/sidekiq_unique_jobs/lock/while_executing_reject.rb +16 -63
- data/lib/sidekiq_unique_jobs/locksmith.rb +36 -13
- data/lib/sidekiq_unique_jobs/logging.rb +24 -1
- data/lib/sidekiq_unique_jobs/normalizer.rb +6 -0
- data/lib/sidekiq_unique_jobs/on_conflict.rb +24 -0
- data/lib/sidekiq_unique_jobs/on_conflict/log.rb +20 -0
- data/lib/sidekiq_unique_jobs/on_conflict/null_strategy.rb +16 -0
- data/lib/sidekiq_unique_jobs/on_conflict/raise.rb +17 -0
- data/lib/sidekiq_unique_jobs/on_conflict/reject.rb +72 -0
- data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +24 -0
- data/lib/sidekiq_unique_jobs/on_conflict/strategy.rb +28 -0
- data/lib/sidekiq_unique_jobs/options_with_fallback.rb +19 -4
- data/lib/sidekiq_unique_jobs/scripts.rb +31 -0
- data/lib/sidekiq_unique_jobs/server/middleware.rb +10 -0
- data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +15 -1
- data/lib/sidekiq_unique_jobs/timeout/calculator.rb +17 -4
- data/lib/sidekiq_unique_jobs/unique_args.rb +47 -5
- data/lib/sidekiq_unique_jobs/unlockable.rb +10 -0
- data/lib/sidekiq_unique_jobs/util.rb +12 -7
- data/lib/sidekiq_unique_jobs/version.rb +1 -1
- data/lib/sidekiq_unique_jobs/web.rb +51 -0
- data/lib/sidekiq_unique_jobs/web/helpers.rb +37 -0
- data/lib/sidekiq_unique_jobs/web/views/unique_digest.erb +28 -0
- data/lib/sidekiq_unique_jobs/web/views/unique_digests.erb +42 -0
- data/redis/create.lua +4 -2
- data/redis/delete.lua +3 -1
- data/redis/delete_by_digest.lua +22 -0
- data/redis/signal.lua +3 -1
- data/sidekiq-unique-jobs.gemspec +2 -0
- metadata +49 -3
- data/lib/sidekiq_unique_jobs/lock/while_executing_requeue.rb +0 -21
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SidekiqUniqueJobs
|
4
|
+
module OnConflict
|
5
|
+
# Abstract conflict strategy class
|
6
|
+
#
|
7
|
+
# @abstract
|
8
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
9
|
+
class Strategy
|
10
|
+
include SidekiqUniqueJobs::Logging
|
11
|
+
|
12
|
+
# The sidekiq job hash
|
13
|
+
# @return [Hash] the Sidekiq job hash
|
14
|
+
attr_reader :item
|
15
|
+
|
16
|
+
# @param [Hash] item the Sidekiq job hash
|
17
|
+
def initialize(item)
|
18
|
+
@item = item
|
19
|
+
end
|
20
|
+
|
21
|
+
# Use strategy on conflict
|
22
|
+
# @raise [NotImplementedError] needs to be implemented in child class
|
23
|
+
def call
|
24
|
+
fail NotImplementedError, 'needs to be implemented in child class'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SidekiqUniqueJobs
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# Module containing methods shared between client and server middleware
|
5
|
+
#
|
6
|
+
# Requires the following methods to be defined in the including class
|
6
7
|
# 1. item (required)
|
7
8
|
# 2. options (can be nil)
|
8
9
|
# 3. worker_class (required, can be anything)
|
10
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
9
11
|
module OptionsWithFallback
|
10
12
|
LOCKS = {
|
11
13
|
until_and_while_executing: SidekiqUniqueJobs::Lock::UntilAndWhileExecuting,
|
@@ -21,32 +23,45 @@ module SidekiqUniqueJobs
|
|
21
23
|
base.send(:include, SidekiqUniqueJobs::SidekiqWorkerMethods)
|
22
24
|
end
|
23
25
|
|
26
|
+
# Check if unique has been enabled
|
27
|
+
# @return [true, false] indicate if the gem has been enabled
|
24
28
|
def unique_enabled?
|
25
29
|
SidekiqUniqueJobs.config.enabled && lock_type
|
26
30
|
end
|
27
31
|
|
32
|
+
# Check if unique has been disabled
|
28
33
|
def unique_disabled?
|
29
34
|
!unique_enabled?
|
30
35
|
end
|
31
36
|
|
37
|
+
# Check if we should log duplicate payloads
|
32
38
|
def log_duplicate_payload?
|
33
39
|
options[LOG_DUPLICATE_KEY] || item[LOG_DUPLICATE_KEY]
|
34
40
|
end
|
35
41
|
|
42
|
+
# Check if we should log duplicate payloads
|
43
|
+
# @return [SidekiqUniqueJobs::Lock::BaseLock] an instance of a child class
|
36
44
|
def lock
|
37
45
|
@lock ||= lock_class.new(item, after_unlock_hook, @redis_pool)
|
38
46
|
end
|
39
47
|
|
48
|
+
# Check if we should log duplicate payloads
|
49
|
+
# @return [SidekiqUniqueJobs::Lock::BaseLock] an instance of a child class
|
40
50
|
def lock_class
|
41
51
|
@lock_class ||= begin
|
42
52
|
LOCKS.fetch(lock_type.to_sym) do
|
43
|
-
fail UnknownLock, "No implementation for `
|
53
|
+
fail UnknownLock, "No implementation for `lock: :#{lock_type}`"
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
|
58
|
+
# @return [Symbol]
|
48
59
|
def lock_type
|
49
|
-
@lock_type ||= options[
|
60
|
+
@lock_type ||= options[LOCK_KEY] || item[LOCK_KEY] || unique_type
|
61
|
+
end
|
62
|
+
|
63
|
+
def unique_type
|
64
|
+
options[UNIQUE_KEY] || item[UNIQUE_KEY]
|
50
65
|
end
|
51
66
|
|
52
67
|
def options
|
@@ -5,6 +5,9 @@ require 'digest/sha1'
|
|
5
5
|
require 'concurrent/map'
|
6
6
|
|
7
7
|
module SidekiqUniqueJobs
|
8
|
+
# Interface to dealing with .lua files
|
9
|
+
#
|
10
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
8
11
|
module Scripts
|
9
12
|
LUA_PATHNAME ||= Pathname.new(__FILE__).dirname.join('../../redis').freeze
|
10
13
|
SCRIPT_SHAS ||= Concurrent::Map.new
|
@@ -13,6 +16,14 @@ module SidekiqUniqueJobs
|
|
13
16
|
|
14
17
|
module_function
|
15
18
|
|
19
|
+
# Call a lua script with the provided file_name
|
20
|
+
# @param [Symbol] file_name the name of the lua script
|
21
|
+
# @param [Sidekiq::RedisConnection, ConnectionPool] redis_pool the redis connection
|
22
|
+
# @param [Hash] options arguments to pass to the script file
|
23
|
+
# @option options [Array] :keys the array of keys to pass to the script
|
24
|
+
# @option options [Array] :argv the array of arguments to pass to the script
|
25
|
+
# @note this method is recursive if we need to load a lua script
|
26
|
+
# that wasn't previously loaded.
|
16
27
|
def call(file_name, redis_pool, options = {})
|
17
28
|
execute_script(file_name, redis_pool, options)
|
18
29
|
rescue Redis::CommandError => ex
|
@@ -21,6 +32,12 @@ module SidekiqUniqueJobs
|
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
35
|
+
# Execute the script file
|
36
|
+
# @param [Symbol] file_name the name of the lua script
|
37
|
+
# @param [Sidekiq::RedisConnection, ConnectionPool] redis_pool the redis connection
|
38
|
+
# @param [Hash] options arguments to pass to the script file
|
39
|
+
# @option options [Array] :keys the array of keys to pass to the script
|
40
|
+
# @option options [Array] :argv the array of arguments to pass to the script
|
24
41
|
def execute_script(file_name, redis_pool, options = {})
|
25
42
|
redis(redis_pool) do |conn|
|
26
43
|
sha = script_sha(conn, file_name)
|
@@ -28,6 +45,10 @@ module SidekiqUniqueJobs
|
|
28
45
|
end
|
29
46
|
end
|
30
47
|
|
48
|
+
# Return sha of already loaded lua script or load it and return the sha
|
49
|
+
# @param [Sidekiq::RedisConnection] conn the redis connection
|
50
|
+
# @param [Symbol] file_name the name of the lua script
|
51
|
+
# @return [String] sha of the script file
|
31
52
|
def script_sha(conn, file_name)
|
32
53
|
if (sha = SCRIPT_SHAS.get(file_name))
|
33
54
|
return sha
|
@@ -38,6 +59,10 @@ module SidekiqUniqueJobs
|
|
38
59
|
sha
|
39
60
|
end
|
40
61
|
|
62
|
+
# Return sha of already loaded lua script or load it and return the sha
|
63
|
+
# @param [Redis::CommandError] ex exception to handle
|
64
|
+
# @param [Symbol] file_name the name of the lua script
|
65
|
+
# @raise [ScriptError] when the error isn't handled
|
41
66
|
def handle_error(ex, file_name)
|
42
67
|
if ex.message == 'NOSCRIPT No matching script. Please use EVAL.'
|
43
68
|
SCRIPT_SHAS.delete(file_name)
|
@@ -47,10 +72,16 @@ module SidekiqUniqueJobs
|
|
47
72
|
raise ScriptError, file_name: file_name, source_exception: ex
|
48
73
|
end
|
49
74
|
|
75
|
+
# Reads the lua file from disk
|
76
|
+
# @param [Symbol] file_name the name of the lua script
|
77
|
+
# @return [String] the content of the lua file
|
50
78
|
def script_source(file_name)
|
51
79
|
script_path(file_name).read
|
52
80
|
end
|
53
81
|
|
82
|
+
# Construct a Pathname to a lua script
|
83
|
+
# @param [Symbol] file_name the name of the lua script
|
84
|
+
# @return [Pathname] the full path to the gems lua script
|
54
85
|
def script_path(file_name)
|
55
86
|
LUA_PATHNAME.join("#{file_name}.lua")
|
56
87
|
end
|
@@ -2,9 +2,19 @@
|
|
2
2
|
|
3
3
|
module SidekiqUniqueJobs
|
4
4
|
module Server
|
5
|
+
# The unique sidekiq middleware for the server processor
|
6
|
+
#
|
7
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
5
8
|
class Middleware
|
6
9
|
include OptionsWithFallback
|
7
10
|
|
11
|
+
# Runs the server middleware
|
12
|
+
# Used from Sidekiq::Processor#process
|
13
|
+
# @param [Sidekiq::Worker] worker_class
|
14
|
+
# @param [Hash] item a sidekiq job hash
|
15
|
+
# @param [String] queue name of the queue
|
16
|
+
# @yield when uniqueness is disabled
|
17
|
+
# @yield when the lock class executes successfully
|
8
18
|
def call(worker_class, item, queue)
|
9
19
|
@worker_class = worker_class
|
10
20
|
@item = item
|
@@ -1,24 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SidekiqUniqueJobs
|
4
|
+
# Module with convenience methods for the Sidekiq::Worker class
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
4
7
|
module SidekiqWorkerMethods
|
8
|
+
# Avoids duplicating worker_class.respond_to? in multiple places
|
9
|
+
# @return [true, false]
|
5
10
|
def worker_method_defined?(method_sym)
|
6
11
|
worker_class.respond_to?(method_sym)
|
7
12
|
end
|
8
13
|
|
14
|
+
# Wraps #get_sidekiq_options to always work with a hash
|
15
|
+
# @return [Hash] of the worker class sidekiq options
|
9
16
|
def worker_options
|
10
17
|
return {} unless sidekiq_worker_class?
|
11
18
|
worker_class.get_sidekiq_options.stringify_keys
|
12
19
|
end
|
13
20
|
|
21
|
+
# Tests that the
|
22
|
+
# @return [true] if worker_class responds to get_sidekiq_options
|
23
|
+
# @return [false] if worker_class does not respond to get_sidekiq_options
|
14
24
|
def sidekiq_worker_class?
|
15
25
|
worker_method_defined?(:get_sidekiq_options)
|
16
26
|
end
|
17
27
|
|
28
|
+
# The Sidekiq::Worker implementation
|
29
|
+
# @return [Sidekiq::Worker]
|
18
30
|
def worker_class
|
19
31
|
@_worker_class ||= worker_class_constantize # rubocop:disable Naming/MemoizedInstanceVariableName
|
20
32
|
end
|
21
33
|
|
34
|
+
# The hook to call after a successful unlock
|
35
|
+
# @return [Proc]
|
22
36
|
def after_unlock_hook
|
23
37
|
-> { worker_class.after_unlock if worker_method_defined?(:after_unlock) }
|
24
38
|
end
|
@@ -26,7 +40,7 @@ module SidekiqUniqueJobs
|
|
26
40
|
# Attempt to constantize a string worker_class argument, always
|
27
41
|
# failing back to the original argument when the constant can't be found
|
28
42
|
#
|
29
|
-
#
|
43
|
+
# @return [Sidekiq::Worker]
|
30
44
|
def worker_class_constantize(klazz = @worker_class)
|
31
45
|
return klazz unless klazz.is_a?(String)
|
32
46
|
Object.const_get(klazz)
|
@@ -2,28 +2,39 @@
|
|
2
2
|
|
3
3
|
module SidekiqUniqueJobs
|
4
4
|
module Timeout
|
5
|
+
# Calculates timeout and expiration
|
6
|
+
#
|
7
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
5
8
|
class Calculator
|
6
9
|
include SidekiqUniqueJobs::SidekiqWorkerMethods
|
10
|
+
|
11
|
+
# @attr [Hash] item the Sidekiq job hash
|
7
12
|
attr_reader :item
|
8
13
|
|
14
|
+
# @param [Hash] item the Sidekiq job hash
|
15
|
+
# @option item [Integer, nil] :lock_expiration the configured lock expiration
|
16
|
+
# @option item [Integer, nil] :lock_timeout the configured lock timeout
|
17
|
+
# @option item [String] :class the class of the sidekiq worker
|
18
|
+
# @option item [Float] :at the unix time the job is scheduled at
|
9
19
|
def initialize(item)
|
10
20
|
@item = item
|
11
21
|
@worker_class = item[CLASS_KEY]
|
12
22
|
end
|
13
23
|
|
24
|
+
# The time until a job is scheduled
|
25
|
+
# @return [Integer] the number of seconds until job is scheduled
|
14
26
|
def time_until_scheduled
|
15
27
|
return 0 unless scheduled_at
|
16
28
|
scheduled_at.to_i - Time.now.utc.to_i
|
17
29
|
end
|
18
30
|
|
31
|
+
# The time a job is scheduled
|
32
|
+
# @return [Float] the exact unix time the job is scheduled at
|
19
33
|
def scheduled_at
|
20
34
|
@scheduled_at ||= item[AT_KEY]
|
21
35
|
end
|
22
36
|
|
23
|
-
|
24
|
-
raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
|
25
|
-
end
|
26
|
-
|
37
|
+
# The configured lock_expiration
|
27
38
|
def lock_expiration
|
28
39
|
@lock_expiration ||= begin
|
29
40
|
expiration = item[LOCK_EXPIRATION_KEY]
|
@@ -32,6 +43,7 @@ module SidekiqUniqueJobs
|
|
32
43
|
end
|
33
44
|
end
|
34
45
|
|
46
|
+
# The configured lock_timeout
|
35
47
|
def lock_timeout
|
36
48
|
@lock_timeout = begin
|
37
49
|
timeout = default_worker_options[LOCK_TIMEOUT_KEY]
|
@@ -41,6 +53,7 @@ module SidekiqUniqueJobs
|
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
56
|
+
# The default lock_timeout of this gem
|
44
57
|
def default_lock_timeout
|
45
58
|
SidekiqUniqueJobs.config.default_lock_timeout
|
46
59
|
end
|
@@ -4,17 +4,25 @@ require 'digest'
|
|
4
4
|
require 'sidekiq_unique_jobs/normalizer'
|
5
5
|
|
6
6
|
module SidekiqUniqueJobs
|
7
|
-
#
|
7
|
+
# Handles uniqueness of sidekiq arguments
|
8
|
+
#
|
9
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
8
10
|
class UniqueArgs
|
9
11
|
include SidekiqUniqueJobs::Logging
|
10
12
|
include SidekiqUniqueJobs::SidekiqWorkerMethods
|
11
13
|
|
14
|
+
# Convenience method for returning a digest
|
15
|
+
# @param [Hash] item a Sidekiq job hash
|
16
|
+
# @return [String] a unique digest
|
12
17
|
def self.digest(item)
|
13
18
|
new(item).unique_digest
|
14
19
|
end
|
15
20
|
|
21
|
+
# The sidekiq job hash
|
22
|
+
# @return [Hash] the Sidekiq job hash
|
16
23
|
attr_reader :item
|
17
24
|
|
25
|
+
# @param [Hash] item a Sidekiq job hash
|
18
26
|
def initialize(item)
|
19
27
|
@item = item
|
20
28
|
@worker_class = item[CLASS_KEY]
|
@@ -22,52 +30,73 @@ module SidekiqUniqueJobs
|
|
22
30
|
add_uniqueness_to_item
|
23
31
|
end
|
24
32
|
|
33
|
+
# Appends the keys unique_prefix, unique_args and {#unique_digest} to the sidekiq job hash {#item}
|
34
|
+
# @return [void]
|
25
35
|
def add_uniqueness_to_item
|
26
36
|
item[UNIQUE_PREFIX_KEY] ||= unique_prefix
|
27
37
|
item[UNIQUE_ARGS_KEY] = unique_args(item[ARGS_KEY])
|
28
38
|
item[UNIQUE_DIGEST_KEY] = unique_digest
|
29
39
|
end
|
30
40
|
|
41
|
+
# Memoized unique_digest
|
42
|
+
# @return [String] a unique digest
|
31
43
|
def unique_digest
|
32
44
|
@unique_digest ||= create_digest
|
33
45
|
end
|
34
46
|
|
47
|
+
# Creates a namespaced unique digest based on the {#digestable_hash} and the {#unique_prefix}
|
48
|
+
# @return [String] a unique digest
|
35
49
|
def create_digest
|
36
50
|
digest = Digest::MD5.hexdigest(Sidekiq.dump_json(digestable_hash))
|
37
51
|
"#{unique_prefix}:#{digest}"
|
38
52
|
end
|
39
53
|
|
54
|
+
# A prefix to use as namespace for the {#unique_digest}
|
55
|
+
# @return [String] a unique digest
|
40
56
|
def unique_prefix
|
41
57
|
worker_options[UNIQUE_PREFIX_KEY] || SidekiqUniqueJobs.config.unique_prefix
|
42
58
|
end
|
43
59
|
|
60
|
+
# Filter a hash to use for digest
|
61
|
+
# @return [Hash] to use for digest
|
44
62
|
def digestable_hash
|
45
63
|
@item.slice(CLASS_KEY, QUEUE_KEY, UNIQUE_ARGS_KEY).tap do |hash|
|
46
|
-
hash.delete(QUEUE_KEY) if
|
64
|
+
hash.delete(QUEUE_KEY) if unique_across_queues?
|
47
65
|
hash.delete(CLASS_KEY) if unique_across_workers?
|
48
66
|
end
|
49
67
|
end
|
50
68
|
|
69
|
+
# The unique arguments to use for creating a lock
|
70
|
+
# @return [Array] the arguments filters by the {#filtered_args} method if {#unique_args_enabled?}
|
51
71
|
def unique_args(args)
|
52
72
|
return filtered_args(args) if unique_args_enabled?
|
53
73
|
args
|
54
74
|
end
|
55
75
|
|
56
|
-
|
76
|
+
# Checks if we should disregard the queue when creating the unique digest
|
77
|
+
# @return [true, false]
|
78
|
+
def unique_across_queues?
|
57
79
|
item[UNIQUE_ACROSS_QUEUES_KEY] || worker_options[UNIQUE_ACROSS_QUEUES_KEY] ||
|
58
80
|
item[UNIQUE_ON_ALL_QUEUES_KEY] || worker_options[UNIQUE_ON_ALL_QUEUES_KEY] # TODO: Remove in v 6.1
|
59
81
|
end
|
60
82
|
|
83
|
+
# Checks if we should disregard the worker when creating the unique digest
|
84
|
+
# @return [true, false]
|
61
85
|
def unique_across_workers?
|
62
86
|
item[UNIQUE_ACROSS_WORKERS_KEY] || worker_options[UNIQUE_ACROSS_WORKERS_KEY]
|
63
87
|
end
|
64
88
|
|
89
|
+
# Checks if the worker class has been enabled for unique_args?
|
90
|
+
# @return [true, false]
|
65
91
|
def unique_args_enabled?
|
66
92
|
unique_args_method # && !unique_args_method.is_a?(Boolean)
|
67
93
|
end
|
68
94
|
|
69
95
|
# Filters unique arguments by proc or symbol
|
70
|
-
#
|
96
|
+
# @param [Array] args the arguments passed to the sidekiq worker
|
97
|
+
# @return [Array] {#filter_by_proc} when {#unique_args_method} is a Proc
|
98
|
+
# @return [Array] {#filter_by_symbol} when {#unique_args_method} is a Symbol
|
99
|
+
# @return [Array] args unfiltered when neither of the above
|
71
100
|
def filtered_args(args)
|
72
101
|
return args if args.empty?
|
73
102
|
json_args = Normalizer.jsonify(args)
|
@@ -83,10 +112,17 @@ module SidekiqUniqueJobs
|
|
83
112
|
end
|
84
113
|
end
|
85
114
|
|
115
|
+
# Filters unique arguments by proc configured in the sidekiq worker
|
116
|
+
# @param [Array] args the arguments passed to the sidekiq worker
|
117
|
+
# @return [Array] with the filtered arguments
|
86
118
|
def filter_by_proc(args)
|
87
119
|
unique_args_method.call(args)
|
88
120
|
end
|
89
121
|
|
122
|
+
# Filters unique arguments by method configured in the sidekiq worker
|
123
|
+
# @param [Array] args the arguments passed to the sidekiq worker
|
124
|
+
# @return [Array] unfiltered unless {#worker_method_defined?}
|
125
|
+
# @return [Array] with the filtered arguments
|
90
126
|
def filter_by_symbol(args)
|
91
127
|
return args unless worker_method_defined?(unique_args_method)
|
92
128
|
|
@@ -96,10 +132,16 @@ module SidekiqUniqueJobs
|
|
96
132
|
args
|
97
133
|
end
|
98
134
|
|
135
|
+
# The method to use for filtering unique arguments
|
99
136
|
def unique_args_method
|
100
137
|
@unique_args_method ||= worker_options[UNIQUE_ARGS_KEY]
|
101
138
|
@unique_args_method ||= :unique_args if worker_method_defined?(:unique_args)
|
102
|
-
@unique_args_method ||=
|
139
|
+
@unique_args_method ||= default_unique_args_method
|
140
|
+
end
|
141
|
+
|
142
|
+
# The global worker options defined in Sidekiq directly
|
143
|
+
def default_unique_args_method
|
144
|
+
Sidekiq.default_worker_options.stringify_keys[UNIQUE_ARGS_KEY]
|
103
145
|
end
|
104
146
|
end
|
105
147
|
end
|
@@ -1,14 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SidekiqUniqueJobs
|
4
|
+
# Utility module to help manage unique keys in redis.
|
5
|
+
# Useful for deleting keys that for whatever reason wasn't deleted
|
6
|
+
#
|
7
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
4
8
|
module Unlockable
|
5
9
|
module_function
|
6
10
|
|
11
|
+
# Unlocks a job.
|
12
|
+
# @param [Hash] item a Sidekiq job hash
|
7
13
|
def unlock(item)
|
8
14
|
SidekiqUniqueJobs::UniqueArgs.digest(item)
|
9
15
|
SidekiqUniqueJobs::Locksmith.new(item).unlock
|
10
16
|
end
|
11
17
|
|
18
|
+
# Deletes a lock regardless of if it was locked or not.
|
19
|
+
#
|
20
|
+
# This is good for situations when a job is locked by another item
|
21
|
+
# @param [Hash] item a Sidekiq job hash
|
12
22
|
def delete(item)
|
13
23
|
SidekiqUniqueJobs::UniqueArgs.digest(item)
|
14
24
|
SidekiqUniqueJobs::Locksmith.new(item).delete!
|