sidekiq-cron 0.6.3 → 1.4.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.
data/Rakefile CHANGED
@@ -1,43 +1,10 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- require 'bundler'
6
-
7
- begin
8
- Bundler.setup(:default, :development)
9
- rescue Bundler::BundlerError => e
10
- $stderr.puts e.message
11
- $stderr.puts "Run `bundle install` to install missing gems"
12
- exit e.status_code
13
- end
14
- require 'rake'
15
-
16
- require 'jeweler'
17
- Jeweler::Tasks.new do |gem|
18
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
- gem.name = "sidekiq-cron"
20
- gem.homepage = "http://github.com/ondrejbartas/sidekiq-cron"
21
- gem.license = "MIT"
22
- gem.summary = %Q{Sidekiq Cron helps to add repeated scheduled jobs}
23
- gem.description = %Q{Enables to set jobs to be run in specified time (using CRON notation)}
24
- gem.email = "ondrej@bartas.cz"
25
- gem.authors = ["Ondrej Bartas"]
26
- # dependencies defined in Gemfile
27
- end
28
- Jeweler::RubygemsDotOrgTasks.new
29
-
30
- #TESTING
31
-
32
- task :doc do
33
- system 'sdoc -N .'
34
- end
35
-
1
+ require 'bundler/gem_tasks'
36
2
  require 'rake/testtask'
3
+
37
4
  task :default => :test
38
5
 
39
6
  Rake::TestTask.new(:test) do |t|
40
- t.test_files = FileList['test/functional/**/*_test.rb', 'test/unit/**/*_test.rb','test/integration/**/*_test.rb']
7
+ t.test_files = FileList['test/unit/**/*_test.rb', 'test/integration/**/*_test.rb']
41
8
  t.warning = false
42
9
  t.verbose = false
43
10
  end
@@ -49,12 +16,6 @@ namespace :test do
49
16
  t.verbose = false
50
17
  end
51
18
 
52
- Rake::TestTask.new(:functional) do |t|
53
- t.test_files = FileList['test/functional/**/*_test.rb']
54
- t.warning = false
55
- t.verbose = false
56
- end
57
-
58
19
  Rake::TestTask.new(:integration) do |t|
59
20
  t.test_files = FileList['test/integration/**/*_test.rb']
60
21
  t.warning = false
@@ -1,6 +1,6 @@
1
+ require 'fugit'
1
2
  require 'sidekiq'
2
3
  require 'sidekiq/util'
3
- require 'rufus-scheduler'
4
4
  require 'sidekiq/cron/support'
5
5
 
6
6
  module Sidekiq
@@ -12,6 +12,10 @@ module Sidekiq
12
12
 
13
13
  #how long we would like to store informations about previous enqueues
14
14
  REMEMBER_THRESHOLD = 24 * 60 * 60
15
+ LAST_ENQUEUE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S %z'
16
+
17
+ # Use the exists? method if we're on a newer version of redis.
18
+ REDIS_EXISTS_METHOD = Gem.loaded_specs['redis'].version < Gem::Version.new('4.2') ? :exists : :exists?
15
19
 
16
20
  #crucial part of whole enquing job
17
21
  def should_enque? time
@@ -46,7 +50,7 @@ module Sidekiq
46
50
 
47
51
  #enque cron job to queue
48
52
  def enque! time = Time.now.utc
49
- @last_enqueue_time = time
53
+ @last_enqueue_time = time.strftime(LAST_ENQUEUE_TIME_FORMAT)
50
54
 
51
55
  klass_const =
52
56
  begin
@@ -55,21 +59,23 @@ module Sidekiq
55
59
  nil
56
60
  end
57
61
 
58
- if klass_const
59
- if defined?(ActiveJob::Base) && klass_const < ActiveJob::Base
60
- enqueue_active_job(klass_const)
61
- else
62
- enqueue_sidekiq_worker(klass_const)
63
- end
64
- else
65
- if @active_job
66
- Sidekiq::Client.push(active_job_message)
62
+ jid =
63
+ if klass_const
64
+ if defined?(ActiveJob::Base) && klass_const < ActiveJob::Base
65
+ enqueue_active_job(klass_const).try :provider_job_id
66
+ else
67
+ enqueue_sidekiq_worker(klass_const)
68
+ end
67
69
  else
