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
@@ -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
@@ -29,21 +29,26 @@ module SidekiqUniqueJobs
29
29
  # These locks should only ever be created in the server process.
30
30
  # @return [true] always returns true
31
31
  def lock
32
- true
32
+ job_id = item[JID]
33
+ yield if block_given?
34
+
35
+ job_id
33
36
  end
34
37
 
35
38
  # Executes in the Sidekiq server process.
36
39
  # These jobs are locked in the server process not from the client
37
40
  # @yield to the worker class perform method
38
- def execute
41
+ def execute(&block)
39
42
  with_logging_context do
40
- server_strategy&.call unless locksmith.lock do
43
+ executed = locksmith.execute do
41
44
  yield
42
- callback_safely
45
+ callback_safely if locksmith.unlock
46
+ ensure
47
+ locksmith.unlock
43
48
  end
49
+
50
+ call_strategy(origin: :server, &block) unless executed
44
51
  end
45
- ensure
46
- locksmith.unlock!
47
52
  end
48
53
 
49
54
  private
@@ -51,7 +56,9 @@ module SidekiqUniqueJobs
51
56
  # This is safe as the base_lock always creates a new digest
52
57
  # The append there for needs to be done every time
53
58
  def append_unique_key_suffix
54
- item[LOCK_DIGEST] = item[LOCK_DIGEST] + RUN_SUFFIX
59
+ return if (lock_digest = item[LOCK_DIGEST]).end_with?(RUN_SUFFIX)
60
+
61
+ item[LOCK_DIGEST] = lock_digest + RUN_SUFFIX
55
62
  end
56
63
  end
57
64
  end
@@ -58,9 +58,9 @@ module SidekiqUniqueJobs
58
58
 
59
59
  def initialize(job_hash = {})
60
60
  @type = job_hash[LOCK]&.to_sym
61
- @worker = job_hash[CLASS]
62
- @limit = job_hash.fetch(LOCK_LIMIT, 1)
63
- @timeout = job_hash.fetch(LOCK_TIMEOUT, 0)
61
+ @worker = SidekiqUniqueJobs.safe_constantize(job_hash[CLASS])
62
+ @limit = job_hash.fetch(LOCK_LIMIT, 1)&.to_i
63
+ @timeout = job_hash.fetch(LOCK_TIMEOUT, 0)&.to_i
64
64
  @ttl = job_hash.fetch(LOCK_TTL) { job_hash.fetch(LOCK_EXPIRATION, nil) }.to_i
65
65
  @pttl = ttl * 1_000
66
66
  @lock_info = job_hash.fetch(LOCK_INFO) { SidekiqUniqueJobs.config.lock_info }
@@ -71,11 +71,15 @@ module SidekiqUniqueJobs
71
71
  @on_server_conflict = job_hash[ON_SERVER_CONFLICT]
72
72
  end
73
73
 
74
+ def lock_info?
75
+ lock_info
76
+ end
77
+
74
78
  #
75
79
  # Indicate if timeout was set
76
80
  #
77
81
  #
78
- # @return [true,fakse]
82
+ # @return [true,false]
79
83
  #
80
84
  def wait_for_lock?
81
85
  timeout.nil? || timeout.positive?
@@ -71,7 +71,7 @@ module SidekiqUniqueJobs
71
71
  ttl ||= item[LOCK_EXPIRATION] # TODO: Deprecate at some point
72
72
  ttl ||= worker_options[LOCK_EXPIRATION] # TODO: Deprecate at some point
73
73
  ttl ||= SidekiqUniqueJobs.config.lock_ttl
74
- ttl && ttl.to_i + time_until_scheduled
74
+ ttl && (ttl.to_i + time_until_scheduled)
75
75
  end
76
76
  end
77
77
  end
@@ -13,6 +13,10 @@ module SidekiqUniqueJobs
13
13
  # @!parse include SidekiqUniqueJobs::Logging
14
14
  include SidekiqUniqueJobs::Logging
15
15
 
16
+ # includes "SidekiqUniqueJobs::Reflectable"
17
+ # @!parse include SidekiqUniqueJobs::Reflectable
18
+ include SidekiqUniqueJobs::Reflectable
19
+
16
20
  # includes "SidekiqUniqueJobs::Timing"
17
21
  # @!parse include SidekiqUniqueJobs::Timing
18
22
  include SidekiqUniqueJobs::Timing
@@ -77,7 +81,7 @@ module SidekiqUniqueJobs
77
81
  # Deletes the lock regardless of if it has a pttl set
