sidekiq-unique-jobs 7.0.2 → 7.1.12

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +331 -69
  3. data/README.md +546 -426
  4. data/lib/sidekiq_unique_jobs/changelog.rb +2 -2
  5. data/lib/sidekiq_unique_jobs/config.rb +55 -4
  6. data/lib/sidekiq_unique_jobs/constants.rb +44 -45
  7. data/lib/sidekiq_unique_jobs/deprecation.rb +65 -0
  8. data/lib/sidekiq_unique_jobs/digests.rb +5 -8
  9. data/lib/sidekiq_unique_jobs/exceptions.rb +10 -0
  10. data/lib/sidekiq_unique_jobs/json.rb +7 -0
  11. data/lib/sidekiq_unique_jobs/lock/base_lock.rb +64 -51
  12. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +37 -9
  13. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +23 -5
  14. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +21 -1
  15. data/lib/sidekiq_unique_jobs/lock/until_expired.rb +27 -0
  16. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +15 -8
  17. data/lib/sidekiq_unique_jobs/lock_config.rb +8 -4
  18. data/lib/sidekiq_unique_jobs/lock_ttl.rb +1 -1
  19. data/lib/sidekiq_unique_jobs/locksmith.rb +93 -80
  20. data/lib/sidekiq_unique_jobs/logging.rb +40 -11
  21. data/lib/sidekiq_unique_jobs/lua/lock.lua +3 -3
  22. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_process_set.lua +1 -1
  23. data/lib/sidekiq_unique_jobs/lua/unlock.lua +12 -5
  24. data/lib/sidekiq_unique_jobs/middleware/client.rb +8 -10
  25. data/lib/sidekiq_unique_jobs/middleware/server.rb +2 -0
  26. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +7 -3
  27. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +2 -13
  28. data/lib/sidekiq_unique_jobs/orphans/manager.rb +49 -3
  29. data/lib/sidekiq_unique_jobs/orphans/reaper_resurrector.rb +170 -0
  30. data/lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb +38 -8
  31. data/lib/sidekiq_unique_jobs/redis/entity.rb +7 -1
  32. data/lib/sidekiq_unique_jobs/redis/sorted_set.rb +1 -1
  33. data/lib/sidekiq_unique_jobs/reflectable.rb +26 -0
  34. data/lib/sidekiq_unique_jobs/reflections.rb +79 -0
  35. data/lib/sidekiq_unique_jobs/script/caller.rb +3 -1
  36. data/lib/sidekiq_unique_jobs/server.rb +14 -1
  37. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +35 -13
  38. data/lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb +57 -2
  39. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +1 -11
  40. data/lib/sidekiq_unique_jobs/timer_task.rb +78 -0
  41. data/lib/sidekiq_unique_jobs/timing.rb +1 -1
  42. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  43. data/lib/sidekiq_unique_jobs/web/helpers.rb +5 -5
  44. data/lib/sidekiq_unique_jobs/web/views/_paging.erb +4 -4
  45. data/lib/sidekiq_unique_jobs/web/views/changelogs.erb +1 -1
  46. data/lib/sidekiq_unique_jobs/web/views/locks.erb +17 -15
  47. data/lib/sidekiq_unique_jobs/web.rb +11 -4
  48. data/lib/sidekiq_unique_jobs.rb +7 -1
  49. data/lib/tasks/changelog.rake +15 -15
  50. metadata +17 -16
@@ -68,8 +68,8 @@ module SidekiqUniqueJobs
68
68
  end
69
69
 
70
70
  [
71
- total_size,
72
- result[0], # next_cursor
71
+ total_size.to_i,
72
+ result[0].to_i, # next_cursor
73
73
  result[1].map { |entry| load_json(entry[0]) }, # entries
74
74
  ]
75
75
  end
@@ -16,6 +16,8 @@ module SidekiqUniqueJobs
16
16
  :reaper_count,
17
17
  :reaper_interval,
18
18
  :reaper_timeout,
19
+ :reaper_resurrector_interval,
20
+ :reaper_resurrector_enabled,
19
21
  :lock_info,
