sidekiq-cron 1.12.0 → 2.0.0.rc1
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.
- 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
|