78
82
  #
79
83
  def delete!
80
- call_script(:delete, key.to_a, [job_id, config.pttl, config.type, config.limit]).positive?
84
+ call_script(:delete, key.to_a, [job_id, config.pttl, config.type, config.limit]).to_i.positive?
81
85
  end
82
86
 
83
87
  #
@@ -85,16 +89,23 @@ module SidekiqUniqueJobs
85
89
  #
86
90
  # @return [String] the Sidekiq job_id that was locked/queued
87
91
  #
88
- def lock(&block)
92
+ def lock(wait: nil)
93
+ method_name = wait ? :primed_async : :primed_sync
89
94
  redis(redis_pool) do |conn|
90
- return lock_async(conn, &block) if block
91
-
92
- lock_sync(conn) do
95
+ lock!(conn, method(method_name), wait) do
93
96
  return job_id
94
97
  end
95
98
  end
96
99
  end
97
100
 
101
+ def execute(&block)
102
+ raise SidekiqUniqueJobs::InvalidArgument, "#execute needs a block" unless block
103
+
104
+ redis(redis_pool) do |conn|
105
+ lock!(conn, method(:primed_async), &block)
106
+ end
107
+ end
108
+
98
109
  #
99
110
  # Removes the lock keys from Redis if locked by the provided jid/token
100
111
  #
@@ -114,11 +125,17 @@ module SidekiqUniqueJobs
114
125
  # @return [String] Sidekiq job_id (jid) if successful
115
126
  #
116
127
  def unlock!(conn = nil)
117
- call_script(:unlock, key.to_a, argv, conn)
128
+ call_script(:unlock, key.to_a, argv, conn) do |unlocked_jid|
129
+ reflect(:debug, :unlocked, item, unlocked_jid) if unlocked_jid == job_id
130
+
131
+ unlocked_jid
132
+ end
118
133
  end
119
134
 
120
135
  # Checks if this instance is considered locked
121
136
  #
137
+ # @param [Sidekiq::RedisConnection, ConnectionPool] conn the redis connection
138
+ #
122
139
  # @return [true, false] true when the :LOCKED hash contains the job_id
123
140
  #
124
141
  def locked?(conn = nil)
@@ -134,7 +151,7 @@ module SidekiqUniqueJobs
134
151
  # @return [String]
135
152
  #
136
153
  def to_s
137
- "Locksmith##{object_id}(digest=#{key} job_id=#{job_id}, locked=#{locked?})"
154
+ "Locksmith##{object_id}(digest=#{key} job_id=#{job_id} locked=#{locked?})"
138
155
  end
139
156
 
140
157
  #
@@ -159,70 +176,74 @@ module SidekiqUniqueJobs
159
176
 
160
177
  attr_reader :redis_pool
161
178
 
162
- def argv
163
- [job_id, config.pttl, config.type, config.limit]
164
- end
165
-
166
179
  #
167
- # Used for runtime locks that need automatic unlock after yielding
180
+ # Used to reduce some duplication from the two methods
168
181
  #
169
- # @param [Redis] conn a redis connection
170
- #
171
- # @return [nil] when lock was not possible
172
- # @return [Object] whatever the block returns when lock was acquired
182
+ # @see lock
183
+ # @see execute
173
184
  #
174
- # @yieldparam [String] job_id a Sidekiq JID
185
+ # @param [Sidekiq::RedisConnection, ConnectionPool] conn the redis connection
186
+ # @param [Method] primed_method reference to the method to use for getting a primed token
175
187
  #
176
- def lock_async(conn)
188
+ # @yieldparam [string] job_id the sidekiq JID
189
+ # @yieldreturn [void] whatever the calling block returns
190
+ def lock!(conn, primed_method, wait = nil)
177
191
  return yield job_id if locked?(conn)
178
192
 
179
- enqueue(conn) do
180
- primed_async(conn) do
181
- locked_token = call_script(:lock, key.to_a, argv, conn)
182
- return yield if locked_token == job_id
193
+ enqueue(conn) do |queued_jid|
194
+ reflect(:debug, item, queued_jid)
195
+
196
+ primed_method.call(conn, wait) do |primed_jid|
197
+ reflect(:debug, :primed, item, primed_jid)
198
+
199
+ locked_jid = call_script(:lock, key.to_a, argv, conn)
200
+ if locked_jid
201
+ reflect(:debug, :locked, item, locked_jid)
202
+ return yield job_id
203
+ end
183
204
  end