20
22
  :raise_on_config_error,
21
23
  :current_redis_version)
@@ -109,6 +111,14 @@ module SidekiqUniqueJobs
109
111
  #
110
112
  # @return [10] stop reaper after 10 seconds
111
113
  REAPER_TIMEOUT = 10
114
+ #
115
+ # @return [3600] check if reaper is dead each 3600 seconds
116
+ REAPER_RESURRECTOR_INTERVAL = 3600
117
+
118
+ #
119
+ # @return [false] enable reaper resurrector
120
+ REAPER_RESURRECTOR_ENABLED = false
121
+
112
122
  #
113
123
  # @return [false] while useful it also adds overhead so disable lock_info by default
114
124
  USE_LOCK_INFO = false
@@ -178,32 +188,73 @@ module SidekiqUniqueJobs
178
188
  REAPER_COUNT,
179
189
  REAPER_INTERVAL,
180
190
  REAPER_TIMEOUT,
191
+ REAPER_RESURRECTOR_INTERVAL,
192
+ REAPER_RESURRECTOR_ENABLED,
181
193
  USE_LOCK_INFO,
182
194
  RAISE_ON_CONFIG_ERROR,
183
195
  REDIS_VERSION,
184
196
  )
185
197
  end
186
198
 
199
+ #
200
+ # Set the default_lock_ttl
201
+ # @deprecated
202
+ #
203
+ # @param [Integer] obj value to set (seconds)
204
+ #
205
+ # @return [<type>] <description>
206
+ #
187
207
  def default_lock_ttl=(obj)
188
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_ttl=` instead."
208
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
209
+ " Please use `#{class_name}#lock_ttl=` instead."
189
210
  self.lock_ttl = obj
190
211
  end
191
212
 
213
+ #
214
+ # Set new value for default_lock_timeout
215
+ # @deprecated
216
+ #
217
+ # @param [Integer] obj value to set (seconds)
218
+ #
219
+ # @return [Integer]
220
+ #
192
221
  def default_lock_timeout=(obj)
193
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_timeout=` instead."
222
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
223
+ " Please use `#{class_name}#lock_timeout=` instead."
194
224
  self.lock_timeout = obj
195
225
  end
196
226
 
227
+ #
228
+ # Default lock TTL (Time To Live)
229
+ # @deprecated
230
+ #
231
+ # @return [nil, Integer] configured value or nil
232
+ #
197
233
  def default_lock_ttl
198
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_ttl` instead."
234
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
235
+ " Please use `#{class_name}#lock_ttl` instead."
199
236
  lock_ttl
200
237
  end
201
238
 
239
+ #
240
+ # Default Lock Timeout
241
+ # @deprecated
242
+ #
243
+ #
244
+ # @return [nil, Integer] configured value or nil
245
+ #
202
246
  def default_lock_timeout
203
- warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated. Please use `#{class_name}#lock_timeout` instead."
247
+ warn "[DEPRECATION] `#{class_name}##{__method__}` is deprecated." \
248
+ " Please use `#{class_name}#lock_timeout` instead."
204
249
  lock_timeout
205
250
  end
206
251
 
252
+ #
253
+ # Memoized variable to get the class name
254
+ #
255
+ #
256
+ # @return [String] name of the class
257
+ #
207
258
  def class_name
208
259
  @class_name ||= self.class.name
209
260
  end