68
- Sidekiq::Client.push(sidekiq_worker_message)
70
+ if @active_job
71
+ Sidekiq::Client.push(active_job_message)
72
+ else
73
+ Sidekiq::Client.push(sidekiq_worker_message)
74
+ end
69
75
  end
70
- end
71
76
 
72
77
  save_last_enqueue_time
78
+ add_jid_history jid
73
79
  logger.debug { "enqueued #{@name}: #{@message}" }
74
80
  end
75
81
 
@@ -81,14 +87,10 @@ module Sidekiq
81
87
 
82
88
  def enqueue_active_job(klass_const)
83
89
  klass_const.set(queue: @queue).perform_later(*@args)
84
-
85
- true
86
90
  end
87
91
 
88
92
  def enqueue_sidekiq_worker(klass_const)
89
93
  klass_const.set(queue: queue_name_with_prefix).perform_async(*@args)
90
-
91
- true
92
94
  end
93
95
 
94
96
  # siodekiq worker message
@@ -123,6 +125,7 @@ module Sidekiq
123
125
  def active_job_message
124
126
  {
125
127
  'class' => 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper',
128
+ 'wrapped' => @klass,
126
129
  'queue' => @queue_name_with_prefix,
127
130
  'description' => @description,
128
131
  'args' => [{
@@ -203,9 +206,9 @@ module Sidekiq
203
206
  job_hashes = nil
204
207
  Sidekiq.redis do |conn|
205
208
  set_members = conn.smembers(jobs_key)
206
- job_hashes = conn.pipelined do
209
+ job_hashes = conn.pipelined do |pipeline|
207
210
  set_members.each do |key|
208
- conn.hgetall(key)
211
+ pipeline.hgetall(key)
209
212
  end
210
213
  end
211
214
  end
@@ -233,7 +236,7 @@ module Sidekiq
233
236
  output = Job.new conn.hgetall( redis_key(name) )
234
237
  end
235
238
  end
236
- output
239
+ output if output && output.valid?
237
240
  end
238
241
 
239
242
  # create new instance of cron job
@@ -273,13 +276,15 @@ module Sidekiq
273
276
 
274
277
  #set last enqueue time - from args or from existing job
275
278
  if args['last_enqueue_time'] && !args['last_enqueue_time'].empty?
276
- @last_enqueue_time = Time.parse(args['last_enqueue_time'])
279
+ @last_enqueue_time = parse_enqueue_time(args['last_enqueue_time'])
277
280
  else
278
281
  @last_enqueue_time = last_enqueue_time_from_redis
279
282
  end
280
283
 
281
284
  #get right arguments for job
285
+ @symbolize_args = args["symbolize_args"] == true || ("#{args["symbolize_args"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
282
286
  @args = args["args"].nil? ? [] : parse_args( args["args"] )
287
+ @args += [Time.now.to_f] if args["date_as_argument"]
283
288
 
284
289
  @active_job = args["active_job"] == true || ("#{args["active_job"]}" =~ (/^(true|t|yes|y|1)$/i)) == 0 || false
285
290
  @active_job_queue_name_prefix = args["queue_name_prefix"]
@@ -347,6 +352,12 @@ module Sidekiq
347
352
  !enabled?
348
353
  end
349
354
 
355
+ def pretty_message
356
+ JSON.pretty_generate Sidekiq.load_json(message)
357
+ rescue JSON::ParserError
358
+ message
359
+ end
360
+
350
361
  def status_from_redis
351
362
  out = "enabled"
352
363
  if fetch_missing_args
@@ -362,12 +373,24 @@ module Sidekiq
362
373
  out = nil
363
374
  if fetch_missing_args
364
375
  Sidekiq.redis do |conn|
365
- out = Time.parse(conn.hget(redis_key, "last_enqueue_time")) rescue nil
376
+ out = parse_enqueue_time(conn.hget(redis_key, "last_enqueue_time")) rescue nil
366
377
  end
367
378
  end
368
379
  out
369
380
  end
370
381
 
382
+ def jid_history_from_redis
383
+ out =
384
+ Sidekiq.redis do |conn|
385
+ conn.lrange(jid_history_key, 0, -1) rescue nil
386
+ end
387
+
388
+ # returns nil if out nil
389
+ out && out.map do |jid_history_raw|
390
+ Sidekiq.load_json jid_history_raw
391
+ end
392
+ end
393
+
371
394
  #export job data to hash
372
395
  def to_hash
373
396
  {
@@ -382,6 +405,7 @@ module Sidekiq
382
405
  queue_name_prefix: @active_job_queue_name_prefix,
383
406
  queue_name_delimiter: @active_job_queue_name_delimiter,
384
407
  last_enqueue_time: @last_enqueue_time,
408
+ symbolize_args: @symbolize_args,
385
409
  }
386
410
  end
387
411
 
@@ -390,7 +414,7 @@ module Sidekiq
390
414
  end
391
415
 
392
416
  def valid?
393
- #clear previos errors
417
+ #clear previous errors
394
418
  @errors = []
395
419
 
396
420
  errors << "'name' must be set" if @name.nil? || @name.size == 0
@@ -398,21 +422,15 @@ module Sidekiq
398
422
  errors << "'cron' must be set"
399
423
  else
400
424
  begin
401
- cron = Rufus::Scheduler::CronLine.new(@cron)
402
- cron.next_time(Time.now.utc).utc
403
- rescue Exception => e
404
- #fix for different versions of cron-parser
405
- if e.message == "Bad Vixie-style specification bad"
406
- errors << "'cron' -> #{@cron}: not a valid cronline"
407
- else
408
- errors << "'cron' -> #{@cron}: #{e.message}"
409
- end
425
+ @parsed_cron = Fugit.do_parse_cron(@cron)
426
+ rescue => e
427
+ errors << "'cron' -> #{@cron.inspect} -> #{e.class}: #{e.message}"
410
428
  end
411
429
  end
412
430
 
413
431
  errors << "'klass' (or class) must be set" unless klass_valid
414
432
 
415
- !errors.any?
433
+ errors.empty?
416
434
  end
417
435
 
418
436
  def klass_valid
@@ -448,9 +466,9 @@ module Sidekiq
448
466
 
449
467
  #add information about last time! - don't enque right after scheduler poller starts!
450
468
  time = Time.now.utc
451
- conn.zadd(job_enqueued_key, time.to_f.to_s, formated_last_time(time).to_s) unless conn.exists(job_enqueued_key)
469
+ conn.zadd(job_enqueued_key, time.to_f.to_s, formated_last_time(time).to_s) unless conn.public_send(REDIS_EXISTS_METHOD, job_enqueued_key)
452
470
  end
453
- logger.info { "Cron Jobs - add job with name: #{@name}" }
471
+ logger.info { "Cron Jobs - added job with name: #{@name}" }
454
472
  end
455
473
 
456
474
  def save_last_enqueue_time
@@ -460,6 +478,20 @@ module Sidekiq
460
478
  end
461
479
  end
462
480
 
481
+ def add_jid_history(jid)
482
+ jid_history = {
483
+ jid: jid,
484
+ enqueued: @last_enqueue_time
485
+ }
486
+ @history_size ||= (Sidekiq.options[:cron_history_size] || 10).to_i - 1
487
+ Sidekiq.redis do |conn|
488
+ conn.lpush jid_history_key,
489
+ Sidekiq.dump_json(jid_history)
490
+ # keep only last 10 entries in a fifo manner
491
+ conn.ltrim jid_history_key, 0, @history_size
492
+ end
493
+ end
494
+
463
495
  # remove job from cron jobs by name
464
496
  # input:
465
497
  # first arg: name (string) - name of job (must be same - case sensitive)
@@ -471,6 +503,9 @@ module Sidekiq
471
503
  #delete runned timestamps
472
504
  conn.del job_enqueued_key
473
505
 
506
+ # delete jid_history
507
+ conn.del jid_history_key
508
+
474
509
  #delete main job
475
510
  conn.del redis_key
476
511
  end
@@ -496,7 +531,7 @@ module Sidekiq
496
531
  # Parse cron specification '* * * * *' and returns
497
532
  # time when last run should be performed
498
533
  def last_time now = Time.now.utc
499
- Rufus::Scheduler::CronLine.new(@cron).previous_time(now.utc).utc
534
+ parsed_cron.previous_time(now.utc).utc
500
535
  end
501
536
 
502
537
  def formated_enqueue_time now = Time.now.utc
@@ -510,7 +545,7 @@ module Sidekiq
510
545
  def self.exists? name
511
546
  out = false
512
547
  Sidekiq.redis do |conn|
513
- out = conn.exists redis_key name
548
+ out = conn.public_send(REDIS_EXISTS_METHOD, redis_key(name))
514
549
  end
515
550
  out
516
551
  end
@@ -525,6 +560,10 @@ module Sidekiq
525
560
 
526
561
  private
527
562
 
563
+ def parsed_cron
564
+ @parsed_cron ||= Fugit.parse_cron(@cron)
565
+ end
566
+
528
567
  def not_enqueued_after?(time)
529
568
  @last_enqueue_time.nil? || @last_enqueue_time.to_i < last_time(time).to_i
530
569
  end
@@ -537,21 +576,50 @@ module Sidekiq
537
576
  case args
538
577
  when String
539
578
  begin
540
- Sidekiq.load_json(args)
579
+ parsed_args = Sidekiq.load_json(args)
580
+ symbolize_args? ? symbolize_args(parsed_args) : parsed_args
541
581
  rescue JSON::ParserError
542
582
  [*args] # cast to string array
543
583
  end
544
584
  when Hash
545
- [args] # just put hash into array
585
+ symbolize_args? ? [symbolize_args(args)] : [args]
546
586
  when Array
547
- args # do nothing, already array
587
+ symbolize_args? ? symbolize_args(args) : args
548
588
  else
549
589
  [*args] # cast to string array
550
590
  end
551
591
  end
552
592
 
593
+ def symbolize_args?
594
+ @symbolize_args
595
+ end
596
+
597
+ def symbolize_args(input)
598
+ if input.is_a?(Array)
599
+ input.map do |arg|
600
+ if arg.respond_to?(:symbolize_keys)
601
+ arg.symbolize_keys
602
+ else
603
+ arg
604
+ end
605
+ end
606
+ elsif input.is_a?(Hash) && input.respond_to?(:symbolize_keys)
607
+ input.symbolize_keys
608
+ else
609
+ input
610
+ end
611
+ end
612
+
613
+ def parse_enqueue_time(timestamp)
614
+ DateTime.strptime(timestamp, LAST_ENQUEUE_TIME_FORMAT).to_time.utc
615
+ rescue ArgumentError
616
+ DateTime.parse(timestamp).to_time.utc
617
+ end
618
+
553
619
  def not_past_scheduled_time?(current_time)
554
- last_cron_time = Rufus::Scheduler::CronLine.new(@cron).previous_time(current_time).utc
620
+ last_cron_time = parsed_cron.previous_time(current_time).utc
621
+ # or could it be?
622
+ #last_cron_time = last_time(current_time)
555
623
  return false if (current_time.to_i - last_cron_time.to_i) > 60
556
624
  true
557
625
  end
@@ -577,12 +645,20 @@ module Sidekiq
577
645
  "cron_job:#{name}:enqueued"
578
646
  end
579
647
 
648
+ def self.jid_history_key name
649
+ "cron_job:#{name}:jid_history"
650
+ end
651
+
580
652
  # Redis key for storing one cron job run times
581
653
  # (when poller added job to queue)
582
654
  def job_enqueued_key
583
655
  self.class.job_enqueued_key @name
584
656
  end
585
657
 
658
+ def jid_history_key
659
+ self.class.jid_history_key @name
660
+ end
661
+
586
662
  # Give Hash
587
663
  # returns array for using it for redis.hmset
588
664
  def hash_to_redis hash
@@ -1,6 +1,3 @@
1
- # require Sidekiq original launcher
2
- require 'sidekiq/launcher'
3
-
4
1
  # require cron poller
5
2
  require 'sidekiq/cron/poller'
6
3
 
@@ -10,44 +7,41 @@ require 'sidekiq/cron/poller'
10
7
  # we are creating new cron poller instance and
11
8
  # adding start and stop commands to launcher
12
9
  module Sidekiq
13
- class Launcher
14
- # Add cron poller to launcher
15
- attr_reader :cron_poller
16
-
17
- # remember old initialize
18
- alias_method :old_initialize, :initialize
19
-
20
- # add cron poller and execute normal initialize of Sidekiq launcher
21
- def initialize(options)
22
- @cron_poller = Sidekiq::Cron::Poller.new
23
- old_initialize options
24
- end
25
-
26
- # remember old run
27
- alias_method :old_run, :run
28
-
29
- # execute normal run of launcher and run cron poller
30
- def run
31
- old_run
32
- cron_poller.start
33
- end
34
-
35
- # remember old quiet
36
- alias_method :old_quiet, :quiet
37
-
38
- # execute normal quiet of launcher and quiet cron poller
39
- def quiet
40
- cron_poller.terminate
41
- old_quiet
10
+ module Cron
11
+ module Launcher
12
+ # Add cron poller to launcher
13
+ attr_reader :cron_poller
14
+
15
+ # add cron poller and execute normal initialize of Sidekiq launcher
16
+ def initialize(options)
17
+ @cron_poller = Sidekiq::Cron::Poller.new
18
+ super(options)
19
+ end
20
+
21
+ # execute normal run of launcher and run cron poller
22
+ def run
23
+ super
24
+ cron_poller.start
25
+ end
26
+
27
+ # execute normal quiet of launcher and quiet cron poller
28
+ def quiet
29
+ cron_poller.terminate
30
+ super
31
+ end
32
+
33
+ # execute normal stop of launcher and stop cron poller
34
+ def stop
35
+ cron_poller.terminate
36
+ super
37
+ end
42
38
  end
39
+ end
40
+ end
43
41
 
44
- # remember old stop
45
- alias_method :old_stop, :stop
42
+ Sidekiq.configure_server do
43
+ # require Sidekiq original launcher
44
+ require 'sidekiq/launcher'
46
45
 
47
- # execute normal stop of launcher and stop cron poller
48
- def stop
49
- cron_poller.terminate
50
- old_stop
51
- end
52
- end
46
+ ::Sidekiq::Launcher.prepend(Sidekiq::Cron::Launcher)
53
47
  end
@@ -5,9 +5,9 @@ de:
5
5
  EnqueueNow: In Warteschlange
6
6
  'Cron string': Cron
7
7
  AreYouSureDeleteCronJob: Sind Sie sicher, dass sie den Cronjob %{job} löschen wollen?
8
- NoCronJobsFound: "Keine Cronjobs gefunden"
8
+ NoCronJobsWereFound: Keine Cronjobs gefunden
9
9
  Enable: Aktivieren
10
10
  Disable: Deaktivieren
11
- 'Last enque': Eingereiht
11
+ 'Last enqueued': Eingereiht
12
12
  disabled: deaktiviert
13
13
  enabled: aktiviert
@@ -8,11 +8,15 @@ en:
8
8
  EnqueueAll: Enqueue All
9
9
  DeleteAll: Delete All
10
10
  'Cron string': Cron
11
+ AreYouSureEnqueueCronJobs: Are you sure you want to enqueue ALL cron jobs?
12
+ AreYouSureEnqueueCronJob: Are you sure you want to enqueue the %{job} cron job?
11
13
  AreYouSureDeleteCronJobs: Are you sure you want to delete ALL cron jobs?
12
14
  AreYouSureDeleteCronJob: Are you sure you want to delete the %{job} cron job?
13
- NoCronJobsFound: "No cron jobs found"
15
+ NoCronJobsWereFound: No cron jobs were found
14
16
  Enable: Enable
15
17
  Disable: Disable
16
- 'Last enque': Last enqueued
18
+ 'Last enqueued': Last enqueued
17
19
  disabled: disabled
18
20
  enabled: enabled
21
+ NoHistoryWereFound: No history were found
22
+ Description: Description
@@ -0,0 +1,18 @@
1
+ ja:
2
+ Job: ジョブ
3
+ Cron: Cron
4
+ CronJobs: Cronジョブ
5
+ EnqueueNow: すぐにキューに入れる
6
+ EnableAll: すべて有効にする
7
+ DisableAll: すべて無効にする
8
+ EnqueueAll: すべてキューに入れる
9
+ DeleteAll: すべて削除
10
+ 'Cron string': Cron
11
+ AreYouSureDeleteCronJobs: 本当にすべてのcronジョブを削除しますか?
12
+ AreYouSureDeleteCronJob: 本当に%{job}のcronジョブを削除しますか?
13
+ NoCronJobsWereFound: Cronジョブが見つかりませんでした
14
+ Enable: 有効にする
15
+ Disable: 無効にする
16
+ 'Last enqueued': 最後のキュー
17
+ disabled: 無効
18
+ enabled: 有効
@@ -6,9 +6,9 @@ ru:
6
6
  'Cron string': Периодичность (синтаксис Cron)
7
7
  EnqueueNow: Запустить
8
8
  AreYouSureDeleteCronJob: Вы действительно хотите удалить задачу «%{job}»?
9
- NoCronJobsFound: "Не найдено периодических задач"
9
+ NoCronJobsWereFound: Не найдено периодических задач
10
10
  Enable: Включить
11
11
  Disable: Отключить
12
- 'Last enque': Последний запуск
12
+ 'Last enqueued': Последний запуск
13
13
  disabled: отключено
14
14
  enabled: включено
@@ -0,0 +1,19 @@
1
+ zh-CN:
2
+ Job: 任务
3
+ Cron: 定时任务
4
+ CronJobs: 定时任务列表
5
+ EnqueueNow: 立刻执行
6
+ EnableAll: 启用所有
7
+ DisableAll: 禁用所有
8
+ EnqueueAll: 执行所有
9
+ DeleteAll: 删除所有
10
+ 'Cron string': 定时策略
11
+ AreYouSureDeleteCronJobs: 你确定删除所有的定时任务吗?
12
+ AreYouSureDeleteCronJob: 你确定删除定时任务(%{job})吗?
13
+ NoCronJobsWereFound: 没有定时任务
14
+ Enable: 启用
15
+ Disable: 禁用
16
+ 'Last enqueued': 放入队列时间
17
+ disabled: 已禁用
18
+ enabled: 已启用
19
+
@@ -17,8 +17,9 @@ module Sidekiq
17
17
  rescue => ex
18
18
  # Most likely a problem with redis networking.
19
19
  # Punt and try again at the next interval
20
- logger.error ex.message
21
- logger.error ex.backtrace.first
20
+ Sidekiq.logger.error ex.message
21
+ Sidekiq.logger.error ex.backtrace.first
22
+ handle_exception(ex) if respond_to?(:handle_exception)
22
23
  end
23
24
 
24
25
  private
@@ -27,12 +28,13 @@ module Sidekiq
27
28
  job.test_and_enque_for_time! time if job && job.valid?
28
29
  rescue => ex
29
30
  # problem somewhere in one job
30
- logger.error "CRON JOB: #{ex.message}"
31
- logger.error "CRON JOB: #{ex.backtrace.first}"
31
+ Sidekiq.logger.error "CRON JOB: #{ex.message}"
32
+ Sidekiq.logger.error "CRON JOB: #{ex.backtrace.first}"
33
+ handle_exception(ex) if respond_to?(:handle_exception)
32
34
  end
33
35
 
34
36
  def poll_interval_average
35
- Sidekiq.options[:poll_interval] || POLL_INTERVAL
37
+ Sidekiq.options[:average_scheduled_poll_interval] || POLL_INTERVAL
36
38
  end
37
39
  end
38
40
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ module Cron
5
+ VERSION = "1.4.0"
6
+ end
7
+ end