sidekiq-cron 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,54 +1,129 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fugit'
4
+ require 'cronex'
2
5
  require 'globalid'
3
- require 'sidekiq'
4
6
  require 'sidekiq/cron/support'
5
- require 'sidekiq/options'
6
7
 
7
8
  module Sidekiq
8
9
  module Cron
9
10
  class Job
10
- # How long we would like to store informations about previous enqueues.
11
+ # How long we would like to store information about previous enqueues.
11
12
  REMEMBER_THRESHOLD = 24 * 60 * 60
12
13
 
13
14
  # Time format for enqueued jobs.
14
15
  LAST_ENQUEUE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S %z'
15
16
 
16
- # Use the exists? method if we're on a newer version of Redis.
17
- REDIS_EXISTS_METHOD = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("7.0.0") || Gem.loaded_specs['redis'].version < Gem::Version.new('4.2') ? :exists : :exists?
18
-
19
17
  # Use serialize/deserialize key of GlobalID.
20
18
  GLOBALID_KEY = "_sc_globalid"
21
19
 
20
+ attr_accessor :name, :namespace, :cron, :description, :klass, :args, :message
21
+ attr_reader :last_enqueue_time, :fetch_missing_args, :source
22
+
23
+ def initialize input_args = {}
24
+ args = Hash[input_args.map{ |k, v| [k.to_s, v] }]
25
+ @fetch_missing_args = args.delete('fetch_missing_args')
26
+ @fetch_missing_args = true if @fetch_missing_args.nil?
27
+
28
+ @name = args["name"]
29
+ @namespace = args["namespace"] || Sidekiq::Cron.configuration.default_namespace
30
+ @cron = args["cron"]
31
+ @description = args["description"] if args["description"]
32
+ @source = args["source"] == "schedule" ? "schedule" : "dynamic"
33
+
34
+ # Get class from klass or class.
35
+ @klass = args["klass"] || args["class"]
36
+
37
+ # Set status of job.
38
+ @status = args['status'] || status_from_redis
39
+
40
+ # Set last enqueue time - from args or from existing job.
41
+ if args['last_enqueue_time'] && !args['last_enqueue_time'].empty?
42
+ @last_enqueue_time = parse_enqueue_time(args['last_enqueue_time'])
43
+ else
44
+ @last_enqueue_time = last_enqueue_time_from_redis
45
+ end
46
+
47
+ # Get right arguments for job.
48
+ @symbolize_args = args["symbolize_args"] == true || ("#{args["symbolize_args"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
49
+ @args = parse_args(args["args"])
50
+
51
+ @date_as_argument = args["date_as_argument"] == true || ("#{args["date_as_argument"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
52
+
53
+ @active_job = args["active_job"] == true || ("#{args["active_job"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
54
+ @active_job_queue_name_prefix = args["queue_name_prefix"]
55
+ @active_job_queue_name_delimiter = args["queue_name_delimiter"]
56
+
57
+ # symbolize_args is only used when active_job is true
58
+ Sidekiq.logger.warn { "Cron Jobs - 'symbolize_args' is gonna be ignored, as it is only used when 'active_job' is true" } if @symbolize_args && !@active_job
59
+
60
+ if args["message"]
61
+ @message = args["message"]
62
+ message_data = Sidekiq.load_json(@message) || {}
63
+ @queue = message_data['queue'] || "default"
64
+ @retry = message_data['retry']
65
+ elsif @klass
66
+ message_data = {
67
+ "class" => @klass.to_s,
68
+ "args" => @args,
69
+ }
70
+
71
+ # Get right data for message,
72
+ # only if message wasn't specified before.
73
+ klass_data = get_job_class_options(@klass)
74
+ message_data = klass_data.merge(message_data)
75
+
76
+ # Override queue and retry if set in config,
77
+ # only if message is hash - can be string (dumped JSON).
78
+ if args['queue']
79
+ @queue = message_data['queue'] = args['queue']
80
+ else
81
+ @queue = message_data['queue'] || "default"
82
+ end
83
+
84
+ if args['retry'] != nil
85
+ @retry = message_data['retry'] = args['retry']
86
+ else
87
+ @retry = message_data['retry']
88
+ end
89
+
90
+ @message = message_data
91
+ end
92
+
93
+ @queue_name_with_prefix = queue_name_with_prefix
94
+ end
95
+
22
96
  # Crucial part of whole enqueuing job.
23
- def should_enque? time
97
+ def should_enqueue? time
98
+ return false unless status == "enabled"
99
+ return false if past_scheduled_time?(time)
100
+ return false if enqueued_after?(time)
101
+
24
102
  enqueue = Sidekiq.redis do |conn|
25
- status == "enabled" &&
26
- not_past_scheduled_time?(time) &&
27
- not_enqueued_after?(time) &&
28
- conn.zadd(job_enqueued_key, formatted_enqueue_time(time), formatted_last_time(time))
103
+ conn.zadd(job_enqueued_key, formatted_enqueue_time(time), formatted_last_time(time))
29
104
  end
30
105
  enqueue == true || enqueue == 1
31
106
  end
32
107
 
33
108
  # Remove previous information about run times,
34
109
  # this will clear Redis and make sure that Redis will not overflow with memory.
35
- def remove_previous_enques time
110
+ def remove_previous_enqueues time
36
111
  Sidekiq.redis do |conn|
37
112
  conn.zremrangebyscore(job_enqueued_key, 0, "(#{(time.to_f - REMEMBER_THRESHOLD).to_s}")
38
113
  end
39
114
  end
40
115
 
41
116
  # Test if job should be enqueued.
42
- def test_and_enque_for_time! time
43
- if should_enque?(time)
44
- enque!
117
+ def test_and_enqueue_for_time! time
118
+ if should_enqueue?(time)
119
+ enqueue!
45
120
 
46
- remove_previous_enques(time)
121
+ remove_previous_enqueues(time)
47
122
  end
48
123
  end
49
124
 
50
125
  # Enqueue cron job to queue.
51
- def enque! time = Time.now.utc
126
+ def enqueue! time = Time.now.utc
52
127
  @last_enqueue_time = time
53
128
 
54
129
  klass_const =
@@ -79,7 +154,7 @@ module Sidekiq
79
154
  end
80
155
 
81
156
  def is_active_job?(klass = nil)
82
- @active_job || defined?(ActiveJob::Base) && (klass || Sidekiq::Cron::Support.constantize(@klass.to_s)) < ActiveJob::Base
157
+ @active_job || defined?(::ActiveJob::Base) && (klass || Sidekiq::Cron::Support.constantize(@klass.to_s)) < ::ActiveJob::Base
83
158
  rescue NameError
84
159
  false
85
160
  end
@@ -98,7 +173,7 @@ module Sidekiq
98
173
  end
99
174
 
100
175
  def enqueue_sidekiq_worker(klass_const)
101
- klass_const.set(queue: queue_name_with_prefix).perform_async(*enqueue_args)
176
+ klass_const.set(queue: queue_name_with_prefix, retry: @retry).perform_async(*enqueue_args)
102
177
  end
103
178
 
104
179
  # Sidekiq worker message.
@@ -113,16 +188,16 @@ module Sidekiq
113
188
 
114
189
  if !"#{@active_job_queue_name_delimiter}".empty?
115
190
  queue_name_delimiter = @active_job_queue_name_delimiter
116
- elsif defined?(ActiveJob::Base) && defined?(ActiveJob::Base.queue_name_delimiter) && !ActiveJob::Base.queue_name_delimiter.empty?
117
- queue_name_delimiter = ActiveJob::Base.queue_name_delimiter
191
+ elsif defined?(::ActiveJob::Base) && defined?(::ActiveJob::Base.queue_name_delimiter) && !::ActiveJob::Base.queue_name_delimiter.empty?
192
+ queue_name_delimiter = ::ActiveJob::Base.queue_name_delimiter
118
193
  else
119
194
  queue_name_delimiter = '_'
120
195
  end
121
196
 
122
197
  if !"#{@active_job_queue_name_prefix}".empty?
123
198
  queue_name = "#{@active_job_queue_name_prefix}#{queue_name_delimiter}#{@queue}"
124
- elsif defined?(ActiveJob::Base) && defined?(ActiveJob::Base.queue_name_prefix) && !"#{ActiveJob::Base.queue_name_prefix}".empty?
125
- queue_name = "#{ActiveJob::Base.queue_name_prefix}#{queue_name_delimiter}#{@queue}"
199
+ elsif defined?(::ActiveJob::Base) && defined?(::ActiveJob::Base.queue_name_prefix) && !"#{::ActiveJob::Base.queue_name_prefix}".empty?
200
+ queue_name = "#{::ActiveJob::Base.queue_name_prefix}#{queue_name_delimiter}#{@queue}"
126
201
  else
127
202
  queue_name = @queue
128
203
  end
@@ -151,6 +226,7 @@ module Sidekiq
151
226
  # Input structure should look like:
152
227
  # {
153
228
  # 'name_of_job' => {
229
+ # 'namespace' => 'MyNamespace',
154
230
  # 'class' => 'MyClass',
155
231
  # 'cron' => '1 * * * *',
156
232
  # 'args' => '(OPTIONAL) [Array or Hash]',
@@ -181,6 +257,7 @@ module Sidekiq
181
257
  # Input structure should look like:
182
258
  # [
183
259
  # {
260
+ # 'namespace' => 'MyNamespace',
184
261
  # 'name' => 'name_of_job',
185
262
  # 'class' => 'MyClass',
186
263
  # 'cron' => '1 * * * *',
@@ -206,19 +283,20 @@ module Sidekiq
206
283
  # Like #load_from_array.
207
284
  # If exists old jobs in Redis but removed from args, destroy old jobs.
208
285
  def self.load_from_array!(array, options = {})
209
- job_names = array.map { |job| job["name"] }
286
+ job_names = array.map { |job| job["name"] || job[:name] }
210
287
  destroy_removed_jobs(job_names)
211
288
  load_from_array(array, options)
212
289
  end
213
290
 
214
291
  # Get all cron jobs.
215
- def self.all
292
+ def self.all(namespace = Sidekiq::Cron.configuration.default_namespace)
216
293
  job_hashes = nil
217
294
  Sidekiq.redis do |conn|
218
- set_members = conn.smembers(jobs_key)
295
+ job_keys = job_keys_from_namespace(namespace)
296
+
219
297
  job_hashes = conn.pipelined do |pipeline|
220
- set_members.each do |key|
221
- pipeline.hgetall(key)
298
+ job_keys.each do |job_key|
299
+ pipeline.hgetall(job_key)
222
300
  end
223
301
  end
224
302
  end
@@ -228,22 +306,25 @@ module Sidekiq
228
306
  end
229
307
  end
230
308
 
231
- def self.count
232
- out = 0
233
- Sidekiq.redis do |conn|
234
- out = conn.scard(jobs_key)
309
+ def self.count(namespace = Sidekiq::Cron.configuration.default_namespace)
310
+ if namespace == '*'
311
+ Namespace.all_with_count.reduce(0) do |memo, namespace_count|
312
+ memo + namespace_count[:count]
313
+ end
314
+ else
315
+ Sidekiq.redis { |conn| conn.scard(jobs_key(namespace)) }
235
316
  end
236
- out
237
317
  end
238
318
 
239
- def self.find name
319
+ def self.find(name, namespace = Sidekiq::Cron.configuration.default_namespace)
240
320
  # If name is hash try to get name from it.
241
321
  name = name[:name] || name['name'] if name.is_a?(Hash)
322
+ return unless exists? name, namespace
242
323
 
243
324
  output = nil
244
325
  Sidekiq.redis do |conn|
245
- if exists? name
246
- output = Job.new conn.hgetall( redis_key(name) )
326
+ if exists? name, namespace
327
+ output = Job.new conn.hgetall(redis_key(name, namespace))
247
328
  end
248
329
  end
249
330
  output if output && output.valid?
@@ -255,93 +336,17 @@ module Sidekiq
255
336
  end
256
337
 
257
338
  # Destroy job by name.
258
- def self.destroy name
339
+ def self.destroy(name, namespace = Sidekiq::Cron.configuration.default_namespace)
259
340
  # If name is hash try to get name from it.
260
341
  name = name[:name] || name['name'] if name.is_a?(Hash)
261
342
 
262
- if job = find(name)
343
+ if (job = find(name, namespace))
263
344
  job.destroy
264
345
  else
265
346
  false
266
347
  end
267
348
  end
268
349
 
269
- attr_accessor :name, :cron, :description, :klass, :args, :message
270
- attr_reader :last_enqueue_time, :fetch_missing_args, :source
271
-
272
- def initialize input_args = {}
273
- args = Hash[input_args.map{ |k, v| [k.to_s, v] }]
274
- @fetch_missing_args = args.delete('fetch_missing_args')
275
- @fetch_missing_args = true if @fetch_missing_args.nil?
276
-
277
- @name = args["name"]
278
- @cron = args["cron"]
279
- @description = args["description"] if args["description"]
280
- @source = args["source"] == "schedule" ? "schedule" : "dynamic"
281
-
282
- # Get class from klass or class.
283
- @klass = args["klass"] || args["class"]
284
-
285
- # Set status of job.
286
- @status = args['status'] || status_from_redis
287
-
288
- # Set last enqueue time - from args or from existing job.
289
- if args['last_enqueue_time'] && !args['last_enqueue_time'].empty?
290
- @last_enqueue_time = parse_enqueue_time(args['last_enqueue_time'])
291
- else
292
- @last_enqueue_time = last_enqueue_time_from_redis
293
- end
294
-
295
- # Get right arguments for job.
296
- @symbolize_args = args["symbolize_args"] == true || ("#{args["symbolize_args"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
297
- @args = args["args"].nil? ? [] : parse_args( args["args"] )
298
-
299
- @date_as_argument = args["date_as_argument"] == true || ("#{args["date_as_argument"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
300
-
301
- @active_job = args["active_job"] == true || ("#{args["active_job"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
302
- @active_job_queue_name_prefix = args["queue_name_prefix"]
303
- @active_job_queue_name_delimiter = args["queue_name_delimiter"]
304
-
305
- if args["message"]
306
- @message = args["message"]
307
- message_data = Sidekiq.load_json(@message) || {}
308
- @queue = message_data['queue'] || "default"
309
- elsif @klass
310
- message_data = {
311
- "class" => @klass.to_s,
312
- "args" => @args,
313
- }
314
-
315
- # Get right data for message,
316
- # only if message wasn't specified before.
317
- klass_data = case @klass
318
- when Class
319
- @klass.get_sidekiq_options
320
- when String
321
- begin
322
- Sidekiq::Cron::Support.constantize(@klass).get_sidekiq_options
323
- rescue Exception => e
324
- # Unknown class
325
- {"queue"=>"default"}
326
- end
327
- end
328
-
329
- message_data = klass_data.merge(message_data)
330
-
331
- # Override queue if setted in config,
332
- # only if message is hash - can be string (dumped JSON).
333
- if args['queue']
334
- @queue = message_data['queue'] = args['queue']
335
- else
336
- @queue = message_data['queue'] || "default"
337
- end
338
-
339
- @message = message_data
340
- end
341
-
342
- @queue_name_with_prefix = queue_name_with_prefix
343
- end
344
-
345
350
  def status
346
351
  @status
347
352
  end
@@ -370,6 +375,12 @@ module Sidekiq
370
375
  message
371
376
  end
372
377
 
378
+ def human_cron
379
+ Cronex::ExpressionDescriptor.new(cron).description
380
+ rescue => e
381
+ cron
382
+ end
383
+
373
384
  def status_from_redis
374
385
  out = "enabled"
375
386
  if fetch_missing_args
@@ -404,27 +415,24 @@ module Sidekiq
404
415
 
405
416
  # Export job data to hash.
406
417
  def to_hash
407
- hash = {
418
+ {
408
419
  name: @name,
420
+ namespace: @namespace,
409
421
  klass: @klass.to_s,
410
422
  cron: @cron,
411
423
  description: @description,
412
424
  source: @source,
413
425
  args: @args.is_a?(String) ? @args : Sidekiq.dump_json(@args || []),
426
+ date_as_argument: date_as_argument? ? "1" : "0",
414
427
  message: @message.is_a?(String) ? @message : Sidekiq.dump_json(@message || {}),
415
428
  status: @status,
416
429
  active_job: @active_job ? "1" : "0",
417
430
  queue_name_prefix: @active_job_queue_name_prefix,
418
431
  queue_name_delimiter: @active_job_queue_name_delimiter,
432
+ retry: @retry.nil? || @retry.is_a?(Numeric) ? @retry : @retry.to_s,
419
433
  last_enqueue_time: serialized_last_enqueue_time,
420
434
  symbolize_args: symbolize_args? ? "1" : "0",
421
435
  }
422
-
423
- if date_as_argument?
424
- hash.merge!(date_as_argument: "1")
425
- end
426
-
427
- hash
428
436
  end
429
437
 
430
438
  def errors
@@ -436,11 +444,14 @@ module Sidekiq
436
444
  @errors = []
437
445
 
438
446
  errors << "'name' must be set" if @name.nil? || @name.size == 0
447
+ errors << "'namespace' must be set" if @namespace.nil? || @namespace.size == 0
448
+ errors << "'namespace' cannot be '*'" if @namespace == "*"
449
+
439
450
  if @cron.nil? || @cron.size == 0
440
451
  errors << "'cron' must be set"
441
452
  else
442
453
  begin
443
- @parsed_cron = Fugit.do_parse_cronish(@cron)
454
+ @parsed_cron = do_parse_cron(@cron)
444
455
  rescue => e
445
456
  errors << "'cron' -> #{@cron.inspect} -> #{e.class}: #{e.message}"
446
457
  end
@@ -466,19 +477,23 @@ module Sidekiq
466
477
  return false unless valid?
467
478
 
468
479
  Sidekiq.redis do |conn|
469
-
470
480
  # Add to set of all jobs
471
- conn.sadd self.class.jobs_key, [redis_key]
481
+ conn.sadd self.class.jobs_key(@namespace), [redis_key]
472
482
 
473
- # Add informations for this job!
474
- conn.hset redis_key, to_hash.transform_values! { |v| v || "" }
483
+ # Add information for this job!
484
+ conn.hset redis_key, to_hash.transform_values! { |v| v || '' }.flatten
475
485
 
476
- # Add information about last time! - don't enque right after scheduler poller starts!
486
+ # Add information about last time! - don't enqueue right after scheduler poller starts!
477
487
  time = Time.now.utc
478
- exists = conn.public_send(REDIS_EXISTS_METHOD, job_enqueued_key)
479
- conn.zadd(job_enqueued_key, time.to_f.to_s, formatted_last_time(time).to_s) unless exists == true || exists == 1
488
+ exists = conn.exists(job_enqueued_key)
489
+
490
+ unless exists == true || exists == 1
491
+ conn.zadd(job_enqueued_key, time.to_f.to_s, formatted_last_time(time).to_s)
492
+ Sidekiq.logger.info { "Cron Jobs - added job with name #{@name} in the namespace #{@namespace}" }
493
+ end
480
494
  end
481
- Sidekiq.logger.info { "Cron Jobs - added job with name: #{@name}" }
495
+
496
+ true
482
497
  end
483
498
 
484
499
  def save_last_enqueue_time
@@ -494,7 +509,7 @@ module Sidekiq
494
509
  enqueued: @last_enqueue_time
495
510
  }
496
511
 
497
- @history_size ||= (Sidekiq::Options[:cron_history_size] || 10).to_i - 1
512
+ @history_size ||= Sidekiq::Cron.configuration.cron_history_size.to_i - 1
498
513
  Sidekiq.redis do |conn|
499
514
  conn.lpush jid_history_key,
500
515
  Sidekiq.dump_json(jid_history)
@@ -506,9 +521,9 @@ module Sidekiq
506
521
  def destroy
507
522
  Sidekiq.redis do |conn|
508
523
  # Delete from set.
509
- conn.srem self.class.jobs_key, [redis_key]
524
+ conn.srem self.class.jobs_key(@namespace), [redis_key]
510
525
 
511
- # Delete runned timestamps.
526
+ # Delete ran timestamps.
512
527
  conn.del job_enqueued_key
513
528
 
514
529
  # Delete jid_history.
@@ -518,7 +533,7 @@ module Sidekiq
518
533
  conn.del redis_key
519
534
  end
520
535
 
521
- Sidekiq.logger.info { "Cron Jobs - deleted job with name: #{@name}" }
536
+ Sidekiq.logger.info { "Cron Jobs - deleted job with name #{@name} from namespace #{@namespace}" }
522
537
  end
523
538
 
524
539
  # Remove all job from cron.
@@ -531,9 +546,17 @@ module Sidekiq
531
546
 
532
547
  # Remove "removed jobs" between current jobs and new jobs
533
548
  def self.destroy_removed_jobs new_job_names
534
- current_job_names = Sidekiq::Cron::Job.all.filter_map { |j| j.name if j.source == "schedule" }
549
+ current_jobs = Sidekiq::Cron::Job.all("*").filter_map { |j| j if j.source == "schedule" }
550
+ current_job_names = current_jobs.map(&:name)
535
551
  removed_job_names = current_job_names - new_job_names
536
- removed_job_names.each { |j| Sidekiq::Cron::Job.destroy(j) }
552
+ removed_job_names.each do |j|
553
+ job_to_destroy = current_jobs.detect { |job| job.name == j }
554
+
555
+ Sidekiq::Cron::Job.destroy(
556
+ job_to_destroy.name,
557
+ job_to_destroy.namespace
558
+ )
559
+ end
537
560
  removed_job_names
538
561
  end
539
562
 
@@ -551,29 +574,48 @@ module Sidekiq
551
574
  last_time(now).getutc.iso8601
552
575
  end
553
576
 
554
- def self.exists? name
577
+ def self.exists?(name, namespace = Sidekiq::Cron.configuration.default_namespace)
555
578
  out = Sidekiq.redis do |conn|
556
- conn.public_send(REDIS_EXISTS_METHOD, redis_key(name))
579
+ conn.exists(redis_key(name, namespace))
557
580
  end
558
- out == true || out == 1
581
+
582
+ [true, 1].include?(out)
559
583
  end
560
584
 
561
585
  def exists?
562
- self.class.exists? @name
586
+ self.class.exists? @name, @namespace
563
587
  end
564
588
 
565
589
  def sort_name
566
590
  "#{status == "enabled" ? 0 : 1}_#{name}".downcase
567
591
  end
568
592
 
593
+ def args=(args)
594
+ @args = parse_args(args)
595
+ end
596
+
569
597
  private
570
598
 
571
599
  def parsed_cron
572
- @parsed_cron ||= Fugit.parse_cronish(@cron)
600
+ @parsed_cron ||= do_parse_cron(@cron)
573
601
  end
574
602
 
575
- def not_enqueued_after?(time)
576
- @last_enqueue_time.nil? || @last_enqueue_time.to_i < last_time(time).to_i
603
+ def do_parse_cron(cron)
604
+ case Sidekiq::Cron.configuration.natural_cron_parsing_mode
605
+ when :single
606
+ Fugit.do_parse_cronish(cron)
607
+ when :strict
608
+ Fugit.parse_cron(cron) || # Ex. '11 1 * * 1'
609
+ Fugit.parse_nat(cron, :multi => :fail) || # Ex. 'every Monday at 01:11'
610
+ fail(ArgumentError.new("invalid cron string #{cron.inspect}"))
611
+ else
612
+ mode = Sidekiq::Cron.configuration.natural_cron_parsing_mode
613
+ raise ArgumentError, "Unknown natural cron parsing mode: #{mode.inspect}"
614
+ end
615
+ end
616
+
617
+ def enqueued_after?(time)
618
+ @last_enqueue_time && @last_enqueue_time.to_i >= last_time(time).to_i
577
619
  end
578
620
 
579
621
  # Try parsing inbound args into an array.
@@ -627,48 +669,83 @@ module Sidekiq
627
669
  DateTime.parse(timestamp).to_time.utc
628
670
  end
629
671
 
630
- def not_past_scheduled_time?(current_time)
672
+ def past_scheduled_time?(current_time)
631
673
  last_cron_time = parsed_cron.previous_time(current_time).utc
632
- return false if (current_time.to_i - last_cron_time.to_i) > 60
633
- true
674
+ period = Sidekiq::Cron.configuration.reschedule_grace_period
675
+
676
+ current_time.to_i - last_cron_time.to_i > period
634
677
  end
635
678
 
636
- # Redis key for set of all cron jobs.
637
- def self.jobs_key
638
- "cron_jobs"
679
+ def self.default_if_blank(namespace)
680
+ if namespace.nil? || namespace == ''
681
+ Sidekiq::Cron.configuration.default_namespace
682
+ else
683
+ namespace
684
+ end
685
+ end
686
+
687
+ def self.job_keys_from_namespace(namespace = Sidekiq::Cron.configuration.default_namespace)
688
+ Sidekiq.redis do |conn|
689
+ if namespace == '*'
690
+ namespaces = conn.keys(jobs_key(namespace))
691
+ namespaces.flat_map { |name| conn.smembers(name) }
692
+ else
693
+ conn.smembers(jobs_key(namespace))
694
+ end
695
+ end
696
+ end
697
+
698
+ def self.migrate_old_jobs_if_needed!
699
+ Sidekiq.redis do |conn|
700
+ old_job_keys = conn.smembers('cron_jobs')
701
+ old_job_keys.each do |old_job|
702
+ old_job_hash = conn.hgetall(old_job)
703
+ old_job_hash[:namespace] = Sidekiq::Cron.configuration.default_namespace
704
+ create(old_job_hash)
705
+ conn.srem('cron_jobs', old_job)
706
+ end
707
+ end
708
+ end
709
+
710
+ # Redis key for set of all cron jobs
711
+ def self.jobs_key(namespace = Sidekiq::Cron.configuration.default_namespace)
712
+ "cron_jobs:#{default_if_blank(namespace)}"
639
713
  end
640
714
 
641
- # Redis key for storing one cron job.
642
- def self.redis_key name
643
- "cron_job:#{name}"
715
+ # Redis key for storing one cron job
716
+ def self.redis_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
717
+ "cron_job:#{default_if_blank(namespace)}:#{name}"
644
718
  end
645
719
 
646
- # Redis key for storing one cron job.
720
+ # Redis key for storing one cron job
647
721
  def redis_key
648
- self.class.redis_key @name
722
+ self.class.redis_key @name, @namespace
649
723
  end
650
724
 
651
- # Redis key for storing one cron job run times (when poller added job to queue)
652
- def self.job_enqueued_key name
653
- "cron_job:#{name}:enqueued"
725
+ # Redis key for storing one cron job run times
726
+ # (when poller added job to queue)
727
+ def self.job_enqueued_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
728
+ "cron_job:#{default_if_blank(namespace)}:#{name}:enqueued"
654
729
  end
655
730
 
656
- def self.jid_history_key name
657
- "cron_job:#{name}:jid_history"
731
+ def self.jid_history_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
732
+ "cron_job:#{default_if_blank(namespace)}:#{name}:jid_history"
658
733
  end
659
734
 
735
+ # Redis key for storing one cron job run times
736
+ # (when poller added job to queue)
660
737
  def job_enqueued_key
661
- self.class.job_enqueued_key @name
738
+ self.class.job_enqueued_key @name, @namespace
662
739
  end
663
740
 
664
741
  def jid_history_key
665
- self.class.jid_history_key @name
742
+ self.class.jid_history_key @name, @namespace
666
743
  end
667
744
 
668
745
  def serialized_last_enqueue_time
669
746
  @last_enqueue_time&.strftime(LAST_ENQUEUE_TIME_FORMAT)
670
747
  end
671
-
748
+
672
749
  def convert_to_global_id_hash(argument)
673
750
  { GLOBALID_KEY => argument.to_global_id.to_s }
674
751
  rescue URI::GID::MissingModelIdError
@@ -715,6 +792,23 @@ module Sidekiq
715
792
  argument
716
793
  end
717
794
  end
795
+
796
+ def get_job_class_options(klass)
797
+ klass = klass.is_a?(Class) ? klass : begin
798
+ Sidekiq::Cron::Support.constantize(klass)
799
+ rescue NameError
800
+ # noop
801
+ end
802
+
803
+ if klass.nil?
804
+ # Unknown class
805
+ {"queue"=>"default"}
806
+ elsif is_active_job?(klass)
807
+ {"queue"=>klass.queue_name}
808
+ else
809
+ klass.get_sidekiq_options
810
+ end
811
+ end
718
812
  end
719
813
  end
720
814
  end