@@ -6,49 +6,48 @@
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
- LOG_DUPLICATE ||= "log_duplicate"
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"
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"
54
53
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ #
5
+ # Class Deprecation provides logging of deprecations
6
+ #
7
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
8
+ #
9
+ class Deprecation
10
+ #
11
+ # Mute warnings from this gem in a threaded context
12
+ #
13
+ #
14
+ # @return [void] <description>
15
+ #
16
+ # @yieldreturn [void]
17
+ def self.muted
18
+ orig_val = Thread.current[:uniquejobs_mute_deprecations]
19
+ Thread.current[:uniquejobs_mute_deprecations] = true
20
+ yield
21
+ ensure
22
+ Thread.current[:uniquejobs_mute_deprecations] = orig_val
23
+ end
24
+
25
+ #
26
+ # Check if deprecation warnings have been muted
27
+ #
28
+ #
29
+ # @return [true,false]
30
+ #
31
+ def self.muted?
32
+ Thread.current[:uniquejobs_mute_deprecations] == true
33
+ end
34
+
35
+ #
36
+ # Warn about deprecation
37
+ #
38
+ # @param [String] msg a descriptive reason for why the deprecation
39
+ #
40
+ # @return [void]
41
+ #
42
+ def self.warn(msg)
43
+ return if SidekiqUniqueJobs::Deprecation.muted?
44
+
45
+ warn "DEPRECATION WARNING: #{msg}"
46
+ nil
47
+ end
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
+ #
56
+ def self.warn_with_backtrace(msg)
57
+ return if SidekiqUniqueJobs::Deprecation.muted?
58
+
59
+ trace = "\n\nCALLED FROM:\n#{caller.join("\n")}"
60
+ warn(msg + trace)
61
+
62
+ nil
63
+ end
64
+ end
65
+ end
@@ -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
  #
@@ -104,8 +101,8 @@ module SidekiqUniqueJobs
104
101
  end
105
102
 
106
103
  [
107
- total_size,
108
- digests[0], # next_cursor
104
+ total_size.to_i,
105
+ digests[0].to_i, # next_cursor
109
106
  digests[1].map { |digest, score| Lock.new(digest, time: score) }, # entries
110
107
  ]
111
108
  end
@@ -18,6 +18,16 @@ module SidekiqUniqueJobs
18
18
  end
19
19
  end
20
20
 
21
+ #
22
+ # Raised when no block was given
23
+ #
24
+ class NoBlockGiven < SidekiqUniqueJobs::UniqueJobsError; end
25
+
26
+ #
27
+ # Raised when a notification has been mistyped
28
+ #
29
+ class NoSuchNotificationError < UniqueJobsError; end
30
+
21
31
  #
22
32
  # Error raised when trying to add a duplicate lock
23
33
  #
@@ -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
 
@@ -7,8 +7,16 @@ module SidekiqUniqueJobs
7
7
  # @abstract
8
8
  # @author Mikael Henriksson <mikael@mhenrixon.com>
9
9
  class BaseLock
10
+ extend Forwardable
11
+
12
+ # includes "SidekiqUniqueJobs::Logging"
13
+ # @!parse include SidekiqUniqueJobs::Logging
10
14
  include SidekiqUniqueJobs::Logging
11
15
 
16
+ # includes "SidekiqUniqueJobs::Reflectable"
17
+ # @!parse include SidekiqUniqueJobs::Reflectable
18
+ include SidekiqUniqueJobs::Reflectable
19
+
12
20
  #
13
21
  # Validates that the sidekiq_options for the worker is valid
14
22
  #
@@ -20,6 +28,10 @@ module SidekiqUniqueJobs
20
28
  Validator.validate(options)
21
29
  end
22
30
 
31
+ # NOTE: Mainly used for a clean testing API
32
+ #
33
+ def_delegators :locksmith, :locked?
34
+
23
35
  # @param [Hash] item the Sidekiq job hash
24
36
  # @param [Proc] callback the callback to use after unlock
25
37
  # @param [Sidekiq::RedisConnection, ConnectionPool] redis_pool the redis connection
@@ -41,10 +53,8 @@ module SidekiqUniqueJobs
41
53
  #
42
54
  # @yield to the caller when given a block
43
55
  #
44
- def lock(&block)
45
- return call_strategy unless (locked_token = locksmith.lock(&block))
46
-
47
- locked_token
56
+ def lock
57
+ raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
48
58
  end
49
59
 
50
60
  # Execute the job in the Sidekiq server processor
@@ -53,31 +63,6 @@ module SidekiqUniqueJobs
53
63
  raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
54
64
  end
55
65
 
