rocketjob 3.3.0 → 3.3.1
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.
- checksums.yaml +4 -4
- data/lib/rocket_job/dirmon_entry.rb +9 -11
- data/lib/rocket_job/plugins/job/model.rb +1 -1
- data/lib/rocket_job/plugins/job/state_machine.rb +8 -2
- data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +7 -5
- data/lib/rocket_job/plugins/transaction.rb +10 -5
- data/lib/rocket_job/version.rb +1 -1
- data/lib/rocketjob.rb +1 -0
- data/test/config/database.yml +5 -0
- data/test/plugins/job/throttle_test.rb +5 -4
- data/test/plugins/restart_test.rb +21 -6
- data/test/plugins/transaction_test.rb +86 -0
- data/test/test_db.sqlite3 +0 -0
- metadata +12 -7
- data/Rakefile +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73879d59ee0d46f407f77acfc5b443b693e16727
|
4
|
+
data.tar.gz: 27d8a5a990b39d19c00ff086d57b3df2263eb727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fb4043fb6aca63ecb3968393aada59cd81101d69453592cc5032722d1751ce3716c095dee54baa0be77136df05f36ce49687d1a4f1a66a37c68feeae22870b9
|
7
|
+
data.tar.gz: ff2c14ea7b456f59f86b85f34174a2ab0d90d72799c7e839b90def2a288b44969f8a558a915129df0dce726d9b0d28252c91d1f9d5aefaf331dcf43d9500cd51
|
@@ -187,15 +187,7 @@ module RocketJob
|
|
187
187
|
# # => {}
|
188
188
|
def self.counts_by_state
|
189
189
|
counts = {}
|
190
|
-
collection.aggregate([
|
191
|
-
{
|
192
|
-
'$group' => {
|
193
|
-
_id: '$state',
|
194
|
-
count: {'$sum' => 1}
|
195
|
-
}
|
196
|
-
}
|
197
|
-
]
|
198
|
-
).each do |result|
|
190
|
+
collection.aggregate([{'$group' => {_id: '$state', count: {'$sum' => 1}}}]).each do |result|
|
199
191
|
counts[result['_id'].to_sym] = result['count']
|
200
192
|
end
|
201
193
|
counts
|
@@ -229,14 +221,20 @@ module RocketJob
|
|
229
221
|
# Case insensitive filename matching
|
230
222
|
Pathname.glob(pattern, File::FNM_CASEFOLD).each do |pathname|
|
231
223
|
next if pathname.directory?
|
232
|
-
pathname
|
224
|
+
pathname = begin
|
225
|
+
pathname.realpath
|
226
|
+
rescue Errno::ENOENT
|
227
|
+
logger.warn("Unable to expand the realpath for #{pathname.inspect}. Skipping file.")
|
228
|
+
next
|
229
|
+
end
|
230
|
+
|
233
231
|
file_name = pathname.to_s
|
234
232
|
|
235
233
|
# Skip archive directories
|
236
234
|
next if file_name.include?(self.class.default_archive_directory)
|
237
235
|
|
238
236
|
# Security check?
|
239
|
-
if (whitelist_paths.size > 0) && whitelist_paths.none? {
|
237
|
+
if (whitelist_paths.size > 0) && whitelist_paths.none? {|whitepath| file_name.to_s.start_with?(whitepath)}
|
240
238
|
logger.error "Skipping file: #{file_name} since it is not in any of the whitelisted paths: #{whitelist_paths.join(', ')}"
|
241
239
|
next
|
242
240
|
end
|
@@ -39,7 +39,7 @@ module RocketJob
|
|
39
39
|
field :priority, type: Integer, default: 50, class_attribute: true, user_editable: true, copy_on_restart: true
|
40
40
|
|
41
41
|
# When the job completes destroy it from both the database and the UI
|
42
|
-
field :destroy_on_complete, type: Boolean, default: true, class_attribute: true,
|
42
|
+
field :destroy_on_complete, type: Boolean, default: true, class_attribute: true, copy_on_restart: true
|
43
43
|
|
44
44
|
# Whether to store the results from this job
|
45
45
|
field :collect_output, type: Boolean, default: false, class_attribute: true
|
@@ -58,12 +58,14 @@ module RocketJob
|
|
58
58
|
end
|
59
59
|
|
60
60
|
event :pause do
|
61
|
-
transitions from: :running, to: :paused
|
61
|
+
transitions from: :running, to: :paused, if: :pausable?
|
62
|
+
# All jobs are pausable prior to starting the job.
|
62
63
|
transitions from: :queued, to: :paused
|
63
64
|
end
|
64
65
|
|
65
66
|
event :resume do
|
66
|
-
transitions from: :paused, to: :running, if: -> { started_at }
|
67
|
+
transitions from: :paused, to: :running, if: -> { pausable? && started_at }
|
68
|
+
# All jobs paused before processing started are pausable.
|
67
69
|
transitions from: :paused, to: :queued, unless: -> { started_at }
|
68
70
|
end
|
69
71
|
|
@@ -82,6 +84,10 @@ module RocketJob
|
|
82
84
|
end
|
83
85
|
# @formatter:on
|
84
86
|
|
87
|
+
# By default all jobs are not pausable / resumable
|
88
|
+
class_attribute(:pausable)
|
89
|
+
self.pausable = false
|
90
|
+
|
85
91
|
# Define a before and after callback method for each event
|
86
92
|
state_machine_define_event_callbacks(*aasm.state_machine.events.keys)
|
87
93
|
|
@@ -16,10 +16,11 @@ module RocketJob
|
|
16
16
|
# end
|
17
17
|
#
|
18
18
|
# Notes:
|
19
|
-
# - The
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
19
|
+
# - The number of running jobs will not exceed this value.
|
20
|
+
# - It may appear that a job is running briefly over this limit, but then is immediately back into queued state.
|
21
|
+
# This is expected behavior and is part of the check to ensure this value is not exceeded.
|
22
|
+
# The worker grabs the job and only then verifies the throttle, this is to prevent any other worker
|
23
|
+
# from attempting to grab the job, which would have exceeded the throttle.
|
23
24
|
module ThrottleRunningJobs
|
24
25
|
extend ActiveSupport::Concern
|
25
26
|
|
@@ -37,7 +38,8 @@ module RocketJob
|
|
37
38
|
def throttle_running_jobs_exceeded?
|
38
39
|
throttle_running_jobs &&
|
39
40
|
(throttle_running_jobs != 0) &&
|
40
|
-
|
41
|
+
# Cannot use class since it will include instances of parent job classes.
|
42
|
+
(RocketJob::Job.running.where('_type' => self.class.name, :id.ne => id).count >= throttle_running_jobs)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
@@ -2,7 +2,7 @@ require 'active_support/concern'
|
|
2
2
|
|
3
3
|
module RocketJob
|
4
4
|
module Plugins
|
5
|
-
#
|
5
|
+
# Wraps every #perform call with an Active Record transaction / unit or work.
|
6
6
|
#
|
7
7
|
# If the perform raises an exception it will cause any database changes to be rolled back.
|
8
8
|
#
|
@@ -23,14 +23,19 @@ module RocketJob
|
|
23
23
|
# Audit.create!(table: 'user', description: 'Changed age to 21')
|
24
24
|
# end
|
25
25
|
# end
|
26
|
+
#
|
27
|
+
# Performance
|
28
|
+
# - On Ruby (MRI) an empty transaction block call takes about 1ms.
|
29
|
+
# - On JRuby an empty transaction block call takes about 55ms.
|
30
|
+
#
|
31
|
+
# Note:
|
32
|
+
# - This plugin will only be activated if ActiveRecord has been loaded first.
|
26
33
|
module Transaction
|
27
34
|
extend ActiveSupport::Concern
|
28
35
|
|
29
36
|
included do
|
30
|
-
if
|
31
|
-
around_slice :rocket_job_transaction
|
32
|
-
else
|
33
|
-
around_perform :rocket_job_transaction
|
37
|
+
if defined?(ActiveRecord::Base)
|
38
|
+
respond_to?(:around_slice) ? around_slice(:rocket_job_transaction) : around_perform(:rocket_job_transaction)
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
data/lib/rocket_job/version.rb
CHANGED
data/lib/rocketjob.rb
CHANGED
@@ -40,6 +40,7 @@ module RocketJob
|
|
40
40
|
autoload :Restart, 'rocket_job/plugins/restart'
|
41
41
|
autoload :Singleton, 'rocket_job/plugins/singleton'
|
42
42
|
autoload :StateMachine, 'rocket_job/plugins/state_machine'
|
43
|
+
autoload :Transaction, 'rocket_job/plugins/transaction'
|
43
44
|
end
|
44
45
|
|
45
46
|
module Jobs
|
@@ -8,6 +8,7 @@ module Plugins
|
|
8
8
|
class ThrottleJob < RocketJob::Job
|
9
9
|
# Only allow one to be processed at a time
|
10
10
|
self.throttle_running_jobs = 1
|
11
|
+
self.pausable = true
|
11
12
|
|
12
13
|
def perform
|
13
14
|
21
|
@@ -85,15 +86,15 @@ module Plugins
|
|
85
86
|
ThrottleJob.create!(state: :failed)
|
86
87
|
ThrottleJob.create!(state: :complete)
|
87
88
|
ThrottleJob.create!(state: :paused)
|
88
|
-
assert job = RocketJob::Job.rocket_job_next_job(@worker_name), -> {
|
89
|
-
assert_equal @job.id, job.id, -> {
|
89
|
+
assert job = RocketJob::Job.rocket_job_next_job(@worker_name), -> {ThrottleJob.all.to_a.ai}
|
90
|
+
assert_equal @job.id, job.id, -> {ThrottleJob.all.to_a.ai}
|
90
91
|
end
|
91
92
|
|
92
93
|
it 'return nil when other jobs are running' do
|
93
94
|
ThrottleJob.create!
|
94
95
|
@job = ThrottleJob.new
|
95
96
|
@job.start!
|
96
|
-
assert_nil RocketJob::Job.rocket_job_next_job(@worker_name), -> {
|
97
|
+
assert_nil RocketJob::Job.rocket_job_next_job(@worker_name), -> {ThrottleJob.all.to_a.ai}
|
97
98
|
end
|
98
99
|
|
99
100
|
it 'add job to filter when other jobs are running' do
|
@@ -101,7 +102,7 @@ module Plugins
|
|
101
102
|
@job = ThrottleJob.new
|
102
103
|
@job.start!
|
103
104
|
filter = {}
|
104
|
-
assert_nil RocketJob::Job.rocket_job_next_job(@worker_name, filter), -> {
|
105
|
+
assert_nil RocketJob::Job.rocket_job_next_job(@worker_name, filter), -> {ThrottleJob.all.to_a.ai}
|
105
106
|
assert_equal 1, filter.size
|
106
107
|
end
|
107
108
|
end
|
@@ -16,11 +16,26 @@ module Plugins
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class RestartablePausableJob < RocketJob::Job
|
20
|
+
include RocketJob::Plugins::Restart
|
21
|
+
|
22
|
+
field :start_at, type: Date
|
23
|
+
field :end_at, type: Date
|
24
|
+
|
25
|
+
# Job will reload itself during process to check if it was paused.
|
26
|
+
self.pausable = true
|
27
|
+
|
28
|
+
def perform
|
29
|
+
self.start_at = Date.today
|
30
|
+
self.end_at = Date.today
|
31
|
+
'DONE'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
19
35
|
describe RocketJob::Plugins::Restart do
|
20
36
|
before do
|
21
|
-
# destroy_all could create new instances
|
22
37
|
RestartableJob.delete_all
|
23
|
-
|
38
|
+
RestartablePausableJob.delete_all
|
24
39
|
end
|
25
40
|
|
26
41
|
after do
|
@@ -89,12 +104,12 @@ module Plugins
|
|
89
104
|
end
|
90
105
|
|
91
106
|
describe '#pause' do
|
92
|
-
it 'does not
|
93
|
-
@job =
|
107
|
+
it 'does not enqueue a new job when paused' do
|
108
|
+
@job = RestartablePausableJob.new
|
94
109
|
@job.start
|
95
110
|
@job.pause!
|
96
111
|
assert @job.paused?
|
97
|
-
assert_equal 1,
|
112
|
+
assert_equal 1, RestartablePausableJob.count
|
98
113
|
end
|
99
114
|
end
|
100
115
|
|
@@ -115,7 +130,7 @@ module Plugins
|
|
115
130
|
end
|
116
131
|
|
117
132
|
it 'aborts from paused' do
|
118
|
-
@job =
|
133
|
+
@job = RestartablePausableJob.new
|
119
134
|
@job.start
|
120
135
|
@job.pause
|
121
136
|
assert @job.paused?
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
5
|
+
ActiveRecord::Base.establish_connection(:test)
|
6
|
+
|
7
|
+
ActiveRecord::Schema.define version: 0 do
|
8
|
+
create_table :users, force: true do |t|
|
9
|
+
t.string :login
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class User < ActiveRecord::Base
|
14
|
+
end
|
15
|
+
|
16
|
+
module Plugins
|
17
|
+
module Job
|
18
|
+
class TransactionTest < Minitest::Test
|
19
|
+
|
20
|
+
class CommitTransactionJob < RocketJob::Job
|
21
|
+
# Wrap perform with a transaction, so that it is rolled back on exception.
|
22
|
+
include RocketJob::Plugins::Transaction
|
23
|
+
|
24
|
+
field :login, type: String
|
25
|
+
|
26
|
+
def perform
|
27
|
+
User.create!(login: login)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class RollbackTransactionJob < RocketJob::Job
|
32
|
+
# Wrap perform with a transaction, so that it is rolled back on exception.
|
33
|
+
include RocketJob::Plugins::Transaction
|
34
|
+
|
35
|
+
field :login, type: String
|
36
|
+
|
37
|
+
def perform
|
38
|
+
User.create!(login: login)
|
39
|
+
raise "This must fail and rollback the transaction"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe RocketJob::Plugins::Job::Logger do
|
44
|
+
before do
|
45
|
+
User.delete_all
|
46
|
+
CommitTransactionJob.delete_all
|
47
|
+
RollbackTransactionJob.delete_all
|
48
|
+
end
|
49
|
+
|
50
|
+
after do
|
51
|
+
@job.destroy if @job && !@job.new_record?
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#rocket_job_transaction' do
|
55
|
+
it 'is registered' do
|
56
|
+
assert CommitTransactionJob.send(:get_callbacks, :perform).find {|c| c.filter == :rocket_job_transaction}
|
57
|
+
assert RollbackTransactionJob.send(:get_callbacks, :perform).find {|c| c.filter == :rocket_job_transaction}
|
58
|
+
refute RocketJob::Job.send(:get_callbacks, :perform).find {|c| c.filter == :rocket_job_transaction}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#perform' do
|
63
|
+
it 'commits on success' do
|
64
|
+
assert_equal 0, User.count
|
65
|
+
job = CommitTransactionJob.new(login: 'Success')
|
66
|
+
job.perform_now
|
67
|
+
assert job.completed?
|
68
|
+
assert_equal 1, User.count
|
69
|
+
assert_equal 'Success', User.first.login
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'rolls back on exception' do
|
73
|
+
assert_equal 0, User.count
|
74
|
+
job = RollbackTransactionJob.new(login: 'Bad')
|
75
|
+
assert_raises RuntimeError do
|
76
|
+
job.perform_now
|
77
|
+
end
|
78
|
+
assert job.failed?
|
79
|
+
assert_equal 0, User.count
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rocketjob
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.3.
|
4
|
+
version: 3.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: semantic_logger
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '4.1'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '4.1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: aasm
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,7 +77,6 @@ extra_rdoc_files: []
|
|
77
77
|
files:
|
78
78
|
- LICENSE.txt
|
79
79
|
- README.md
|
80
|
-
- Rakefile
|
81
80
|
- bin/rocketjob
|
82
81
|
- bin/rocketjob_perf
|
83
82
|
- lib/rocket_job/active_worker.rb
|
@@ -117,6 +116,7 @@ files:
|
|
117
116
|
- lib/rocket_job/version.rb
|
118
117
|
- lib/rocket_job/worker.rb
|
119
118
|
- lib/rocketjob.rb
|
119
|
+
- test/config/database.yml
|
120
120
|
- test/config/mongoid.yml
|
121
121
|
- test/config_test.rb
|
122
122
|
- test/dirmon_entry_test.rb
|
@@ -138,6 +138,8 @@ files:
|
|
138
138
|
- test/plugins/singleton_test.rb
|
139
139
|
- test/plugins/state_machine_event_callbacks_test.rb
|
140
140
|
- test/plugins/state_machine_test.rb
|
141
|
+
- test/plugins/transaction_test.rb
|
142
|
+
- test/test_db.sqlite3
|
141
143
|
- test/test_helper.rb
|
142
144
|
homepage: http://rocketjob.io
|
143
145
|
licenses:
|
@@ -164,6 +166,7 @@ signing_key:
|
|
164
166
|
specification_version: 4
|
165
167
|
summary: Ruby's missing batch system.
|
166
168
|
test_files:
|
169
|
+
- test/config/database.yml
|
167
170
|
- test/config/mongoid.yml
|
168
171
|
- test/config_test.rb
|
169
172
|
- test/dirmon_entry_test.rb
|
@@ -185,4 +188,6 @@ test_files:
|
|
185
188
|
- test/plugins/singleton_test.rb
|
186
189
|
- test/plugins/state_machine_event_callbacks_test.rb
|
187
190
|
- test/plugins/state_machine_test.rb
|
191
|
+
- test/plugins/transaction_test.rb
|
192
|
+
- test/test_db.sqlite3
|
188
193
|
- test/test_helper.rb
|
data/Rakefile
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Setup bundler to avoid having to run bundle exec all the time.
|
2
|
-
require 'rubygems'
|
3
|
-
require 'bundler/setup'
|
4
|
-
|
5
|
-
require 'rake/testtask'
|
6
|
-
require_relative 'lib/rocket_job/version'
|
7
|
-
|
8
|
-
task :gem do
|
9
|
-
system 'gem build rocketjob.gemspec'
|
10
|
-
end
|
11
|
-
|
12
|
-
task publish: :gem do
|
13
|
-
system "git tag -a v#{RocketJob::VERSION} -m 'Tagging #{RocketJob::VERSION}'"
|
14
|
-
system 'git push --tags'
|
15
|
-
system "gem push rocketjob-#{RocketJob::VERSION}.gem"
|
16
|
-
system "rm rocketjob-#{RocketJob::VERSION}.gem"
|
17
|
-
end
|
18
|
-
|
19
|
-
Rake::TestTask.new(:test) do |t|
|
20
|
-
t.pattern = 'test/**/*_test.rb'
|
21
|
-
t.verbose = true
|
22
|
-
t.warning = false
|
23
|
-
end
|
24
|
-
|
25
|
-
# By default run tests against all appraisals
|
26
|
-
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
27
|
-
require 'appraisal'
|
28
|
-
task default: :appraisal
|
29
|
-
else
|
30
|
-
task default: :test
|
31
|
-
end
|