sidekiq-unique-jobs 7.1.6 → 7.1.30

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +312 -3
  3. data/README.md +19 -15
  4. data/lib/sidekiq_unique_jobs/batch_delete.rb +8 -7
  5. data/lib/sidekiq_unique_jobs/changelog.rb +3 -3
  6. data/lib/sidekiq_unique_jobs/cli.rb +33 -8
  7. data/lib/sidekiq_unique_jobs/config.rb +48 -4
  8. data/lib/sidekiq_unique_jobs/constants.rb +45 -44
  9. data/lib/sidekiq_unique_jobs/core_ext.rb +1 -1
  10. data/lib/sidekiq_unique_jobs/deprecation.rb +30 -0
  11. data/lib/sidekiq_unique_jobs/digests.rb +8 -11
  12. data/lib/sidekiq_unique_jobs/exceptions.rb +4 -3
  13. data/lib/sidekiq_unique_jobs/expiring_digests.rb +14 -0
  14. data/lib/sidekiq_unique_jobs/job.rb +5 -0
  15. data/lib/sidekiq_unique_jobs/json.rb +7 -0
  16. data/lib/sidekiq_unique_jobs/key.rb +13 -8
  17. data/lib/sidekiq_unique_jobs/lock/base_lock.rb +30 -23
  18. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +9 -3
  19. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +15 -4
  20. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +14 -4
  21. data/lib/sidekiq_unique_jobs/lock/until_expired.rb +13 -5
  22. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +10 -6
  23. data/lib/sidekiq_unique_jobs/lock.rb +27 -10
  24. data/lib/sidekiq_unique_jobs/lock_args.rb +19 -15
  25. data/lib/sidekiq_unique_jobs/lock_config.rb +6 -6
  26. data/lib/sidekiq_unique_jobs/lock_digest.rb +7 -7
  27. data/lib/sidekiq_unique_jobs/lock_info.rb +2 -2
  28. data/lib/sidekiq_unique_jobs/lock_timeout.rb +4 -4
  29. data/lib/sidekiq_unique_jobs/lock_ttl.rb +5 -5
  30. data/lib/sidekiq_unique_jobs/lock_type.rb +37 -0
  31. data/lib/sidekiq_unique_jobs/locksmith.rb +41 -10
  32. data/lib/sidekiq_unique_jobs/logging.rb +23 -0
  33. data/lib/sidekiq_unique_jobs/lua/lock.lua +18 -12
  34. data/lib/sidekiq_unique_jobs/lua/lock_until_expired.lua +92 -0
  35. data/lib/sidekiq_unique_jobs/lua/reap_orphans.lua +31 -3
  36. data/lib/sidekiq_unique_jobs/lua/shared/_common.lua +1 -1
  37. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_process_set.lua +1 -1
  38. data/lib/sidekiq_unique_jobs/lua/unlock.lua +17 -5
  39. data/lib/sidekiq_unique_jobs/middleware/client.rb +3 -1
  40. data/lib/sidekiq_unique_jobs/middleware/server.rb +2 -0
  41. data/lib/sidekiq_unique_jobs/middleware.rb +4 -4
  42. data/lib/sidekiq_unique_jobs/on_conflict/reject.rb +4 -4
  43. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +3 -3
  44. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +6 -8
  45. data/lib/sidekiq_unique_jobs/orphans/lua_reaper.rb +1 -1
  46. data/lib/sidekiq_unique_jobs/orphans/manager.rb +40 -12
  47. data/lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb +99 -14
  48. data/lib/sidekiq_unique_jobs/redis/string.rb +3 -1
  49. data/lib/sidekiq_unique_jobs/reflectable.rb +11 -2
  50. data/lib/sidekiq_unique_jobs/reflections.rb +12 -1
  51. data/lib/sidekiq_unique_jobs/script/caller.rb +7 -7
  52. data/lib/sidekiq_unique_jobs/server.rb +13 -1
  53. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +1 -1
  54. data/lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb +13 -3
  55. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +40 -21
  56. data/lib/sidekiq_unique_jobs/testing.rb +53 -21
  57. data/lib/sidekiq_unique_jobs/timer_task.rb +266 -45
  58. data/lib/sidekiq_unique_jobs/timing.rb +1 -1
  59. data/lib/sidekiq_unique_jobs/upgrade_locks.rb +6 -6
  60. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  61. data/lib/sidekiq_unique_jobs/web/helpers.rb +11 -1
  62. data/lib/sidekiq_unique_jobs/web/views/lock.erb +5 -3
  63. data/lib/sidekiq_unique_jobs/web.rb +22 -3
  64. data/lib/sidekiq_unique_jobs.rb +2 -0
  65. data/lib/tasks/changelog.rake +16 -16
  66. metadata +26 -13