184
205
  end
185
- ensure
186
- unlock!(conn)
187
206
  end
188
207
 
189
208
  #
190
- # Pops an enqueued token
191
- # @note Used for runtime locks to avoid problems with blocking commands
192
- # in current thread
209
+ # Prepares all the various lock data
193
210
  #
194
211
  # @param [Redis] conn a redis connection
195
212
  #
196
- # @return [nil] when lock was not possible
197
- # @return [Object] whatever the block returns when lock was acquired
213
+ # @return [nil] when redis was already prepared for this lock
214
+ # @return [yield<String>] when successfully enqueued
198
215
  #
199
- def primed_async(conn)
200
- return yield if Concurrent::Promises
201
- .future(conn) { |red_con| pop_queued(red_con) }
202
- .value(drift(config.ttl))
216
+ def enqueue(conn)
217
+ queued_jid, elapsed = timed do
218
+ call_script(:queue, key.to_a, argv, conn)
219
+ end
220
+
221
+ return unless queued_jid
222
+ return unless [job_id, "1"].include?(queued_jid)
223
+
224
+ validity = config.pttl - elapsed - drift(config.pttl)
225
+ return unless validity >= 0 || config.pttl.zero?
203
226
 
204
- warn_about_timeout
227
+ write_lock_info(conn)
228
+ yield job_id
205
229
  end
206
230
 
207
231
  #
208
- # Used for non-runtime locks (no block was given)
232
+ # Pops an enqueued token
233
+ # @note Used for runtime locks to avoid problems with blocking commands
234
+ # in current thread
209
235
  #
210
236
  # @param [Redis] conn a redis connection
211
237
  #
212
238
  # @return [nil] when lock was not possible
213
239
  # @return [Object] whatever the block returns when lock was acquired
214
240
  #
215
- # @yieldparam [String] job_id a Sidekiq JID
216
- #
217
- def lock_sync(conn)
218
- return yield if locked?(conn)
241
+ def primed_async(conn, wait = nil, &block)
242
+ primed_jid = Concurrent::Promises
243
+ .future(conn) { |red_con| pop_queued(red_con, wait) }
244
+ .value(add_drift(wait || config.timeout))
219
245
 
220
- enqueue(conn) do
221
- primed_sync(conn) do
222
- locked_token = call_script(:lock, key.to_a, argv, conn)
223
- return yield if locked_token
224
- end
225
- end
246
+ handle_primed(primed_jid, &block)
226
247
  end
227
248
 
228
249
  #
@@ -234,10 +255,15 @@ module SidekiqUniqueJobs
234
255
  # @return [nil] when lock was not possible
235
256
  # @return [Object] whatever the block returns when lock was acquired
236
257
  #
237
- def primed_sync(conn)
238
- return yield if pop_queued(conn)
258
+ def primed_sync(conn, wait = nil, &block)
259
+ primed_jid = pop_queued(conn, wait)
260
+ handle_primed(primed_jid, &block)
261
+ end
262
+
263
+ def handle_primed(primed_jid)
264
+ return yield job_id if [job_id, "1"].include?(primed_jid)
239
265
 
240
- warn_about_timeout
266
+ reflect(:timeout, item) unless config.wait_for_lock?
241
267
  end
242
268
 
243
269
  #
@@ -247,20 +273,24 @@ module SidekiqUniqueJobs
247
273
  #
248
274
  # @return [String] a previously enqueued token (now taken off the queue)
249
275
  #
250
- def pop_queued(conn)
251
- if config.wait_for_lock?
252
- brpoplpush(conn)
253
- else
276
+ def pop_queued(conn, wait = nil)
277
+ wait ||= config.timeout if config.wait_for_lock?
278
+
279
+ if wait.nil?
254
280
  rpoplpush(conn)
281
+ else
282
+ brpoplpush(conn, wait)
255
283
  end
256
284
  end
257
285
 
258
286
  #
259
287
  # @api private
260
288
  #
261
- def brpoplpush(conn)
289
+ def brpoplpush(conn, wait)
290
+ raise InvalidArgument, "wait must be an integer" unless wait.is_a?(Integer)
291
+
262
292
  # passing timeout 0 to brpoplpush causes it to block indefinitely
263
- conn.brpoplpush(key.queued, key.primed, timeout: config.timeout || 0)
293
+ conn.brpoplpush(key.queued, key.primed, timeout: wait)
264
294
  end
265
295
 
