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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -0
  3. data/lib/rocket_job/active_server.rb +48 -0
  4. data/lib/rocket_job/cli.rb +29 -17
  5. data/lib/rocket_job/config.rb +19 -31
  6. data/lib/rocket_job/dirmon_entry.rb +15 -45
  7. data/lib/rocket_job/extensions/mongo/logging.rb +26 -0
  8. data/lib/rocket_job/extensions/rocket_job_adapter.rb +3 -5
  9. data/lib/rocket_job/heartbeat.rb +18 -23
  10. data/lib/rocket_job/job.rb +0 -1
  11. data/lib/rocket_job/job_exception.rb +11 -13
  12. data/lib/rocket_job/jobs/dirmon_job.rb +8 -8
  13. data/lib/rocket_job/jobs/housekeeping_job.rb +13 -15
  14. data/lib/rocket_job/performance.rb +5 -5
  15. data/lib/rocket_job/plugins/cron.rb +3 -10
  16. data/lib/rocket_job/plugins/document.rb +58 -33
  17. data/lib/rocket_job/plugins/job/model.rb +43 -71
  18. data/lib/rocket_job/plugins/job/persistence.rb +7 -63
  19. data/lib/rocket_job/plugins/job/worker.rb +24 -26
  20. data/lib/rocket_job/plugins/processing_window.rb +6 -9
  21. data/lib/rocket_job/plugins/retry.rb +3 -8
  22. data/lib/rocket_job/plugins/singleton.rb +1 -1
  23. data/lib/rocket_job/plugins/state_machine.rb +1 -7
  24. data/lib/rocket_job/server.rb +352 -0
  25. data/lib/rocket_job/version.rb +1 -1
  26. data/lib/rocket_job/worker.rb +46 -336
  27. data/lib/rocketjob.rb +5 -4
  28. data/test/config/mongoid.yml +88 -0
  29. data/test/config_test.rb +1 -1
  30. data/test/dirmon_entry_test.rb +15 -79
  31. data/test/dirmon_job_test.rb +6 -6
  32. data/test/job_test.rb +2 -2
  33. data/test/plugins/job/callbacks_test.rb +40 -32
  34. data/test/plugins/job/defaults_test.rb +10 -8
  35. data/test/plugins/job/model_test.rb +1 -3
  36. data/test/plugins/job/persistence_test.rb +11 -13
  37. data/test/plugins/job/worker_test.rb +45 -26
  38. data/test/plugins/processing_window_test.rb +4 -4
  39. data/test/plugins/restart_test.rb +11 -12
  40. data/test/plugins/state_machine_event_callbacks_test.rb +20 -18
  41. data/test/plugins/state_machine_test.rb +5 -5
  42. data/test/test_helper.rb +4 -1
  43. metadata +15 -29
  44. data/lib/rocket_job/extensions/mongo.rb +0 -23
  45. data/lib/rocket_job/extensions/mongo_mapper.rb +0 -30
  46. data/lib/rocket_job/plugins/job/defaults.rb +0 -40
  47. data/test/config/mongo.yml +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 69c06ffc4a4e337c787f06d36873d763ccced1be
4
- data.tar.gz: 401c2bda273c7413b0ceda2853aef6c325b15159
3
+ metadata.gz: 328cd371a61e647e687589d126b222c603dfe3a0
4
+ data.tar.gz: c13d9c10d2156acbacbf92b05af835639c15b112
5
5
  SHA512:
6
- metadata.gz: 87d8f78b0e766a6d1ab06efb00ff83a3b7b27ade12d11109630f01bed3a753ba8f1fe6241d198209870657ee2e6256d5f6c6ba49293c21ad6dc305c85cc4835e
7
- data.tar.gz: d87017700e5fb67815b6ce934b73a683d4c08d96ed04e5f802e58ced4a4aa28b2c67be371258c6f643edb6444f90d66e57f0dc1d698b7398dd199bb80eb1d4c6
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
@@ -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, :threads, :environment, :pidfile, :directory, :quiet, :log_level, :log_file
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 = nil
11
- @threads = nil
12
- @quiet = false
13
- @environment = nil
14
- @pidfile = nil
15
- @directory = '.'
16
- @log_level = nil
17
- @log_file = nil
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::Worker from the command line
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[:max_threads] = threads if threads
32
- Worker.run(opts)
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
- RocketJob::Worker.logger.measure_info('Eager loaded Rails and all Engines') do
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 worker instance (Default: host_name:PID)') do |arg|
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('-t', '--threads COUNT', 'Number of worker threads to start') do |arg|
137
- @threads = arg.to_i
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
@@ -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
- # @formatter:off
25
- # The maximum number of worker threads to create on any one worker
26
- key :max_worker_threads, Integer, default: 10
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 Worker processes
29
- key :heartbeat_seconds, Integer, default: 15
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
- key :max_poll_seconds, Integer, default: 5
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
- key :re_check_seconds, Integer, default: 60
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 MongoMapper
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/mongo.yml')
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 MongoDB configuration from: #{config_file}"
68
- config = YAML.load(ERB.new(config_file.read).result)
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
- key :name, String
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
- key :pattern, String
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
- key :job_class_name, String
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
- key :properties, Hash
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
- key :archive_directory, String
49
+ field :archive_directory, type: String
68
50
 
69
51
  # If this DirmonEntry is in the failed state, exception contains the cause
70
- one :exception, class_name: 'RocketJob::JobException'
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
- #key :max_hits, Integer, default: 100
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
- key :state, Symbol, default: :pending
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, to: :enabled
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, to: :disabled
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.merge(arguments: arguments))
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 the first argument must be a Hash")
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
- key :active_job_id, String
42
- key :active_job_class, String
43
- key :active_job_queue, String
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