rocketjob 2.1.3 → 3.0.0.alpha
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/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
|