266
296
  #
@@ -270,27 +300,6 @@ module SidekiqUniqueJobs
270
300
  conn.rpoplpush(key.queued, key.primed)
271
301
  end
272
302
 
273
- #
274
- # Prepares all the various lock data
275
- #
276
- # @param [Redis] conn a redis connection
277
- #
278
- # @return [nil] when redis was already prepared for this lock
279
- # @return [yield<String>] when successfully enqueued
280
- #
281
- def enqueue(conn)
282
- queued_token, elapsed = timed do
283
- call_script(:queue, key.to_a, argv, conn)
284
- end
285
-
286
- validity = config.pttl - elapsed - drift(config.pttl)
287
-
288
- return unless queued_token && (validity >= 0 || config.pttl.zero?)
289
-
290
- write_lock_info(conn)
291
- yield
292
- end
293
-
294
303
  #
295
304
  # Writes lock information to redis.
296
305
  # The lock information contains information about worker, queue, limit etc.
@@ -299,7 +308,7 @@ module SidekiqUniqueJobs
299
308
  # @return [void]
300
309
  #
301
310
  def write_lock_info(conn)
302
- return unless config.lock_info
311
+ return unless config.lock_info?
303
312
 
304
313
  conn.set(key.info, lock_info)
305
314
  end
@@ -315,7 +324,11 @@ module SidekiqUniqueJobs
315
324
  # Add 2 milliseconds to the drift to account for Redis expires
316
325
  # precision, which is 1 millisecond, plus 1 millisecond min drift
317
326
  # for small TTLs.
318
- (val.to_i * CLOCK_DRIFT_FACTOR).to_i + 2
327
+ (val + 2).to_f * CLOCK_DRIFT_FACTOR
328
+ end
329
+
330
+ def add_drift(val)
331
+ val + drift(val)
319
332
  end
320
333
 
321
334
  #
@@ -329,8 +342,8 @@ module SidekiqUniqueJobs
329
342
  conn.hexists(key.locked, job_id)
330
343
  end
331
344
 
332
- def warn_about_timeout
333
- log_warn("Timed out after #{config.timeout}s while waiting for primed token (digest: #{key}, job_id: #{job_id})")
345
+ def argv
346
+ [job_id, config.pttl, config.type, config.limit]
334
347
  end
335
348
 
336
349
  def lock_info
@@ -340,7 +353,7 @@ module SidekiqUniqueJobs
340
353
  LIMIT => item[LOCK_LIMIT],
341
354
  TIMEOUT => item[LOCK_TIMEOUT],
342
355
  TTL => item[LOCK_TTL],
343
- LOCK => config.type,
356
+ TYPE => config.type,
344
357
  LOCK_ARGS => item[LOCK_ARGS],
345
358
  TIME => now_f,
346
359
  )
@@ -29,8 +29,9 @@ module SidekiqUniqueJobs
29
29
  #
30
30
  # @yield [String, Exception] the message or exception to use for log message
31
31
  #
32
- def log_debug(message_or_exception = nil, &block)
33
- logger.debug(message_or_exception, &block)
32
+ def log_debug(message_or_exception = nil, item = nil, &block)
33
+ message = build_message(message_or_exception, item)
34
+ logger.debug(message, &block)
34
35
  nil
35
36
  end
36
37
 
@@ -43,8 +44,9 @@ module SidekiqUniqueJobs
43
44
  #
44
45
  # @yield [String, Exception] the message or exception to use for log message
45
46
  #
46
- def log_info(message_or_exception = nil, &block)
47
- logger.info(message_or_exception, &block)
47
+ def log_info(message_or_exception = nil, item = nil, &block)
48
+ message = build_message(message_or_exception, item)
49
+ logger.info(message, &block)
48
50
  nil
49
51
  end
50
52
 
@@ -57,8 +59,9 @@ module SidekiqUniqueJobs
57
59
  #
58
60
  # @yield [String, Exception] the message or exception to use for log message
59
61
  #
60
- def log_warn(message_or_exception = nil, &block)
61
- logger.warn(message_or_exception, &block)
62
+ def log_warn(message_or_exception = nil, item = nil, &block)
63
+ message = build_message(message_or_exception, item)
64
+ logger.warn(message, &block)
62
65
  nil
63
66
  end
64
67
 
@@ -71,8 +74,9 @@ module SidekiqUniqueJobs
71
74
  #
72
75
  # @yield [String, Exception] the message or exception to use for log message
73
76
  #
