rufus-scheduler 2.0.24 → 3.1.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 +76 -0
- data/CREDITS.txt +23 -0
- data/LICENSE.txt +1 -1
- data/README.md +1439 -0
- data/Rakefile +1 -5
- data/TODO.txt +149 -55
- data/lib/rufus/{sc → scheduler}/cronline.rb +167 -53
- data/lib/rufus/scheduler/job_array.rb +92 -0
- data/lib/rufus/scheduler/jobs.rb +633 -0
- data/lib/rufus/scheduler/locks.rb +95 -0
- data/lib/rufus/scheduler/util.rb +306 -0
- data/lib/rufus/scheduler/zones.rb +174 -0
- data/lib/rufus/scheduler/zotime.rb +154 -0
- data/lib/rufus/scheduler.rb +608 -27
- data/rufus-scheduler.gemspec +6 -4
- data/spec/basics_spec.rb +54 -0
- data/spec/cronline_spec.rb +479 -152
- data/spec/error_spec.rb +139 -0
- data/spec/job_array_spec.rb +39 -0
- data/spec/job_at_spec.rb +58 -0
- data/spec/job_cron_spec.rb +128 -0
- data/spec/job_every_spec.rb +104 -0
- data/spec/job_in_spec.rb +20 -0
- data/spec/job_interval_spec.rb +68 -0
- data/spec/job_repeat_spec.rb +357 -0
- data/spec/job_spec.rb +498 -109
- data/spec/lock_custom_spec.rb +47 -0
- data/spec/lock_flock_spec.rb +47 -0
- data/spec/lock_lockfile_spec.rb +61 -0
- data/spec/lock_spec.rb +59 -0
- data/spec/parse_spec.rb +263 -0
- data/spec/schedule_at_spec.rb +158 -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 +928 -124
- data/spec/spec_helper.rb +126 -0
- data/spec/threads_spec.rb +96 -0
- data/spec/zotime_spec.rb +396 -0
- metadata +56 -33
- 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/lib/rufus/sc/version.rb +0 -32
- 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
@@ -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
|
-
|
data/test/kjw.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
#
|
4
|
-
# A test by http://twitter.com/kjw
|
5
|
-
#
|
6
|
-
|
7
|
-
require 'test/unit'
|
8
|
-
$LOAD_PATH << 'lib'
|
9
|
-
require 'rufus/sc/scheduler'
|
10
|
-
|
11
|
-
require 'rubygems'
|
12
|
-
require 'eventmachine'
|
13
|
-
|
14
|
-
class CronTest < Test::Unit::TestCase
|
15
|
-
|
16
|
-
#
|
17
|
-
# Stress test program for rufus-scheduler
|
18
|
-
#
|
19
|
-
|
20
|
-
SECONDS_FROM_NOW = 30
|
21
|
-
MODE = :cron
|
22
|
-
JOB_COUNT = 1000
|
23
|
-
JOB_IDS = (1..JOB_COUNT).to_a
|
24
|
-
NUM_RESCHEDULES = 20
|
25
|
-
|
26
|
-
def setup
|
27
|
-
@trigger_queue = Queue.new
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_stress_schedule_unschedule_plain_cron
|
31
|
-
stress_schedule_unschedule(:cron, Rufus::Scheduler::PlainScheduler.start_new)
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_stress_schedule_unschedule_plain_at
|
35
|
-
stress_schedule_unschedule(:at, Rufus::Scheduler::PlainScheduler.start_new)
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_stress_schedule_unschedule_plain_every
|
39
|
-
stress_schedule_unschedule(:every, Rufus::Scheduler::PlainScheduler.start_new)
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_stress_schedule_unschedule_em_cron
|
43
|
-
stress_schedule_unschedule(:cron, Rufus::Scheduler::EmScheduler.start_new)
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_stress_schedule_unschedule_em_at
|
47
|
-
stress_schedule_unschedule(:at, Rufus::Scheduler::EmScheduler.start_new)
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_stress_schedule_unschedule_em_every
|
51
|
-
stress_schedule_unschedule(:every, Rufus::Scheduler::EmScheduler.start_new)
|
52
|
-
end
|
53
|
-
|
54
|
-
protected
|
55
|
-
|
56
|
-
def stress_schedule_unschedule(mode, scheduler)
|
57
|
-
|
58
|
-
# Schedule all jobs, then unschedule and (re)schedule a number of times
|
59
|
-
schedule_unschedule(scheduler, mode, NUM_RESCHEDULES)
|
60
|
-
|
61
|
-
# give scheduler thread 10 seconds to process the schedule and unschedule requests
|
62
|
-
# but don't wait for the jobs to trigger (which is in less than 30 seconds)
|
63
|
-
sleep 10
|
64
|
-
# print_scheduler_counts(scheduler, 10)
|
65
|
-
|
66
|
-
# by now the scheduler should have processed everything, check
|
67
|
-
assert(JOB_IDS.sort == scheduler.find_jobs.map{ |job| job.job_id }.sort)
|
68
|
-
sleep SECONDS_FROM_NOW # wait for jobs to trigger
|
69
|
-
assert(JOB_COUNT == @trigger_queue.size)
|
70
|
-
end
|
71
|
-
|
72
|
-
def schedule_unschedule(scheduler, mode, num_reschedules)
|
73
|
-
schedule_jobs(scheduler, mode)
|
74
|
-
1.upto(num_reschedules) do
|
75
|
-
sleep 0.01 # cause schedule's to happen before unscheduling
|
76
|
-
unschedule_jobs(scheduler)
|
77
|
-
schedule_jobs(scheduler, mode)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def print_scheduler_counts(scheduler, seconds)
|
82
|
-
1.upto(seconds) do
|
83
|
-
puts "all:%d at:%d cron:%d every:%d pending:%d" % [
|
84
|
-
scheduler.all_jobs.size,
|
85
|
-
scheduler.at_job_count,
|
86
|
-
scheduler.cron_job_count,
|
87
|
-
scheduler.every_job_count,
|
88
|
-
scheduler.pending_job_count]
|
89
|
-
sleep 1
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def schedule_jobs(scheduler, mode)
|
94
|
-
trigger_proc = lambda { |params| @trigger_queue << params[:job_id] }
|
95
|
-
JOB_IDS.each do |job_id|
|
96
|
-
case mode
|
97
|
-
when :at
|
98
|
-
scheduler.at(Time.now + SECONDS_FROM_NOW, {:job_id => job_id}, &trigger_proc)
|
99
|
-
when :every
|
100
|
-
scheduler.every("#{SECONDS_FROM_NOW}s", {:job_id => job_id}, &trigger_proc)
|
101
|
-
when :cron
|
102
|
-
scheduler.cron("%d * * * * *" % ((Time.now.to_i%60) + SECONDS_FROM_NOW) % 60,
|
103
|
-
{:job_id => job_id}, &trigger_proc)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def unschedule_jobs(scheduler)
|
109
|
-
JOB_IDS.each { |job_id| scheduler.unschedule(job_id) }
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
|
data/test/t.rb
DELETED