scheduled_job 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +16 -4
- data/lib/scheduled_job.rb +31 -10
- data/lib/scheduled_job/version.rb +1 -1
- data/lib/tasks/jobs.rb +1 -11
- data/spec/lib/scheduled_job_spec.rb +55 -3
- data/spec/spec_helper.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8992a23a08c1f37078a3f9a41e3e79807ccdcd86
|
4
|
+
data.tar.gz: f0fa25cd3bd9e27fb4b827a76e0c581f558ea451
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bdada7d253e3124f3ef9ac9e303015a80082a8e3ef3ba61a2f34a30c9b6de9ee3e9ad238484e2d280b4b1bad61efc07144611f56f942b18d80c53d481a98b4e
|
7
|
+
data.tar.gz: b7da6a1ed79dab91050c9bec04668117b37ec2688f7726aeca25b99b8f5f76590ec2269b454bc68b218b0d24e02a831876aaca816804dd62b75d1aff379e2ea4
|
data/CHANGELOG.md
CHANGED
@@ -1,2 +1,24 @@
|
|
1
1
|
# 0.0.2
|
2
2
|
Initial public release
|
3
|
+
|
4
|
+
# 0.0.3
|
5
|
+
Removing hard coded Delayed Job dependencies
|
6
|
+
|
7
|
+
# 0.0.4
|
8
|
+
Adds one second back off to the fast mode
|
9
|
+
|
10
|
+
# 0.0.5
|
11
|
+
Removed the compulsory .configure call
|
12
|
+
|
13
|
+
# 0.0.7
|
14
|
+
Fixes substring job lookup bug
|
15
|
+
|
16
|
+
# 0.0.8
|
17
|
+
Bug fix for multiple scheduling potential
|
18
|
+
|
19
|
+
# 0.0.9
|
20
|
+
Adds local db for development
|
21
|
+
|
22
|
+
# 0.1.0
|
23
|
+
Makes available the `rake jobs:reschedule` rake task
|
24
|
+
Adds a config to allow the scheduling for more than one instance of the same job
|
data/README.md
CHANGED
@@ -92,7 +92,7 @@ First you must include the scheduled job module in any DelayedJob that needs to
|
|
92
92
|
include ::ScheduledJob
|
93
93
|
```
|
94
94
|
|
95
|
-
Then you need to say what the job is
|
95
|
+
Then you need to say what the job is actually to do. This is done by implementing the perform method.
|
96
96
|
|
97
97
|
```ruby
|
98
98
|
def perform
|
@@ -100,7 +100,7 @@ def perform
|
|
100
100
|
end
|
101
101
|
```
|
102
102
|
|
103
|
-
|
103
|
+
Finally we need to write the logic for when we want the job to run. This is done by implementing the time_to_recur method which is passed the time the job last completed as its parameter.
|
104
104
|
|
105
105
|
```ruby
|
106
106
|
def self.time_to_recur(last_run_at)
|
@@ -108,6 +108,18 @@ def self.time_to_recur(last_run_at)
|
|
108
108
|
end
|
109
109
|
```
|
110
110
|
|
111
|
+
Recently added is the new jobs configuration. This adds two major new benefits. Firstly this will allow you to define jobs that are allow to run in multiple instances. Say for example that there should always be two instances of a given job running. This can now be defined using the following configuration:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
ScheduledJob.configure do |config|
|
115
|
+
config.jobs = {
|
116
|
+
MyAwesomeParallelJobClass => { count: 2 }
|
117
|
+
}
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
This lets scheduled job know that it is OK to have two pending job instances for MyAwesomeParallelJobClass in the delayed job table. Additionally by using this configuration you also get access to the new reschedule rake task for free. ScheduledJob now adds `rake jobs:reschedule`. This will loop through your jobs configuration and automatically call schedule job up to the number of times you intend the jobs pending. This is useful for heavy users of scheduled job to "prime" your database with your recurring jobs. Note you can still add jobs to this configuration that you do not want any instances of by setting the count to 0. This is useful if you are looking to access all classes you have that you have registered with scheduled job.
|
122
|
+
|
111
123
|
There are also callbacks that are available using ScheduledJob. These allow you to hook into the scheduling life cycle. Also note that as this uses DelayedJob under the hood all of the delayed job callbacks are still available for use.
|
112
124
|
|
113
125
|
These can be defined when configuring the gem for you application on the configure block:
|
@@ -126,7 +138,7 @@ config.before_callback = -> (job, scheduled_job) do
|
|
126
138
|
end
|
127
139
|
```
|
128
140
|
|
129
|
-
The success_callback is called on
|
141
|
+
The success_callback is called on successful completion of the job and is also passed the delayed job object and the scheduled job instance.
|
130
142
|
|
131
143
|
```ruby
|
132
144
|
config.success_callback = -> (job, _) do
|
@@ -134,7 +146,7 @@ config.success_callback = -> (job, _) do
|
|
134
146
|
end
|
135
147
|
```
|
136
148
|
|
137
|
-
Then there is the fast mode. This is checked prior to scheduling another run of your job e.g. after a job has completed. This allows you to override the scheduling logic and ask the job to run
|
149
|
+
Then there is the fast mode. This is checked prior to scheduling another run of your job e.g. after a job has completed. This allows you to override the scheduling logic and ask the job to run immediately. This is passed the scheduled job class. This means you can have state stored elsewhere to change the scheduling without having to modify the code. This could be getting an array from a database for example:
|
138
150
|
|
139
151
|
```ruby
|
140
152
|
config.fast_mode = -> (job) do
|
data/lib/scheduled_job.rb
CHANGED
@@ -5,6 +5,9 @@ require 'delayed_job_active_record'
|
|
5
5
|
require File.dirname(__FILE__) + '/tasks/jobs.rb'
|
6
6
|
|
7
7
|
module ScheduledJob
|
8
|
+
class ConfigError < StandardError
|
9
|
+
end
|
10
|
+
|
8
11
|
class << self
|
9
12
|
attr_writer :config
|
10
13
|
def config
|
@@ -12,32 +15,43 @@ module ScheduledJob
|
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
18
|
+
def self.reschedule
|
19
|
+
config.jobs.each do |job, options|
|
20
|
+
options[:count].times do
|
21
|
+
job.schedule_job
|
22
|
+
end
|
23
|
+
end if config.jobs
|
24
|
+
end
|
25
|
+
|
15
26
|
def self.logger
|
16
27
|
self.config.logger
|
17
28
|
end
|
18
29
|
|
19
30
|
def self.configure
|
20
31
|
yield(config)
|
32
|
+
validate_job_hash(config.jobs) if config.jobs
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.validate_job_hash(jobs)
|
36
|
+
jobs.each do |klass, options|
|
37
|
+
raise ConfigError.new("Jobs config found invalid class: #{klass}") unless klass.class == Class
|
38
|
+
raise ConfigError.new("Jobs config found invalid job count: #{options[:count]}") unless options[:count].to_i >= 0
|
39
|
+
end
|
21
40
|
end
|
22
41
|
|
23
42
|
class Config
|
24
|
-
attr_accessor :logger, :before_callback, :success_callback, :fast_mode
|
43
|
+
attr_accessor :logger, :before_callback, :success_callback, :fast_mode, :jobs
|
25
44
|
|
26
45
|
def initialize
|
46
|
+
@jobs ||= {}
|
27
47
|
@logger = Logger.new(STDOUT)
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
31
51
|
def self.included(base)
|
32
|
-
@classes ||= []
|
33
|
-
@classes << base.name.constantize
|
34
52
|
base.extend ScheduledJobClassMethods
|
35
53
|
end
|
36
54
|
|
37
|
-
def self.classes
|
38
|
-
@classes
|
39
|
-
end
|
40
|
-
|
41
55
|
def before(job)
|
42
56
|
callback = ScheduledJob.config.before_callback
|
43
57
|
callback.call(job, self) if callback
|
@@ -65,7 +79,7 @@ module ScheduledJob
|
|
65
79
|
# This method should be called when scheduling a recurring job as it checks to ensure no
|
66
80
|
# other instances of the job are already running.
|
67
81
|
def schedule_job(job = nil)
|
68
|
-
|
82
|
+
if can_schedule_job?(job)
|
69
83
|
callback = ScheduledJob.config.fast_mode
|
70
84
|
in_fast_mode = callback ? callback.call(self) : false
|
71
85
|
|
@@ -84,13 +98,20 @@ module ScheduledJob
|
|
84
98
|
(base + Random.new.rand((-1 * random_delta)..random_delta)).minutes
|
85
99
|
end
|
86
100
|
|
87
|
-
def
|
101
|
+
def can_schedule_job?(job = nil)
|
88
102
|
conditions = ['(handler like ? OR handler like ?) AND failed_at IS NULL', "%:#{self.name} %", "%:#{self.name}\n%"]
|
89
103
|
unless job.blank?
|
90
104
|
conditions[0] << " AND id != ?"
|
91
105
|
conditions << job.id
|
92
106
|
end
|
93
|
-
Delayed::Job.
|
107
|
+
job_count = Delayed::Job.where(conditions).count
|
108
|
+
intended_job_count = 1
|
109
|
+
|
110
|
+
if ScheduledJob.config.jobs && ScheduledJob.config.jobs[self.name]
|
111
|
+
intended_job_count = ScheduledJob.config.jobs[self.name][:count]
|
112
|
+
end
|
113
|
+
|
114
|
+
job_count < intended_job_count
|
94
115
|
end
|
95
116
|
|
96
117
|
def run_duration_threshold
|
data/lib/tasks/jobs.rb
CHANGED
@@ -5,20 +5,10 @@ module ScheduledJob
|
|
5
5
|
include Rake::DSL if defined? Rake::DSL
|
6
6
|
|
7
7
|
def install_tasks
|
8
|
-
def check_schedule_job(job)
|
9
|
-
if job.respond_to?(:time_to_recur)
|
10
|
-
job.schedule_job
|
11
|
-
else
|
12
|
-
job.descendants.each { |j| check_schedule_job(j) }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
8
|
namespace:jobs do
|
17
9
|
desc "Will schedule all scheduled jobs"
|
18
10
|
task :reschedule => :environment do
|
19
|
-
ScheduledJob.
|
20
|
-
check_schedule_job(job)
|
21
|
-
end
|
11
|
+
ScheduledJob.reschedule
|
22
12
|
end
|
23
13
|
end
|
24
14
|
end
|
@@ -20,6 +20,7 @@ class Test < UnderTest
|
|
20
20
|
end
|
21
21
|
|
22
22
|
describe ScheduledJob do
|
23
|
+
before { ScheduledJob.configure { |config| config.jobs = nil } }
|
23
24
|
|
24
25
|
let(:under_test) { UnderTest.new }
|
25
26
|
|
@@ -37,8 +38,59 @@ describe ScheduledJob do
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
describe 'job config' do
|
42
|
+
it 'takes a jobs hash config' do
|
43
|
+
expect {
|
44
|
+
ScheduledJob.configure do |config|
|
45
|
+
config.jobs = {
|
46
|
+
UnderTest => { count: 1 }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
}.not_to raise_error
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'validates the job hash' do
|
53
|
+
it 'detects an bad job class' do
|
54
|
+
expect {
|
55
|
+
ScheduledJob.configure do |config|
|
56
|
+
config.jobs = {
|
57
|
+
'UnderTest' => { count: 1 }
|
58
|
+
}
|
59
|
+
end
|
60
|
+
}.to raise_error(ScheduledJob::ConfigError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'detects a bad job count' do
|
64
|
+
expect {
|
65
|
+
ScheduledJob.configure do |config|
|
66
|
+
config.jobs = {
|
67
|
+
UnderTest => { count: -1 }
|
68
|
+
}
|
69
|
+
end
|
70
|
+
}.to raise_error(ScheduledJob::ConfigError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'reschedule' do
|
76
|
+
before do
|
77
|
+
ScheduledJob.configure do |config|
|
78
|
+
config.jobs = {
|
79
|
+
UnderTest => { count: 1 },
|
80
|
+
Test => { count: 5 }
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'calls reschedule on all config jobs up to the job count limit' do
|
86
|
+
expect(UnderTest).to receive(:schedule_job).once
|
87
|
+
expect(Test).to receive(:schedule_job).exactly(5).times
|
88
|
+
ScheduledJob.reschedule
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
40
92
|
describe 'fast mode' do
|
41
|
-
before { expect(Delayed::Job).to receive(:
|
93
|
+
before { expect(Delayed::Job).to receive(:where).and_return([]) }
|
42
94
|
|
43
95
|
context 'when the job is not in run fast mode' do
|
44
96
|
it 'uses the value from time to recur' do
|
@@ -121,7 +173,7 @@ describe ScheduledJob do
|
|
121
173
|
allow(job).to receive(:id) { 4 }
|
122
174
|
instance = double("instance")
|
123
175
|
allow(UnderTest).to receive(:new) { instance }
|
124
|
-
expect(Delayed::Job).to receive(:
|
176
|
+
expect(Delayed::Job).to receive(:where).and_return([])
|
125
177
|
expect(Delayed::Job).to receive(:enqueue).with(instance, run_at: "time to recur", queue: "TESTING")
|
126
178
|
UnderTest.schedule_job job
|
127
179
|
end
|
@@ -136,7 +188,7 @@ describe ScheduledJob do
|
|
136
188
|
dummy_job = double("job")
|
137
189
|
allow(dummy_job).to receive(:id)
|
138
190
|
expect(dummy_job).to receive(:update_attributes!)
|
139
|
-
expect(Delayed::Job).to receive(:
|
191
|
+
expect(Delayed::Job).to receive(:where).twice.and_return([])
|
140
192
|
expect(Delayed::Job).to receive(:enqueue).exactly(2).times
|
141
193
|
UnderTest.schedule_job
|
142
194
|
under_test.failure(dummy_job)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scheduled_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CallumD
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-02-
|
14
|
+
date: 2015-02-16 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: delayed_job
|