rufus-scheduler 3.4.2 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +393 -0
- data/CREDITS.md +137 -0
- data/LICENSE.txt +1 -1
- data/Makefile +5 -1
- data/README.md +175 -96
- data/lib/rufus/scheduler.rb +528 -440
- data/lib/rufus/scheduler/job_array.rb +37 -47
- data/lib/rufus/scheduler/jobs_core.rb +363 -0
- data/lib/rufus/scheduler/jobs_one_time.rb +53 -0
- data/lib/rufus/scheduler/jobs_repeat.rb +333 -0
- data/lib/rufus/scheduler/locks.rb +41 -44
- data/lib/rufus/scheduler/util.rb +75 -124
- data/rufus-scheduler.gemspec +17 -6
- metadata +39 -36
- data/CHANGELOG.txt +0 -353
- data/CREDITS.txt +0 -124
- data/TODO.txt +0 -151
- data/fail.txt +0 -2
- data/fail18.txt +0 -12
- data/lib/rufus/scheduler/cronline.rb +0 -498
- data/lib/rufus/scheduler/jobs.rb +0 -650
- data/log.txt +0 -285
- data/n.txt +0 -38
- data/pics.txt +0 -15
- data/sofia.md +0 -89
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2005-
|
2
|
+
Copyright (c) 2005-2020, John Mettraux, jmettraux@gmail.com
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/Makefile
CHANGED
@@ -4,6 +4,10 @@ NAME = \
|
|
4
4
|
VERSION = \
|
5
5
|
$(shell ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.version")
|
6
6
|
|
7
|
+
count_lines:
|
8
|
+
find lib -name "*.rb" | xargs cat | ruby -e "p STDIN.readlines.count { |l| l = l.strip; l[0, 1] != '#' && l != '' }"
|
9
|
+
find spec -name "*_spec.rb" | xargs cat | ruby -e "p STDIN.readlines.count { |l| l = l.strip; l[0, 1] != '#' && l != '' }"
|
10
|
+
cl: count_lines
|
7
11
|
|
8
12
|
gemspec_validate:
|
9
13
|
@echo "---"
|
@@ -19,5 +23,5 @@ build: gemspec_validate
|
|
19
23
|
mv $(NAME)-$(VERSION).gem pkg/
|
20
24
|
|
21
25
|
push: build
|
22
|
-
gem push pkg/$(NAME)-$(VERSION).gem
|
26
|
+
gem push --otp "$(OTP)" pkg/$(NAME)-$(VERSION).gem
|
23
27
|
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
|
2
2
|
# rufus-scheduler
|
3
3
|
|
4
|
-
[![Build Status](https://secure.travis-ci.org/jmettraux/rufus-scheduler.svg)](
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/rufus-scheduler.svg)](
|
4
|
+
[![Build Status](https://secure.travis-ci.org/jmettraux/rufus-scheduler.svg)](https://travis-ci.org/jmettraux/rufus-scheduler)
|
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
|
|
@@ -46,6 +51,9 @@ end
|
|
46
51
|
scheduler.every '3h' do
|
47
52
|
# do something every 3 hours
|
48
53
|
end
|
54
|
+
scheduler.every '3h10m' do
|
55
|
+
# do something every 3 hours and 10 minutes
|
56
|
+
end
|
49
57
|
|
50
58
|
scheduler.cron '5 0 * * *' do
|
51
59
|
# do something every day, five minutes after midnight
|
@@ -55,21 +63,24 @@ end
|
|
55
63
|
# ...
|
56
64
|
```
|
57
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
|
+
|
58
68
|
## non-features
|
59
69
|
|
60
70
|
Rufus-scheduler (out of the box) is an in-process, in-memory scheduler. It uses threads.
|
61
71
|
|
62
72
|
It does not persist your schedules. When the process is gone and the scheduler instance with it, the schedules are gone.
|
63
73
|
|
64
|
-
A rufus-scheduler instance will go on scheduling while it is present among the
|
74
|
+
A rufus-scheduler instance will go on scheduling while it is present among the objects in a Ruby process. To make it stop scheduling you have to call its [`#shutdown` method](#schedulershutdown).
|
65
75
|
|
66
76
|
|
67
77
|
## related and similar gems
|
68
78
|
|
69
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
|
70
81
|
* [Clockwork](https://github.com/Rykian/clockwork) - rufus-scheduler inspired gem
|
71
82
|
* [Crono](https://github.com/plashchynski/crono) - an in-Rails cron scheduler
|
72
|
-
* [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
|
73
84
|
|
74
85
|
(please note: rufus-scheduler is not a cron replacement)
|
75
86
|
|
@@ -86,16 +97,16 @@ There is no EventMachine-based scheduler anymore.
|
|
86
97
|
I'll drive you right to the [tracks](#so-rails).
|
87
98
|
|
88
99
|
|
89
|
-
##
|
100
|
+
## notable changes:
|
90
101
|
|
91
102
|
* As said, no more EventMachine-based scheduler
|
92
|
-
* ```scheduler.every('100') {``` will schedule every 100 seconds (previously, it would have been 0.1s). This aligns rufus-scheduler
|
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)```
|
93
104
|
* The scheduler isn't catching the whole of Exception anymore, only StandardError
|
94
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)
|
95
106
|
* Rufus::Scheduler::TimeOutError renamed to Rufus::Scheduler::TimeoutError
|
96
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"
|
97
|
-
* Introduction of a :
|
98
|
-
* "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.
|
99
110
|
* Introduction of Scheduler #on_pre_trigger and #on_post_trigger callback points
|
100
111
|
|
101
112
|
|
@@ -105,29 +116,30 @@ So you need help. People can help you, but first help them help you, and don't w
|
|
105
116
|
|
106
117
|
"hello", "please" and "thanks" are not swear words.
|
107
118
|
|
108
|
-
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.
|
109
120
|
|
110
121
|
Update: [help_help.md](https://gist.github.com/jmettraux/310fed75f568fd731814) might help help you.
|
111
122
|
|
112
|
-
### on
|
123
|
+
### on Gitter
|
113
124
|
|
114
|
-
|
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.
|
115
126
|
|
116
|
-
Please
|
127
|
+
Please be courteous.
|
117
128
|
|
118
129
|
### issues
|
119
130
|
|
120
|
-
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.
|
121
132
|
|
122
133
|
### faq
|
123
134
|
|
124
|
-
* [It doesn't work...](
|
125
|
-
* [I want a refund](
|
126
|
-
* [Passenger and rufus-scheduler](
|
127
|
-
* [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)
|
128
139
|
* [Passenger in-depth spawn methods](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/)
|
129
140
|
* [Passenger in-depth spawn methods (smart spawning)](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#smart-spawning-hooks)
|
130
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)
|
142
|
+
* [The job triggers twice](https://github.com/jmettraux/rufus-scheduler#lockfile--mylockfiletxt)
|
131
143
|
* [I don't get any of this, I just want it to work in my Rails application](#so-rails)
|
132
144
|
* [I get "zotime.rb:41:in `initialize': cannot determine timezone from nil"](#i-get-zotimerb41in-initialize-cannot-determine-timezone-from-nil)
|
133
145
|
|
@@ -182,7 +194,7 @@ end
|
|
182
194
|
|
183
195
|
Every jobs try hard to trigger following the frequency they were scheduled with.
|
184
196
|
|
185
|
-
Interval jobs
|
197
|
+
Interval jobs trigger, execute and then trigger again after the interval elapsed. (every jobs time between trigger times, interval jobs time between trigger termination and the next trigger start).
|
186
198
|
|
187
199
|
Cron jobs are based on the venerable cron utility (```man 5 crontab```). They trigger following a pattern given in (almost) the same language cron uses.
|
188
200
|
|
@@ -220,7 +232,7 @@ job =
|
|
220
232
|
|
221
233
|
Sometimes it pays to be less verbose.
|
222
234
|
|
223
|
-
The ```#schedule``` methods schedules an at, in or cron job. It just
|
235
|
+
The ```#schedule``` methods schedules an at, in or cron job. It just decides based on its input. It returns the Job instance.
|
224
236
|
|
225
237
|
```ruby
|
226
238
|
scheduler.schedule '10d' do; end.class
|
@@ -243,13 +255,13 @@ scheduler.repeat '* * * * *' do; end.class
|
|
243
255
|
# => Rufus::Scheduler::CronJob
|
244
256
|
```
|
245
257
|
|
246
|
-
(Yes, no combination
|
258
|
+
(Yes, no combination here gives back an IntervalJob).
|
247
259
|
|
248
260
|
### schedule blocks arguments (job, time)
|
249
261
|
|
250
262
|
A schedule block may be given 0, 1 or 2 arguments.
|
251
263
|
|
252
|
-
The first argument is "job", it's
|
264
|
+
The first argument is "job", it's simply the Job instance involved. It might be useful if the job is to be unscheduled for some reason.
|
253
265
|
|
254
266
|
```ruby
|
255
267
|
scheduler.every '10m' do |job|
|
@@ -289,7 +301,7 @@ It should work as well with cron jobs, not so with interval jobs whose next_time
|
|
289
301
|
|
290
302
|
### scheduling handler instances
|
291
303
|
|
292
|
-
It's OK to pass any object, as long as it
|
304
|
+
It's OK to pass any object, as long as it responds to #call(), when scheduling:
|
293
305
|
|
294
306
|
```ruby
|
295
307
|
class Handler
|
@@ -366,29 +378,46 @@ While paused, the scheduler still accepts schedules, but no schedule will get tr
|
|
366
378
|
|
367
379
|
## job options
|
368
380
|
|
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
|
+
|
369
398
|
### :blocking => true
|
370
399
|
|
371
|
-
By default, jobs are triggered in their own, new
|
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.
|
372
401
|
|
373
402
|
### :overlap => false
|
374
403
|
|
375
|
-
Since, by default, jobs are triggered in their own new
|
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.
|
376
405
|
|
377
|
-
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.
|
378
407
|
|
379
408
|
The `:overlap` option is considered before the `:mutex` option when the scheduler is reviewing jobs for triggering.
|
380
409
|
|
381
410
|
### :mutex => mutex_instance / mutex_name / array of mutexes
|
382
411
|
|
383
|
-
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
|
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).
|
384
413
|
|
385
|
-
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.
|
386
415
|
|
387
|
-
|
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.
|
388
417
|
|
389
418
|
Array of mutexes: original idea and implementation by [Rainux Luo](https://github.com/rainux)
|
390
419
|
|
391
|
-
|
420
|
+
Note: creating lots of different mutexes is OK. Rufus-scheduler will place them in its Scheduler#mutexes hash... And they won't get garbage collected.
|
392
421
|
|
393
422
|
The `:overlap` option is considered before the `:mutex` option when the scheduler is reviewing jobs for triggering.
|
394
423
|
|
@@ -415,7 +444,7 @@ This option is for repeat jobs (cron / every) only.
|
|
415
444
|
It's used to specify the first time after which the repeat job should trigger for the first time.
|
416
445
|
|
417
446
|
In the case of an "every" job, this will be the first time (modulo the scheduler frequency) the job triggers.
|
418
|
-
For a "cron" job,
|
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.
|
419
448
|
|
420
449
|
```ruby
|
421
450
|
scheduler.every '2d', :first_at => Time.now + 10 * 3600 do
|
@@ -431,7 +460,7 @@ scheduler.cron '00 14 * * *', :first_in => '3d' do
|
|
431
460
|
end
|
432
461
|
```
|
433
462
|
|
434
|
-
:first, :first_at and :first_in all accept a point in time or a duration (number or time string). Use the symbol you think
|
463
|
+
:first, :first_at and :first_in all accept a point in time or a duration (number or time string). Use the symbol you think makes your schedule more readable.
|
435
464
|
|
436
465
|
Note: it's OK to change the first_at (a Time instance) directly:
|
437
466
|
```ruby
|
@@ -453,7 +482,6 @@ s.every '3s', :first => :now do
|
|
453
482
|
end
|
454
483
|
|
455
484
|
s.join
|
456
|
-
|
457
485
|
```
|
458
486
|
|
459
487
|
that'll output something like:
|
@@ -486,7 +514,7 @@ scheduler.every '10m', :last_in => 10 * 3600 do
|
|
486
514
|
# ... do something every 10 minutes for 10 hours
|
487
515
|
end
|
488
516
|
```
|
489
|
-
:last, :last_at and :last_in all accept a point in time or a duration (number or time string). Use the symbol you think
|
517
|
+
:last, :last_at and :last_in all accept a point in time or a duration (number or time string). Use the symbol you think makes your schedule more readable.
|
490
518
|
|
491
519
|
Note: it's OK to change the last_at (nil or a Time instance) directly:
|
492
520
|
```ruby
|
@@ -536,7 +564,7 @@ job.times = 10
|
|
536
564
|
|
537
565
|
## Job methods
|
538
566
|
|
539
|
-
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
|
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).
|
540
568
|
|
541
569
|
```ruby
|
542
570
|
require 'rufus-scheduler'
|
@@ -599,7 +627,7 @@ job.original
|
|
599
627
|
|
600
628
|
callable() returns the scheduled block (or the call method of the callable object passed in lieu of a block)
|
601
629
|
|
602
|
-
handler() returns nil if a block was scheduled and the instance scheduled
|
630
|
+
handler() returns nil if a block was scheduled and the instance scheduled otherwise.
|
603
631
|
|
604
632
|
```ruby
|
605
633
|
# when passing a block
|
@@ -683,6 +711,12 @@ The job keeps track of how long its work was in the `last_work_time` attribute.
|
|
683
711
|
|
684
712
|
The attribute `mean_work_time` contains a computed mean work time. It's recomputed after every run (if it's a repeat job).
|
685
713
|
|
714
|
+
### next_times(n)
|
715
|
+
|
716
|
+
Returns an array of `EtOrbi::EoTime` instances (Time instances with a designated time zone), listing the `n` next occurrences for this job.
|
717
|
+
|
718
|
+
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.
|
719
|
+
|
686
720
|
### unschedule
|
687
721
|
|
688
722
|
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.
|
@@ -709,7 +743,7 @@ Returns true if the job is scheduled (is due to trigger). For repeat jobs it sho
|
|
709
743
|
|
710
744
|
### pause, resume, paused?, paused_at
|
711
745
|
|
712
|
-
These four methods are only available to CronJob, EveryJob and IntervalJob instances. One can pause or resume such
|
746
|
+
These four methods are only available to CronJob, EveryJob and IntervalJob instances. One can pause or resume such jobs thanks to these methods.
|
713
747
|
|
714
748
|
```ruby
|
715
749
|
job =
|
@@ -744,9 +778,9 @@ job.tags
|
|
744
778
|
# => [ 'hello' ]
|
745
779
|
```
|
746
780
|
|
747
|
-
### []=, [], key
|
781
|
+
### []=, [], key?, has_key?, keys, values, and entries
|
748
782
|
|
749
|
-
Threads have thread-local variables
|
783
|
+
Threads have thread-local variables, similarly Rufus-scheduler jobs have job-local variables. Those are more like a dict with thread-safe access.
|
750
784
|
|
751
785
|
```ruby
|
752
786
|
job =
|
@@ -761,13 +795,29 @@ sleep 3.6
|
|
761
795
|
job[:counter]
|
762
796
|
# => 3
|
763
797
|
|
764
|
-
job.key?(:timestamp)
|
765
|
-
|
766
|
-
job.keys
|
767
|
-
|
798
|
+
job.key?(:timestamp) # => true
|
799
|
+
job.has_key?(:timestamp) # => true
|
800
|
+
job.keys # => [ :timestamp, :counter ]
|
801
|
+
```
|
802
|
+
|
803
|
+
Locals can be set at schedule time:
|
804
|
+
```ruby
|
805
|
+
job0 =
|
806
|
+
@scheduler.schedule_cron '*/15 12 * * *', locals: { a: 0 } do
|
807
|
+
# ...
|
808
|
+
end
|
809
|
+
job1 =
|
810
|
+
@scheduler.schedule_cron '*/15 13 * * *', l: { a: 1 } do
|
811
|
+
# ...
|
812
|
+
end
|
768
813
|
```
|
769
814
|
|
770
|
-
Job
|
815
|
+
One can fetch the Hash directly with `Job#locals`. Of course, direct manipulation is not thread-safe.
|
816
|
+
```ruby
|
817
|
+
job.locals.entries do |k, v|
|
818
|
+
p "#{k}: #{v}"
|
819
|
+
end
|
820
|
+
```
|
771
821
|
|
772
822
|
### call
|
773
823
|
|
@@ -815,7 +865,7 @@ Returns when the job will trigger (hopefully).
|
|
815
865
|
|
816
866
|
### next_time
|
817
867
|
|
818
|
-
An alias
|
868
|
+
An alias for time.
|
819
869
|
|
820
870
|
## EveryJob, IntervalJob and CronJob methods
|
821
871
|
|
@@ -845,34 +895,40 @@ Every jobs use a time duration between each start of their execution, while inte
|
|
845
895
|
|
846
896
|
## CronJob methods
|
847
897
|
|
848
|
-
###
|
849
|
-
|
850
|
-
It returns the shortest interval of time between two potential occurrences of the job.
|
851
|
-
|
852
|
-
For instance:
|
853
|
-
```ruby
|
854
|
-
Rufus::Scheduler.parse('* * * * *').frequency # ==> 60
|
855
|
-
Rufus::Scheduler.parse('* * * * * *').frequency # ==> 1
|
898
|
+
### brute_frequency
|
856
899
|
|
857
|
-
|
858
|
-
Rufus::Scheduler.parse('5 * * * *').frequency # ==> 3600
|
859
|
-
Rufus::Scheduler.parse('10,20,30 * * * *').frequency # ==> 600
|
900
|
+
An expensive method to run, it's brute. It caches its results. By default it runs for 2017 (a non leap-year).
|
860
901
|
|
861
|
-
Rufus::Scheduler.parse('10,20,30 * * * * *').frequency # ==> 10
|
862
902
|
```
|
903
|
+
require 'rufus-scheduler'
|
863
904
|
|
864
|
-
|
905
|
+
Rufus::Scheduler.parse('* * * * *').brute_frequency
|
906
|
+
#
|
907
|
+
# => #<Fugit::Cron::Frequency:0x00007fdf4520c5e8
|
908
|
+
# @span=31536000.0, @delta_min=60, @delta_max=60,
|
909
|
+
# @occurrences=525600, @span_years=1.0, @yearly_occurrences=525600.0>
|
910
|
+
#
|
911
|
+
# Occurs 525600 times in a span of 1 year (2017) and 1 day.
|
912
|
+
# There are least 60 seconds between "triggers" and at most 60 seconds.
|
865
913
|
|
866
|
-
|
914
|
+
Rufus::Scheduler.parse('0 12 * * *').brute_frequency
|
915
|
+
# => #<Fugit::Cron::Frequency:0x00007fdf451ec6d0
|
916
|
+
# @span=31536000.0, @delta_min=86400, @delta_max=86400,
|
917
|
+
# @occurrences=365, @span_years=1.0, @yearly_occurrences=365.0>
|
918
|
+
Rufus::Scheduler.parse('0 12 * * *').brute_frequency.to_debug_s
|
919
|
+
# => "dmin: 1D, dmax: 1D, ocs: 365, spn: 52W1D, spnys: 1, yocs: 365"
|
920
|
+
#
|
921
|
+
# 365 occurrences, at most 1 day between each, at least 1 day.
|
922
|
+
```
|
867
923
|
|
868
|
-
|
924
|
+
The `CronJob#frequency` method found in rufus-scheduler < 3.5 has been retired.
|
869
925
|
|
870
926
|
|
871
927
|
## looking up jobs
|
872
928
|
|
873
929
|
### Scheduler#job(job_id)
|
874
930
|
|
875
|
-
The scheduler ```#job(job_id)``` method can be used to
|
931
|
+
The scheduler ```#job(job_id)``` method can be used to look up Job instances.
|
876
932
|
|
877
933
|
```ruby
|
878
934
|
require 'rufus-scheduler'
|
@@ -904,7 +960,7 @@ Here is an example:
|
|
904
960
|
|
905
961
|
### Scheduler#jobs(:tag / :tags => x)
|
906
962
|
|
907
|
-
When scheduling a job, one can specify one or more tags attached to the job. These can be used to
|
963
|
+
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.
|
908
964
|
|
909
965
|
```ruby
|
910
966
|
scheduler.in '10d', :tag => 'main_process' do
|
@@ -944,6 +1000,10 @@ Shuts down the scheduler, ceases any scheduler/triggering activity.
|
|
944
1000
|
|
945
1001
|
Shuts down the scheduler, waits (blocks) until all the jobs cease running.
|
946
1002
|
|
1003
|
+
### Scheduler#shutdown(wait: n)
|
1004
|
+
|
1005
|
+
Shuts down the scheduler, waits (blocks) at most n seconds until all the jobs cease running. (Jobs are killed after n seconds have elapsed).
|
1006
|
+
|
947
1007
|
### Scheduler#shutdown(:kill)
|
948
1008
|
|
949
1009
|
Kills all the job (threads) and then shuts the scheduler down. Radical.
|
@@ -964,7 +1024,9 @@ Returns since the count of seconds for which the scheduler has been running.
|
|
964
1024
|
|
965
1025
|
### Scheduler#join
|
966
1026
|
|
967
|
-
|
1027
|
+
Lets the current thread join the scheduling thread in rufus-scheduler. The thread comes back when the scheduler gets shut down.
|
1028
|
+
|
1029
|
+
`#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.
|
968
1030
|
|
969
1031
|
### Scheduler#threads
|
970
1032
|
|
@@ -1039,9 +1101,9 @@ TODO: talk about callable#on_error (if implemented)
|
|
1039
1101
|
|
1040
1102
|
### Rufus::Scheduler#stderr=
|
1041
1103
|
|
1042
|
-
By default, rufus-scheduler intercepts all errors (that inherit from StandardError) and dumps
|
1104
|
+
By default, rufus-scheduler intercepts all errors (that inherit from StandardError) and dumps abundant details to $stderr.
|
1043
1105
|
|
1044
|
-
If, for example, you'd like to divert that flow to another file (descriptor)
|
1106
|
+
If, for example, you'd like to divert that flow to another file (descriptor), you can reassign $stderr for the current Ruby process
|
1045
1107
|
|
1046
1108
|
```ruby
|
1047
1109
|
$stderr = File.open('/var/log/myapplication.log', 'ab')
|
@@ -1078,7 +1140,9 @@ def scheduler.on_error(job, error)
|
|
1078
1140
|
end
|
1079
1141
|
```
|
1080
1142
|
|
1081
|
-
##
|
1143
|
+
## Callbacks
|
1144
|
+
|
1145
|
+
### Rufus::Scheduler #on_pre_trigger and #on_post_trigger callbacks
|
1082
1146
|
|
1083
1147
|
One can bind callbacks before and after jobs trigger:
|
1084
1148
|
|
@@ -1100,13 +1164,28 @@ end
|
|
1100
1164
|
|
1101
1165
|
The ```trigger_time``` is the time at which the job triggers. It might be a bit before ```Time.now```.
|
1102
1166
|
|
1103
|
-
Warning: these two callbacks are executed in the scheduler thread, not in the work threads (the threads
|
1167
|
+
Warning: these two callbacks are executed in the scheduler thread, not in the work threads (the threads where the job execution really happens).
|
1168
|
+
|
1169
|
+
### Rufus::Scheduler#around_trigger
|
1170
|
+
|
1171
|
+
One can create an around callback which will wrap a job:
|
1172
|
+
|
1173
|
+
```ruby
|
1174
|
+
def s.around_trigger(job)
|
1175
|
+
t = Time.now
|
1176
|
+
puts "Starting job #{job.id}..."
|
1177
|
+
yield
|
1178
|
+
puts "job #{job.id} finished in #{Time.now-t} seconds."
|
1179
|
+
end
|
1180
|
+
```
|
1181
|
+
|
1182
|
+
The around callback is executed in the thread.
|
1104
1183
|
|
1105
1184
|
### Rufus::Scheduler#on_pre_trigger as a guard
|
1106
1185
|
|
1107
1186
|
Returning ```false``` in on_pre_trigger will prevent the job from triggering. Returning anything else (nil, -1, true, ...) will let the job trigger.
|
1108
1187
|
|
1109
|
-
Note: your business logic should go in the scheduled block itself (or the scheduled instance). Don't put business logic in on_pre_trigger. Return false for admin reasons (backend down, etc) not for business reasons that are tied to the job itself.
|
1188
|
+
Note: your business logic should go in the scheduled block itself (or the scheduled instance). Don't put business logic in on_pre_trigger. Return false for admin reasons (backend down, etc), not for business reasons that are tied to the job itself.
|
1110
1189
|
|
1111
1190
|
```ruby
|
1112
1191
|
def s.on_pre_trigger(job, trigger_time)
|
@@ -1144,7 +1223,7 @@ This feature only works on OSes that support the flock (man 2 flock) call.
|
|
1144
1223
|
|
1145
1224
|
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.
|
1146
1225
|
|
1147
|
-
The idea is to guarantee only one scheduler (in a group of
|
1226
|
+
The idea is to guarantee only one scheduler (in a group of schedulers sharing the same lockfile) is running.
|
1148
1227
|
|
1149
1228
|
This is useful in environments where the Ruby process holding the scheduler gets started multiple times.
|
1150
1229
|
|
@@ -1221,7 +1300,7 @@ scheduler.max_work_threads += 10
|
|
1221
1300
|
## Rufus::Scheduler.singleton
|
1222
1301
|
|
1223
1302
|
Do not want to store a reference to your rufus-scheduler instance?
|
1224
|
-
Then ```Rufus::Scheduler.singleton``` can help, it returns a
|
1303
|
+
Then ```Rufus::Scheduler.singleton``` can help, it returns a singleton instance of the scheduler, initialized the first time this class method is called.
|
1225
1304
|
|
1226
1305
|
```ruby
|
1227
1306
|
Rufus::Scheduler.singleton.every '10s' { puts "hello, world!" }
|
@@ -1243,11 +1322,11 @@ Rufus::Scheduler.s.every '10s' { puts "hello, world!" }
|
|
1243
1322
|
|
1244
1323
|
## advanced lock schemes
|
1245
1324
|
|
1246
|
-
As seen above, rufus-scheduler proposes the [:lockfile](#lockfile--mylockfiletxt) system out of the box. If in a group of schedulers only one is supposed to run, the lockfile
|
1325
|
+
As seen above, rufus-scheduler proposes the [:lockfile](#lockfile--mylockfiletxt) system out of the box. If in a group of schedulers only one is supposed to run, the lockfile mechanism prevents schedulers that have not set/created the lockfile from running.
|
1247
1326
|
|
1248
|
-
There are
|
1327
|
+
There are situations where this is not sufficient.
|
1249
1328
|
|
1250
|
-
By overriding #lock and #unlock, one can customize how
|
1329
|
+
By overriding #lock and #unlock, one can customize how schedulers lock.
|
1251
1330
|
|
1252
1331
|
This example was provided by [Eric Lindvall](https://github.com/eric):
|
1253
1332
|
|
@@ -1280,7 +1359,7 @@ class ZookeptScheduler < Rufus::Scheduler
|
|
1280
1359
|
end
|
1281
1360
|
```
|
1282
1361
|
|
1283
|
-
This uses a [zookeeper](
|
1362
|
+
This uses a [zookeeper](https://zookeeper.apache.org/) to make sure only one scheduler in a group of distributed schedulers runs.
|
1284
1363
|
|
1285
1364
|
The methods #lock and #unlock are overridden and #confirm_lock is provided,
|
1286
1365
|
to make sure that the lock is still valid.
|
@@ -1304,7 +1383,9 @@ Warning: you may think you're heading towards "high availability" by using a tri
|
|
1304
1383
|
|
1305
1384
|
## parsing cronlines and time strings
|
1306
1385
|
|
1307
|
-
|
1386
|
+
(Please note that [fugit](https://github.com/floraison/fugit) does the heavy-lifting parsing work for rufus-scheduler).
|
1387
|
+
|
1388
|
+
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).
|
1308
1389
|
|
1309
1390
|
```ruby
|
1310
1391
|
require 'rufus-scheduler'
|
@@ -1324,13 +1405,13 @@ Rufus::Scheduler.parse(0.1)
|
|
1324
1405
|
# => 0.1
|
1325
1406
|
|
1326
1407
|
Rufus::Scheduler.parse('* * * * *')
|
1327
|
-
# => #<
|
1328
|
-
#
|
1329
|
-
#
|
1330
|
-
#
|
1408
|
+
# => #<Fugit::Cron:0x00007fb7a3045508
|
1409
|
+
# @original="* * * * *", @cron_s=nil,
|
1410
|
+
# @seconds=[0], @minutes=nil, @hours=nil, @monthdays=nil, @months=nil,
|
1411
|
+
# @weekdays=nil, @zone=nil, @timezone=nil>
|
1331
1412
|
```
|
1332
1413
|
|
1333
|
-
It returns a number when the
|
1414
|
+
It returns a number when the input is a duration and a Fugit::Cron instance when the input is a cron string.
|
1334
1415
|
|
1335
1416
|
It will raise an ArgumentError if it can't parse the input.
|
1336
1417
|
|
@@ -1383,7 +1464,7 @@ require 'rufus-scheduler'
|
|
1383
1464
|
|
1384
1465
|
Time.now
|
1385
1466
|
# => 2013-10-26 07:07:08 +0900
|
1386
|
-
Rufus::Scheduler.parse('* * * * mon#1').next_time
|
1467
|
+
Rufus::Scheduler.parse('* * * * mon#1').next_time.to_s
|
1387
1468
|
# => 2013-11-04 00:00:00 +0900
|
1388
1469
|
```
|
1389
1470
|
|
@@ -1396,7 +1477,7 @@ In this example, the cronline is supposed to trigger every last day of the month
|
|
1396
1477
|
require 'rufus-scheduler'
|
1397
1478
|
Time.now
|
1398
1479
|
# => 2013-10-26 07:22:09 +0900
|
1399
|
-
Rufus::Scheduler.parse('00 12 L * *').next_time
|
1480
|
+
Rufus::Scheduler.parse('00 12 L * *').next_time.to_s
|
1400
1481
|
# => 2013-10-31 12:00:00 +0900
|
1401
1482
|
```
|
1402
1483
|
|
@@ -1449,7 +1530,7 @@ rufus-scheduler/lib/rufus/scheduler/zotime.rb:41:
|
|
1449
1530
|
...
|
1450
1531
|
```
|
1451
1532
|
|
1452
|
-
It may happen on Windows or on systems that poorly
|
1533
|
+
It may happen on Windows or on systems that poorly hint to Ruby which timezone to use. It should be solved by setting explicitly the `ENV['TZ']` before the scheduler instantiation:
|
1453
1534
|
```ruby
|
1454
1535
|
ENV['TZ'] = 'Asia/Shanghai'
|
1455
1536
|
scheduler = Rufus::Scheduler.new
|
@@ -1540,9 +1621,9 @@ The rufus-scheduler singleton is instantiated in the ```config/initializers/sche
|
|
1540
1621
|
|
1541
1622
|
### avoid scheduling when running the Ruby on Rails console
|
1542
1623
|
|
1543
|
-
(Written in reply to https://github.com/jmettraux/rufus-scheduler/issues/186
|
1624
|
+
(Written in reply to [gh-186](https://github.com/jmettraux/rufus-scheduler/issues/186))
|
1544
1625
|
|
1545
|
-
If you don't want rufus-scheduler to
|
1626
|
+
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:
|
1546
1627
|
|
1547
1628
|
```ruby
|
1548
1629
|
#
|
@@ -1552,21 +1633,19 @@ require 'rufus-scheduler'
|
|
1552
1633
|
|
1553
1634
|
s = Rufus::Scheduler.singleton
|
1554
1635
|
|
1636
|
+
return if defined?(Rails::Console) || Rails.env.test? || File.split($0).last == 'rake'
|
1637
|
+
# return if $PROGRAM_NAME.include?('spring')
|
1638
|
+
# see https://github.com/jmettraux/rufus-scheduler/issues/186
|
1555
1639
|
|
1556
|
-
|
1557
|
-
|
1558
|
-
# only schedule when not running from the Ruby on Rails console
|
1559
|
-
# or from a rake task
|
1640
|
+
# do not schedule when Rails is run from its console, for a test/spec, or
|
1641
|
+
# from a Rake task
|
1560
1642
|
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
Rails.logger.flush
|
1565
|
-
end
|
1643
|
+
s.every '1m' do
|
1644
|
+
Rails.logger.info "hello, it's #{Time.now}"
|
1645
|
+
Rails.logger.flush
|
1566
1646
|
end
|
1567
1647
|
```
|
1568
1648
|
|
1569
|
-
It should work for Ruby on Rails 3 and 4.
|
1570
1649
|
|
1571
1650
|
### rails server -d
|
1572
1651
|
|
@@ -1578,8 +1657,8 @@ I avoid running `-d` in development mode and bother about daemonizing only for p
|
|
1578
1657
|
|
1579
1658
|
These are two well crafted articles on process daemonization, please read them:
|
1580
1659
|
|
1581
|
-
*
|
1582
|
-
*
|
1660
|
+
* https://www.mikeperham.com/2014/09/22/dont-daemonize-your-daemons/
|
1661
|
+
* https://www.mikeperham.com/2014/07/07/use-runit/
|
1583
1662
|
|
1584
1663
|
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).
|
1585
1664
|
|