recurrent 0.2.0 → 0.3.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/.gitignore +2 -0
- data/.zenflow +10 -0
- data/CHANGELOG.md +7 -0
- data/README.markdown +9 -0
- data/lib/recurrent.rb +1 -0
- data/lib/recurrent/configuration.rb +1 -1
- data/lib/recurrent/scheduler.rb +23 -23
- data/lib/recurrent/task.rb +36 -7
- data/lib/recurrent/task_collection.rb +72 -0
- data/lib/recurrent/version.rb +1 -1
- data/lib/recurrent/worker.rb +5 -5
- data/spec/scheduler_spec.rb +7 -26
- data/spec/task_collection_spec.rb +172 -0
- data/spec/task_spec.rb +60 -8
- metadata +9 -4
data/.gitignore
CHANGED
data/.zenflow
ADDED
data/CHANGELOG.md
ADDED
data/README.markdown
CHANGED
@@ -121,6 +121,15 @@ How long to wait before killing tasks that are still running.
|
|
121
121
|
|
122
122
|
configure.wait_for_running_tasks_on_exit_for = 10.seconds
|
123
123
|
|
124
|
+
###Limiting the number of concurrent tasks
|
125
|
+
To limit the number of tasks that run simultaneously:
|
126
|
+
|
127
|
+
configure.maximum_concurrent_tasks = 5
|
128
|
+
|
129
|
+
This will run up to the above number of tasks simultaneously, other tasks will wait until a slot opens up before executing. Tasks that run more frequently, e.g. once a minute, will have precedence over tasks that run once an hour or day etc.
|
130
|
+
|
131
|
+
|
132
|
+
|
124
133
|
Submitting an Issue
|
125
134
|
-------------------
|
126
135
|
We use the [GitHub issue tracker](http://github.com/zencoder/recurrent/issues) to track bugs and
|
data/lib/recurrent.rb
CHANGED
data/lib/recurrent/scheduler.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
module Recurrent
|
2
2
|
class Scheduler
|
3
3
|
|
4
|
-
attr_accessor :tasks, :logger
|
4
|
+
attr_accessor :tasks, :logger, :executing_tasks, :mutex
|
5
5
|
|
6
6
|
def initialize(task_file=nil)
|
7
|
-
@tasks =
|
7
|
+
@tasks = TaskCollection.new
|
8
8
|
identifier = "host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
|
9
9
|
@logger = Logger.new(identifier)
|
10
|
+
@mutex = Mutex.new
|
11
|
+
@executing_tasks = 0
|
10
12
|
eval(File.read(task_file)) if task_file
|
11
13
|
end
|
12
14
|
|
@@ -87,30 +89,16 @@ module Recurrent
|
|
87
89
|
|
88
90
|
def every(frequency, key, options={}, &block)
|
89
91
|
logger.info "Adding Task: #{key}"
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
task = Task.new(:name => key,
|
93
|
+
:schedule => create_schedule(key, frequency, options[:start_time]),
|
94
|
+
:action => block,
|
95
|
+
:save => options[:save],
|
96
|
+
:logger => logger,
|
97
|
+
:scheduler => self)
|
98
|
+
@tasks.add_or_update(task)
|
95
99
|
logger.info "| #{key} added to Scheduler"
|
96
100
|
end
|
97
101
|
|
98
|
-
def next_task_time
|
99
|
-
tasks.map { |task| task.next_occurrence }.sort.first
|
100
|
-
end
|
101
|
-
|
102
|
-
def running_tasks
|
103
|
-
tasks.select do |task|
|
104
|
-
task.running?
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def tasks_at_time(time)
|
109
|
-
tasks.select do |task|
|
110
|
-
task.next_occurrence == time
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
102
|
def use_saved_schedule_if_rules_match(saved_schedule, new_schedule)
|
115
103
|
if new_schedule.has_same_rules? saved_schedule
|
116
104
|
logger.info "| Schedule matches a saved schedule, using saved schedule."
|
@@ -121,6 +109,18 @@ module Recurrent
|
|
121
109
|
end
|
122
110
|
end
|
123
111
|
|
112
|
+
def increment_executing_tasks
|
113
|
+
mutex.synchronize do
|
114
|
+
@executing_tasks += 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def decrement_executing_tasks
|
119
|
+
mutex.synchronize do
|
120
|
+
@executing_tasks -= 1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
124
|
def self.define_frequencies(*frequencies)
|
125
125
|
frequencies.each do |frequency|
|
126
126
|
method_name = frequency == :day ? :daily? : :"#{frequency}ly?"
|
data/lib/recurrent/task.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Recurrent
|
2
|
+
class TooManyExecutingTasks < StandardError; end
|
3
|
+
|
2
4
|
class Task
|
3
|
-
attr_accessor :action, :name, :logger, :save, :schedule, :thread
|
5
|
+
attr_accessor :action, :name, :logger, :save, :schedule, :thread, :scheduler
|
4
6
|
|
5
7
|
def initialize(options={})
|
6
8
|
@name = options[:name]
|
@@ -8,6 +10,7 @@ module Recurrent
|
|
8
10
|
@action = options[:action]
|
9
11
|
@save = options[:save]
|
10
12
|
@logger = options[:logger]
|
13
|
+
@scheduler = options[:scheduler]
|
11
14
|
Configuration.save_task_schedule.call(name, schedule) if Configuration.save_task_schedule
|
12
15
|
end
|
13
16
|
|
@@ -16,13 +19,15 @@ module Recurrent
|
|
16
19
|
@thread = Thread.new do
|
17
20
|
Thread.current["execution_time"] = execution_time
|
18
21
|
begin
|
19
|
-
if Configuration.
|
20
|
-
|
21
|
-
return_value = action.call(previous_value)
|
22
|
+
if Configuration.maximum_concurrent_tasks.present?
|
23
|
+
limit_execution_to_max_concurrency
|
22
24
|
else
|
23
|
-
|
25
|
+
call_action
|
24
26
|
end
|
25
|
-
|
27
|
+
rescue TooManyExecutingTasks
|
28
|
+
scheduler.decrement_executing_tasks
|
29
|
+
sleep(0.1)
|
30
|
+
retry
|
26
31
|
rescue => e
|
27
32
|
logger.warn("#{name} - #{e.message}")
|
28
33
|
logger.warn(e.backtrace)
|
@@ -30,10 +35,34 @@ module Recurrent
|
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
38
|
+
def limit_execution_to_max_concurrency
|
39
|
+
if (scheduler.increment_executing_tasks <= Configuration.maximum_concurrent_tasks) && task_is_next_in_line?
|
40
|
+
call_action
|
41
|
+
scheduler.decrement_executing_tasks
|
42
|
+
else
|
43
|
+
raise TooManyExecutingTasks
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def task_is_next_in_line?
|
48
|
+
self == scheduler.tasks.next_for_execution_at_time(Thread.current["execution_time"])
|
49
|
+
end
|
50
|
+
|
51
|
+
def call_action
|
52
|
+
if Configuration.load_task_return_value && action.arity == 1
|
53
|
+
previous_value = Configuration.load_task_return_value.call(name)
|
54
|
+
|
55
|
+
return_value = action.call(previous_value)
|
56
|
+
else
|
57
|
+
return_value = action.call
|
58
|
+
end
|
59
|
+
save_results(return_value) if save?
|
60
|
+
end
|
61
|
+
|
33
62
|
def handle_still_running(current_time)
|
34
63
|
logger.info "#{name}: Execution from #{thread['execution_time'].to_s(:seconds)} still running, aborting this execution."
|
35
64
|
if Configuration.handle_slow_task
|
36
|
-
Configuration.handle_slow_task.call(name, current_time)
|
65
|
+
Configuration.handle_slow_task.call(name, current_time, thread['execution_time'])
|
37
66
|
end
|
38
67
|
end
|
39
68
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Recurrent
|
2
|
+
class TaskCollection
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@mutex = Mutex.new
|
6
|
+
@tasks = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_or_update(new_task)
|
10
|
+
@mutex.synchronize do
|
11
|
+
old_task_index = @tasks.index {|task| task.name == new_task.name }
|
12
|
+
if old_task_index
|
13
|
+
@tasks[old_task_index].schedule = new_task.schedule
|
14
|
+
@tasks[old_task_index].action = new_task.action
|
15
|
+
else
|
16
|
+
@tasks << new_task
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_for_execution_at_time(time)
|
22
|
+
@mutex.synchronize do
|
23
|
+
tasks_running_at_time = @tasks.select {|task| task.running? && task.thread['execution_time'] == time }
|
24
|
+
TaskCollection.sort_by_frequency(tasks_running_at_time).first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_execution_time
|
29
|
+
@mutex.synchronize do
|
30
|
+
@tasks.map { |task| task.next_occurrence }.sort.first
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove(name)
|
35
|
+
@mutex.synchronize do
|
36
|
+
@tasks.reject! {|task| task.name == name }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def running
|
41
|
+
@mutex.synchronize do
|
42
|
+
@tasks.select {|task| task.running? }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def scheduled_to_execute_at(time, opts={})
|
47
|
+
@mutex.synchronize do
|
48
|
+
current_tasks = @tasks.select {|task| task.next_occurrence == time }
|
49
|
+
if opts[:sort_by_frequency]
|
50
|
+
TaskCollection.sort_by_frequency(current_tasks)
|
51
|
+
else
|
52
|
+
current_tasks
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.sort_by_frequency(task_list)
|
58
|
+
task_list.sort_by do |task|
|
59
|
+
task.schedule.rrules.sort_by do |rule|
|
60
|
+
rule.frequency_in_seconds
|
61
|
+
end.first.frequency_in_seconds
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def method_missing(id, *args, &block)
|
67
|
+
@mutex.synchronize do
|
68
|
+
@tasks.send(id, *args, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/recurrent/version.rb
CHANGED
data/lib/recurrent/worker.rb
CHANGED
@@ -40,8 +40,8 @@ module Recurrent
|
|
40
40
|
|
41
41
|
def execute
|
42
42
|
loop do
|
43
|
-
execution_time = scheduler.
|
44
|
-
tasks_to_execute = scheduler.
|
43
|
+
execution_time = scheduler.tasks.next_execution_time
|
44
|
+
tasks_to_execute = scheduler.tasks.scheduled_to_execute_at(execution_time, :sort_by_frequency => !!Configuration.maximum_concurrent_tasks)
|
45
45
|
|
46
46
|
wait_for_running_tasks && break if $exit
|
47
47
|
|
@@ -80,12 +80,12 @@ module Recurrent
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def wait_for_running_tasks_for(seconds)
|
83
|
-
while scheduler.
|
83
|
+
while scheduler.tasks.running.any? do
|
84
84
|
logger.info "Killing running tasks in #{seconds.inspect}."
|
85
85
|
seconds -= 1
|
86
86
|
sleep(1)
|
87
87
|
if seconds == 0
|
88
|
-
scheduler.
|
88
|
+
scheduler.tasks.running.each do |task|
|
89
89
|
logger.info "Killing #{task.name}."
|
90
90
|
task.thread = nil unless task.thread.try(:kill).try(:alive?)
|
91
91
|
end
|
@@ -95,7 +95,7 @@ module Recurrent
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def wait_for_running_tasks_indefinitely
|
98
|
-
if task = scheduler.
|
98
|
+
if task = scheduler.tasks.running.first
|
99
99
|
logger.info "Waiting for #{task.name} to finish."
|
100
100
|
task.thread.try(:join)
|
101
101
|
wait_for_running_tasks_indefinitely
|
data/spec/scheduler_spec.rb
CHANGED
@@ -195,36 +195,17 @@ module Recurrent
|
|
195
195
|
describe "#next_task_time" do
|
196
196
|
context "when there are multiple tasks" do
|
197
197
|
it "should return the soonest time at which a task is scheduled" do
|
198
|
-
task1 = stub('task1')
|
198
|
+
task1 = stub('task1', :name => :task1)
|
199
199
|
task1.stub(:next_occurrence).and_return(10.minutes.from_now)
|
200
|
-
task2 = stub('task2')
|
200
|
+
task2 = stub('task2', :name => :task2)
|
201
201
|
task2.stub(:next_occurrence).and_return(5.minutes.from_now)
|
202
|
-
task3 = stub('task3')
|
202
|
+
task3 = stub('task3', :name => :task3)
|
203
203
|
task3.stub(:next_occurrence).and_return(15.minutes.from_now)
|
204
204
|
schedule = Scheduler.new
|
205
|
-
schedule.tasks
|
206
|
-
schedule.tasks
|
207
|
-
schedule.tasks
|
208
|
-
schedule.
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
describe "#tasks_at_time" do
|
214
|
-
context "when there are multiple tasks" do
|
215
|
-
it "should return all the tasks whose next_occurrence is at the specified time" do
|
216
|
-
in_five_minutes = 5.minutes.from_now
|
217
|
-
task1 = stub('task1')
|
218
|
-
task1.stub(:next_occurrence).and_return(in_five_minutes)
|
219
|
-
task2 = stub('task2')
|
220
|
-
task2.stub(:next_occurrence).and_return(10.minutes.from_now)
|
221
|
-
task3 = stub('task3')
|
222
|
-
task3.stub(:next_occurrence).and_return(in_five_minutes)
|
223
|
-
schedule = Scheduler.new
|
224
|
-
schedule.tasks << task1
|
225
|
-
schedule.tasks << task2
|
226
|
-
schedule.tasks << task3
|
227
|
-
schedule.tasks_at_time(in_five_minutes).should =~ [task1, task3]
|
205
|
+
schedule.tasks.add_or_update(task1)
|
206
|
+
schedule.tasks.add_or_update(task2)
|
207
|
+
schedule.tasks.add_or_update(task3)
|
208
|
+
schedule.tasks.next_execution_time.should == task2.next_occurrence
|
228
209
|
end
|
229
210
|
end
|
230
211
|
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Recurrent
|
4
|
+
describe TaskCollection do
|
5
|
+
before(:all) do
|
6
|
+
Configuration.logging = "quiet"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#add_or_update_task" do
|
10
|
+
before(:each) do
|
11
|
+
@tasks = TaskCollection.new
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when adding a new task" do
|
15
|
+
before(:each) do
|
16
|
+
@task = Task.new(:name => :new_task)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "adds the task to the list of tasks" do
|
20
|
+
@tasks.size.should == 0
|
21
|
+
@tasks.add_or_update(@task)
|
22
|
+
@tasks.size.should == 1
|
23
|
+
@tasks.first.should == @task
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when updating a task" do
|
28
|
+
before(:each) do
|
29
|
+
@original_frequency = Scheduler.new.create_schedule(:task, 5.seconds)
|
30
|
+
@original_action = proc { "I am the original task!" }
|
31
|
+
@original_task = Task.new(:name => :task,
|
32
|
+
:frequency => @original_frequency,
|
33
|
+
:action => @original_action)
|
34
|
+
|
35
|
+
@new_frequency = Scheduler.new.create_schedule(:task, 10.seconds)
|
36
|
+
@new_action = proc { "I am the new task!" }
|
37
|
+
@new_task = Task.new(:name => :task,
|
38
|
+
:frequency => @new_frequency,
|
39
|
+
:action => @new_action)
|
40
|
+
@tasks << @original_task
|
41
|
+
end
|
42
|
+
|
43
|
+
context "before updating the task" do
|
44
|
+
it "has one task" do
|
45
|
+
@tasks.size.should == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
it "has the original task's action" do
|
49
|
+
@tasks.first.action.call.should == "I am the original task!"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "has the original task's frequency" do
|
53
|
+
@tasks.first.schedule.should == @original_schedule
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "after updating the task" do
|
58
|
+
before(:each) do
|
59
|
+
@tasks.add_or_update(@new_task)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "has one task" do
|
63
|
+
@tasks.size.should == 1
|
64
|
+
end
|
65
|
+
|
66
|
+
it "has the new task's action" do
|
67
|
+
@tasks.first.action.call.should == "I am the new task!"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "has the new task's frequency" do
|
71
|
+
@tasks.first.schedule.should == @new_schedule
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#remove_task" do
|
78
|
+
context "A TaskCollection with 3 tasks" do
|
79
|
+
before(:each) do
|
80
|
+
@tasks = TaskCollection.new
|
81
|
+
@task1 = Task.new(:name => :task1)
|
82
|
+
@task2 = Task.new(:name => :task2)
|
83
|
+
@task3 = Task.new(:name => :task3)
|
84
|
+
@tasks.add_or_update(@task1)
|
85
|
+
@tasks.add_or_update(@task2)
|
86
|
+
@tasks.add_or_update(@task3)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "has 3 tasks" do
|
90
|
+
@tasks.size.should == 3
|
91
|
+
(@tasks | []).should == [@task1, @task2, @task3]
|
92
|
+
end
|
93
|
+
|
94
|
+
context "that removes a task" do
|
95
|
+
before(:each) do
|
96
|
+
@tasks.remove(:task2)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "has 2 tasks" do
|
100
|
+
@tasks.size.should == 2
|
101
|
+
(@tasks | []).should == [@task1, @task3]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#scheduled_to_execute_at" do
|
108
|
+
context "when there are multiple tasks" do
|
109
|
+
it "should return all the tasks whose next_occurrence is at the specified time" do
|
110
|
+
task_1_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
111
|
+
task_1_schedule.add_recurrence_rule(IceCube::Rule.minutely(10))
|
112
|
+
|
113
|
+
task_2_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
114
|
+
task_2_schedule.add_recurrence_rule(IceCube::Rule.minutely(5))
|
115
|
+
|
116
|
+
task_3_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
117
|
+
task_3_schedule.add_recurrence_rule(IceCube::Rule.minutely(1))
|
118
|
+
|
119
|
+
current_time = Time.utc(2012, 1, 10, 14, 4)
|
120
|
+
Timecop.freeze(current_time)
|
121
|
+
|
122
|
+
task1 = Task.new(:name => 'task1',
|
123
|
+
:schedule => task_1_schedule)
|
124
|
+
task2 = Task.new(:name => 'task2',
|
125
|
+
:schedule => task_2_schedule)
|
126
|
+
task3 = Task.new(:name => 'task3',
|
127
|
+
:schedule => task_3_schedule)
|
128
|
+
tasks = TaskCollection.new
|
129
|
+
tasks.add_or_update(task1)
|
130
|
+
tasks.add_or_update(task2)
|
131
|
+
tasks.add_or_update(task3)
|
132
|
+
|
133
|
+
tasks.scheduled_to_execute_at(Time.utc(2012, 1, 10, 14, 5)).should =~ [task2, task3]
|
134
|
+
Timecop.return
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when :sort_by_frequency => true is passed as an option" do
|
138
|
+
it "should return the sorted by frequency, most frequent first" do
|
139
|
+
task_1_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
140
|
+
task_1_schedule.add_recurrence_rule(IceCube::Rule.minutely(10))
|
141
|
+
|
142
|
+
task_2_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
143
|
+
task_2_schedule.add_recurrence_rule(IceCube::Rule.minutely(5))
|
144
|
+
|
145
|
+
task_3_schedule = IceCube::Schedule.new(Time.utc(2012, 1, 10))
|
146
|
+
task_3_schedule.add_recurrence_rule(IceCube::Rule.minutely(1))
|
147
|
+
|
148
|
+
current_time = Time.utc(2012, 1, 10, 14, 4)
|
149
|
+
Timecop.freeze(current_time)
|
150
|
+
|
151
|
+
task1 = Task.new(:name => 'task1',
|
152
|
+
:schedule => task_1_schedule)
|
153
|
+
task2 = Task.new(:name => 'task2',
|
154
|
+
:schedule => task_2_schedule)
|
155
|
+
task3 = Task.new(:name => 'task3',
|
156
|
+
:schedule => task_3_schedule)
|
157
|
+
tasks = TaskCollection.new
|
158
|
+
tasks.add_or_update(task1)
|
159
|
+
tasks.add_or_update(task2)
|
160
|
+
tasks.add_or_update(task3)
|
161
|
+
|
162
|
+
first_task, second_task = *tasks.scheduled_to_execute_at(Time.utc(2012, 1, 10, 14, 5), :sort_by_frequency => true)
|
163
|
+
first_task.should == task3
|
164
|
+
second_task.should == task2
|
165
|
+
Timecop.return
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
data/spec/task_spec.rb
CHANGED
@@ -72,13 +72,6 @@ module Recurrent
|
|
72
72
|
Configuration.load_task_return_value = nil
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
76
|
-
context "load_task_return_value is not configured" do
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
75
|
end
|
83
76
|
|
84
77
|
describe "#next_occurrence" do
|
@@ -129,7 +122,7 @@ module Recurrent
|
|
129
122
|
|
130
123
|
it "logs that the task is still running and calls the method" do
|
131
124
|
@task.logger.should_receive(:info).with("handle_still_running_test: Execution from #{@executing_task_time.to_s(:seconds)} still running, aborting this execution.")
|
132
|
-
Configuration.handle_slow_task.should_receive(:call).with('handle_still_running_test', @current_time)
|
125
|
+
Configuration.handle_slow_task.should_receive(:call).with('handle_still_running_test', @current_time, @executing_task_time)
|
133
126
|
@task.handle_still_running(@current_time)
|
134
127
|
end
|
135
128
|
|
@@ -219,7 +212,66 @@ module Recurrent
|
|
219
212
|
t.running?.should be_false
|
220
213
|
end
|
221
214
|
end
|
215
|
+
end
|
222
216
|
|
217
|
+
describe "Restricting to a maximum number of concurrent tasks" do
|
218
|
+
before(:each) do
|
219
|
+
scheduler = Scheduler.new
|
220
|
+
schedule = IceCube::Schedule.new(Time.now.utc.beginning_of_day)
|
221
|
+
schedule.add_recurrence_rule IceCube::Rule.minutely(1)
|
222
|
+
@task1 = Task.new(:name => 'task1',
|
223
|
+
:scheduler => scheduler,
|
224
|
+
:schedule => schedule.clone,
|
225
|
+
:action => lambda { sleep(1)})
|
226
|
+
@task2 = Task.new(:name => 'task2',
|
227
|
+
:scheduler => scheduler,
|
228
|
+
:schedule => schedule.clone,
|
229
|
+
:action => lambda { sleep(1) })
|
230
|
+
|
231
|
+
scheduler.tasks.add_or_update(@task1)
|
232
|
+
scheduler.tasks.add_or_update(@task2)
|
233
|
+
end
|
234
|
+
|
235
|
+
describe "when there is no concurrent task limit set" do
|
236
|
+
it "should run all tasks at the same time" do
|
237
|
+
current_time = Time.now
|
238
|
+
[@task1, @task2].each do |task|
|
239
|
+
task.execute(current_time)
|
240
|
+
end
|
241
|
+
|
242
|
+
[@task1, @task2].each {|task| task.thread.join }
|
243
|
+
|
244
|
+
finished_at = Time.now
|
245
|
+
elapsed = finished_at - current_time
|
246
|
+
|
247
|
+
elapsed.round.should == 1.seconds
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "when there is a maximum concurrency limit set" do
|
252
|
+
before(:each) do
|
253
|
+
Configuration.maximum_concurrent_tasks = 1
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should run only up to the number of tasks specified at once" do
|
257
|
+
current_time = Time.now
|
258
|
+
[@task1, @task2].each do |task|
|
259
|
+
task.execute(current_time)
|
260
|
+
end
|
261
|
+
|
262
|
+
[@task1, @task2].each {|task| task.thread.join }
|
263
|
+
|
264
|
+
finished_at = Time.now
|
265
|
+
elapsed = finished_at - current_time
|
266
|
+
|
267
|
+
elapsed.round.should == 2.seconds
|
268
|
+
end
|
269
|
+
|
270
|
+
after(:each) do
|
271
|
+
Configuration.maximum_concurrent_tasks = nil
|
272
|
+
end
|
273
|
+
end
|
223
274
|
end
|
275
|
+
|
224
276
|
end
|
225
277
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recurrent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Adam Kittelson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-28 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: ice_cube
|
@@ -158,6 +158,8 @@ files:
|
|
158
158
|
- .autotest
|
159
159
|
- .gitignore
|
160
160
|
- .rspec
|
161
|
+
- .zenflow
|
162
|
+
- CHANGELOG.md
|
161
163
|
- Gemfile
|
162
164
|
- LICENSE
|
163
165
|
- README.markdown
|
@@ -170,12 +172,14 @@ files:
|
|
170
172
|
- lib/recurrent/logger.rb
|
171
173
|
- lib/recurrent/scheduler.rb
|
172
174
|
- lib/recurrent/task.rb
|
175
|
+
- lib/recurrent/task_collection.rb
|
173
176
|
- lib/recurrent/version.rb
|
174
177
|
- lib/recurrent/worker.rb
|
175
178
|
- recurrent.gemspec
|
176
179
|
- spec/logger_spec.rb
|
177
180
|
- spec/scheduler_spec.rb
|
178
181
|
- spec/spec_helper.rb
|
182
|
+
- spec/task_collection_spec.rb
|
179
183
|
- spec/task_spec.rb
|
180
184
|
- spec/worker_spec.rb
|
181
185
|
homepage: http://github.com/zencoder/recurrent
|
@@ -215,5 +219,6 @@ test_files:
|
|
215
219
|
- spec/logger_spec.rb
|
216
220
|
- spec/scheduler_spec.rb
|
217
221
|
- spec/spec_helper.rb
|
222
|
+
- spec/task_collection_spec.rb
|
218
223
|
- spec/task_spec.rb
|
219
224
|
- spec/worker_spec.rb
|