@@ -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,
@@ -196,26 +201,65 @@ module SidekiqUniqueJobs
196
201
  )
197
202
  end
198
203
 
204
+ #
205
+ # Set the default_lock_ttl
206
+ # @deprecated
207
+ #
208
+ # @param [Integer] obj value to set (seconds)
209
+ #
210
+ # @return [<type>] <description>
211
+ #
199
212
  def default_lock_ttl=(obj)
200
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_ttl=` instead."
213
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
214
+ " Please use `#{class_name}#lock_ttl=` instead."
201
215
  self.lock_ttl = obj
202
216
  end
203
217
 
218
+ #
219
+ # Set new value for default_lock_timeout
220
+ # @deprecated
221
+ #
222
+ # @param [Integer] obj value to set (seconds)
223
+ #
224
+ # @return [Integer]
225
+ #
204
226
  def default_lock_timeout=(obj)
205
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_timeout=` instead."
227
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
228
+ " Please use `#{class_name}#lock_timeout=` instead."
206
229
  self.lock_timeout = obj
207
230
  end
208
231
 
232
+ #
233
+ # Default lock TTL (Time To Live)
234
+ # @deprecated
235
+ #
236
+ # @return [nil, Integer] configured value or nil
237
+ #
209
238
  def default_lock_ttl
210
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_ttl` instead."
239
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
240
+ " Please use `#{class_name}#lock_ttl` instead."
211
241
  lock_ttl
212
242
  end
213
243
 
244
+ #
245
+ # Default Lock Timeout
246
+ # @deprecated
247
+ #
248
+ #
249
+ # @return [nil, Integer] configured value or nil
250
+ #
214
251
  def default_lock_timeout
215
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_timeout` instead."
252
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
253
+ " Please use `#{class_name}#lock_timeout` instead."
216
254
  lock_timeout
217
255
  end
218
256
 
257
+ #
258
+ # Memoized variable to get the class name
259
+ #
260
+ #
261
+ # @return [String] name of the class
262
+ #
219
263
  def class_name
220
264
  @class_name ||= self.class.name
221
265
  end
@@ -6,48 +6,49 @@
6
6
  # @author Mikael Henriksson <mikael@mhenrixon.com>
7
7
  #
8
8
  module SidekiqUniqueJobs
