rufus-scheduler 2.0.6 → 2.0.7
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/.gitignore +2 -0
- data/.rspec +1 -0
- data/CHANGELOG.txt +6 -0
- data/CREDITS.txt +2 -0
- data/README.rdoc +45 -12
- data/Rakefile +27 -22
- data/lib/rufus/sc/cronline.rb +61 -44
- data/lib/rufus/sc/jobqueues.rb +6 -7
- data/lib/rufus/sc/jobs.rb +27 -10
- data/lib/rufus/sc/rtime.rb +46 -38
- data/lib/rufus/sc/scheduler.rb +33 -23
- data/lib/rufus/sc/version.rb +1 -1
- data/lib/rufus/scheduler.rb +2 -2
- data/rufus-scheduler.gemspec +29 -13
- data/spec/at_in_spec.rb +8 -9
- data/spec/at_spec.rb +25 -26
- data/spec/blocking_spec.rb +7 -7
- data/spec/cron_spec.rb +23 -23
- data/spec/cronline_spec.rb +175 -40
- data/spec/every_spec.rb +71 -33
- data/spec/exception_spec.rb +11 -12
- data/spec/in_spec.rb +35 -36
- data/spec/rtime_spec.rb +47 -47
- data/spec/schedulable_spec.rb +15 -15
- data/spec/scheduler_spec.rb +14 -15
- data/spec/spec_base.rb +6 -13
- data/spec/stress_schedule_unschedule_spec.rb +124 -120
- data/spec/timeout_spec.rb +24 -24
- metadata +32 -10
- data/spec/spec.rb +0 -14
data/spec/scheduler_spec.rb
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
# Sat Mar 21 17:43:23 JST 2009
|
6
6
|
#
|
7
7
|
|
8
|
-
require File.dirname(__FILE__)
|
8
|
+
require File.join(File.dirname(__FILE__), 'spec_base')
|
9
9
|
|
10
10
|
|
11
11
|
describe SCHEDULER_CLASS do
|
12
12
|
|
13
|
-
it '
|
13
|
+
it 'stops' do
|
14
14
|
|
15
15
|
var = nil
|
16
16
|
|
@@ -19,33 +19,33 @@ describe SCHEDULER_CLASS do
|
|
19
19
|
|
20
20
|
stop_scheduler(s)
|
21
21
|
|
22
|
-
var.should
|
22
|
+
var.should == nil
|
23
23
|
sleep 4
|
24
|
-
var.should
|
24
|
+
var.should == nil
|
25
25
|
end
|
26
26
|
|
27
27
|
unless SCHEDULER_CLASS == Rufus::Scheduler::EmScheduler
|
28
28
|
|
29
|
-
it '
|
29
|
+
it 'sets a default scheduler thread name' do
|
30
30
|
|
31
31
|
s = start_scheduler
|
32
32
|
|
33
|
-
s.instance_variable_get(:@thread)['name'].should
|
33
|
+
s.instance_variable_get(:@thread)['name'].should match(
|
34
34
|
/Rufus::Scheduler::.*Scheduler - \d+\.\d+\.\d+/)
|
35
35
|
|
36
36
|
stop_scheduler(s)
|
37
37
|
end
|
38
38
|
|
39
|
-
it '
|
39
|
+
it 'sets the scheduler thread name' do
|
40
40
|
|
41
41
|
s = start_scheduler(:thread_name => 'nada')
|
42
|
-
s.instance_variable_get(:@thread)['name'].should
|
42
|
+
s.instance_variable_get(:@thread)['name'].should == 'nada'
|
43
43
|
|
44
44
|
stop_scheduler(s)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
it '
|
48
|
+
it 'accepts a custom frequency' do
|
49
49
|
|
50
50
|
var = nil
|
51
51
|
|
@@ -54,26 +54,25 @@ describe SCHEDULER_CLASS do
|
|
54
54
|
s.in('1s') { var = true }
|
55
55
|
|
56
56
|
sleep 1
|
57
|
-
var.should
|
57
|
+
var.should == nil
|
58
58
|
|
59
59
|
sleep 1
|
60
|
-
var.should
|
60
|
+
var.should == nil
|
61
61
|
|
62
62
|
sleep 2
|
63
|
-
var.should
|
63
|
+
var.should == true
|
64
64
|
|
65
65
|
stop_scheduler(s)
|
66
66
|
end
|
67
|
-
|
68
67
|
end
|
69
68
|
|
70
69
|
describe 'Rufus::Scheduler#start_new' do
|
71
70
|
|
72
|
-
it '
|
71
|
+
it 'piggybacks EM if present and running' do
|
73
72
|
|
74
73
|
s = Rufus::Scheduler.start_new
|
75
74
|
|
76
|
-
s.class.should
|
75
|
+
s.class.should == SCHEDULER_CLASS
|
77
76
|
|
78
77
|
stop_scheduler(s)
|
79
78
|
end
|
data/spec/spec_base.rb
CHANGED
@@ -5,23 +5,16 @@
|
|
5
5
|
# Fri Mar 20 22:53:33 JST 2009
|
6
6
|
#
|
7
7
|
|
8
|
-
|
9
|
-
#
|
10
|
-
# bacon
|
11
|
-
|
12
8
|
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
13
9
|
|
14
10
|
require 'rubygems'
|
15
11
|
require 'fileutils'
|
16
12
|
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
puts
|
23
|
-
|
24
|
-
Bacon.summary_on_exit
|
14
|
+
#$:.unshift(File.expand_path('~/tmp/bacon/lib')) # my own bacon for a while
|
15
|
+
#require 'bacon'
|
16
|
+
#puts
|
17
|
+
#Bacon.summary_on_exit
|
25
18
|
|
26
19
|
|
27
20
|
#
|
@@ -59,11 +52,11 @@ SCHEDULER_CLASS = $plain ?
|
|
59
52
|
#
|
60
53
|
# helper methods
|
61
54
|
|
62
|
-
def start_scheduler
|
55
|
+
def start_scheduler(opts={})
|
63
56
|
SCHEDULER_CLASS.start_new(opts)
|
64
57
|
end
|
65
58
|
|
66
|
-
def stop_scheduler
|
59
|
+
def stop_scheduler(s)
|
67
60
|
#s.stop(:stop_em => true)
|
68
61
|
#sleep 0.200 # give time to the EM to stop
|
69
62
|
s.stop
|
@@ -1,155 +1,159 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
2
|
+
#
|
3
|
+
# a spec by Klaas Jan Wierenga
|
4
|
+
#
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), '/spec_base')
|
4
7
|
|
5
|
-
require 'rubygems'
|
6
|
-
require 'eventmachine'
|
7
8
|
|
8
|
-
#require 'spec/autorun'
|
9
|
-
require File.dirname(__FILE__) + '/spec_base'
|
10
9
|
|
11
10
|
JOB_COUNT = 500 # 1000
|
12
11
|
JOB_IDS = (1..JOB_COUNT).to_a
|
13
12
|
NUM_RESCHEDULES = 5 # 10
|
14
13
|
TRIGGER_DELAY = 4 # 15
|
15
14
|
|
15
|
+
|
16
16
|
describe SCHEDULER_CLASS do
|
17
17
|
|
18
|
-
# helper methods
|
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
|
19
36
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
value = yield # read variable once
|
30
|
-
# puts "got #{value}, expected #{target}"
|
31
|
-
break if target == value
|
32
|
-
sleep precision
|
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."
|
33
46
|
end
|
34
|
-
target == value
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
def benchmark
|
39
|
-
now = Time.now
|
40
|
-
yield
|
41
|
-
benchmark = Time.now - now
|
42
|
-
print " (scheduling took #{benchmark}s)"
|
43
|
-
if benchmark > TRIGGER_DELAY
|
44
|
-
puts "\nTEST RESULT INVALID/UNRELIABLE"
|
45
|
-
puts "Scheduling took longer than TRIGGER_DELAY (#{TRIGGER_DELAY}s)."
|
46
|
-
puts "Increase TRIGGER_DELAY to a value larger than largest scheduling time."
|
47
47
|
end
|
48
|
-
end
|
49
48
|
|
50
|
-
def schedule_unschedule_same_ids_spec
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
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
|
59
58
|
|
60
|
-
def schedule_unschedule_unique_ids_spec
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
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
|
70
69
|
|
71
|
-
def scheduler_counts(scheduler)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
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
|
79
78
|
|
80
|
-
def schedule_unschedule(scheduler, mode, num_reschedules, generate_ids
|
81
|
-
job_ids = schedule_jobs(scheduler, mode, generate_ids)
|
82
|
-
1.upto(num_reschedules) do
|
83
|
-
unschedule_jobs(scheduler, job_ids)
|
79
|
+
def schedule_unschedule(scheduler, mode, num_reschedules, generate_ids=false)
|
84
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
|
85
86
|
end
|
86
|
-
job_ids
|
87
|
-
end
|
88
87
|
|
89
|
-
def schedule_jobs(scheduler, mode, generate_ids
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
107
113
|
end
|
114
|
+
job_ids
|
108
115
|
end
|
109
|
-
job_ids
|
110
|
-
end
|
111
116
|
|
112
|
-
def unschedule_jobs(scheduler, job_ids)
|
113
|
-
|
114
|
-
end
|
117
|
+
def unschedule_jobs(scheduler, job_ids)
|
118
|
+
job_ids.each { |job_id| scheduler.unschedule(job_id) }
|
119
|
+
end
|
115
120
|
|
116
|
-
# the actual tests
|
121
|
+
# the actual tests
|
117
122
|
|
118
|
-
before
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
after do #(:each) do
|
127
|
-
@trigger_queue = nil
|
128
|
-
end
|
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
|
129
130
|
|
130
|
-
|
131
|
-
|
132
|
-
end
|
131
|
+
after(:each) do
|
132
|
+
@trigger_queue = nil
|
133
|
+
end
|
133
134
|
|
134
|
-
it "
|
135
|
-
|
136
|
-
end
|
135
|
+
it "sustains frequent schedule/unschedule 'cron' jobs with same ids" do
|
136
|
+
schedule_unschedule_same_ids_spec(:cron)
|
137
|
+
end
|
137
138
|
|
138
|
-
it "
|
139
|
-
|
140
|
-
end
|
139
|
+
it "sustains frequent schedule/unschedule 'at' jobs with same ids" do
|
140
|
+
schedule_unschedule_same_ids_spec(:at)
|
141
|
+
end
|
141
142
|
|
142
|
-
it "
|
143
|
-
|
144
|
-
end
|
143
|
+
it "sustains frequent schedule/unschedule 'every' jobs same ids" do
|
144
|
+
schedule_unschedule_same_ids_spec(:every)
|
145
|
+
end
|
145
146
|
|
146
|
-
it "
|
147
|
-
|
148
|
-
end
|
147
|
+
it "sustains frequent schedule/unschedule 'cron' jobs with unique ids" do
|
148
|
+
schedule_unschedule_unique_ids_spec(:cron)
|
149
|
+
end
|
149
150
|
|
150
|
-
it "
|
151
|
-
|
152
|
-
end
|
151
|
+
it "sustains frequent schedule/unschedule 'at' jobs with unique ids" do
|
152
|
+
schedule_unschedule_unique_ids_spec(:at)
|
153
|
+
end
|
153
154
|
|
155
|
+
it "sustains frequent schedule/unschedule 'every' jobs unique ids" do
|
156
|
+
schedule_unschedule_unique_ids_spec(:every)
|
157
|
+
end
|
154
158
|
end
|
155
159
|
|
data/spec/timeout_spec.rb
CHANGED
@@ -5,42 +5,42 @@
|
|
5
5
|
# Sun May 3 15:44:28 JST 2009
|
6
6
|
#
|
7
7
|
|
8
|
-
require File.dirname(__FILE__)
|
8
|
+
require File.join(File.dirname(__FILE__), '/spec_base')
|
9
9
|
|
10
10
|
|
11
11
|
describe "#{SCHEDULER_CLASS} timeouts" do
|
12
12
|
|
13
|
-
before do
|
13
|
+
before(:each) do
|
14
14
|
@s = start_scheduler
|
15
15
|
end
|
16
|
-
after do
|
16
|
+
after(:each) do
|
17
17
|
stop_scheduler(@s)
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
20
|
+
it 'refuses to schedule a job with :timeout and :blocking' do
|
21
21
|
|
22
22
|
lambda {
|
23
23
|
@s.in '1s', :timeout => '3s', :blocking => true do
|
24
24
|
end
|
25
|
-
}.should
|
25
|
+
}.should raise_error(ArgumentError)
|
26
26
|
end
|
27
27
|
|
28
|
-
it '
|
28
|
+
it 'schedules a dedicated job for the timeout' do
|
29
29
|
|
30
30
|
@s.in '1s', :timeout => '3s' do
|
31
31
|
sleep 5
|
32
32
|
end
|
33
33
|
|
34
|
-
@s.jobs.size.should
|
34
|
+
@s.jobs.size.should == 1
|
35
35
|
|
36
36
|
# the timeout job is left
|
37
37
|
|
38
38
|
sleep 2
|
39
|
-
@s.jobs.size.should
|
40
|
-
@s.find_by_tag('timeout').size.should
|
39
|
+
@s.jobs.size.should == 1
|
40
|
+
@s.find_by_tag('timeout').size.should == 1
|
41
41
|
end
|
42
42
|
|
43
|
-
it '
|
43
|
+
it 'times out' do
|
44
44
|
|
45
45
|
var = nil
|
46
46
|
timedout = false
|
@@ -56,12 +56,12 @@ describe "#{SCHEDULER_CLASS} timeouts" do
|
|
56
56
|
|
57
57
|
sleep 4
|
58
58
|
|
59
|
-
var.should
|
60
|
-
@s.jobs.size.should
|
61
|
-
timedout.should
|
59
|
+
var.should == nil
|
60
|
+
@s.jobs.size.should == 0
|
61
|
+
timedout.should == true
|
62
62
|
end
|
63
63
|
|
64
|
-
it '
|
64
|
+
it 'dies silently if job finished before timeout' do
|
65
65
|
|
66
66
|
var = nil
|
67
67
|
timedout = false
|
@@ -76,12 +76,12 @@ describe "#{SCHEDULER_CLASS} timeouts" do
|
|
76
76
|
|
77
77
|
sleep 3
|
78
78
|
|
79
|
-
var.should
|
80
|
-
@s.jobs.size.should
|
81
|
-
timedout.should
|
79
|
+
var.should == true
|
80
|
+
@s.jobs.size.should == 0
|
81
|
+
timedout.should == false
|
82
82
|
end
|
83
83
|
|
84
|
-
it '
|
84
|
+
it 'does not timeout other jobs (in case of every)' do
|
85
85
|
|
86
86
|
timeouts = []
|
87
87
|
|
@@ -96,11 +96,11 @@ describe "#{SCHEDULER_CLASS} timeouts" do
|
|
96
96
|
|
97
97
|
sleep 5
|
98
98
|
|
99
|
-
timeouts.size.should
|
100
|
-
timeouts.each { |to| (to * 10).to_i.should
|
99
|
+
timeouts.size.should == 3
|
100
|
+
timeouts.each { |to| (to * 10).to_i.should == 16 }
|
101
101
|
end
|
102
102
|
|
103
|
-
it '
|
103
|
+
it 'points to their "parent" job' do
|
104
104
|
|
105
105
|
@s.in '1s', :timeout => '3s', :job_id => 'nada' do
|
106
106
|
sleep 4
|
@@ -108,10 +108,10 @@ describe "#{SCHEDULER_CLASS} timeouts" do
|
|
108
108
|
|
109
109
|
sleep 2
|
110
110
|
|
111
|
-
@s.jobs.values.first.parent.job_id.should
|
111
|
+
@s.jobs.values.first.parent.job_id.should == 'nada'
|
112
112
|
end
|
113
113
|
|
114
|
-
it '
|
114
|
+
it 'does not survive their job' do
|
115
115
|
|
116
116
|
@s.in '1s', :timeout => '3s' do
|
117
117
|
sleep 0.100
|
@@ -119,7 +119,7 @@ describe "#{SCHEDULER_CLASS} timeouts" do
|
|
119
119
|
|
120
120
|
sleep 2
|
121
121
|
|
122
|
-
@s.jobs.size.should
|
122
|
+
@s.jobs.size.should == 0
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|