74
- def log_error(message_or_exception = nil, &block)
75
- logger.error(message_or_exception, &block)
77
+ def log_error(message_or_exception = nil, item = nil, &block)
78
+ message = build_message(message_or_exception, item)
79
+ logger.error(message, &block)
76
80
  nil
77
81
  end
78
82
 
@@ -85,11 +89,36 @@ module SidekiqUniqueJobs
85
89
  #
86
90
  # @yield [String, Exception] the message or exception to use for log message
87
91
  #
88
- def log_fatal(message_or_exception = nil, &block)
89
- logger.fatal(message_or_exception, &block)
92
+ def log_fatal(message_or_exception = nil, item = nil, &block)
93
+ message = build_message(message_or_exception, item)
94
+ logger.fatal(message, &block)
95
+
90
96
  nil
91
97
  end
92
98
 
99
+ #
100
+ # Build a log message
101
+ #
102
+ # @param [String, Exception] message_or_exception an entry to log
103
+ # @param [Hash] item the sidekiq job hash
104
+ #
105
+ # @return [String] a complete log entry
106
+ #
107
+ def build_message(message_or_exception, item = nil)
108
+ return nil if message_or_exception.nil?
109
+ return message_or_exception if item.nil?
110
+
111
+ message = message_or_exception.dup
112
+ details = item.slice(LOCK, QUEUE, CLASS, JID, LOCK_DIGEST).each_with_object([]) do |(key, value), memo|
113
+ memo << "#{key}=#{value}"
114
+ end
115
+ message << " ("
116
+ message << details.join(" ")
117
+ message << ")"
118
+
119
+ message
120
+ end
121
+
93
122
  #
94
123
  # Wraps the middleware logic with context aware logging
95
124
  #
@@ -172,7 +201,7 @@ module SidekiqUniqueJobs
172
201
  end
173
202
 
174
203
  def sidekiq_logger_context_method
175
- logger.method(:with_context) if logger_respond_to_with_context?
204
+ logger.method(:with_context) if logger_respond_to_with_context?
176
205
  end
177
206
 
178
207
  def sidekiq_logging_context_method
@@ -76,6 +76,9 @@ if pttl and pttl > 0 then
76
76
 
77
77
  log_debug("PEXPIRE", locked, pttl)
78
78
  redis.call("PEXPIRE", locked, pttl)
79
+
80
+ log_debug("PEXPIRE", info, pttl)
81
+ redis.call("PEXPIRE", info, pttl)
79
82
  end
80
83
 
81
84
  log_debug("PEXPIRE", queued, 1000)
@@ -84,9 +87,6 @@ redis.call("PEXPIRE", queued, 1000)
84
87
  log_debug("PEXPIRE", primed, 1000)
85
88
  redis.call("PEXPIRE", primed, 1000)
86
89
 
87
- log_debug("PEXPIRE", info, 1000)
88
- redis.call("PEXPIRE", info, 1000)
89
-
90
90
  log("Locked")
91
91
  log_debug("END lock digest:", digest, "job_id:", job_id)
92
92
  return job_id
@@ -27,7 +27,7 @@ local function find_digest_in_process_set(digest, threshold)
27
27
  else
28
28
  for i = 1, #jobs, 2 do
29
29
  local jobstr = jobs[i +1]
30
- if string.find(jobstr, digest) then
30
+ if string.find(string.gsub(jobstr, ':RUN', ''), string.gsub(digest, ':RUN', '')) then
31
31
  log_debug("Found digest", digest, "in:", workers_key)
32
32
  found = true
33
33
  break
@@ -65,11 +65,6 @@ redis.call("LREM", queued, -1, job_id)
65
65
  log_debug("LREM", primed, -1, job_id)
66
66
  redis.call("LREM", primed, -1, job_id)
67
67
 
68
- if limit and limit <= 1 and locked_count and locked_count <= 1 then
69
- log_debug("ZREM", digests, digest)
70
- redis.call("ZREM", digests, digest)
71
- end
72
-
73
68
  local redis_version = toversion(redisversion)
74
69
  local del_cmd = "DEL"
75
70
 
@@ -83,6 +78,18 @@ if lock_type ~= "until_expired" then
83
78
  redis.call("HDEL", locked, job_id)
84
79
  end
85
80
 