9
- ARGS ||= "args"
10
- APARTMENT ||= "apartment"
11
- AT ||= "at"
12
- CHANGELOGS ||= "uniquejobs:changelog"
13
- CLASS ||= "class"
14
- CREATED_AT ||= "created_at"
15
- DEAD_VERSION ||= "uniquejobs:dead"
16
- DIGESTS ||= "uniquejobs:digests"
17
- ERRORS ||= "errors"
18
- JID ||= "jid"
19
- LIMIT ||= "limit"
20
- LIVE_VERSION ||= "uniquejobs:live"
21
- LOCK ||= "lock"
22
- LOCK_ARGS ||= "lock_args"
23
- LOCK_ARGS_METHOD ||= "lock_args_method"
24
- LOCK_DIGEST ||= "lock_digest"
25
- LOCK_EXPIRATION ||= "lock_expiration"
26
- LOCK_INFO ||= "lock_info"
27
- LOCK_LIMIT ||= "lock_limit"
28
- LOCK_PREFIX ||= "lock_prefix"
29
- LOCK_TIMEOUT ||= "lock_timeout"
30
- LOCK_TTL ||= "lock_ttl"
31
- LOCK_TYPE ||= "lock_type"
32
- ON_CLIENT_CONFLICT ||= "on_client_conflict"
33
- ON_CONFLICT ||= "on_conflict"
34
- ON_SERVER_CONFLICT ||= "on_server_conflict"
35
- PAYLOAD ||= "payload"
36
- PROCESSES ||= "processes"
37
- QUEUE ||= "queue"
38
- RETRY ||= "retry"
39
- SCHEDULE ||= "schedule"
40
- TIME ||= "time"
41
- TIMEOUT ||= "timeout"
42
- TTL ||= "ttl"
43
- TYPE ||= "type"
44
- UNIQUE ||= "unique"
45
- UNIQUE_ACROSS_QUEUES ||= "unique_across_queues"
46
- UNIQUE_ACROSS_WORKERS ||= "unique_across_workers"
47
- UNIQUE_ARGS ||= "unique_args"
48
- UNIQUE_ARGS_METHOD ||= "unique_args_method"
49
- UNIQUE_DIGEST ||= "unique_digest"
50
- UNIQUE_PREFIX ||= "unique_prefix"
51
- UNIQUE_REAPER ||= "uniquejobs:reaper"
52
- WORKER ||= "worker"
9
+ ARGS = "args"
10
+ APARTMENT = "apartment"
11
+ AT = "at"
12
+ CHANGELOGS = "uniquejobs:changelog"
13
+ CLASS = "class"
14
+ CREATED_AT = "created_at"
15
+ DEAD_VERSION = "uniquejobs:dead"
16
+ DIGESTS = "uniquejobs:digests"
17
+ EXPIRING_DIGESTS = "uniquejobs:expiring_digests"
18
+ ERRORS = "errors"
19
+ JID = "jid"
20
+ LIMIT = "limit"
21
+ LIVE_VERSION = "uniquejobs:live"
22
+ LOCK = "lock"
23
+ LOCK_ARGS = "lock_args"
24
+ LOCK_ARGS_METHOD = "lock_args_method"
25
+ LOCK_DIGEST = "lock_digest"
26
+ LOCK_EXPIRATION = "lock_expiration"
27
+ LOCK_INFO = "lock_info"
28
+ LOCK_LIMIT = "lock_limit"
29
+ LOCK_PREFIX = "lock_prefix"
30
+ LOCK_TIMEOUT = "lock_timeout"
31
+ LOCK_TTL = "lock_ttl"
32
+ LOCK_TYPE = "lock_type"
33
+ ON_CLIENT_CONFLICT = "on_client_conflict"
34
+ ON_CONFLICT = "on_conflict"
35
+ ON_SERVER_CONFLICT = "on_server_conflict"
36
+ PAYLOAD = "payload"
37
+ PROCESSES = "processes"
38
+ QUEUE = "queue"
39
+ RETRY = "retry"
40
+ SCHEDULE = "schedule"
41
+ TIME = "time"
42
+ TIMEOUT = "timeout"
43
+ TTL = "ttl"
44
+ TYPE = "type"
45
+ UNIQUE = "unique"
46
+ UNIQUE_ACROSS_QUEUES = "unique_across_queues"
47
+ UNIQUE_ACROSS_WORKERS = "unique_across_workers"
48
+ UNIQUE_ARGS = "unique_args"
49
+ UNIQUE_ARGS_METHOD = "unique_args_method"
50
+ UNIQUE_DIGEST = "unique_digest"
51
+ UNIQUE_PREFIX = "unique_prefix"
52
+ UNIQUE_REAPER = "uniquejobs:reaper"
53
+ WORKER = "worker"
53
54
  end
@@ -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,6 +7,13 @@ module SidekiqUniqueJobs
7
7
  # @author Mikael Henriksson <mikael@mhenrixon.com>
8
8
  #
9
9
  class Deprecation
10
+ #
11
+ # Mute warnings from this gem in a threaded context
12
+ #
13
+ #
14
+ # @return [void] <description>
15
+ #
16
+ # @yieldreturn [void]
10
17
  def self.muted
11
18
  orig_val = Thread.current[:uniquejobs_mute_deprecations]
12
19
  Thread.current[:uniquejobs_mute_deprecations] = true
@@ -15,21 +22,44 @@ module SidekiqUniqueJobs
15
22
  Thread.current[:uniquejobs_mute_deprecations] = orig_val
16
23
  end
17
24
 
25
+ #
26
+ # Check if deprecation warnings have been muted
27
+ #
28
+ #
29
+ # @return [true,false]
30
+ #
18
31
  def self.muted?