56
- # Unlocks the job from redis
57
- # @return [String] sidekiq job id when successful
58
- # @return [false] when unsuccessful
59
- def unlock
60
- locksmith.unlock # Only signal to release the lock
61
- end
62
-
63
- # Deletes the job from redis if it is locked.
64
- def delete
65
- locksmith.delete # Soft delete (don't forcefully remove when expiration is set)
66
- end
67
-
68
- # Forcefully deletes the job from redis.
69
- # This is good for jobs when a previous lock was not unlocked
70
- def delete!
71
- locksmith.delete! # Force delete the lock
72
- end
73
-
74
- # Checks if the item has achieved a lock
75
- # @return [true] when this jid has locked the job
76
- # @return [false] when this jid has not locked the job
77
- def locked?
78
- locksmith.locked?
79
- end
80
-
81
66
  #
82
67
  # The lock manager/client
83
68
  #
@@ -90,23 +75,6 @@ module SidekiqUniqueJobs
90
75
 
91
76
  private
92
77
 
93
- def prepare_item
94
- return if item.key?(LOCK_DIGEST)
95
-
96
- # The below should only be done to ease testing
97
- # in production this will be done by the middleware
98
- SidekiqUniqueJobs::Job.prepare(item)
99
- end
100
-
101
- def call_strategy
102
- @attempt += 1
103
- client_strategy.call { lock if replace? }
104
- end
105
-
106
- def replace?
107
- client_strategy.replace? && attempt < 2
108
- end
109
-
110
78
  # @!attribute [r] item
111
79
  # @return [Hash<String, Object>] the Sidekiq job hash
112
80
  attr_reader :item
@@ -123,21 +91,66 @@ module SidekiqUniqueJobs
123
91
  # @return [Integer] the current locking attempt
124
92
  attr_reader :attempt
125
93
 
126
- def unlock_with_callback
127
- return log_warn("might need to be unlocked manually") unless unlock
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
+ #
101
+ def prepare_item
102
+ return if item.key?(LOCK_DIGEST)
128
103
 
129
- callback_safely
130
- item[JID]
104
+ # The below should only be done to ease testing
105
+ # in production this will be done by the middleware
106
+ SidekiqUniqueJobs::Job.prepare(item)
107
+ end
108
+
109
+ #
110
+ # Call whatever strategry that has been configured
111
+ #
112
+ # @param [Symbol] origin: the origin `:client` or `:server`
113
+ #
114
+ # @return [void] the return value is irrelevant
115
+ #
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.
119
+ def call_strategy(origin:)
120
+ new_job_id = nil
121
+ strategy = strategy_for(origin)
122
+ @attempt += 1
123
+
124
+ strategy.call { new_job_id = lock if strategy.replace? && @attempt < 2 }
125
+ yield if new_job_id && block_given?
126
+ end
127
+
128
+ def unlock_and_callback
129
+ return callback_safely if locksmith.unlock
130
+
131
+ reflect(:unlock_failed, item)
131
132
  end
132
133
 
133
134
  def callback_safely
134
135
  callback&.call
135
136
  item[JID]
136
137
  rescue StandardError
137
- log_warn("unlocked successfully but the #after_unlock callback failed!")
138
+ reflect(:after_unlock_callback_failed, item)
138
139
  raise
139
140
  end
140
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
+
141
154
  def client_strategy
142
155
  @client_strategy ||=
143
156
  OnConflict.find_strategy(lock_config.on_client_conflict).new(item, redis_pool)
@@ -13,30 +13,58 @@ module SidekiqUniqueJobs
13
13
  #
14
14
  # @author Mikael Henriksson <mikael@mhenrixon.com>
15
15
  class UntilAndWhileExecuting < BaseLock
16
+ #
17
+ # Locks a sidekiq job
18
+ #
19
+ # @note Will call a conflict strategy if lock can't be achieved.
20
+ #
21
+ # @return [String, nil] the locked jid when properly locked, else nil.
22
+ #
23
+ # @yield to the caller when given a block
24
+ #
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
34
+
35
+ token
36
+ end
37
+
16
38
  # Executes in the Sidekiq server process
17
39
  # @yield to the worker class perform method
18
40
  def execute
19
- if unlock
20
- lock_on_failure do
21
- runtime_lock.execute { return yield }
22
- end
41
+ if locksmith.unlock
42
+ # ensure_relocked do
43
+ runtime_lock.execute { return yield }
44
+ # end
23
45
  else
