rocketjob 2.1.3 → 3.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -0
- data/lib/rocket_job/active_server.rb +48 -0
- data/lib/rocket_job/cli.rb +29 -17
- data/lib/rocket_job/config.rb +19 -31
- data/lib/rocket_job/dirmon_entry.rb +15 -45
- data/lib/rocket_job/extensions/mongo/logging.rb +26 -0
- data/lib/rocket_job/extensions/rocket_job_adapter.rb +3 -5
- data/lib/rocket_job/heartbeat.rb +18 -23
- data/lib/rocket_job/job.rb +0 -1
- data/lib/rocket_job/job_exception.rb +11 -13
- data/lib/rocket_job/jobs/dirmon_job.rb +8 -8
- data/lib/rocket_job/jobs/housekeeping_job.rb +13 -15
- data/lib/rocket_job/performance.rb +5 -5
- data/lib/rocket_job/plugins/cron.rb +3 -10
- data/lib/rocket_job/plugins/document.rb +58 -33
- data/lib/rocket_job/plugins/job/model.rb +43 -71
- data/lib/rocket_job/plugins/job/persistence.rb +7 -63
- data/lib/rocket_job/plugins/job/worker.rb +24 -26
- data/lib/rocket_job/plugins/processing_window.rb +6 -9
- data/lib/rocket_job/plugins/retry.rb +3 -8
- data/lib/rocket_job/plugins/singleton.rb +1 -1
- data/lib/rocket_job/plugins/state_machine.rb +1 -7
- data/lib/rocket_job/server.rb +352 -0
- data/lib/rocket_job/version.rb +1 -1
- data/lib/rocket_job/worker.rb +46 -336
- data/lib/rocketjob.rb +5 -4
- data/test/config/mongoid.yml +88 -0
- data/test/config_test.rb +1 -1
- data/test/dirmon_entry_test.rb +15 -79
- data/test/dirmon_job_test.rb +6 -6
- data/test/job_test.rb +2 -2
- data/test/plugins/job/callbacks_test.rb +40 -32
- data/test/plugins/job/defaults_test.rb +10 -8
- data/test/plugins/job/model_test.rb +1 -3
- data/test/plugins/job/persistence_test.rb +11 -13
- data/test/plugins/job/worker_test.rb +45 -26
- data/test/plugins/processing_window_test.rb +4 -4
- data/test/plugins/restart_test.rb +11 -12
- data/test/plugins/state_machine_event_callbacks_test.rb +20 -18
- data/test/plugins/state_machine_test.rb +5 -5
- data/test/test_helper.rb +4 -1
- metadata +15 -29
- data/lib/rocket_job/extensions/mongo.rb +0 -23
- data/lib/rocket_job/extensions/mongo_mapper.rb +0 -30
- data/lib/rocket_job/plugins/job/defaults.rb +0 -40
- data/test/config/mongo.yml +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 328cd371a61e647e687589d126b222c603dfe3a0
|
4
|
+
data.tar.gz: c13d9c10d2156acbacbf92b05af835639c15b112
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb17eef819ef602e0d58fd89546495ac6f080e803c8421511be078e6821f9500860e40443120f417710cd1313e8e0fd329328c4cdcc0ace6ce4056aa7ab9703f
|
7
|
+
data.tar.gz: 74691dda4d998f54208a5ee3edbb004f49b078dd9f6d9d003b4795b654999aa292986fc36041aca282bca22f5b3b70d45e8a15e55323ebad602195b0d47a4e32
|
data/README.md
CHANGED
@@ -17,6 +17,42 @@ Checkout http://rocketjob.io/
|
|
17
17
|
* Questions? Join the chat room on Gitter for [rocketjob support](https://gitter.im/rocketjob/support)
|
18
18
|
* [Report bugs](https://github.com/rocketjob/rocketjob/issues)
|
19
19
|
|
20
|
+
## Upgrading to V3
|
21
|
+
|
22
|
+
* V3 replaces MongoMapper with Mongoid which supports the latest MongoDB Ruby client driver.
|
23
|
+
* Replase usages of `rocket_job do` to set default values:
|
24
|
+
|
25
|
+
~~~ruby
|
26
|
+
rocket_job do |job|
|
27
|
+
job.priority = 25
|
28
|
+
end
|
29
|
+
~~~
|
30
|
+
|
31
|
+
With:
|
32
|
+
|
33
|
+
~~~ruby
|
34
|
+
self.priority = 25
|
35
|
+
~~~
|
36
|
+
|
37
|
+
* Replace `key` with `field` when adding attributes to a job:
|
38
|
+
|
39
|
+
~~~ruby
|
40
|
+
key :inquiry_defaults, Hash
|
41
|
+
~~~
|
42
|
+
|
43
|
+
With:
|
44
|
+
|
45
|
+
~~~ruby
|
46
|
+
field :inquiry_defaults, type: Hash, default: {}
|
47
|
+
~~~
|
48
|
+
|
49
|
+
* Replace usage of `public_rocket_job_properties` with the `user_editable` option:
|
50
|
+
|
51
|
+
~~~ruby
|
52
|
+
field :priority, type: Integer, default: 50, user_editable: true
|
53
|
+
~~~
|
54
|
+
|
55
|
+
|
20
56
|
## Ruby Support
|
21
57
|
|
22
58
|
Rocket Job is tested and supported on the following Ruby platforms:
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RocketJob
|
2
|
+
# Information about a server currently working on a job
|
3
|
+
class ActiveServer
|
4
|
+
# When this server started working on this job / slice
|
5
|
+
attr_accessor :started_at
|
6
|
+
|
7
|
+
attr_accessor :name, :job
|
8
|
+
|
9
|
+
# Returns [Hash<String:ActiveWorker>] hash of all servers sorted by name
|
10
|
+
# and what they are currently working on.
|
11
|
+
# Returns {} if no servers are currently busy doing any work
|
12
|
+
def self.all
|
13
|
+
servers = {}
|
14
|
+
# Need paused, failed or aborted since servers may still be working on active slices
|
15
|
+
RocketJob::Job.where(state: [:running, :paused, :failed, :aborted]).each do |job|
|
16
|
+
job.rocket_job_active_servers.each_pair do |name, active_server|
|
17
|
+
(servers[name] ||= []) << active_server
|
18
|
+
end
|
19
|
+
end
|
20
|
+
servers
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(name, started_at, job)
|
24
|
+
@name = name
|
25
|
+
@started_at = started_at
|
26
|
+
@job = job
|
27
|
+
end
|
28
|
+
|
29
|
+
# Duration in human readable form
|
30
|
+
def duration
|
31
|
+
RocketJob.seconds_as_duration(duration_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Number of seconds this server has been working on this job / slice
|
35
|
+
def duration_s
|
36
|
+
Time.now - started_at
|
37
|
+
end
|
38
|
+
|
39
|
+
def server
|
40
|
+
@server ||= RocketJob::Server.find_by(name: name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def name=(name)
|
44
|
+
@server = nil
|
45
|
+
@name = name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/rocket_job/cli.rb
CHANGED
@@ -4,21 +4,23 @@ module RocketJob
|
|
4
4
|
# Command Line Interface parser for RocketJob
|
5
5
|
class CLI
|
6
6
|
include SemanticLogger::Loggable
|
7
|
-
attr_accessor :name, :
|
7
|
+
attr_accessor :name, :workers, :environment, :pidfile, :directory, :quiet, :log_level, :log_file, :mongo_config, :symmetric_encryption_config
|
8
8
|
|
9
9
|
def initialize(argv)
|
10
|
-
@name
|
11
|
-
@
|
12
|
-
@quiet
|
13
|
-
@environment
|
14
|
-
@pidfile
|
15
|
-
@directory
|
16
|
-
@log_level
|
17
|
-
@log_file
|
10
|
+
@name = nil
|
11
|
+
@workers = nil
|
12
|
+
@quiet = false
|
13
|
+
@environment = nil
|
14
|
+
@pidfile = nil
|
15
|
+
@directory = '.'
|
16
|
+
@log_level = nil
|
17
|
+
@log_file = nil
|
18
|
+
@mongo_config = nil
|
19
|
+
@symmetric_encryption_config = nil
|
18
20
|
parse(argv)
|
19
21
|
end
|
20
22
|
|
21
|
-
# Run a RocketJob::
|
23
|
+
# Run a RocketJob::Server from the command line
|
22
24
|
def run
|
23
25
|
Thread.current.name = 'rocketjob main'
|
24
26
|
setup_environment
|
@@ -28,8 +30,8 @@ module RocketJob
|
|
28
30
|
|
29
31
|
opts = {}
|
30
32
|
opts[:name] = name if name
|
31
|
-
opts[:
|
32
|
-
|
33
|
+
opts[:max_workers] = workers if workers
|
34
|
+
Server.run(opts)
|
33
35
|
end
|
34
36
|
|
35
37
|
def rails?
|
@@ -57,7 +59,7 @@ module RocketJob
|
|
57
59
|
SemanticLogger.default_level = log_level.to_sym if log_level
|
58
60
|
|
59
61
|
if Rails.configuration.eager_load
|
60
|
-
|
62
|
+
logger.measure_info('Eager loaded Rails and all Engines') do
|
61
63
|
Rails.application.eager_load!
|
62
64
|
Rails::Engine.subclasses.each(&:eager_load!)
|
63
65
|
end
|
@@ -85,7 +87,7 @@ module RocketJob
|
|
85
87
|
SemanticLogger.add_appender(file_name: path.to_s, formatter: :color)
|
86
88
|
|
87
89
|
logger.info "Rails not detected. Running standalone: #{environment}"
|
88
|
-
RocketJob::Config.load!(environment)
|
90
|
+
RocketJob::Config.load!(environment, mongo_config, symmetric_encryption_config)
|
89
91
|
self.class.eager_load_jobs
|
90
92
|
end
|
91
93
|
|
@@ -130,11 +132,15 @@ module RocketJob
|
|
130
132
|
# Parse command line options placing results in the corresponding instance variables
|
131
133
|
def parse(argv)
|
132
134
|
parser = OptionParser.new do |o|
|
133
|
-
o.on('-n', '--name NAME', 'Unique Name of this
|
135
|
+
o.on('-n', '--name NAME', 'Unique Name of this server (Default: host_name:PID)') do |arg|
|
134
136
|
@name = arg
|
135
137
|
end
|
136
|
-
o.on('-
|
137
|
-
@
|
138
|
+
o.on('-w', '--workers COUNT', 'Number of workers (threads) to start') do |arg|
|
139
|
+
@workers = arg.to_i
|
140
|
+
end
|
141
|
+
o.on('-t', '--threads COUNT', 'Deprecated') do |arg|
|
142
|
+
warn '-t and --threads are deprecated, use -w or --workers'
|
143
|
+
@workers = arg.to_i
|
138
144
|
end
|
139
145
|
o.on('-q', '--quiet', 'Do not write to stdout, only to logfile. Necessary when running as a daemon') do
|
140
146
|
@quiet = true
|
@@ -154,6 +160,12 @@ module RocketJob
|
|
154
160
|
o.on('--pidfile PATH', 'Use PATH as a pidfile') do |arg|
|
155
161
|
@pidfile = arg
|
156
162
|
end
|
163
|
+
o.on('-m', '--mongo MONGO_CONFIG_FILE_NAME', 'Path and filename of config file. Default: config/mongoid.yml') do |arg|
|
164
|
+
@mongo_config = arg
|
165
|
+
end
|
166
|
+
o.on('-s', '--symmetric-encryption SYMMETRIC_ENCRYPTION_CONFIG_FILE_NAME', 'Path and filename of Symmetric Encryption config file. Default: config/symmetric-encryption.yml') do |arg|
|
167
|
+
@symmetric_encryption_config = arg
|
168
|
+
end
|
157
169
|
o.on('-v', '--version', 'Print the version information') do
|
158
170
|
puts "Rocket Job v#{RocketJob::VERSION}"
|
159
171
|
exit 1
|
data/lib/rocket_job/config.rb
CHANGED
@@ -21,15 +21,22 @@ module RocketJob
|
|
21
21
|
# Also, exceptions will be raised instead of failing the job.
|
22
22
|
cattr_accessor(:inline_mode) { false }
|
23
23
|
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
24
|
+
#
|
25
|
+
# Servers
|
26
|
+
#
|
27
|
+
|
28
|
+
# The maximum number of workers to create on any one server
|
29
|
+
field :max_worker_threads, type: Integer, default: 10
|
27
30
|
|
28
|
-
# Number of seconds between heartbeats from Rocket Job
|
29
|
-
|
31
|
+
# Number of seconds between heartbeats from a Rocket Job Server process
|
32
|
+
field :heartbeat_seconds, type: Integer, default: 15
|
33
|
+
|
34
|
+
#
|
35
|
+
# Workers
|
36
|
+
#
|
30
37
|
|
31
38
|
# Maximum number of seconds a Worker will wait before checking for new jobs
|
32
|
-
|
39
|
+
field :max_poll_seconds, type: Integer, default: 5
|
33
40
|
|
34
41
|
# Number of seconds between checking for:
|
35
42
|
# - Jobs with a higher priority
|
@@ -40,33 +47,14 @@ module RocketJob
|
|
40
47
|
#
|
41
48
|
# Note:
|
42
49
|
# Not all job types support pausing in the middle
|
43
|
-
|
44
|
-
|
45
|
-
# @formatter:on
|
46
|
-
|
47
|
-
# Replace the MongoMapper default mongo connection for holding jobs
|
48
|
-
def self.mongo_connection=(connection)
|
49
|
-
connection(connection)
|
50
|
-
Worker.connection(connection)
|
51
|
-
Job.connection(connection)
|
52
|
-
Config.connection(connection)
|
53
|
-
DirmonEntry.connection(connection)
|
54
|
-
|
55
|
-
db_name = connection.db.name
|
56
|
-
set_database_name(db_name)
|
57
|
-
Worker.set_database_name(db_name)
|
58
|
-
Job.set_database_name(db_name)
|
59
|
-
Config.set_database_name(db_name)
|
60
|
-
DirmonEntry.set_database_name(db_name)
|
61
|
-
end
|
50
|
+
field :re_check_seconds, type: Integer, default: 60
|
62
51
|
|
63
|
-
# Configure
|
64
|
-
def self.load!(environment='development', file_name=nil, encryption_file_name=nil)
|
65
|
-
config_file = file_name ? Pathname.new(file_name) : Pathname.pwd.join('config/
|
52
|
+
# Configure Mongoid
|
53
|
+
def self.load!(environment = 'development', file_name = nil, encryption_file_name = nil)
|
54
|
+
config_file = file_name ? Pathname.new(file_name) : Pathname.pwd.join('config/mongoid.yml')
|
66
55
|
if config_file.file?
|
67
|
-
logger.debug "Reading
|
68
|
-
|
69
|
-
MongoMapper.setup(config, environment)
|
56
|
+
logger.debug "Reading Mongo configuration from: #{config_file}"
|
57
|
+
Mongoid.load!(config_file, environment)
|
70
58
|
else
|
71
59
|
raise(ArgumentError, "Mongo Configuration file: #{config_file.to_s} not found")
|
72
60
|
end
|
@@ -6,9 +6,8 @@ module RocketJob
|
|
6
6
|
include Plugins::Document
|
7
7
|
include Plugins::StateMachine
|
8
8
|
|
9
|
-
# @formatter:off
|
10
9
|
# User defined name used to identify this DirmonEntry in Mission Control
|
11
|
-
|
10
|
+
field :name, type: String
|
12
11
|
|
13
12
|
# Pattern for finding files
|
14
13
|
#
|
@@ -27,36 +26,19 @@ module RocketJob
|
|
27
26
|
# - If there is no '*' in the pattern then an exact filename match is expected
|
28
27
|
# - The pattern is not validated to ensure the path exists, it will be validated against the
|
29
28
|
# `whitelist_paths` when processed by DirmonJob
|
30
|
-
|
29
|
+
field :pattern, type: String
|
31
30
|
|
32
31
|
# Job to enqueue for processing for every file that matches the pattern
|
33
32
|
#
|
34
33
|
# Example:
|
35
34
|
# "ProcessItJob"
|
36
|
-
|
37
|
-
|
38
|
-
# Any user supplied arguments for the method invocation
|
39
|
-
# All keys must be UTF-8 strings. The values can be any valid BSON type:
|
40
|
-
# Integer
|
41
|
-
# Float
|
42
|
-
# Time (UTC)
|
43
|
-
# String (UTF-8)
|
44
|
-
# Array
|
45
|
-
# Hash
|
46
|
-
# True
|
47
|
-
# False
|
48
|
-
# Symbol
|
49
|
-
# nil
|
50
|
-
# Regular Expression
|
51
|
-
#
|
52
|
-
# Note: Date is not supported, convert it to a UTC time
|
53
|
-
key :arguments, Array
|
35
|
+
field :job_class_name, type: String
|
54
36
|
|
55
37
|
# Any job properties to set
|
56
38
|
#
|
57
39
|
# Example, override the default job priority:
|
58
40
|
# { priority: 45 }
|
59
|
-
|
41
|
+
field :properties, type: Hash, default: {}
|
60
42
|
|
61
43
|
# Archive directory to move files to when processed to prevent processing the
|
62
44
|
# file again.
|
@@ -64,10 +46,10 @@ module RocketJob
|
|
64
46
|
# If supplied, the file will be moved to this directory before the job is started
|
65
47
|
# If the file was in a sub-directory, the corresponding sub-directory will
|
66
48
|
# be created in the archive directory.
|
67
|
-
|
49
|
+
field :archive_directory, type: String
|
68
50
|
|
69
51
|
# If this DirmonEntry is in the failed state, exception contains the cause
|
70
|
-
|
52
|
+
embeds_one :exception, class_name: 'RocketJob::JobException'
|
71
53
|
|
72
54
|
# The maximum number of files that should ever match during a single poll of the pattern.
|
73
55
|
#
|
@@ -75,14 +57,17 @@ module RocketJob
|
|
75
57
|
# Exceeding this number will result in an exception being logged in a failed Dirmon instance.
|
76
58
|
# Dirmon processing will continue with new instances.
|
77
59
|
# TODO: Implement max_hits
|
78
|
-
#
|
60
|
+
#field :max_hits, type: Integer, default: 100
|
79
61
|
|
80
62
|
#
|
81
63
|
# Read-only attributes
|
82
64
|
#
|
83
65
|
|
84
66
|
# Current state, as set by the state machine. Do not modify directly.
|
85
|
-
|
67
|
+
field :state, type: Symbol, default: :pending
|
68
|
+
|
69
|
+
# Unique index on pattern to help prevent two entries from scanning the same files
|
70
|
+
index({pattern: 1}, background: true, unique: true, drop_dups: true)
|
86
71
|
|
87
72
|
# State Machine events and transitions
|
88
73
|
#
|
@@ -107,13 +92,13 @@ module RocketJob
|
|
107
92
|
state :disabled
|
108
93
|
|
109
94
|
event :enable do
|
110
|
-
transitions from: :pending,
|
95
|
+
transitions from: :pending, to: :enabled
|
111
96
|
transitions from: :disabled, to: :enabled
|
112
97
|
end
|
113
98
|
|
114
99
|
event :disable do
|
115
100
|
transitions from: :enabled, to: :disabled
|
116
|
-
transitions from: :failed,
|
101
|
+
transitions from: :failed, to: :disabled
|
117
102
|
end
|
118
103
|
|
119
104
|
event :fail, before: :set_exception do
|
@@ -134,13 +119,6 @@ module RocketJob
|
|
134
119
|
record.errors.add(attr, 'job_class_name must be defined and must be derived from RocketJob::Job') unless exists
|
135
120
|
end
|
136
121
|
|
137
|
-
validates_each :arguments do |record, attr, value|
|
138
|
-
if klass = record.job_class
|
139
|
-
count = klass.rocket_job_argument_count
|
140
|
-
record.errors.add(attr, "There must be #{count} argument(s)") if value.size != count
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
122
|
validates_each :properties do |record, attr, value|
|
145
123
|
if record.job_class && (methods = record.job_class.instance_methods)
|
146
124
|
value.each_pair do |k, v|
|
@@ -149,12 +127,6 @@ module RocketJob
|
|
149
127
|
end
|
150
128
|
end
|
151
129
|
|
152
|
-
# Create indexes
|
153
|
-
def self.create_indexes
|
154
|
-
# Unique index on pattern to help prevent two entries from scanning the same files
|
155
|
-
ensure_index({pattern: 1}, background: true, unique: true)
|
156
|
-
end
|
157
|
-
|
158
130
|
# Security Settings
|
159
131
|
#
|
160
132
|
# A whitelist of paths from which to process files.
|
@@ -306,7 +278,7 @@ module RocketJob
|
|
306
278
|
def later(pathname)
|
307
279
|
if klass = job_class
|
308
280
|
logger.measure_info "Enqueued: #{name}, Job class: #{job_class_name}" do
|
309
|
-
job = klass.new(properties
|
281
|
+
job = klass.new(properties)
|
310
282
|
upload_file(job, pathname)
|
311
283
|
job.save!
|
312
284
|
job
|
@@ -341,10 +313,8 @@ module RocketJob
|
|
341
313
|
job.upload_file_name = full_file_name
|
342
314
|
elsif job.respond_to?(:full_file_name=)
|
343
315
|
job.full_file_name = full_file_name
|
344
|
-
elsif job.arguments.first.is_a?(Hash)
|
345
|
-
job.arguments.first[:full_file_name] = full_file_name
|
346
316
|
else
|
347
|
-
raise(ArgumentError, "#{job_class_name} must either have attribute 'upload_file_name' or
|
317
|
+
raise(ArgumentError, "#{job_class_name} must either have attribute 'upload_file_name' or 'full_file_name'")
|
348
318
|
end
|
349
319
|
end
|
350
320
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mongo/monitoring/command_log_subscriber'
|
2
|
+
|
3
|
+
module Mongo
|
4
|
+
class Monitoring
|
5
|
+
class CommandLogSubscriber
|
6
|
+
include SemanticLogger::Loggable
|
7
|
+
self.logger.name = 'Mongo'
|
8
|
+
|
9
|
+
def started(event)
|
10
|
+
@event_command = event.command
|
11
|
+
end
|
12
|
+
|
13
|
+
def succeeded(event)
|
14
|
+
logger.debug(message: prefix(event), duration: (event.duration * 1000), payload: @event_command)
|
15
|
+
end
|
16
|
+
|
17
|
+
def failed(event)
|
18
|
+
logger.debug(message: "#{prefix(event)} Failed: #{event.message}", duration: (event.duration * 1000), payload: @event_command)
|
19
|
+
end
|
20
|
+
|
21
|
+
def prefix(event)
|
22
|
+
"#{event.address.to_s} | #{event.database_name}.#{event.command_name}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
#require 'rocketjob'
|
2
|
-
|
3
1
|
module ActiveJob
|
4
2
|
module QueueAdapters
|
5
3
|
# == Rocket Job adapter for Active Job
|
@@ -38,9 +36,9 @@ module ActiveJob
|
|
38
36
|
end
|
39
37
|
|
40
38
|
class JobWrapper < RocketJob::Job #:nodoc:
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
field :active_job_id, type: String
|
40
|
+
field :active_job_class, type: String
|
41
|
+
field :active_job_queue, type: String
|
44
42
|
|
45
43
|
def perform(job_data)
|
46
44
|
Base.execute job_data
|