sidekiq-cron 1.11.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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