24
- log_warn "couldn't unlock digest: #{item[LOCK_DIGEST]} #{item[JID]}"
46
+ reflect(:unlock_failed, item)
25
47
  end
48
+ rescue Exception # rubocop:disable Lint/RescueException
49
+ reflect(:execution_failed, item)
50
+ locksmith.lock(wait: 2)
51
+
52
+ raise
26
53
  end
27
54
 
28
55
  private
29
56
 
30
- def lock_on_failure
57
+ def ensure_relocked
31
58
  yield
32
59
  rescue Exception # rubocop:disable Lint/RescueException
33
- log_error("Runtime lock failed to execute job, restoring server lock")
34
- lock
60
+ reflect(:execution_failed, item)
61
+ locksmith.lock
62
+
35
63
  raise
36
64
  end
37
65
 
38
66
  def runtime_lock
39
- @runtime_lock ||= SidekiqUniqueJobs::Lock::WhileExecuting.new(item, callback, redis_pool)
67
+ @runtime_lock ||= SidekiqUniqueJobs::Lock::WhileExecuting.new(item.dup, callback, redis_pool)
40
68
  end
41
69
  end
42
70
  end
@@ -8,16 +8,34 @@ module SidekiqUniqueJobs
8
8
  #
9
9
  # @author Mikael Henriksson <mikael@mhenrixon.com>
10
10
  class UntilExecuted < BaseLock
11
- OK ||= "OK"
11
+ #
12
+ # Locks a sidekiq job
13
+ #
14
+ # @note Will call a conflict strategy if lock can't be achieved.
15
+ #
16
+ # @return [String, nil] the locked jid when properly locked, else nil.
17
+ #
18
+ # @yield to the caller when given a block
19
+ #
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
29
+
30
+ token
31
+ end
12
32
 
13
33
  # Executes in the Sidekiq server process
14
34
  # @yield to the worker class perform method
15
35
  def execute
16
- lock do
36
+ locksmith.execute do
17
37
  yield
18
- unlock_with_callback
19
- callback_safely
20
- item[JID]
38
+ unlock_and_callback
21
39
  end
22
40
  end
23
41
  end
@@ -8,10 +8,30 @@ module SidekiqUniqueJobs
8
8
  #
9
9
  # @author Mikael Henriksson <mikael@mhenrixon.com>
10
10
  class UntilExecuting < BaseLock
11
+ #
12
+ # Locks a sidekiq job
13
+ #
14
+ # @note Will call a conflict strategy if lock can't be achieved.
15
+ #
16
+ # @return [String, nil] the locked jid when properly locked, else nil.
17
+ #
18
+ def lock(&block)
19
+ unless (token = locksmith.lock)
20
+ reflect(:lock_failed, item)
21
+ call_strategy(origin: :client, &block)
22
+
23
+ return
24
+ end
25
+
26
+ yield if block
27
+
28
+ token
29
+ end
30
+
11
31
  # Executes in the Sidekiq server process
12
32
  # @yield to the worker class perform method
13
33
  def execute
14
- unlock_with_callback
34
+ callback_safely if locksmith.unlock
15
35
  yield
16
36
  end
17
37
  end
@@ -8,6 +8,33 @@ module SidekiqUniqueJobs
8
8
  # @author Mikael Henriksson <mikael@mhenrixon.com>
9
9
  #
10
10
  class UntilExpired < UntilExecuted
11
+ #
12
+ # Locks a sidekiq job
13
+ #
14
+ # @note Will call a conflict strategy if lock can't be achieved.
15
+ #
16
+ # @return [String, nil] the locked jid when properly locked, else nil.
17
+ #
18
+ # @yield to the caller when given a block
19
+ #
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
29
+
30
+ token
31
+ end
32
+
33
+ # Executes in the Sidekiq server process
34
+ # @yield to the worker class perform method
35
+ def execute(&block)
36
+ locksmith.execute(&block)
37
+ end
11
38
  end
12
39
  end
13
40
  end