sidekiq-cron 1.12.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/README.md +141 -6
- data/lib/sidekiq/cron/job.rb +217 -131
- data/lib/sidekiq/cron/launcher.rb +1 -1
- data/lib/sidekiq/cron/locales/id.yml +22 -0
- data/lib/sidekiq/cron/namespace.rb +45 -0
- data/lib/sidekiq/cron/poller.rb +13 -6
- data/lib/sidekiq/cron/version.rb +1 -1
- data/lib/sidekiq/cron/views/cron.erb +32 -13
- data/lib/sidekiq/cron/views/cron_show.erb +6 -2
- data/lib/sidekiq/cron/web_extension.rb +59 -20
- data/lib/sidekiq/cron.rb +44 -0
- data/sidekiq-cron.gemspec +2 -1
- metadata +26 -10
data/lib/sidekiq/cron/job.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fugit'
|
4
|
+
require 'cronex'
|
2
5
|
require 'globalid'
|
3
6
|
require 'sidekiq'
|
4
7
|
require 'sidekiq/cron/support'
|
@@ -7,7 +10,7 @@ require 'sidekiq/options'
|
|
7
10
|
module Sidekiq
|
8
11
|
module Cron
|
9
12
|
class Job
|
10
|
-
# How long we would like to store
|
13
|
+
# How long we would like to store information about previous enqueues.
|
11
14
|
REMEMBER_THRESHOLD = 24 * 60 * 60
|
12
15
|
|
13
16
|
# Time format for enqueued jobs.
|
@@ -19,11 +22,80 @@ module Sidekiq
|
|
19
22
|
# Use serialize/deserialize key of GlobalID.
|
20
23
|
GLOBALID_KEY = "_sc_globalid"
|
21
24
|
|
25
|
+
attr_accessor :name, :namespace, :cron, :description, :klass, :args, :message
|
26
|
+
attr_reader :last_enqueue_time, :fetch_missing_args, :source
|
27
|
+
|
28
|
+
def initialize input_args = {}
|
29
|
+
args = Hash[input_args.map{ |k, v| [k.to_s, v] }]
|
30
|
+
@fetch_missing_args = args.delete('fetch_missing_args')
|
31
|
+
@fetch_missing_args = true if @fetch_missing_args.nil?
|
32
|
+
|
33
|
+
@name = args["name"]
|
34
|
+
@namespace = args["namespace"] || Sidekiq::Cron.configuration.default_namespace
|
35
|
+
@cron = args["cron"]
|
36
|
+
@description = args["description"] if args["description"]
|
37
|
+
@source = args["source"] == "schedule" ? "schedule" : "dynamic"
|
38
|
+
|
39
|
+
# Get class from klass or class.
|
40
|
+
@klass = args["klass"] || args["class"]
|
41
|
+
|
42
|
+
# Set status of job.
|
43
|
+
@status = args['status'] || status_from_redis
|
44
|
+
|
45
|
+
# Set last enqueue time - from args or from existing job.
|
46
|
+
if args['last_enqueue_time'] && !args['last_enqueue_time'].empty?
|
47
|
+
@last_enqueue_time = parse_enqueue_time(args['last_enqueue_time'])
|
48
|
+
else
|
49
|
+
@last_enqueue_time = last_enqueue_time_from_redis
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get right arguments for job.
|
53
|
+
@symbolize_args = args["symbolize_args"] == true || ("#{args["symbolize_args"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
|
54
|
+
@args = parse_args(args["args"])
|
55
|
+
|
56
|
+
@date_as_argument = args["date_as_argument"] == true || ("#{args["date_as_argument"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
|
57
|
+
|
58
|
+
@active_job = args["active_job"] == true || ("#{args["active_job"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
|
59
|
+
@active_job_queue_name_prefix = args["queue_name_prefix"]
|
60
|
+
@active_job_queue_name_delimiter = args["queue_name_delimiter"]
|
61
|
+
|
62
|
+
# symbolize_args is only used when active_job is true
|
63
|
+
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
|
64
|
+
|
65
|
+
if args["message"]
|
66
|
+
@message = args["message"]
|
67
|
+
message_data = Sidekiq.load_json(@message) || {}
|
68
|
+
@queue = message_data['queue'] || "default"
|
69
|
+
elsif @klass
|
70
|
+
message_data = {
|
71
|
+
"class" => @klass.to_s,
|
72
|
+
"args" => @args,
|
73
|
+
}
|
74
|
+
|
75
|
+
# Get right data for message,
|
76
|
+
# only if message wasn't specified before.
|
77
|
+
klass_data = get_job_class_options(@klass)
|
78
|
+
message_data = klass_data.merge(message_data)
|
79
|
+
|
80
|
+
# Override queue if set in config,
|
81
|
+
# only if message is hash - can be string (dumped JSON).
|
82
|
+
if args['queue']
|
83
|
+
@queue = message_data['queue'] = args['queue']
|
84
|
+
else
|
85
|
+
@queue = message_data['queue'] || "default"
|
86
|
+
end
|
87
|
+
|
88
|
+
@message = message_data
|
89
|
+
end
|
90
|
+
|
91
|
+
@queue_name_with_prefix = queue_name_with_prefix
|
92
|
+
end
|
93
|
+
|
22
94
|
# Crucial part of whole enqueuing job.
|
23
95
|
def should_enque? time
|
24
96
|
return false unless status == "enabled"
|
25
|
-
return false
|
26
|
-
return false
|
97
|
+
return false if past_scheduled_time?(time)
|
98
|
+
return false if enqueued_after?(time)
|
27
99
|
|
28
100
|
enqueue = Sidekiq.redis do |conn|
|
29
101
|
conn.zadd(job_enqueued_key, formatted_enqueue_time(time), formatted_last_time(time))
|
@@ -152,6 +224,7 @@ module Sidekiq
|
|
152
224
|
# Input structure should look like:
|
153
225
|
# {
|
154
226
|
# 'name_of_job' => {
|
227
|
+
# 'namespace' => 'MyNamespace',
|
155
228
|
# 'class' => 'MyClass',
|
156
229
|
# 'cron' => '1 * * * *',
|
157
230
|
# 'args' => '(OPTIONAL) [Array or Hash]',
|
@@ -182,6 +255,7 @@ module Sidekiq
|
|
182
255
|
# Input structure should look like:
|
183
256
|
# [
|
184
257
|
# {
|
258
|
+
# 'namespace' => 'MyNamespace',
|
185
259
|
# 'name' => 'name_of_job',
|
186
260
|
# 'class' => 'MyClass',
|
187
261
|
# 'cron' => '1 * * * *',
|
@@ -207,19 +281,20 @@ module Sidekiq
|
|
207
281
|
# Like #load_from_array.
|
208
282
|
# If exists old jobs in Redis but removed from args, destroy old jobs.
|
209
283
|
def self.load_from_array!(array, options = {})
|
210
|
-
job_names = array.map { |job| job["name"] }
|
284
|
+
job_names = array.map { |job| job["name"] || job[:name] }
|
211
285
|
destroy_removed_jobs(job_names)
|
212
286
|
load_from_array(array, options)
|
213
287
|
end
|
214
288
|
|
215
289
|
# Get all cron jobs.
|
216
|
-
def self.all
|
290
|
+
def self.all(namespace = Sidekiq::Cron.configuration.default_namespace)
|
217
291
|
job_hashes = nil
|
218
292
|
Sidekiq.redis do |conn|
|
219
|
-
|
293
|
+
job_keys = job_keys_from_namespace(namespace)
|
294
|
+
|
220
295
|
job_hashes = conn.pipelined do |pipeline|
|
221
|
-
|
222
|
-
pipeline.hgetall(
|
296
|
+
job_keys.each do |job_key|
|
297
|
+
pipeline.hgetall(job_key)
|
223
298
|
end
|
224
299
|
end
|
225
300
|
end
|
@@ -229,22 +304,26 @@ module Sidekiq
|
|
229
304
|
end
|
230
305
|
end
|
231
306
|
|
232
|
-
def self.count
|
233
|
-
|
234
|
-
|
235
|
-
|
307
|
+
def self.count(namespace = Sidekiq::Cron.configuration.default_namespace)
|
308
|
+
if namespace == '*'
|
309
|
+
Namespace.all_with_count.reduce(0) do |memo, namespace_count|
|
310
|
+
memo + namespace_count[:count]
|
311
|
+
end
|
312
|
+
else
|
313
|
+
Sidekiq.redis { |conn| conn.scard(jobs_key(namespace)) }
|
236
314
|
end
|
237
|
-
out
|
238
315
|
end
|
239
316
|
|
240
|
-
def self.find
|
317
|
+
def self.find(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
241
318
|
# If name is hash try to get name from it.
|
242
319
|
name = name[:name] || name['name'] if name.is_a?(Hash)
|
243
|
-
return unless exists? name
|
320
|
+
return unless exists? name, namespace
|
244
321
|
|
245
322
|
output = nil
|
246
323
|
Sidekiq.redis do |conn|
|
247
|
-
|
324
|
+
if exists? name, namespace
|
325
|
+
output = Job.new conn.hgetall(redis_key(name, namespace))
|
326
|
+
end
|
248
327
|
end
|
249
328
|
output if output && output.valid?
|
250
329
|
end
|
@@ -255,93 +334,17 @@ module Sidekiq
|
|
255
334
|
end
|
256
335
|
|
257
336
|
# Destroy job by name.
|
258
|
-
def self.destroy
|
337
|
+
def self.destroy(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
259
338
|
# If name is hash try to get name from it.
|
260
339
|
name = name[:name] || name['name'] if name.is_a?(Hash)
|
261
340
|
|
262
|
-
if job = find(name)
|
341
|
+
if (job = find(name, namespace))
|
263
342
|
job.destroy
|
264
343
|
else
|
265
344
|
false
|
266
345
|
end
|
267
346
|
end
|
268
347
|
|
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 = 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
348
|
def status
|
346
349
|
@status
|
347
350
|
end
|
@@ -370,6 +373,12 @@ module Sidekiq
|
|
370
373
|
message
|
371
374
|
end
|
372
375
|
|
376
|
+
def human_cron
|
377
|
+
Cronex::ExpressionDescriptor.new(cron).description
|
378
|
+
rescue => e
|
379
|
+
cron
|
380
|
+
end
|
381
|
+
|
373
382
|
def status_from_redis
|
374
383
|
out = "enabled"
|
375
384
|
if fetch_missing_args
|
@@ -406,6 +415,7 @@ module Sidekiq
|
|
406
415
|
def to_hash
|
407
416
|
hash = {
|
408
417
|
name: @name,
|
418
|
+
namespace: @namespace,
|
409
419
|
klass: @klass.to_s,
|
410
420
|
cron: @cron,
|
411
421
|
description: @description,
|
@@ -436,11 +446,13 @@ module Sidekiq
|
|
436
446
|
@errors = []
|
437
447
|
|
438
448
|
errors << "'name' must be set" if @name.nil? || @name.size == 0
|
449
|
+
errors << "'namespace' must be set" if @namespace.nil? || @namespace.size == 0
|
450
|
+
|
439
451
|
if @cron.nil? || @cron.size == 0
|
440
452
|
errors << "'cron' must be set"
|
441
453
|
else
|
442
454
|
begin
|
443
|
-
@parsed_cron =
|
455
|
+
@parsed_cron = do_parse_cron(@cron)
|
444
456
|
rescue => e
|
445
457
|
errors << "'cron' -> #{@cron.inspect} -> #{e.class}: #{e.message}"
|
446
458
|
end
|
@@ -466,19 +478,18 @@ module Sidekiq
|
|
466
478
|
return false unless valid?
|
467
479
|
|
468
480
|
Sidekiq.redis do |conn|
|
469
|
-
|
470
481
|
# Add to set of all jobs
|
471
|
-
conn.sadd self.class.jobs_key, [redis_key]
|
482
|
+
conn.sadd self.class.jobs_key(@namespace), [redis_key]
|
472
483
|
|
473
|
-
# Add
|
474
|
-
conn.hset redis_key, to_hash.transform_values! { |v| v ||
|
484
|
+
# Add information for this job!
|
485
|
+
conn.hset redis_key, to_hash.transform_values! { |v| v || '' }
|
475
486
|
|
476
487
|
# Add information about last time! - don't enque right after scheduler poller starts!
|
477
488
|
time = Time.now.utc
|
478
489
|
exists = conn.public_send(REDIS_EXISTS_METHOD, job_enqueued_key)
|
479
490
|
conn.zadd(job_enqueued_key, time.to_f.to_s, formatted_last_time(time).to_s) unless exists == true || exists == 1
|
480
491
|
end
|
481
|
-
Sidekiq.logger.info { "Cron Jobs - added job with name
|
492
|
+
Sidekiq.logger.info { "Cron Jobs - added job with name #{@name} in the namespace #{@namespace}" }
|
482
493
|
end
|
483
494
|
|
484
495
|
def save_last_enqueue_time
|
@@ -506,9 +517,9 @@ module Sidekiq
|
|
506
517
|
def destroy
|
507
518
|
Sidekiq.redis do |conn|
|
508
519
|
# Delete from set.
|
509
|
-
conn.srem self.class.jobs_key, [redis_key]
|
520
|
+
conn.srem self.class.jobs_key(@namespace), [redis_key]
|
510
521
|
|
511
|
-
# Delete
|
522
|
+
# Delete ran timestamps.
|
512
523
|
conn.del job_enqueued_key
|
513
524
|
|
514
525
|
# Delete jid_history.
|
@@ -518,7 +529,7 @@ module Sidekiq
|
|
518
529
|
conn.del redis_key
|
519
530
|
end
|
520
531
|
|
521
|
-
Sidekiq.logger.info { "Cron Jobs - deleted job with name
|
532
|
+
Sidekiq.logger.info { "Cron Jobs - deleted job with name #{@name} from namespace #{@namespace}" }
|
522
533
|
end
|
523
534
|
|
524
535
|
# Remove all job from cron.
|
@@ -531,9 +542,17 @@ module Sidekiq
|
|
531
542
|
|
532
543
|
# Remove "removed jobs" between current jobs and new jobs
|
533
544
|
def self.destroy_removed_jobs new_job_names
|
534
|
-
|
545
|
+
current_jobs = Sidekiq::Cron::Job.all("*").filter_map { |j| j if j.source == "schedule" }
|
546
|
+
current_job_names = current_jobs.map(&:name)
|
535
547
|
removed_job_names = current_job_names - new_job_names
|
536
|
-
removed_job_names.each
|
548
|
+
removed_job_names.each do |j|
|
549
|
+
job_to_destroy = current_jobs.detect { |job| job.name == j }
|
550
|
+
|
551
|
+
Sidekiq::Cron::Job.destroy(
|
552
|
+
job_to_destroy.name,
|
553
|
+
job_to_destroy.namespace
|
554
|
+
)
|
555
|
+
end
|
537
556
|
removed_job_names
|
538
557
|
end
|
539
558
|
|
@@ -551,15 +570,16 @@ module Sidekiq
|
|
551
570
|
last_time(now).getutc.iso8601
|
552
571
|
end
|
553
572
|
|
554
|
-
def self.exists?
|
573
|
+
def self.exists?(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
555
574
|
out = Sidekiq.redis do |conn|
|
556
|
-
conn.public_send(REDIS_EXISTS_METHOD, redis_key(name))
|
575
|
+
conn.public_send(REDIS_EXISTS_METHOD, redis_key(name, namespace))
|
557
576
|
end
|
558
|
-
|
577
|
+
|
578
|
+
[true, 1].include?(out)
|
559
579
|
end
|
560
580
|
|
561
581
|
def exists?
|
562
|
-
self.class.exists? @name
|
582
|
+
self.class.exists? @name, @namespace
|
563
583
|
end
|
564
584
|
|
565
585
|
def sort_name
|
@@ -573,11 +593,25 @@ module Sidekiq
|
|
573
593
|
private
|
574
594
|
|
575
595
|
def parsed_cron
|
576
|
-
@parsed_cron ||=
|
596
|
+
@parsed_cron ||= do_parse_cron(@cron)
|
597
|
+
end
|
598
|
+
|
599
|
+
def do_parse_cron(cron)
|
600
|
+
case Sidekiq::Cron.configuration.natural_cron_parsing_mode
|
601
|
+
when :single
|
602
|
+
Fugit.do_parse_cronish(cron)
|
603
|
+
when :strict
|
604
|
+
Fugit.parse_cron(cron) || # Ex. '11 1 * * 1'
|
605
|
+
Fugit.parse_nat(cron, :multi => :fail) || # Ex. 'every Monday at 01:11'
|
606
|
+
fail(ArgumentError.new("invalid cron string #{cron.inspect}"))
|
607
|
+
else
|
608
|
+
mode = Sidekiq::Cron.configuration.natural_cron_parsing_mode
|
609
|
+
raise ArgumentError, "Unknown natural cron parsing mode: #{mode.inspect}"
|
610
|
+
end
|
577
611
|
end
|
578
612
|
|
579
|
-
def
|
580
|
-
@last_enqueue_time
|
613
|
+
def enqueued_after?(time)
|
614
|
+
@last_enqueue_time && @last_enqueue_time.to_i >= last_time(time).to_i
|
581
615
|
end
|
582
616
|
|
583
617
|
# Try parsing inbound args into an array.
|
@@ -631,48 +665,83 @@ module Sidekiq
|
|
631
665
|
DateTime.parse(timestamp).to_time.utc
|
632
666
|
end
|
633
667
|
|
634
|
-
def
|
668
|
+
def past_scheduled_time?(current_time)
|
635
669
|
last_cron_time = parsed_cron.previous_time(current_time).utc
|
636
|
-
|
637
|
-
|
670
|
+
period = Sidekiq::Cron.configuration.reschedule_grace_period
|
671
|
+
|
672
|
+
current_time.to_i - last_cron_time.to_i > period
|
673
|
+
end
|
674
|
+
|
675
|
+
def self.default_if_blank(namespace)
|
676
|
+
if namespace.nil? || namespace == ''
|
677
|
+
Sidekiq::Cron.configuration.default_namespace
|
678
|
+
else
|
679
|
+
namespace
|
680
|
+
end
|
638
681
|
end
|
639
682
|
|
640
|
-
|
641
|
-
|
642
|
-
|
683
|
+
def self.job_keys_from_namespace(namespace = Sidekiq::Cron.configuration.default_namespace)
|
684
|
+
Sidekiq.redis do |conn|
|
685
|
+
if namespace == '*'
|
686
|
+
namespaces = conn.keys(jobs_key(namespace))
|
687
|
+
namespaces.flat_map { |name| conn.smembers(name) }
|
688
|
+
else
|
689
|
+
conn.smembers(jobs_key(namespace))
|
690
|
+
end
|
691
|
+
end
|
643
692
|
end
|
644
693
|
|
645
|
-
|
646
|
-
|
647
|
-
|
694
|
+
def self.migrate_old_jobs_if_needed!
|
695
|
+
Sidekiq.redis do |conn|
|
696
|
+
old_job_keys = conn.smembers('cron_jobs')
|
697
|
+
old_job_keys.each do |old_job|
|
698
|
+
old_job_hash = conn.hgetall(old_job)
|
699
|
+
old_job_hash[:namespace] = Sidekiq::Cron.configuration.default_namespace
|
700
|
+
create(old_job_hash)
|
701
|
+
conn.srem('cron_jobs', old_job)
|
702
|
+
end
|
703
|
+
end
|
648
704
|
end
|
649
705
|
|
650
|
-
# Redis key for
|
706
|
+
# Redis key for set of all cron jobs
|
707
|
+
def self.jobs_key(namespace = Sidekiq::Cron.configuration.default_namespace)
|
708
|
+
"cron_jobs:#{default_if_blank(namespace)}"
|
709
|
+
end
|
710
|
+
|
711
|
+
# Redis key for storing one cron job
|
712
|
+
def self.redis_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
713
|
+
"cron_job:#{default_if_blank(namespace)}:#{name}"
|
714
|
+
end
|
715
|
+
|
716
|
+
# Redis key for storing one cron job
|
651
717
|
def redis_key
|
652
|
-
self.class.redis_key @name
|
718
|
+
self.class.redis_key @name, @namespace
|
653
719
|
end
|
654
720
|
|
655
|
-
# Redis key for storing one cron job run times
|
656
|
-
|
657
|
-
|
721
|
+
# Redis key for storing one cron job run times
|
722
|
+
# (when poller added job to queue)
|
723
|
+
def self.job_enqueued_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
724
|
+
"cron_job:#{default_if_blank(namespace)}:#{name}:enqueued"
|
658
725
|
end
|
659
726
|
|
660
|
-
def self.jid_history_key
|
661
|
-
"cron_job:#{name}:jid_history"
|
727
|
+
def self.jid_history_key(name, namespace = Sidekiq::Cron.configuration.default_namespace)
|
728
|
+
"cron_job:#{default_if_blank(namespace)}:#{name}:jid_history"
|
662
729
|
end
|
663
730
|
|
731
|
+
# Redis key for storing one cron job run times
|
732
|
+
# (when poller added job to queue)
|
664
733
|
def job_enqueued_key
|
665
|
-
self.class.job_enqueued_key @name
|
734
|
+
self.class.job_enqueued_key @name, @namespace
|
666
735
|
end
|
667
736
|
|
668
737
|
def jid_history_key
|
669
|
-
self.class.jid_history_key @name
|
738
|
+
self.class.jid_history_key @name, @namespace
|
670
739
|
end
|
671
740
|
|
672
741
|
def serialized_last_enqueue_time
|
673
742
|
@last_enqueue_time&.strftime(LAST_ENQUEUE_TIME_FORMAT)
|
674
743
|
end
|
675
|
-
|
744
|
+
|
676
745
|
def convert_to_global_id_hash(argument)
|
677
746
|
{ GLOBALID_KEY => argument.to_global_id.to_s }
|
678
747
|
rescue URI::GID::MissingModelIdError
|
@@ -719,6 +788,23 @@ module Sidekiq
|
|
719
788
|
argument
|
720
789
|
end
|
721
790
|
end
|
791
|
+
|
792
|
+
def get_job_class_options(klass)
|
793
|
+
klass = klass.is_a?(Class) ? klass : begin
|
794
|
+
Sidekiq::Cron::Support.constantize(klass)
|
795
|
+
rescue NameError
|
796
|
+
# noop
|
797
|
+
end
|
798
|
+
|
799
|
+
if klass.nil?
|
800
|
+
# Unknown class
|
801
|
+
{"queue"=>"default"}
|
802
|
+
elsif is_active_job?(klass)
|
803
|
+
{"queue"=>klass.queue_name}
|
804
|
+
else
|
805
|
+
klass.get_sidekiq_options
|
806
|
+
end
|
807
|
+
end
|
722
808
|
end
|
723
809
|
end
|
724
810
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
id:
|
2
|
+
Job: Job
|
3
|
+
Cron: Cron
|
4
|
+
CronJobs: Cron Job
|
5
|
+
EnqueueNow: Tambahkan ke Antrian Sekarang
|
6
|
+
EnableAll: Aktifkan Semua
|
7
|
+
DisableAll: Nonaktifkan Semua
|
8
|
+
EnqueueAll: Tambahkan Semua ke Antrian
|
9
|
+
DeleteAll: Hapus Semua
|
10
|
+
'Cron string': Cron string
|
11
|
+
AreYouSureEnqueueCronJobs: Apakah Anda yakin ingin menambahkan SEMUA cron job ke antrian?
|
12
|
+
AreYouSureEnqueueCronJob: Apakah Anda yakin ingin menambahkan cron job %{job} ke antrian?
|
13
|
+
AreYouSureDeleteCronJobs: Apakah Anda yakin ingin menghapus SEMUA cron job?
|
14
|
+
AreYouSureDeleteCronJob: Apakah Anda yakin ingin menghapus cron job %{job}?
|
15
|
+
NoCronJobsWereFound: Tidak ada cron job
|
16
|
+
Enable: Aktifkan
|
17
|
+
Disable: Nonaktifkan
|
18
|
+
'Last enqueued': Terakhir kali ditambahkan ke antrian
|
19
|
+
disabled: dinonaktifkan
|
20
|
+
enabled: diaktifkan
|
21
|
+
NoHistoryWereFound: Tidak ada riwayat
|
22
|
+
Description: Deskripsi
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Cron
|
5
|
+
class Namespace
|
6
|
+
def self.all
|
7
|
+
namespaces = nil
|
8
|
+
|
9
|
+
Sidekiq.redis do |conn|
|
10
|
+
namespaces = conn.keys('cron_jobs:*').collect do |key|
|
11
|
+
key.split(':').last
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds the default namespace if not present
|
16
|
+
has_default = namespaces.detect do |name|
|
17
|
+
name == Sidekiq::Cron.configuration.default_namespace
|
18
|
+
end
|
19
|
+
|
20
|
+
unless has_default
|
21
|
+
namespaces << Sidekiq::Cron.configuration.default_namespace
|
22
|
+
end
|
23
|
+
|
24
|
+
namespaces
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.all_with_count
|
28
|
+
all.map do |namespace_name|
|
29
|
+
{
|
30
|
+
count: count(namespace_name),
|
31
|
+
name: namespace_name
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.count(name = Sidekiq::Cron.configuration.default_namespace)
|
37
|
+
out = 0
|
38
|
+
Sidekiq.redis do |conn|
|
39
|
+
out = conn.scard("cron_jobs:#{name}")
|
40
|
+
end
|
41
|
+
out
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/sidekiq/cron/poller.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sidekiq'
|
2
4
|
require 'sidekiq/cron'
|
3
5
|
require 'sidekiq/scheduled'
|
@@ -5,21 +7,26 @@ require 'sidekiq/options'
|
|
5
7
|
|
6
8
|
module Sidekiq
|
7
9
|
module Cron
|
8
|
-
# The Poller checks Redis every N seconds for
|
10
|
+
# The Poller checks Redis every N seconds for scheduled cron jobs.
|
9
11
|
class Poller < Sidekiq::Scheduled::Poller
|
10
12
|
def initialize(config = nil)
|
11
|
-
if Gem::Version.new(Sidekiq::VERSION)
|
12
|
-
super
|
13
|
-
else
|
13
|
+
if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new('6.5.0')
|
14
14
|
# Old version of Sidekiq does not accept a config argument.
|
15
15
|
@config = config
|
16
|
-
super()
|
17
16
|
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
Sidekiq::Cron::Job.migrate_old_jobs_if_needed!
|
23
|
+
|
24
|
+
super
|
18
25
|
end
|
19
26
|
|
20
27
|
def enqueue
|
21
28
|
time = Time.now.utc
|
22
|
-
Sidekiq::Cron::Job.all.each do |job|
|
29
|
+
Sidekiq::Cron::Job.all('*').each do |job|
|
23
30
|
enqueue_job(job, time)
|
24
31
|
end
|
25
32
|
rescue => ex
|