rufus-scheduler 3.5.2 → 3.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/CREDITS.md +10 -0
- data/LICENSE.txt +1 -1
- data/Makefile +1 -1
- data/README.md +179 -85
- data/lib/rufus/scheduler/job_array.rb +37 -47
- data/lib/rufus/scheduler/jobs_core.rb +369 -0
- data/lib/rufus/scheduler/jobs_one_time.rb +53 -0
- data/lib/rufus/scheduler/jobs_repeat.rb +335 -0
- data/lib/rufus/scheduler/locks.rb +41 -44
- data/lib/rufus/scheduler/util.rb +166 -150
- data/lib/rufus/scheduler.rb +529 -432
- data/rufus-scheduler.gemspec +2 -3
- metadata +13 -12
- data/lib/rufus/scheduler/jobs.rb +0 -682
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
|
2
2
|
# rufus-scheduler
|
3
3
|
|
4
|
-
[![
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/rufus-scheduler.svg)](
|
4
|
+
[![tests](https://github.com/jmettraux/rufus-scheduler/workflows/test/badge.svg)](https://github.com/jmettraux/rufus-scheduler/actions)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/rufus-scheduler.svg)](https://badge.fury.io/rb/rufus-scheduler)
|
6
|
+
[![Join the chat at https://gitter.im/floraison/fugit](https://badges.gitter.im/floraison/fugit.svg)](https://gitter.im/floraison/fugit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
6
7
|
|
7
8
|
Job scheduler for Ruby (at, cron, in and every jobs).
|
8
9
|
|
@@ -23,7 +24,11 @@ scheduler.in '3s' do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
scheduler.join
|
27
|
+
#
|
26
28
|
# let the current thread join the scheduler thread
|
29
|
+
#
|
30
|
+
# (please note that this join should be removed when scheduling
|
31
|
+
# in a web application (Rails and friends) initializer)
|
27
32
|
```
|
28
33
|
(run with `ruby quickstart.rb`)
|
29
34
|
|
@@ -58,6 +63,8 @@ end
|
|
58
63
|
# ...
|
59
64
|
```
|
60
65
|
|
66
|
+
Rufus-scheduler uses [fugit](https://github.com/floraison/fugit) for parsing time strings, [et-orbi](https://github.com/floraison/et-orbi) for pairing time and [tzinfo](https://github.com/tzinfo/tzinfo) timezones.
|
67
|
+
|
61
68
|
## non-features
|
62
69
|
|
63
70
|
Rufus-scheduler (out of the box) is an in-process, in-memory scheduler. It uses threads.
|
@@ -70,9 +77,10 @@ A rufus-scheduler instance will go on scheduling while it is present among the o
|
|
70
77
|
## related and similar gems
|
71
78
|
|
72
79
|
* [Whenever](https://github.com/javan/whenever) - let cron call back your Ruby code, trusted and reliable cron drives your schedule
|
80
|
+
* [ruby-clock](https://github.com/jjb/ruby-clock) - a clock process / job scheduler for Ruby
|
73
81
|
* [Clockwork](https://github.com/Rykian/clockwork) - rufus-scheduler inspired gem
|
74
82
|
* [Crono](https://github.com/plashchynski/crono) - an in-Rails cron scheduler
|
75
|
-
* [PerfectSched](https://github.com/treasure-data/perfectsched) - highly available distributed cron built on [Sequel](
|
83
|
+
* [PerfectSched](https://github.com/treasure-data/perfectsched) - highly available distributed cron built on [Sequel](https://sequel.jeremyevans.net) and more
|
76
84
|
|
77
85
|
(please note: rufus-scheduler is not a cron replacement)
|
78
86
|
|
@@ -89,7 +97,7 @@ There is no EventMachine-based scheduler anymore.
|
|
89
97
|
I'll drive you right to the [tracks](#so-rails).
|
90
98
|
|
91
99
|
|
92
|
-
##
|
100
|
+
## notable changes:
|
93
101
|
|
94
102
|
* As said, no more EventMachine-based scheduler
|
95
103
|
* ```scheduler.every('100') {``` will schedule every 100 seconds (previously, it would have been 0.1s). This aligns rufus-scheduler with Ruby's ```sleep(100)```
|
@@ -97,8 +105,8 @@ I'll drive you right to the [tracks](#so-rails).
|
|
97
105
|
* The error_handler is [#on_error](#rufusscheduleron_errorjob-error) (instead of #on_exception), by default it now prints the details of the error to $stderr (used to be $stdout)
|
98
106
|
* Rufus::Scheduler::TimeOutError renamed to Rufus::Scheduler::TimeoutError
|
99
107
|
* Introduction of "interval" jobs. Whereas "every" jobs are like "every 10 minutes, do this", interval jobs are like "do that, then wait for 10 minutes, then do that again, and so on"
|
100
|
-
* Introduction of a :
|
101
|
-
* "discard_past" is on by default. If the scheduler (its host) sleeps for 1 hour and a
|
108
|
+
* Introduction of a lockfile: true/filename mechanism to prevent multiple schedulers from executing
|
109
|
+
* "discard_past" is on by default. If the scheduler (its host) sleeps for 1 hour and a `every '10m'` job is on, it will trigger once at wakeup, not 6 times (discard_past was false by default in rufus-scheduler 2.x). No intention to re-introduce `discard_past: false` in 3.0 for now.
|
102
110
|
* Introduction of Scheduler #on_pre_trigger and #on_post_trigger callback points
|
103
111
|
|
104
112
|
|
@@ -108,26 +116,26 @@ So you need help. People can help you, but first help them help you, and don't w
|
|
108
116
|
|
109
117
|
"hello", "please" and "thanks" are not swear words.
|
110
118
|
|
111
|
-
Go read [how to report bugs effectively](
|
119
|
+
Go read [how to report bugs effectively](https://www.chiark.greenend.org.uk/~sgtatham/bugs.html), twice.
|
112
120
|
|
113
121
|
Update: [help_help.md](https://gist.github.com/jmettraux/310fed75f568fd731814) might help help you.
|
114
122
|
|
115
|
-
### on
|
123
|
+
### on Gitter
|
116
124
|
|
117
|
-
|
125
|
+
You can find help via chat over at [https://gitter.im/floraison/fugit](https://gitter.im/floraison/fugit). It's [fugit](https://github.com/floraison/fugit), [et-orbi](https://github.com/floraison/et-orbi), and rufus-scheduler combined chat room.
|
118
126
|
|
119
|
-
Please
|
127
|
+
Please be courteous.
|
120
128
|
|
121
129
|
### issues
|
122
130
|
|
123
|
-
Yes, issues can be reported in [rufus-scheduler issues](https://github.com/jmettraux/rufus-scheduler/issues), I'd actually prefer bugs in there. If there is nothing wrong with rufus-scheduler, a [Stack Overflow question](
|
131
|
+
Yes, issues can be reported in [rufus-scheduler issues](https://github.com/jmettraux/rufus-scheduler/issues), I'd actually prefer bugs in there. If there is nothing wrong with rufus-scheduler, a [Stack Overflow question](https://stackoverflow.com/questions/ask?tags=rufus-scheduler+ruby) is better.
|
124
132
|
|
125
133
|
### faq
|
126
134
|
|
127
|
-
* [It doesn't work...](
|
128
|
-
* [I want a refund](
|
129
|
-
* [Passenger and rufus-scheduler](
|
130
|
-
* [Passenger and rufus-scheduler (2)](
|
135
|
+
* [It doesn't work...](https://www.chiark.greenend.org.uk/~sgtatham/bugs.html)
|
136
|
+
* [I want a refund](https://blog.nodejitsu.com/getting-refunds-on-open-source-projects)
|
137
|
+
* [Passenger and rufus-scheduler](https://stackoverflow.com/questions/18108719/debugging-rufus-scheduler/18156180#18156180)
|
138
|
+
* [Passenger and rufus-scheduler (2)](https://stackoverflow.com/questions/21861387/rufus-cron-job-not-working-in-apache-passenger#answer-21868555)
|
131
139
|
* [Passenger in-depth spawn methods](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/)
|
132
140
|
* [Passenger in-depth spawn methods (smart spawning)](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#smart-spawning-hooks)
|
133
141
|
* [The scheduler comes up when running the Rails console or a Rake task](https://github.com/jmettraux/rufus-scheduler#avoid-scheduling-when-running-the-ruby-on-rails-console)
|
@@ -215,7 +223,7 @@ job =
|
|
215
223
|
# also
|
216
224
|
|
217
225
|
job =
|
218
|
-
scheduler.in '10d', :
|
226
|
+
scheduler.in '10d', job: true do
|
219
227
|
# ...
|
220
228
|
end
|
221
229
|
```
|
@@ -370,23 +378,40 @@ While paused, the scheduler still accepts schedules, but no schedule will get tr
|
|
370
378
|
|
371
379
|
## job options
|
372
380
|
|
373
|
-
### :
|
381
|
+
### name: string
|
382
|
+
|
383
|
+
Sets the name of the job.
|
384
|
+
|
385
|
+
```ruby
|
386
|
+
scheduler.cron '*/15 8 * * *', name: 'Robert' do |job|
|
387
|
+
puts "A, it's #{Time.now} and my name is #{job.name}"
|
388
|
+
end
|
389
|
+
|
390
|
+
job1 =
|
391
|
+
scheduler.schedule_cron '*/30 9 * * *', n: 'temporary' do |job|
|
392
|
+
puts "B, it's #{Time.now} and my name is #{job.name}"
|
393
|
+
end
|
394
|
+
# ...
|
395
|
+
job1.name = 'Beowulf'
|
396
|
+
```
|
397
|
+
|
398
|
+
### blocking: true
|
374
399
|
|
375
|
-
By default, jobs are triggered in their own, new threads. When
|
400
|
+
By default, jobs are triggered in their own, new threads. When `blocking: true`, the job is triggered in the scheduler thread (a new thread is not created). Yes, while a blocking job is running, the scheduler is not scheduling.
|
376
401
|
|
377
|
-
### :
|
402
|
+
### overlap: false
|
378
403
|
|
379
404
|
Since, by default, jobs are triggered in their own new threads, job instances might overlap. For example, a job that takes 10 minutes and is scheduled every 7 minutes will have overlaps.
|
380
405
|
|
381
|
-
To prevent overlap, one can set
|
406
|
+
To prevent overlap, one can set `overlap: false`. Such a job will not trigger if one of its instances is already running.
|
382
407
|
|
383
408
|
The `:overlap` option is considered before the `:mutex` option when the scheduler is reviewing jobs for triggering.
|
384
409
|
|
385
|
-
### :
|
410
|
+
### mutex: mutex_instance / mutex_name / array of mutexes
|
386
411
|
|
387
412
|
When a job with a mutex triggers, the job's block is executed with the mutex around it, preventing other jobs with the same mutex from entering (it makes the other jobs wait until it exits the mutex).
|
388
413
|
|
389
|
-
This is different from
|
414
|
+
This is different from `overlap: false`, which is, first, limited to instances of the same job, and, second, doesn't make the incoming job instance block/wait but give up.
|
390
415
|
|
391
416
|
`:mutex` accepts a mutex instance or a mutex name (String). It also accept an array of mutex names / mutex instances. It allows for complex relations between jobs.
|
392
417
|
|
@@ -396,12 +421,12 @@ Note: creating lots of different mutexes is OK. Rufus-scheduler will place them
|
|
396
421
|
|
397
422
|
The `:overlap` option is considered before the `:mutex` option when the scheduler is reviewing jobs for triggering.
|
398
423
|
|
399
|
-
### :
|
424
|
+
### timeout: duration or point in time
|
400
425
|
|
401
426
|
It's OK to specify a timeout when scheduling some work. After the time specified, it gets interrupted via a Rufus::Scheduler::TimeoutError.
|
402
427
|
|
403
428
|
```ruby
|
404
|
-
scheduler.in '10d', :
|
429
|
+
scheduler.in '10d', timeout: '1d' do
|
405
430
|
begin
|
406
431
|
# ... do something
|
407
432
|
rescue Rufus::Scheduler::TimeoutError
|
@@ -422,15 +447,15 @@ In the case of an "every" job, this will be the first time (modulo the scheduler
|
|
422
447
|
For a "cron" job as well, the :first will point to the first time the job has to trigger, the following trigger times are then determined by the cron string.
|
423
448
|
|
424
449
|
```ruby
|
425
|
-
scheduler.every '2d', :
|
450
|
+
scheduler.every '2d', first_at: Time.now + 10 * 3600 do
|
426
451
|
# ... every two days, but start in 10 hours
|
427
452
|
end
|
428
453
|
|
429
|
-
scheduler.every '2d', :
|
454
|
+
scheduler.every '2d', first_in: '10h' do
|
430
455
|
# ... every two days, but start in 10 hours
|
431
456
|
end
|
432
457
|
|
433
|
-
scheduler.cron '00 14 * * *', :
|
458
|
+
scheduler.cron '00 14 * * *', first_in: '3d' do
|
434
459
|
# ... every day at 14h00, but start after 3 * 24 hours
|
435
460
|
end
|
436
461
|
```
|
@@ -452,12 +477,11 @@ s = Rufus::Scheduler.new
|
|
452
477
|
|
453
478
|
n = Time.now; p [ :scheduled_at, n, n.to_f ]
|
454
479
|
|
455
|
-
s.every '3s', :
|
480
|
+
s.every '3s', first: :now do
|
456
481
|
n = Time.now; p [ :in, n, n.to_f ]
|
457
482
|
end
|
458
483
|
|
459
484
|
s.join
|
460
|
-
|
461
485
|
```
|
462
486
|
|
463
487
|
that'll output something like:
|
@@ -478,15 +502,15 @@ This option is for repeat jobs (cron / every) only.
|
|
478
502
|
It indicates the point in time after which the job should unschedule itself.
|
479
503
|
|
480
504
|
```ruby
|
481
|
-
scheduler.cron '5 23 * * *', :
|
505
|
+
scheduler.cron '5 23 * * *', last_in: '10d' do
|
482
506
|
# ... do something every evening at 23:05 for 10 days
|
483
507
|
end
|
484
508
|
|
485
|
-
scheduler.every '10m', :
|
509
|
+
scheduler.every '10m', last_at: Time.now + 10 * 3600 do
|
486
510
|
# ... do something every 10 minutes for 10 hours
|
487
511
|
end
|
488
512
|
|
489
|
-
scheduler.every '10m', :
|
513
|
+
scheduler.every '10m', last_in: 10 * 3600 do
|
490
514
|
# ... do something every 10 minutes for 10 hours
|
491
515
|
end
|
492
516
|
```
|
@@ -501,16 +525,16 @@ job.last_at = Rufus::Scheduler.parse('2029-12-12')
|
|
501
525
|
# set the last bound
|
502
526
|
```
|
503
527
|
|
504
|
-
### :
|
528
|
+
### times: nb of times (before auto-unscheduling)
|
505
529
|
|
506
530
|
One can tell how many times a repeat job (CronJob or EveryJob) is to execute before unscheduling by itself.
|
507
531
|
|
508
532
|
```ruby
|
509
|
-
scheduler.every '2d', :
|
533
|
+
scheduler.every '2d', times: 10 do
|
510
534
|
# ... do something every two days, but not more than 10 times
|
511
535
|
end
|
512
536
|
|
513
|
-
scheduler.cron '0 23 * * *', :
|
537
|
+
scheduler.cron '0 23 * * *', times: 31 do
|
514
538
|
# ... do something every day at 23:00 but do it no more than 31 times
|
515
539
|
end
|
516
540
|
```
|
@@ -518,7 +542,7 @@ end
|
|
518
542
|
It's OK to assign nil to :times to make sure the repeat job is not limited. It's useful when the :times is determined at scheduling time.
|
519
543
|
|
520
544
|
```ruby
|
521
|
-
scheduler.cron '0 23 * * *', :
|
545
|
+
scheduler.cron '0 23 * * *', times: (nolimit ? nil : 10) do
|
522
546
|
# ...
|
523
547
|
end
|
524
548
|
```
|
@@ -540,7 +564,7 @@ job.times = 10
|
|
540
564
|
|
541
565
|
## Job methods
|
542
566
|
|
543
|
-
When calling a schedule method, the id (String) of the job is returned. Longer schedule methods return Job instances directly. Calling the shorter schedule methods with the :
|
567
|
+
When calling a schedule method, the id (String) of the job is returned. Longer schedule methods return Job instances directly. Calling the shorter schedule methods with the `job: true` also returns Job instances instead of Job ids (Strings).
|
544
568
|
|
545
569
|
```ruby
|
546
570
|
require 'rufus-scheduler'
|
@@ -558,7 +582,7 @@ When calling a schedule method, the id (String) of the job is returned. Longer s
|
|
558
582
|
end
|
559
583
|
|
560
584
|
job =
|
561
|
-
scheduler.in '1w', :
|
585
|
+
scheduler.in '1w', job: true do
|
562
586
|
# ...
|
563
587
|
end
|
564
588
|
```
|
@@ -584,7 +608,7 @@ Returns the scheduler instance itself.
|
|
584
608
|
Returns the options passed at the Job creation.
|
585
609
|
|
586
610
|
```ruby
|
587
|
-
job = scheduler.schedule_in('10d', :
|
611
|
+
job = scheduler.schedule_in('10d', tag: 'hello') do; end
|
588
612
|
job.opts
|
589
613
|
# => { :tag => 'hello' }
|
590
614
|
```
|
@@ -594,7 +618,7 @@ job.opts
|
|
594
618
|
Returns the original schedule.
|
595
619
|
|
596
620
|
```ruby
|
597
|
-
job = scheduler.schedule_in('10d', :
|
621
|
+
job = scheduler.schedule_in('10d', tag: 'hello') do; end
|
598
622
|
job.original
|
599
623
|
# => '10d'
|
600
624
|
```
|
@@ -641,12 +665,30 @@ job.callable
|
|
641
665
|
# => #<MyHandler:0x0000000163ae88 @counter=0>
|
642
666
|
```
|
643
667
|
|
668
|
+
### source_location
|
669
|
+
|
670
|
+
Added to rufus-scheduler 3.8.0.
|
671
|
+
|
672
|
+
Returns the array `[ 'path/to/file.rb', 123 ]` like `Proc#source_location` does.
|
673
|
+
|
674
|
+
```ruby
|
675
|
+
require 'rufus-scheduler'
|
676
|
+
|
677
|
+
scheduler = Rufus::Scheduler.new
|
678
|
+
|
679
|
+
job = scheduler.schedule_every('2h') { p Time.now }
|
680
|
+
|
681
|
+
p job.source_location
|
682
|
+
# ==> [ '/home/jmettraux/rufus-scheduler/test.rb', 6 ]
|
683
|
+
|
684
|
+
```
|
685
|
+
|
644
686
|
### scheduled_at
|
645
687
|
|
646
688
|
Returns the Time instance when the job got created.
|
647
689
|
|
648
690
|
```ruby
|
649
|
-
job = scheduler.schedule_in('10d', :
|
691
|
+
job = scheduler.schedule_in('10d', tag: 'hello') do; end
|
650
692
|
job.scheduled_at
|
651
693
|
# => 2013-07-17 23:48:54 +0900
|
652
694
|
```
|
@@ -687,6 +729,12 @@ The job keeps track of how long its work was in the `last_work_time` attribute.
|
|
687
729
|
|
688
730
|
The attribute `mean_work_time` contains a computed mean work time. It's recomputed after every run (if it's a repeat job).
|
689
731
|
|
732
|
+
### next_times(n)
|
733
|
+
|
734
|
+
Returns an array of `EtOrbi::EoTime` instances (Time instances with a designated time zone), listing the `n` next occurrences for this job.
|
735
|
+
|
736
|
+
Please note that for "interval" jobs, a mean work time is computed each time and it's used by this `#next_times(n)` method to approximate the next times beyond the immediate next time.
|
737
|
+
|
690
738
|
### unschedule
|
691
739
|
|
692
740
|
Unschedule the job, preventing it from firing again and removing it from the schedule. This doesn't prevent a running thread for this job to run until its end.
|
@@ -743,14 +791,14 @@ job = scheduler.schedule_in('10d') do; end
|
|
743
791
|
job.tags
|
744
792
|
# => []
|
745
793
|
|
746
|
-
job = scheduler.schedule_in('10d', :
|
794
|
+
job = scheduler.schedule_in('10d', tag: 'hello') do; end
|
747
795
|
job.tags
|
748
796
|
# => [ 'hello' ]
|
749
797
|
```
|
750
798
|
|
751
|
-
### []=, [], key
|
799
|
+
### []=, [], key?, has_key?, keys, values, and entries
|
752
800
|
|
753
|
-
Threads have thread-local variables
|
801
|
+
Threads have thread-local variables, similarly Rufus-scheduler jobs have job-local variables. Those are more like a dict with thread-safe access.
|
754
802
|
|
755
803
|
```ruby
|
756
804
|
job =
|
@@ -765,13 +813,29 @@ sleep 3.6
|
|
765
813
|
job[:counter]
|
766
814
|
# => 3
|
767
815
|
|
768
|
-
job.key?(:timestamp)
|
769
|
-
|
770
|
-
job.keys
|
771
|
-
# => [ :timestamp, :counter ]
|
816
|
+
job.key?(:timestamp) # => true
|
817
|
+
job.has_key?(:timestamp) # => true
|
818
|
+
job.keys # => [ :timestamp, :counter ]
|
772
819
|
```
|
773
820
|
|
774
|
-
|
821
|
+
Locals can be set at schedule time:
|
822
|
+
```ruby
|
823
|
+
job0 =
|
824
|
+
@scheduler.schedule_cron '*/15 12 * * *', locals: { a: 0 } do
|
825
|
+
# ...
|
826
|
+
end
|
827
|
+
job1 =
|
828
|
+
@scheduler.schedule_cron '*/15 13 * * *', l: { a: 1 } do
|
829
|
+
# ...
|
830
|
+
end
|
831
|
+
```
|
832
|
+
|
833
|
+
One can fetch the Hash directly with `Job#locals`. Of course, direct manipulation is not thread-safe.
|
834
|
+
```ruby
|
835
|
+
job.locals.entries do |k, v|
|
836
|
+
p "#{k}: #{v}"
|
837
|
+
end
|
838
|
+
```
|
775
839
|
|
776
840
|
### call
|
777
841
|
|
@@ -912,24 +976,24 @@ Here is an example:
|
|
912
976
|
scheduler.at_jobs.each(&:unschedule)
|
913
977
|
```
|
914
978
|
|
915
|
-
### Scheduler#jobs(:
|
979
|
+
### Scheduler#jobs(tag: / tags: x)
|
916
980
|
|
917
981
|
When scheduling a job, one can specify one or more tags attached to the job. These can be used to look up the job later on.
|
918
982
|
|
919
983
|
```ruby
|
920
|
-
scheduler.in '10d', :
|
984
|
+
scheduler.in '10d', tag: 'main_process' do
|
921
985
|
# ...
|
922
986
|
end
|
923
|
-
scheduler.in '10d', :
|
987
|
+
scheduler.in '10d', tags: [ 'main_process', 'side_dish' ] do
|
924
988
|
# ...
|
925
989
|
end
|
926
990
|
|
927
991
|
# ...
|
928
992
|
|
929
|
-
jobs = scheduler.jobs(:
|
993
|
+
jobs = scheduler.jobs(tag: 'main_process')
|
930
994
|
# find all the jobs with the 'main_process' tag
|
931
995
|
|
932
|
-
jobs = scheduler.jobs(:
|
996
|
+
jobs = scheduler.jobs(tags: [ 'main_process', 'side_dish' ]
|
933
997
|
# find all the jobs with the 'main_process' AND 'side_dish' tags
|
934
998
|
```
|
935
999
|
|
@@ -954,6 +1018,10 @@ Shuts down the scheduler, ceases any scheduler/triggering activity.
|
|
954
1018
|
|
955
1019
|
Shuts down the scheduler, waits (blocks) until all the jobs cease running.
|
956
1020
|
|
1021
|
+
### Scheduler#shutdown(wait: n)
|
1022
|
+
|
1023
|
+
Shuts down the scheduler, waits (blocks) at most n seconds until all the jobs cease running. (Jobs are killed after n seconds have elapsed).
|
1024
|
+
|
957
1025
|
### Scheduler#shutdown(:kill)
|
958
1026
|
|
959
1027
|
Kills all the job (threads) and then shuts the scheduler down. Radical.
|
@@ -976,6 +1044,8 @@ Returns since the count of seconds for which the scheduler has been running.
|
|
976
1044
|
|
977
1045
|
Lets the current thread join the scheduling thread in rufus-scheduler. The thread comes back when the scheduler gets shut down.
|
978
1046
|
|
1047
|
+
`#join` is mostly used in standalone scheduling script (or tiny one file examples). Calling `#join` from a web application initializer will probably hijack the main thread and prevent the web application from being served. Do not put a `#join` in such a web application initializer file.
|
1048
|
+
|
979
1049
|
### Scheduler#threads
|
980
1050
|
|
981
1051
|
Returns all the threads associated with the scheduler, including the scheduler thread itself.
|
@@ -988,7 +1058,7 @@ Lists the work threads associated with the scheduler. The query option defaults
|
|
988
1058
|
* :active : all the work threads currently running a Job
|
989
1059
|
* :vacant : all the work threads currently not running a Job
|
990
1060
|
|
991
|
-
Note that the main schedule thread will be returned if it is currently running a Job (ie one of those :
|
1061
|
+
Note that the main schedule thread will be returned if it is currently running a Job (ie one of those `blocking: true` jobs).
|
992
1062
|
|
993
1063
|
### Scheduler#scheduled?(job_or_job_id)
|
994
1064
|
|
@@ -996,7 +1066,7 @@ Returns true if the arg is a currently scheduled job (see Job#scheduled?).
|
|
996
1066
|
|
997
1067
|
### Scheduler#occurrences(time0, time1)
|
998
1068
|
|
999
|
-
Returns a hash
|
1069
|
+
Returns a hash `{ job => [ t0, t1, ... ] }` mapping jobs to their potential trigger time within the `[ time0, time1 ]` span.
|
1000
1070
|
|
1001
1071
|
Please note that, for interval jobs, the ```#mean_work_time``` is used, so the result is only a prediction.
|
1002
1072
|
|
@@ -1088,7 +1158,9 @@ def scheduler.on_error(job, error)
|
|
1088
1158
|
end
|
1089
1159
|
```
|
1090
1160
|
|
1091
|
-
##
|
1161
|
+
## Callbacks
|
1162
|
+
|
1163
|
+
### Rufus::Scheduler #on_pre_trigger and #on_post_trigger callbacks
|
1092
1164
|
|
1093
1165
|
One can bind callbacks before and after jobs trigger:
|
1094
1166
|
|
@@ -1112,6 +1184,21 @@ The ```trigger_time``` is the time at which the job triggers. It might be a bit
|
|
1112
1184
|
|
1113
1185
|
Warning: these two callbacks are executed in the scheduler thread, not in the work threads (the threads where the job execution really happens).
|
1114
1186
|
|
1187
|
+
### Rufus::Scheduler#around_trigger
|
1188
|
+
|
1189
|
+
One can create an around callback which will wrap a job:
|
1190
|
+
|
1191
|
+
```ruby
|
1192
|
+
def s.around_trigger(job)
|
1193
|
+
t = Time.now
|
1194
|
+
puts "Starting job #{job.id}..."
|
1195
|
+
yield
|
1196
|
+
puts "job #{job.id} finished in #{Time.now-t} seconds."
|
1197
|
+
end
|
1198
|
+
```
|
1199
|
+
|
1200
|
+
The around callback is executed in the thread.
|
1201
|
+
|
1115
1202
|
### Rufus::Scheduler#on_pre_trigger as a guard
|
1116
1203
|
|
1117
1204
|
Returning ```false``` in on_pre_trigger will prevent the job from triggering. Returning anything else (nil, -1, true, ...) will let the job trigger.
|
@@ -1136,23 +1223,23 @@ By default, rufus-scheduler sleeps 0.300 second between every step. At each step
|
|
1136
1223
|
The :frequency option lets you change that 0.300 second to something else.
|
1137
1224
|
|
1138
1225
|
```ruby
|
1139
|
-
scheduler = Rufus::Scheduler.new(:
|
1226
|
+
scheduler = Rufus::Scheduler.new(frequency: 5)
|
1140
1227
|
```
|
1141
1228
|
|
1142
1229
|
It's OK to use a time string to specify the frequency.
|
1143
1230
|
|
1144
1231
|
```ruby
|
1145
|
-
scheduler = Rufus::Scheduler.new(:
|
1232
|
+
scheduler = Rufus::Scheduler.new(frequency: '2h10m')
|
1146
1233
|
# this scheduler will sleep 2 hours and 10 minutes between every "step"
|
1147
1234
|
```
|
1148
1235
|
|
1149
1236
|
Use with care.
|
1150
1237
|
|
1151
|
-
### :
|
1238
|
+
### lockfile: "mylockfile.txt"
|
1152
1239
|
|
1153
1240
|
This feature only works on OSes that support the flock (man 2 flock) call.
|
1154
1241
|
|
1155
|
-
Starting the scheduler with
|
1242
|
+
Starting the scheduler with ```lockfile: '.rufus-scheduler.lock'``` will make the scheduler attempt to create and lock the file ```.rufus-scheduler.lock``` in the current working directory. If that fails, the scheduler will not start.
|
1156
1243
|
|
1157
1244
|
The idea is to guarantee only one scheduler (in a group of schedulers sharing the same lockfile) is running.
|
1158
1245
|
|
@@ -1181,7 +1268,7 @@ class HostLock
|
|
1181
1268
|
end
|
1182
1269
|
|
1183
1270
|
scheduler =
|
1184
|
-
Rufus::Scheduler.new(:
|
1271
|
+
Rufus::Scheduler.new(scheduler_lock: HostLock.new('coffee.example.com'))
|
1185
1272
|
```
|
1186
1273
|
|
1187
1274
|
By default, the scheduler_lock is an instance of `Rufus::Scheduler::NullLock`, with a `#lock` that returns true.
|
@@ -1204,7 +1291,7 @@ class PingLock
|
|
1204
1291
|
end
|
1205
1292
|
|
1206
1293
|
scheduler =
|
1207
|
-
Rufus::Scheduler.new(:
|
1294
|
+
Rufus::Scheduler.new(trigger_lock: PingLock.new('main.example.com'))
|
1208
1295
|
```
|
1209
1296
|
|
1210
1297
|
By default, the trigger_lock is an instance of `Rufus::Scheduler::NullLock`, with a `#lock` that always returns true.
|
@@ -1218,7 +1305,7 @@ In rufus-scheduler 2.x, by default, each job triggering received its own, brand
|
|
1218
1305
|
One can set this maximum value when starting the scheduler.
|
1219
1306
|
|
1220
1307
|
```ruby
|
1221
|
-
scheduler = Rufus::Scheduler.new(:
|
1308
|
+
scheduler = Rufus::Scheduler.new(max_work_threads: 77)
|
1222
1309
|
```
|
1223
1310
|
|
1224
1311
|
It's OK to increase the :max_work_threads of a running scheduler.
|
@@ -1231,7 +1318,7 @@ scheduler.max_work_threads += 10
|
|
1231
1318
|
## Rufus::Scheduler.singleton
|
1232
1319
|
|
1233
1320
|
Do not want to store a reference to your rufus-scheduler instance?
|
1234
|
-
Then ```Rufus::Scheduler.singleton``` can help, it returns a
|
1321
|
+
Then ```Rufus::Scheduler.singleton``` can help, it returns a singleton instance of the scheduler, initialized the first time this class method is called.
|
1235
1322
|
|
1236
1323
|
```ruby
|
1237
1324
|
Rufus::Scheduler.singleton.every '10s' { puts "hello, world!" }
|
@@ -1240,8 +1327,8 @@ Rufus::Scheduler.singleton.every '10s' { puts "hello, world!" }
|
|
1240
1327
|
It's OK to pass initialization arguments (like :frequency or :max_work_threads) but they will only be taken into account the first time ```.singleton``` is called.
|
1241
1328
|
|
1242
1329
|
```ruby
|
1243
|
-
Rufus::Scheduler.singleton(:
|
1244
|
-
Rufus::Scheduler.singleton(:
|
1330
|
+
Rufus::Scheduler.singleton(max_work_threads: 77)
|
1331
|
+
Rufus::Scheduler.singleton(max_work_threads: 277) # no effect
|
1245
1332
|
```
|
1246
1333
|
|
1247
1334
|
The ```.s``` is a shortcut for ```.singleton```.
|
@@ -1290,7 +1377,7 @@ class ZookeptScheduler < Rufus::Scheduler
|
|
1290
1377
|
end
|
1291
1378
|
```
|
1292
1379
|
|
1293
|
-
This uses a [zookeeper](
|
1380
|
+
This uses a [zookeeper](https://zookeeper.apache.org/) to make sure only one scheduler in a group of distributed schedulers runs.
|
1294
1381
|
|
1295
1382
|
The methods #lock and #unlock are overridden and #confirm_lock is provided,
|
1296
1383
|
to make sure that the lock is still valid.
|
@@ -1314,6 +1401,8 @@ Warning: you may think you're heading towards "high availability" by using a tri
|
|
1314
1401
|
|
1315
1402
|
## parsing cronlines and time strings
|
1316
1403
|
|
1404
|
+
(Please note that [fugit](https://github.com/floraison/fugit) does the heavy-lifting parsing work for rufus-scheduler).
|
1405
|
+
|
1317
1406
|
Rufus::Scheduler provides a class method ```.parse``` to parse time durations and cron strings. It's what it's using when receiving schedules. One can use it directly (no need to instantiate a Scheduler).
|
1318
1407
|
|
1319
1408
|
```ruby
|
@@ -1356,7 +1445,7 @@ Rufus::Scheduler.to_duration_hash(60)
|
|
1356
1445
|
Rufus::Scheduler.to_duration_hash(62.127)
|
1357
1446
|
# => { :m => 1, :s => 2, :ms => 127 }
|
1358
1447
|
|
1359
|
-
Rufus::Scheduler.to_duration_hash(62.127, :
|
1448
|
+
Rufus::Scheduler.to_duration_hash(62.127, drop_seconds: true)
|
1360
1449
|
# => { :m => 1 }
|
1361
1450
|
```
|
1362
1451
|
|
@@ -1539,7 +1628,7 @@ class ScheController < ApplicationController
|
|
1539
1628
|
Rails.logger.info "time flies, it's now #{Time.now}"
|
1540
1629
|
end
|
1541
1630
|
|
1542
|
-
render :
|
1631
|
+
render text: "scheduled job #{job_id}"
|
1543
1632
|
end
|
1544
1633
|
end
|
1545
1634
|
```
|
@@ -1550,9 +1639,9 @@ The rufus-scheduler singleton is instantiated in the ```config/initializers/sche
|
|
1550
1639
|
|
1551
1640
|
### avoid scheduling when running the Ruby on Rails console
|
1552
1641
|
|
1553
|
-
(Written in reply to https://github.com/jmettraux/rufus-scheduler/issues/186
|
1642
|
+
(Written in reply to [gh-186](https://github.com/jmettraux/rufus-scheduler/issues/186))
|
1554
1643
|
|
1555
|
-
If you don't want rufus-scheduler to
|
1644
|
+
If you don't want rufus-scheduler to trigger anything while running the Ruby on Rails console, running for tests/specs, or running from a Rake task, you can insert a conditional return statement before jobs are added to the scheduler instance:
|
1556
1645
|
|
1557
1646
|
```ruby
|
1558
1647
|
#
|
@@ -1560,23 +1649,25 @@ If you don't want rufus-scheduler to kick in when running the Ruby on Rails cons
|
|
1560
1649
|
|
1561
1650
|
require 'rufus-scheduler'
|
1562
1651
|
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1652
|
+
return if defined?(Rails::Console) || Rails.env.test? || File.split($PROGRAM_NAME).last == 'rake'
|
1653
|
+
#
|
1654
|
+
# do not schedule when Rails is run from its console, for a test/spec, or
|
1655
|
+
# from a Rake task
|
1567
1656
|
|
1568
|
-
|
1569
|
-
#
|
1657
|
+
# return if $PROGRAM_NAME.include?('spring')
|
1658
|
+
#
|
1659
|
+
# see https://github.com/jmettraux/rufus-scheduler/issues/186
|
1570
1660
|
|
1571
|
-
|
1661
|
+
s = Rufus::Scheduler.singleton
|
1572
1662
|
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1663
|
+
s.every '1m' do
|
1664
|
+
Rails.logger.info "hello, it's #{Time.now}"
|
1665
|
+
Rails.logger.flush
|
1576
1666
|
end
|
1577
1667
|
```
|
1578
1668
|
|
1579
|
-
|
1669
|
+
(Beware later version of Rails where Spring takes care pre-running the initializers. Running `spring stop` or disabling Spring might be necessary in some cases to see changes to initializers being taken into account.)
|
1670
|
+
|
1580
1671
|
|
1581
1672
|
### rails server -d
|
1582
1673
|
|
@@ -1588,11 +1679,14 @@ I avoid running `-d` in development mode and bother about daemonizing only for p
|
|
1588
1679
|
|
1589
1680
|
These are two well crafted articles on process daemonization, please read them:
|
1590
1681
|
|
1591
|
-
*
|
1592
|
-
*
|
1682
|
+
* https://www.mikeperham.com/2014/09/22/dont-daemonize-your-daemons/
|
1683
|
+
* https://www.mikeperham.com/2014/07/07/use-runit/
|
1593
1684
|
|
1594
1685
|
If, anyway, you need something like `rails server -d`, why not try `bundle exec unicorn -D` instead? In my (limited) experience, it worked out of the box (well, had to add `gem 'unicorn'` to `Gemfile` first).
|
1595
1686
|
|
1687
|
+
### executor / reloader
|
1688
|
+
|
1689
|
+
You might benefit from wraping your scheduled code in the executor or reloader. Read more here: https://guides.rubyonrails.org/threading_and_code_execution.html
|
1596
1690
|
|
1597
1691
|
## support
|
1598
1692
|
|