19
32
  Thread.current[:uniquejobs_mute_deprecations] == true
20
33
  end
21
34
 
35
+ #
36
+ # Warn about deprecation
37
+ #
38
+ # @param [String] msg a descriptive reason for why the deprecation
39
+ #
40
+ # @return [void]
41
+ #
22
42
  def self.warn(msg)
23
43
  return if SidekiqUniqueJobs::Deprecation.muted?
24
44
 
25
45
  warn "DEPRECATION WARNING: #{msg}"
46
+ nil
26
47
  end
27
48
 
49
+ #
50
+ # Warn about deprecation and provide a context
51
+ #
52
+ # @param [String] msg a descriptive reason for why the deprecation
53
+ #
54
+ # @return [void]
55
+ #
28
56
  def self.warn_with_backtrace(msg)
29
57
  return if SidekiqUniqueJobs::Deprecation.muted?
30
58
 
31
59
  trace = "\n\nCALLED FROM:\n#{caller.join("\n")}"
32
60
  warn(msg + trace)
61
+
62
+ nil
33
63
  end
34
64
  end
35
65
  end
@@ -14,8 +14,8 @@ module SidekiqUniqueJobs
14
14
  # @return [String] the default pattern to use for matching
15
15
  SCAN_PATTERN = "*"
16
16
 
17
- def initialize
18
- super(DIGESTS)
17
+ def initialize(digests_key = DIGESTS)
18
+ super(digests_key)
19
19
  end
20
20
 
21
21
  #
@@ -31,7 +31,8 @@ module SidekiqUniqueJobs
31
31
  #
32
32
  # @param [String] pattern a key pattern to match with
33
33
  # @param [Integer] count the maximum number
34
- # @return [Array<String>] with unique digests
34
+ # @return [Hash<String,Float>] Hash mapping of digest matching the given pattern and score
35
+
35
36
  def delete_by_pattern(pattern, count: DEFAULT_COUNT)
36
37
  result, elapsed = timed do
37
38
  digests = entries(pattern: pattern, count: count).keys
@@ -80,11 +81,7 @@ module SidekiqUniqueJobs
80
81
  options[:match] = pattern
81
82
  options[:count] = count
82
83
 
83
- result = redis { |conn| conn.zscan_each(key, **options).to_a }
84
-
85
- result.each_with_object({}) do |entry, hash|
86
- hash[entry[0]] = entry[1]
87
- end
84
+ redis { |conn| conn.zscan_each(key, **options).to_a }.to_h
88
85
  end
89
86
 
90
87
  #
@@ -98,9 +95,9 @@ module SidekiqUniqueJobs
98
95
  #
99
96
  def page(cursor: 0, pattern: SCAN_PATTERN, page_size: 100)
100
97
  redis do |conn|
101
- total_size, digests = conn.multi do
102
- conn.zcard(key)
103
- conn.zscan(key, cursor, match: pattern, count: page_size)
98
+ total_size, digests = conn.multi do |pipeline|
99
+ pipeline.zcard(key)
100
+ pipeline.zscan(key, cursor, match: pattern, count: page_size)
104
101
  end
105
102
 
