rufus-scheduler 2.0.24 → 3.0.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/CHANGELOG.txt +6 -0
- data/CREDITS.txt +4 -0
- data/README.md +1064 -0
- data/Rakefile +1 -4
- data/TODO.txt +145 -55
- data/lib/rufus/scheduler.rb +502 -26
- data/lib/rufus/{sc → scheduler}/cronline.rb +46 -17
- data/lib/rufus/{sc/version.rb → scheduler/job_array.rb} +56 -4
- data/lib/rufus/scheduler/jobs.rb +548 -0
- data/lib/rufus/scheduler/util.rb +318 -0
- data/rufus-scheduler.gemspec +30 -4
- data/spec/cronline_spec.rb +29 -8
- data/spec/error_spec.rb +116 -0
- data/spec/job_array_spec.rb +39 -0
- data/spec/job_at_spec.rb +58 -0
- data/spec/job_cron_spec.rb +67 -0
- data/spec/job_every_spec.rb +71 -0
- data/spec/job_in_spec.rb +20 -0
- data/spec/job_interval_spec.rb +68 -0
- data/spec/job_repeat_spec.rb +308 -0
- data/spec/job_spec.rb +387 -115
- data/spec/lockfile_spec.rb +61 -0
- data/spec/parse_spec.rb +203 -0
- data/spec/schedule_at_spec.rb +129 -0
- data/spec/schedule_cron_spec.rb +66 -0
- data/spec/schedule_every_spec.rb +109 -0
- data/spec/schedule_in_spec.rb +80 -0
- data/spec/schedule_interval_spec.rb +128 -0
- data/spec/scheduler_spec.rb +831 -124
- data/spec/spec_helper.rb +65 -0
- data/spec/threads_spec.rb +75 -0
- metadata +64 -59
- data/README.rdoc +0 -661
- data/lib/rufus/otime.rb +0 -3
- data/lib/rufus/sc/jobqueues.rb +0 -160
- data/lib/rufus/sc/jobs.rb +0 -471
- data/lib/rufus/sc/rtime.rb +0 -363
- data/lib/rufus/sc/scheduler.rb +0 -636
- data/spec/at_in_spec.rb +0 -47
- data/spec/at_spec.rb +0 -125
- data/spec/blocking_spec.rb +0 -64
- data/spec/cron_spec.rb +0 -134
- data/spec/every_spec.rb +0 -304
- data/spec/exception_spec.rb +0 -113
- data/spec/in_spec.rb +0 -150
- data/spec/mutex_spec.rb +0 -159
- data/spec/rtime_spec.rb +0 -137
- data/spec/schedulable_spec.rb +0 -97
- data/spec/spec_base.rb +0 -87
- data/spec/stress_schedule_unschedule_spec.rb +0 -159
- data/spec/timeout_spec.rb +0 -148
- data/test/kjw.rb +0 -113
- data/test/t.rb +0 -20
data/spec/schedulable_spec.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Specifying rufus-scheduler
|
4
|
-
#
|
5
|
-
# Tue May 5 14:47:16 JST 2009
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'spec_base'
|
9
|
-
|
10
|
-
|
11
|
-
describe Rufus::Scheduler::Schedulable do
|
12
|
-
|
13
|
-
before(:each) do
|
14
|
-
@s = start_scheduler
|
15
|
-
end
|
16
|
-
after(:each) do
|
17
|
-
stop_scheduler(@s)
|
18
|
-
end
|
19
|
-
|
20
|
-
class JobAlpha
|
21
|
-
attr_reader :value
|
22
|
-
def trigger(params)
|
23
|
-
@value = params
|
24
|
-
end
|
25
|
-
end
|
26
|
-
class JobBravo
|
27
|
-
attr_reader :value
|
28
|
-
def call(job)
|
29
|
-
@value = job
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'schedules via :schedulable' do
|
34
|
-
|
35
|
-
j = JobAlpha.new
|
36
|
-
|
37
|
-
@s.in '1s', :schedulable => j
|
38
|
-
|
39
|
-
sleep 1.4
|
40
|
-
|
41
|
-
j.value.class.should == Hash
|
42
|
-
j.value[:job].class.should == Rufus::Scheduler::InJob
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'honours schedulables that reply to :call' do
|
46
|
-
|
47
|
-
j = JobBravo.new
|
48
|
-
|
49
|
-
@s.in '1s', :schedulable => j
|
50
|
-
|
51
|
-
sleep 1.4
|
52
|
-
|
53
|
-
j.value.class.should == Rufus::Scheduler::InJob
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'accepts trigger schedulables as second param' do
|
57
|
-
|
58
|
-
j = JobAlpha.new
|
59
|
-
|
60
|
-
@s.in '1s', j
|
61
|
-
|
62
|
-
sleep 1.4
|
63
|
-
|
64
|
-
j.value.class.should == Hash
|
65
|
-
j.value[:job].class.should == Rufus::Scheduler::InJob
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'accepts call schedulables as second param' do
|
69
|
-
|
70
|
-
j = JobBravo.new
|
71
|
-
|
72
|
-
@s.in '1s', j
|
73
|
-
|
74
|
-
sleep 1.4
|
75
|
-
|
76
|
-
j.value.class.should == Rufus::Scheduler::InJob
|
77
|
-
end
|
78
|
-
|
79
|
-
class MySchedulable
|
80
|
-
def self.job
|
81
|
-
@@job
|
82
|
-
end
|
83
|
-
def self.call(job)
|
84
|
-
@@job = job
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'accepts schedulable classes as second param' do
|
89
|
-
|
90
|
-
@s.in '1s', MySchedulable
|
91
|
-
|
92
|
-
sleep 1.4
|
93
|
-
|
94
|
-
MySchedulable.job.class.should == Rufus::Scheduler::InJob
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
data/spec/spec_base.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Specifying rufus-scheduler
|
4
|
-
#
|
5
|
-
# Fri Mar 20 22:53:33 JST 2009
|
6
|
-
#
|
7
|
-
|
8
|
-
$:.unshift(File.expand_path('../../lib', __FILE__))
|
9
|
-
|
10
|
-
require 'rubygems'
|
11
|
-
require 'fileutils'
|
12
|
-
|
13
|
-
|
14
|
-
Thread.abort_on_exception = true
|
15
|
-
|
16
|
-
|
17
|
-
#$:.unshift(File.expand_path('~/tmp/bacon/lib')) # my own bacon for a while
|
18
|
-
#require 'bacon'
|
19
|
-
#puts
|
20
|
-
#Bacon.summary_on_exit
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
# rufus/scheduler/em
|
25
|
-
|
26
|
-
# EM or plain ?
|
27
|
-
|
28
|
-
$plain = ! ENV['EVENTMACHINE']
|
29
|
-
|
30
|
-
require 'rufus/scheduler'
|
31
|
-
|
32
|
-
if ( ! $plain)
|
33
|
-
|
34
|
-
require 'eventmachine'
|
35
|
-
|
36
|
-
unless (EM.reactor_running?)
|
37
|
-
|
38
|
-
Thread.new { EM.run { } }
|
39
|
-
|
40
|
-
sleep 0.200
|
41
|
-
while (not EM.reactor_running?)
|
42
|
-
Thread.pass
|
43
|
-
end
|
44
|
-
#
|
45
|
-
# all this waiting, especially for the JRuby eventmachine, which seems
|
46
|
-
# rather 'diesel'
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
SCHEDULER_CLASS = $plain ?
|
52
|
-
Rufus::Scheduler::PlainScheduler :
|
53
|
-
Rufus::Scheduler::EmScheduler
|
54
|
-
|
55
|
-
#
|
56
|
-
# helper methods
|
57
|
-
|
58
|
-
def start_scheduler(opts={})
|
59
|
-
SCHEDULER_CLASS.start_new(opts)
|
60
|
-
end
|
61
|
-
|
62
|
-
def stop_scheduler(s)
|
63
|
-
#s.stop(:stop_em => true)
|
64
|
-
#sleep 0.200 # give time to the EM to stop
|
65
|
-
s.stop
|
66
|
-
sleep 0.200
|
67
|
-
end
|
68
|
-
|
69
|
-
def wait_next_tick
|
70
|
-
#if defined?(EM)
|
71
|
-
# t = Thread.current
|
72
|
-
# EM.next_tick { t.wakeup }
|
73
|
-
# Thread.stop
|
74
|
-
#else
|
75
|
-
sleep 0.500
|
76
|
-
#end
|
77
|
-
end
|
78
|
-
|
79
|
-
def local(*args)
|
80
|
-
Time.local(*args)
|
81
|
-
end
|
82
|
-
alias lo local
|
83
|
-
|
84
|
-
def utc(*args)
|
85
|
-
Time.utc(*args)
|
86
|
-
end
|
87
|
-
|
@@ -1,159 +0,0 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# a spec by Klaas Jan Wierenga
|
4
|
-
#
|
5
|
-
|
6
|
-
require 'spec_base'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
JOB_COUNT = 500 # 1000
|
11
|
-
JOB_IDS = (1..JOB_COUNT).to_a
|
12
|
-
NUM_RESCHEDULES = 5 # 10
|
13
|
-
TRIGGER_DELAY = 4 # 15
|
14
|
-
|
15
|
-
|
16
|
-
describe SCHEDULER_CLASS do
|
17
|
-
|
18
|
-
# helper methods
|
19
|
-
|
20
|
-
# Wait for a variable to become a certain value.
|
21
|
-
# This method returns a block which loops waiting for the passed in
|
22
|
-
# block paramter to have value 'target'.
|
23
|
-
#
|
24
|
-
def eventually(timeout = TRIGGER_DELAY * 2, precision = 1)
|
25
|
-
lambda { |target|
|
26
|
-
value = nil
|
27
|
-
(timeout/precision).to_i.times do
|
28
|
-
value = yield # read variable once
|
29
|
-
# puts "got #{value}, expected #{target}"
|
30
|
-
break if target == value
|
31
|
-
sleep precision
|
32
|
-
end
|
33
|
-
target == value
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
def benchmark
|
38
|
-
now = Time.now
|
39
|
-
yield
|
40
|
-
benchmark = Time.now - now
|
41
|
-
print " (scheduling took #{benchmark}s)"
|
42
|
-
if benchmark > TRIGGER_DELAY
|
43
|
-
puts "\nTEST RESULT INVALID/UNRELIABLE"
|
44
|
-
puts "Scheduling took longer than TRIGGER_DELAY (#{TRIGGER_DELAY}s)."
|
45
|
-
puts "Increase TRIGGER_DELAY to a value larger than largest scheduling time."
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def schedule_unschedule_same_ids_spec(mode)
|
50
|
-
scheduler = SCHEDULER_CLASS.start_new
|
51
|
-
benchmark { schedule_unschedule(scheduler, mode, NUM_RESCHEDULES) }
|
52
|
-
JOB_COUNT.should satisfy &eventually { scheduler.all_jobs.size }
|
53
|
-
JOB_IDS.sort.should == scheduler.find_jobs.map{ |job| job.job_id }.sort
|
54
|
-
JOB_COUNT.should satisfy &eventually { @trigger_queue.size }
|
55
|
-
@trigger_queue.size.should == JOB_COUNT
|
56
|
-
scheduler.stop
|
57
|
-
end
|
58
|
-
|
59
|
-
def schedule_unschedule_unique_ids_spec(mode)
|
60
|
-
scheduler = SCHEDULER_CLASS.start_new
|
61
|
-
job_ids = []
|
62
|
-
benchmark { job_ids = schedule_unschedule(scheduler, mode, NUM_RESCHEDULES, true) }
|
63
|
-
JOB_COUNT.should satisfy &eventually { scheduler.all_jobs.size }
|
64
|
-
job_ids.sort.should == scheduler.find_jobs.map{ |job| job.job_id }.sort
|
65
|
-
JOB_COUNT.should satisfy &eventually { @trigger_queue.size }
|
66
|
-
@trigger_queue.size.should == JOB_COUNT
|
67
|
-
scheduler.stop
|
68
|
-
end
|
69
|
-
|
70
|
-
def scheduler_counts(scheduler)
|
71
|
-
"all:%d at:%d cron:%d every:%d pending:%d" % [
|
72
|
-
scheduler.all_jobs.size,
|
73
|
-
scheduler.at_job_count,
|
74
|
-
scheduler.cron_job_count,
|
75
|
-
scheduler.every_job_count,
|
76
|
-
scheduler.pending_job_count ]
|
77
|
-
end
|
78
|
-
|
79
|
-
def schedule_unschedule(scheduler, mode, num_reschedules, generate_ids=false)
|
80
|
-
job_ids = schedule_jobs(scheduler, mode, generate_ids)
|
81
|
-
1.upto(num_reschedules) do
|
82
|
-
unschedule_jobs(scheduler, job_ids)
|
83
|
-
job_ids = schedule_jobs(scheduler, mode, generate_ids)
|
84
|
-
end
|
85
|
-
job_ids
|
86
|
-
end
|
87
|
-
|
88
|
-
def schedule_jobs(scheduler, mode, generate_ids=false)
|
89
|
-
job_ids = []
|
90
|
-
JOB_IDS.each do |job_id|
|
91
|
-
case mode
|
92
|
-
when :cron
|
93
|
-
job_ids << scheduler.cron(
|
94
|
-
"%d * * * * *" % @cron_trigger,
|
95
|
-
{ :job_id => (generate_ids ? nil : job_id) },
|
96
|
-
&@trigger_proc
|
97
|
-
).job_id
|
98
|
-
when :at
|
99
|
-
job_ids << scheduler.at(
|
100
|
-
@at_trigger,
|
101
|
-
{ :job_id => (generate_ids ? nil : job_id) },
|
102
|
-
&@trigger_proc
|
103
|
-
).job_id
|
104
|
-
when :every
|
105
|
-
job_ids << scheduler.every(
|
106
|
-
@every_trigger,
|
107
|
-
{ :job_id => (generate_ids ? nil : job_id) },
|
108
|
-
&@trigger_proc
|
109
|
-
).job_id
|
110
|
-
else
|
111
|
-
raise ArgumentError
|
112
|
-
end
|
113
|
-
end
|
114
|
-
job_ids
|
115
|
-
end
|
116
|
-
|
117
|
-
def unschedule_jobs(scheduler, job_ids)
|
118
|
-
job_ids.each { |job_id| scheduler.unschedule(job_id) }
|
119
|
-
end
|
120
|
-
|
121
|
-
# the actual tests
|
122
|
-
|
123
|
-
before(:each) do
|
124
|
-
@trigger_queue = Queue.new
|
125
|
-
@cron_trigger = ((Time.now.to_i%60) + TRIGGER_DELAY) % 60 # 30 seconds from now
|
126
|
-
@at_trigger = Time.now + TRIGGER_DELAY
|
127
|
-
@every_trigger = "#{TRIGGER_DELAY}s"
|
128
|
-
@trigger_proc = lambda { |job| @trigger_queue << job.job_id }
|
129
|
-
end
|
130
|
-
|
131
|
-
after(:each) do
|
132
|
-
@trigger_queue = nil
|
133
|
-
end
|
134
|
-
|
135
|
-
it "sustains frequent schedule/unschedule 'cron' jobs with same ids" do
|
136
|
-
schedule_unschedule_same_ids_spec(:cron)
|
137
|
-
end
|
138
|
-
|
139
|
-
it "sustains frequent schedule/unschedule 'at' jobs with same ids" do
|
140
|
-
schedule_unschedule_same_ids_spec(:at)
|
141
|
-
end
|
142
|
-
|
143
|
-
it "sustains frequent schedule/unschedule 'every' jobs same ids" do
|
144
|
-
schedule_unschedule_same_ids_spec(:every)
|
145
|
-
end
|
146
|
-
|
147
|
-
it "sustains frequent schedule/unschedule 'cron' jobs with unique ids" do
|
148
|
-
schedule_unschedule_unique_ids_spec(:cron)
|
149
|
-
end
|
150
|
-
|
151
|
-
it "sustains frequent schedule/unschedule 'at' jobs with unique ids" do
|
152
|
-
schedule_unschedule_unique_ids_spec(:at)
|
153
|
-
end
|
154
|
-
|
155
|
-
it "sustains frequent schedule/unschedule 'every' jobs unique ids" do
|
156
|
-
schedule_unschedule_unique_ids_spec(:every)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
data/spec/timeout_spec.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Specifying rufus-scheduler
|
4
|
-
#
|
5
|
-
# Sun May 3 15:44:28 JST 2009
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'spec_base'
|
9
|
-
|
10
|
-
|
11
|
-
describe "#{SCHEDULER_CLASS} timeouts" do
|
12
|
-
|
13
|
-
before(:each) do
|
14
|
-
@s = start_scheduler
|
15
|
-
end
|
16
|
-
after(:each) do
|
17
|
-
stop_scheduler(@s)
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'refuses to schedule a job with :timeout and :blocking' do
|
21
|
-
|
22
|
-
lambda {
|
23
|
-
@s.in '1s', :timeout => '3s', :blocking => true do
|
24
|
-
end
|
25
|
-
}.should raise_error(ArgumentError)
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'schedules a dedicated job for the timeout' do
|
29
|
-
|
30
|
-
@s.in '1s', :timeout => '3s' do
|
31
|
-
sleep 5
|
32
|
-
end
|
33
|
-
|
34
|
-
@s.jobs.size.should == 1
|
35
|
-
|
36
|
-
# the timeout job is left
|
37
|
-
|
38
|
-
sleep 2
|
39
|
-
@s.jobs.size.should == 1
|
40
|
-
@s.find_by_tag('timeout').size.should == 1
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'times out' do
|
44
|
-
|
45
|
-
var = nil
|
46
|
-
timedout = false
|
47
|
-
|
48
|
-
@s.in '1s', :timeout => '1s' do
|
49
|
-
begin
|
50
|
-
sleep 2
|
51
|
-
var = true
|
52
|
-
rescue Rufus::Scheduler::TimeOutError => e
|
53
|
-
timedout = true
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
sleep 4
|
58
|
-
|
59
|
-
var.should == nil
|
60
|
-
@s.jobs.size.should == 0
|
61
|
-
timedout.should == true
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'dies silently if job finished before timeout' do
|
65
|
-
|
66
|
-
var = nil
|
67
|
-
timedout = false
|
68
|
-
|
69
|
-
@s.in '1s', :timeout => '1s' do
|
70
|
-
begin
|
71
|
-
var = true
|
72
|
-
rescue Rufus::Scheduler::TimeOutError => e
|
73
|
-
timedout = true
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
sleep 3
|
78
|
-
|
79
|
-
var.should == true
|
80
|
-
@s.jobs.size.should == 0
|
81
|
-
timedout.should == false
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'does not timeout other jobs (in case of every)' do
|
85
|
-
|
86
|
-
timeouts = []
|
87
|
-
|
88
|
-
@s.every '1s', :timeout => '1s500' do
|
89
|
-
start = Time.now
|
90
|
-
begin
|
91
|
-
sleep 2.0
|
92
|
-
rescue Rufus::Scheduler::TimeOutError => e
|
93
|
-
timeouts << (Time.now - start)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
sleep 5.5
|
98
|
-
|
99
|
-
timeouts.size.should == 3
|
100
|
-
timeouts.each { |to| to.should be_within(0.5).of(1.5) }
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'points to their "parent" job' do
|
104
|
-
|
105
|
-
@s.in '1s', :timeout => '3s', :job_id => 'nada' do
|
106
|
-
sleep 4
|
107
|
-
end
|
108
|
-
|
109
|
-
sleep 2
|
110
|
-
|
111
|
-
@s.jobs.values.first.parent.job_id.should == 'nada'
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'does not survive their job' do
|
115
|
-
|
116
|
-
@s.in '1s', :timeout => '3s' do
|
117
|
-
sleep 0.100
|
118
|
-
end
|
119
|
-
|
120
|
-
sleep 2
|
121
|
-
|
122
|
-
@s.jobs.size.should == 0
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'times out properly after waiting for a mutex' do
|
126
|
-
|
127
|
-
mutex = Mutex.new
|
128
|
-
timedout = false
|
129
|
-
|
130
|
-
@s.in '0s', :mutex => mutex do
|
131
|
-
sleep 1
|
132
|
-
end
|
133
|
-
|
134
|
-
@s.in '0s', :mutex => mutex, :timeout => 0.1 do
|
135
|
-
begin
|
136
|
-
sleep 2
|
137
|
-
rescue Rufus::Scheduler::TimeOutError => e
|
138
|
-
timedout = true
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
sleep 2
|
143
|
-
|
144
|
-
@s.jobs.size.should == 0
|
145
|
-
timedout.should be_true
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|