81
+ local locked_count = redis.call("HLEN", locked)
82
+
83
+ if locked_count and locked_count < 1 then
84
+ log_debug(del_cmd, locked)
85
+ redis.call(del_cmd, locked)
86
+ end
87
+
88
+ if limit and limit <= 1 and locked_count and locked_count <= 1 then
89
+ log_debug("ZREM", digests, digest)
90
+ redis.call("ZREM", digests, digest)
91
+ end
92
+
86
93
  log_debug("LPUSH", queued, "1")
87
94
  redis.call("LPUSH", queued, "1")
88
95
 
@@ -6,7 +6,12 @@ module SidekiqUniqueJobs
6
6
  #
7
7
  # @author Mikael Henriksson <mikael@mhenrixon.com>
8
8
  class Client
9
+ # prepend "SidekiqUniqueJobs::Middleware"
10
+ # @!parse prepends SidekiqUniqueJobs::Middleware
9
11
  prepend SidekiqUniqueJobs::Middleware
12
+ # includes "SidekiqUniqueJobs::Reflectable"
13
+ # @!parse include SidekiqUniqueJobs::Reflectable
14
+ include SidekiqUniqueJobs::Reflectable
10
15
 
11
16
  # Calls this client middleware
12
17
  # Used from Sidekiq.process_single
@@ -25,18 +30,11 @@ module SidekiqUniqueJobs
25
30
  private
26
31
 
27
32
  def lock
28
- if (_token = lock_instance.lock)
29
- yield
30
- else
31
- warn_about_duplicate
33
+ lock_instance.lock do
34
+ reflect(:locked, item)
35
+ return yield
32
36
  end
33
37
  end
34
-
35
- def warn_about_duplicate
36
- return unless log_duplicate?
37
-
38
- log_warn "Already locked with another job_id (#{dump_json(item)})"
39
- end
40
38
  end
41
39
  end
42
40
  end
@@ -6,6 +6,8 @@ module SidekiqUniqueJobs
6
6
  #
7
7
  # @author Mikael Henriksson <mikael@mhenrixon.com>
8
8
  class Server
9
+ # prepend "SidekiqUniqueJobs::Middleware"
10
+ # @!parse prepends SidekiqUniqueJobs::Middleware
9
11
  prepend SidekiqUniqueJobs::Middleware
10
12
 
11
13
  #
@@ -9,6 +9,7 @@ module SidekiqUniqueJobs
9
9
  include SidekiqUniqueJobs::SidekiqWorkerMethods
10
10
  include SidekiqUniqueJobs::Logging
11
11
  include SidekiqUniqueJobs::JSON
12
+ include SidekiqUniqueJobs::Reflectable
12
13
 
13
14
  # @param [Hash] item sidekiq job hash
14
15
  def initialize(item, redis_pool = nil)
@@ -20,10 +21,13 @@ module SidekiqUniqueJobs
20
21
  # This will mess up sidekiq stats because a new job is created
21
22
  def call
22
23
  if sidekiq_worker_class?
23
- log_info("Rescheduling #{item[LOCK_DIGEST]}")
24
- worker_class.perform_in(5, *item[ARGS])
24
+ if worker_class.perform_in(5, *item[ARGS])
25
+ reflect(:rescheduled, item)
26
+ else
27
+ reflect(:reschedule_failed, item)
28
+ end
25
29
  else
26
- log_warn("Skip rescheduling of #{item[LOCK_DIGEST]} because #{worker_class} is not a Sidekiq::Worker")
30
+ reflect(:unknown_sidekiq_worker, item)
27
31
  end
28
32
  end
29
33
  end
@@ -29,15 +29,6 @@ module SidekiqUniqueJobs
29
29
  !unique_enabled?
30
30
  end
31
31
 
32
- # Should duplicate payloads be logged?
33
- #
34
- #
35
- # @return [true, false, nil]
36
- #
37
- def log_duplicate?
38
- options[LOG_DUPLICATE] || item[LOG_DUPLICATE]
39
- end
40
-
41
32
  #
42
33
  # A new lock for this Sidekiq Job
43
34
  #
@@ -55,10 +46,8 @@ module SidekiqUniqueJobs
55
46
  # @return [Class]
56
47
  #
57
48
  def lock_class
58
- @lock_class ||= begin
59
- locks.fetch(lock_type.to_sym) do
60
- raise UnknownLock, "No implementation for `lock: :#{lock_type}`"
61
- end
49
+ @lock_class ||= locks.fetch(lock_type.to_sym) do
50
+ raise UnknownLock, "No implementation for `lock: :#{lock_type}`"
62
51
  end
63
52
  end
64
53