106
103
  [
@@ -22,6 +22,7 @@ module SidekiqUniqueJobs
22
22
  # Raised when no block was given
23
23
  #
24
24
  class NoBlockGiven < SidekiqUniqueJobs::UniqueJobsError; end
25
+
25
26
  #
26
27
  # Raised when a notification has been mistyped
27
28
  #
@@ -71,14 +72,14 @@ module SidekiqUniqueJobs
71
72
  class InvalidUniqueArguments < UniqueJobsError
72
73
  def initialize(options)
73
74
  given = options[:given]
74
- worker_class = options[:worker_class]
75
+ job_class = options[:job_class]
75
76
  lock_args_method = options[:lock_args_method]
76
- lock_args_meth = worker_class.method(lock_args_method)
77
+ lock_args_meth = job_class.method(lock_args_method)
77
78
  num_args = lock_args_meth.arity
78
79
  source_location = lock_args_meth.source_location
79
80
 
80
81
  super(
81
- "#{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}" \
82
83
  "\n\n" \
83
84
  " #{source_location.join(':')}"
84
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
@@ -11,6 +11,7 @@ module SidekiqUniqueJobs
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
@@ -20,6 +20,13 @@ module SidekiqUniqueJobs
20
20
  ::JSON.parse(string)
21
21
  end
22
22
 
23
+ #
24
+ # Prevents trying JSON.load from raising errors given argument is a hash
25
+ #
26
+ # @param [String, Hash] string the JSON string to parse
27
+ #
28
+ # @return [Hash,Array]
29
+ #
23
30
  def safe_load_json(string)
24
31
  return string if string.is_a?(Hash)
25
32
 
@@ -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
@@ -91,6 +91,13 @@ module SidekiqUniqueJobs
91
91
  # @return [Integer] the current locking attempt
92
92
  attr_reader :attempt
93
93
 
94
+ #
95
+ # Eases testing by allowing the lock implementation to add the missing
96
+ # keys to the job hash.
97
+ #
98
+ #
99
+ # @return [void] the return value should be irrelevant
100
+ #
94
101
  def prepare_item
95
102
  return if item.key?(LOCK_DIGEST)
96
103
 
@@ -100,34 +107,22 @@ module SidekiqUniqueJobs
100
107
  end
101
108
 
102
109
  #
103
- # Handle when lock failed
110
+ # Call whatever strategry that has been configured
104
111
  #
105
- # @param [Symbol] location: :client or :server
112
+ # @param [Symbol] origin: the origin `:client` or `:server`
106
113
  #
107
- # @return [void]
114
+ # @return [void] the return value is irrelevant
108
115
  #
109
- def lock_failed(origin: :client)
110
- reflect(:lock_failed, item)
111
- call_strategy(origin: origin)
112
- nil
113
- end
114
-
116
+ # @yieldparam [void] if a new job id was set and a block is given
117
+ # @yieldreturn [void] the yield is irrelevant, it only provides a mechanism in
118
+ # one specific situation to yield back to the middleware.
115
119
  def call_strategy(origin:)
116
- @attempt += 1
120
+ new_job_id = nil
121
+ strategy = strategy_for(origin)
122
+ @attempt += 1
117
123
 
118
- case origin
119
- when :client
120
- client_strategy.call { lock if replace? }
121
- when :server
122
- server_strategy.call { lock if replace? }
123
- else
124
- raise SidekiqUniqueJobs::InvalidArgument,
125
- "either `for: :server` or `for: :client` needs to be specified"
126
- end
127
- end
128
-
129
- def replace?
130
- client_strategy.replace? && attempt < 2
124
+ strategy.call { new_job_id = lock if strategy.replace? && @attempt < 2 }
125
+ yield if new_job_id && block_given?
131
126
  end
132
127
 
133
128
  def unlock_and_callback
@@ -144,6 +139,18 @@ module SidekiqUniqueJobs
144
139
  raise
145
140
  end
146
141
 
142
+ def strategy_for(origin)
143
+ case origin
144
+ when :client
145
+ client_strategy
146
+ when :server
147
+ server_strategy
148
+ else
149
+ raise SidekiqUniqueJobs::InvalidArgument,
150
+ "#origin needs to be either `:server` or `:client`"
151
+ end
152
+ end
153
+
147
154
  def client_strategy
148
155
  @client_strategy ||=
149
156
  OnConflict.find_strategy(lock_config.on_client_conflict).new(item, redis_pool)
@@ -22,9 +22,15 @@ module SidekiqUniqueJobs
22
22
  #
23
23
  # @yield to the caller when given a block
24
24
  #
25
- def lock(origin: :client)
26
- return lock_failed(origin: origin) unless (token = locksmith.lock)
27
- return yield token if block_given?
25
+ def lock(origin: :client, &block)
26
+ unless (token = locksmith.lock)
27
+ reflect(:lock_failed, item)
28
+ call_strategy(origin: origin, &block)
29
+
30
+ return
31
+ end
32
+
33
+ yield if block
28
34
 
29
35
  token
30
36
  end
@@ -17,9 +17,15 @@ module SidekiqUniqueJobs
17
17
  #
18
18
  # @yield to the caller when given a block
19
19
  #
20
- def lock
21
- return lock_failed(origin: :client) unless (token = locksmith.lock)
22
- return yield token if block_given?
20
+ def lock(&block)
21
+ unless (token = locksmith.lock)
22
+ reflect(:lock_failed, item)
23
+ call_strategy(origin: :client, &block)
24
+
25
+ return
26
+ end
27
+
28
+ yield if block
23
29
 
24
30
  token
25
31
  end
@@ -27,10 +33,15 @@ module SidekiqUniqueJobs
27
33
  # Executes in the Sidekiq server process
28
34
  # @yield to the worker class perform method
29
35
  def execute
30
- locksmith.execute do
36
+ executed = locksmith.execute do
31
37
  yield
38
+ ensure
32
39
  unlock_and_callback
33
40
  end
41
+
42
+ reflect(:execution_failed, item) unless executed
43
+
44
+ nil
34
45
  end
35
46
  end
36
47
  end
@@ -15,11 +15,17 @@ module SidekiqUniqueJobs
15
15
  #
16
16
  # @return [String, nil] the locked jid when properly locked, else nil.
17
17
  #
18
- def lock
19
- return lock_failed unless (job_id = locksmith.lock)
20
- return yield job_id if block_given?
18
+ def lock(&block)
19
+ unless (token = locksmith.lock)
20
+ reflect(:lock_failed, item)
21
+ call_strategy(origin: :client, &block)
21
22
 
22
- job_id
23
+ return
24
+ end
25
+
26
+ yield if block
27
+
28
+ token
23
29
  end
24
30
 
25
31
  # Executes in the Sidekiq server process
@@ -27,6 +33,10 @@ module SidekiqUniqueJobs
27
33
  def execute
28
34
  callback_safely if locksmith.unlock
29
35
  yield
36
+ rescue StandardError => ex
37
+ reflect(:execution_failed, item, ex)
38
+ locksmith.lock(wait: 1)
39
+ raise
30
40
  end
31
41
  end
32
42
  end
@@ -17,17 +17,25 @@ module SidekiqUniqueJobs
17
17
  #
18
18
  # @yield to the caller when given a block
19
19
  #
20
- def lock
21
- return lock_failed unless (job_id = locksmith.lock)
22
- return yield job_id if block_given?
20
+ def lock(&block)
21
+ unless (token = locksmith.lock)
22
+ reflect(:lock_failed, item)
23
+ call_strategy(origin: :client, &block)
23
24
 
24
- job_id
25
+ return
26
+ end
27
+
28
+ yield if block
29
+
30
+ token
25
31
  end
26
32
 
27
33
  # Executes in the Sidekiq server process
28
34
  # @yield to the worker class perform method
29
35
  def execute(&block)
30
- locksmith.execute(&block)
36
+ executed = locksmith.execute(&block)
37
+
38
+ reflect(:execution_failed, item) unless executed
31
39
  end
32
40
  end
33
41
  end
@@ -11,7 +11,7 @@ module SidekiqUniqueJobs
11
11
  #
12
12
  # @author Mikael Henriksson <mikael@mhenrixon.com>
13
13
  class WhileExecuting < BaseLock
14
- RUN_SUFFIX ||= ":RUN"
14
+ RUN_SUFFIX = ":RUN"
15
15
 
16
16
  include SidekiqUniqueJobs::OptionsWithFallback
17
17
  include SidekiqUniqueJobs::Logging::Middleware
@@ -30,7 +30,7 @@ module SidekiqUniqueJobs
30
30
  # @return [true] always returns true
31
31
  def lock
32
32
  job_id = item[JID]
33
- yield job_id if block_given?
33
+ yield if block_given?
34
34
 
35
35
  job_id
36
36
  end
@@ -38,13 +38,17 @@ module SidekiqUniqueJobs
38
38
  # Executes in the Sidekiq server process.
39
39
  # These jobs are locked in the server process not from the client
40
40
  # @yield to the worker class perform method
41
- def execute
41
+ def execute(&block)
42
42
  with_logging_context do
43
- call_strategy(origin: :server) unless locksmith.execute do
43
+ executed = locksmith.execute do
44
44
  yield
45
- callback_safely if locksmith.unlock
46
45
  ensure
47
- locksmith.unlock
46
+ unlock_and_callback
47
+ end
48
+
49
+ unless executed
50
+ reflect(:execution_failed, item)
51
+ call_strategy(origin: :server, &block)
48
52
  end
49
53
  end
50
54
  end