rocketjob 3.0.0.beta2 → 3.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rocket_job/cli.rb +8 -1
- data/lib/rocket_job/config.rb +2 -0
- data/lib/rocket_job/dirmon_entry.rb +2 -0
- data/lib/rocket_job/job.rb +1 -0
- data/lib/rocket_job/performance.rb +30 -22
- data/lib/rocket_job/plugins/job/model.rb +1 -5
- data/lib/rocket_job/plugins/job/persistence.rb +28 -12
- data/lib/rocket_job/plugins/job/throttle.rb +75 -0
- data/lib/rocket_job/plugins/job/worker.rb +22 -12
- data/lib/rocket_job/server.rb +28 -13
- data/lib/rocket_job/version.rb +1 -1
- data/lib/rocket_job/worker.rb +18 -12
- data/lib/rocketjob.rb +1 -0
- data/test/plugins/job/throttle_test.rb +56 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2893cad7faa49cc61dc42a18e1769b7e4b0684cd
|
4
|
+
data.tar.gz: 0484cd6616b0d1dbef6ecd67d0235ef0d3834623
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13e4e156847cfebb938b7700eaddda5899b79f816d690b3555d77aa6b993cc046e76c6b796404e83f3767ce482a4db50134a7037a6e7ba3ea9fd85b296834689
|
7
|
+
data.tar.gz: 60856f69ce93c2c0143cbf349f8c9eb393c038e44897d3f1ab16c550cec10557656628892d92ce6fbc833c5353f7b4a936a790932c219b9df7c8384cdd6f360d
|
data/lib/rocket_job/cli.rb
CHANGED
@@ -4,7 +4,9 @@ module RocketJob
|
|
4
4
|
# Command Line Interface parser for RocketJob
|
5
5
|
class CLI
|
6
6
|
include SemanticLogger::Loggable
|
7
|
-
attr_accessor :name, :workers, :environment, :pidfile, :directory, :quiet,
|
7
|
+
attr_accessor :name, :workers, :environment, :pidfile, :directory, :quiet,
|
8
|
+
:log_level, :log_file, :mongo_config, :symmetric_encryption_config,
|
9
|
+
:filter
|
8
10
|
|
9
11
|
def initialize(argv)
|
10
12
|
@name = nil
|
@@ -17,6 +19,7 @@ module RocketJob
|
|
17
19
|
@log_file = nil
|
18
20
|
@mongo_config = nil
|
19
21
|
@symmetric_encryption_config = nil
|
22
|
+
@filter = nil
|
20
23
|
parse(argv)
|
21
24
|
end
|
22
25
|
|
@@ -31,6 +34,7 @@ module RocketJob
|
|
31
34
|
opts = {}
|
32
35
|
opts[:name] = name if name
|
33
36
|
opts[:max_workers] = workers if workers
|
37
|
+
opts[:filter] = {:_type => filter} if filter
|
34
38
|
Server.run(opts)
|
35
39
|
end
|
36
40
|
|
@@ -142,6 +146,9 @@ module RocketJob
|
|
142
146
|
warn '-t and --threads are deprecated, use -w or --workers'
|
143
147
|
@workers = arg.to_i
|
144
148
|
end
|
149
|
+
o.on('-F', '--filter REGEXP', 'Limit this worker to only those job classes that match this regular expression (case-insensitive)') do |arg|
|
150
|
+
@filter = Regexp.new(arg, true)
|
151
|
+
end
|
145
152
|
o.on('-q', '--quiet', 'Do not write to stdout, only to logfile. Necessary when running as a daemon') do
|
146
153
|
@quiet = true
|
147
154
|
end
|
data/lib/rocket_job/config.rb
CHANGED
data/lib/rocket_job/job.rb
CHANGED
@@ -2,41 +2,49 @@ require 'csv'
|
|
2
2
|
require 'yaml'
|
3
3
|
module RocketJob
|
4
4
|
class Performance
|
5
|
-
attr_accessor :count, :
|
5
|
+
attr_accessor :count, :servers, :workers, :version, :ruby, :environment, :mongo_config
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
@version
|
9
|
-
@ruby
|
10
|
-
@count
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@environment
|
14
|
-
@mongo_config
|
8
|
+
@version = RocketJob::VERSION
|
9
|
+
@ruby = defined?(JRuby) ? "jruby_#{JRUBY_VERSION}" : "ruby_#{RUBY_VERSION}"
|
10
|
+
@count = 100_000
|
11
|
+
@servers = 0
|
12
|
+
@workers = 0
|
13
|
+
@environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
14
|
+
@mongo_config = 'config/mongoid.yml'
|
15
15
|
end
|
16
16
|
|
17
17
|
def run_test_case(count = self.count)
|
18
|
-
|
19
|
-
raise 'Please start workers before starting the performance test' if worker_processes == 0
|
18
|
+
raise 'Please start servers before starting the performance test' if RocketJob::Server.where(:state.in => ['running', 'paused']).count == 0
|
20
19
|
|
21
|
-
self.
|
22
|
-
self.
|
23
|
-
RocketJob::Server.running.each do |
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
20
|
+
self.servers = 0
|
21
|
+
self.workers = 0
|
22
|
+
RocketJob::Server.running.each do |server|
|
23
|
+
next if server.zombie?
|
24
|
+
self.servers += 1
|
25
|
+
self.workers += server.heartbeat.workers
|
28
26
|
end
|
29
|
-
puts "Running: #{
|
27
|
+
puts "Running: #{workers} workers, distributed across #{servers} servers"
|
30
28
|
|
31
29
|
puts 'Waiting for workers to pause'
|
32
30
|
RocketJob::Server.pause_all
|
33
31
|
RocketJob::Jobs::SimpleJob.delete_all
|
34
|
-
|
32
|
+
|
33
|
+
# Wait for paused workers to stop
|
34
|
+
loop do
|
35
|
+
running = 0
|
36
|
+
RocketJob::Server.paused.each do |server|
|
37
|
+
running += server.heartbeat.workers unless server.zombie?
|
38
|
+
end
|
39
|
+
puts "Waiting for #{running} workers"
|
40
|
+
break if running == 0
|
41
|
+
sleep 1
|
42
|
+
end
|
35
43
|
|
36
44
|
puts 'Enqueuing jobs'
|
37
|
-
first = RocketJob::Jobs::SimpleJob.create(priority: 1, destroy_on_complete: false)
|
45
|
+
first = RocketJob::Jobs::SimpleJob.create!(priority: 1, destroy_on_complete: false)
|
38
46
|
(count - 2).times { |i| RocketJob::Jobs::SimpleJob.create! }
|
39
|
-
last = RocketJob::Jobs::SimpleJob.create(priority: 100, destroy_on_complete: false)
|
47
|
+
last = RocketJob::Jobs::SimpleJob.create!(priority: 100, destroy_on_complete: false)
|
40
48
|
|
41
49
|
puts 'Resuming workers'
|
42
50
|
RocketJob::Server.resume_all
|
@@ -51,7 +59,7 @@ module RocketJob
|
|
51
59
|
|
52
60
|
# Export the Results hash to a CSV file
|
53
61
|
def export_results(results)
|
54
|
-
CSV.open("job_results_#{ruby}_#{
|
62
|
+
CSV.open("job_results_#{ruby}_#{servers}s_#{workers}w_v#{version}.csv", 'wb') do |csv|
|
55
63
|
csv << results.first.keys
|
56
64
|
results.each { |result| csv << result.values }
|
57
65
|
end
|
@@ -148,11 +148,7 @@ module RocketJob
|
|
148
148
|
# Scope for queued jobs that can run now
|
149
149
|
# I.e. Queued jobs excluding scheduled jobs
|
150
150
|
def queued_now
|
151
|
-
queued.or(
|
152
|
-
{:run_at.exists => false},
|
153
|
-
{:run_at => nil},
|
154
|
-
{:run_at.lte => Time.now}
|
155
|
-
)
|
151
|
+
queued.or({:run_at => nil}, {:run_at.lte => Time.now})
|
156
152
|
end
|
157
153
|
|
158
154
|
# DEPRECATED
|
@@ -12,24 +12,34 @@ module RocketJob
|
|
12
12
|
# Store all job types in this collection
|
13
13
|
store_in collection: 'rocket_job.jobs'
|
14
14
|
|
15
|
+
after_initialize :remove_arguments
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
15
19
|
# Retrieves the next job to work on in priority based order
|
16
20
|
# and assigns it to this worker
|
17
21
|
#
|
18
22
|
# Returns nil if no jobs are available for processing
|
19
23
|
#
|
20
24
|
# Parameters
|
21
|
-
# worker_name [String]
|
25
|
+
# worker_name: [String]
|
22
26
|
# Name of the worker that will be processing this job
|
23
27
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
# filter: [Hash]
|
29
|
+
# Filter to apply to the query.
|
30
|
+
# For example: to exclude jobs from being returned.
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# # Skip any job ids from the job_ids_list
|
34
|
+
# filter = {:id.nin => job_ids_list}
|
35
|
+
# job = RocketJob::Job.rocket_job_retrieve('host:pid:worker', filter)
|
36
|
+
def rocket_job_retrieve(worker_name, filter)
|
37
|
+
SemanticLogger.silence(:info) do
|
38
|
+
query = queued_now
|
39
|
+
query = query.where(filter) unless filter.blank?
|
40
|
+
update = {'$set' => {'worker_name' => worker_name, 'state' => 'running', 'started_at' => Time.now}}
|
41
|
+
query.sort(priority: 1, _id: 1).find_one_and_update(update, bypass_document_validation: true)
|
42
|
+
end
|
33
43
|
end
|
34
44
|
|
35
45
|
# Returns [Hash<String:Integer>] of the number of jobs in each state
|
@@ -59,7 +69,7 @@ module RocketJob
|
|
59
69
|
# :running => 25,
|
60
70
|
# :completed => 1237
|
61
71
|
# }
|
62
|
-
def
|
72
|
+
def counts_by_state
|
63
73
|
counts = {}
|
64
74
|
collection.aggregate([
|
65
75
|
{
|
@@ -86,7 +96,6 @@ module RocketJob
|
|
86
96
|
end
|
87
97
|
counts
|
88
98
|
end
|
89
|
-
|
90
99
|
end
|
91
100
|
|
92
101
|
# Set in-memory job to complete if `destroy_on_complete` and the job has been destroyed
|
@@ -104,6 +113,13 @@ module RocketJob
|
|
104
113
|
end
|
105
114
|
end
|
106
115
|
|
116
|
+
private
|
117
|
+
|
118
|
+
# Remove old style arguments that were stored as an array
|
119
|
+
def remove_arguments
|
120
|
+
attributes.delete('arguments') unless respond_to?('arguments='.to_sym)
|
121
|
+
end
|
122
|
+
|
107
123
|
end
|
108
124
|
end
|
109
125
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module RocketJob
|
5
|
+
module Plugins
|
6
|
+
module Job
|
7
|
+
# Throttle number of jobs of a specific class that are processed at the same time.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# class MyJob < RocketJob
|
11
|
+
# # Maximum number of workers to process instances of this job at the same time.
|
12
|
+
# self.throttle_max_workers = 25
|
13
|
+
#
|
14
|
+
# def perform
|
15
|
+
# # ....
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Notes:
|
20
|
+
# - The actual number will be around this value, it con go over slightly and
|
21
|
+
# can drop depending on check interval can drop slightly below this value.
|
22
|
+
# - By avoid hard locks and counters performance can be maintained while still
|
23
|
+
# supporting good enough throttling.
|
24
|
+
# - If throughput is not as important as preventing brief spikes when many
|
25
|
+
# workers are running, add a double check into the perform:
|
26
|
+
# class MyJob < RocketJob
|
27
|
+
# self.throttle_max_workers = 25
|
28
|
+
#
|
29
|
+
# def perform
|
30
|
+
# # (Optional) Prevent a brief spike from exceeding the wax worker throttle
|
31
|
+
# self.class.throttle_double_check
|
32
|
+
#
|
33
|
+
# # ....
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
module Throttle
|
37
|
+
extend ActiveSupport::Concern
|
38
|
+
|
39
|
+
included do
|
40
|
+
class_attribute :throttle_max_workers
|
41
|
+
self.throttle_max_workers = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Throttle to add when the throttle is exceeded
|
45
|
+
def throttle_filter
|
46
|
+
{:_type.nin => self.class.name}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns [Boolean] whether the throttle for this job has been exceeded
|
50
|
+
def throttle_exceeded?
|
51
|
+
throttle_max_workers && (throttle_max_workers != 0) ? (self.class.running.count >= throttle_max_workers) : false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Prevent a brief spike from exceeding the wax worker throttle
|
55
|
+
def throttle_double_check(check_seconds = 1)
|
56
|
+
while !throttle_exceeded?
|
57
|
+
sleep check_seconds
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Merge filter(s)
|
64
|
+
def throttle_merge_filter(target, source)
|
65
|
+
source.each_key do |k, v|
|
66
|
+
previous = target[k]
|
67
|
+
target[k] = previous ? (Array(previous) << v) : v
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -48,8 +48,8 @@ module RocketJob
|
|
48
48
|
#
|
49
49
|
# Note:
|
50
50
|
# If a job is in queued state it will be started
|
51
|
-
def rocket_job_next_job(worker_name,
|
52
|
-
while (job = rocket_job_retrieve(worker_name,
|
51
|
+
def rocket_job_next_job(worker_name, filter = {})
|
52
|
+
while (job = rocket_job_retrieve(worker_name, filter))
|
53
53
|
case
|
54
54
|
when job.running?
|
55
55
|
# Batch Job
|
@@ -57,6 +57,11 @@ module RocketJob
|
|
57
57
|
when job.expired?
|
58
58
|
job.rocket_job_fail_on_exception!(worker_name) { job.destroy }
|
59
59
|
logger.info "Destroyed expired job #{job.class.name}, id:#{job.id}"
|
60
|
+
when job.throttle_exceeded?
|
61
|
+
# Add jobs filter to the current filter
|
62
|
+
throttle_merge_filter(filter, job.throttle_filter)
|
63
|
+
# Restore retrieved job so that other workers can process it later
|
64
|
+
job.set(worker_name: nil, state: :queued)
|
60
65
|
else
|
61
66
|
job.worker_name = worker_name
|
62
67
|
job.rocket_job_fail_on_exception!(worker_name) do
|
@@ -70,7 +75,7 @@ module RocketJob
|
|
70
75
|
# Requeues all jobs that were running on a server that died
|
71
76
|
def requeue_dead_server(server_name)
|
72
77
|
# Need to requeue paused, failed since user may have transitioned job before it finished
|
73
|
-
where(:state.in => [:running, :paused, :
|
78
|
+
where(:state.in => [:running, :paused, :failed]).each do |job|
|
74
79
|
job.requeue!(server_name) if job.may_requeue?(server_name)
|
75
80
|
end
|
76
81
|
end
|
@@ -136,19 +141,24 @@ module RocketJob
|
|
136
141
|
# is set in the job itself.
|
137
142
|
#
|
138
143
|
# Thread-safe, can be called by multiple threads at the same time
|
139
|
-
def rocket_job_work(worker, re_raise_exceptions = false)
|
144
|
+
def rocket_job_work(worker, re_raise_exceptions = false, filter = nil)
|
140
145
|
raise(ArgumentError, 'Job must be started before calling #rocket_job_work') unless running?
|
141
146
|
rocket_job_fail_on_exception!(worker.name, re_raise_exceptions) do
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
147
|
+
if _perform_callbacks.empty?
|
148
|
+
@rocket_job_output = perform
|
149
|
+
else
|
150
|
+
# Allows @rocket_job_output to be modified by after/around callbacks
|
151
|
+
run_callbacks(:perform) do
|
152
|
+
# Allow callbacks to fail, complete or abort the job
|
153
|
+
@rocket_job_output = perform if running?
|
150
154
|
end
|
151
155
|
end
|
156
|
+
|
157
|
+
if collect_output?
|
158
|
+
# Result must be a Hash, if not put it in a Hash
|
159
|
+
self.result = @rocket_job_output.is_a?(Hash) ? @rocket_job_output : {'result' => @rocket_job_output}
|
160
|
+
end
|
161
|
+
|
152
162
|
if new_record? || destroyed?
|
153
163
|
complete if may_complete?
|
154
164
|
else
|
data/lib/rocket_job/server.rb
CHANGED
@@ -30,6 +30,8 @@ module RocketJob
|
|
30
30
|
include Plugins::StateMachine
|
31
31
|
include SemanticLogger::Loggable
|
32
32
|
|
33
|
+
store_in collection: 'rocket_job.servers'
|
34
|
+
|
33
35
|
# Unique Name of this server instance
|
34
36
|
# Default: `host name:PID`
|
35
37
|
# The unique name is used on re-start to re-queue any jobs that were being processed
|
@@ -43,6 +45,9 @@ module RocketJob
|
|
43
45
|
# When this server process was started
|
44
46
|
field :started_at, type: Time
|
45
47
|
|
48
|
+
# Filter to apply to control which job classes this server can process
|
49
|
+
field :filter, type: Hash
|
50
|
+
|
46
51
|
# The heartbeat information for this server
|
47
52
|
embeds_one :heartbeat, class_name: 'RocketJob::Heartbeat'
|
48
53
|
|
@@ -188,7 +193,6 @@ module RocketJob
|
|
188
193
|
|
189
194
|
server = create!(attrs)
|
190
195
|
server.send(:run)
|
191
|
-
|
192
196
|
ensure
|
193
197
|
server.destroy if server
|
194
198
|
end
|
@@ -225,22 +229,31 @@ module RocketJob
|
|
225
229
|
logger.info "Using MongoDB Database: #{RocketJob::Job.collection.database.name}"
|
226
230
|
build_heartbeat(updated_at: Time.now, workers: 0)
|
227
231
|
started!
|
228
|
-
|
229
|
-
logger.info "RocketJob Server started with #{workers.size} workers running"
|
232
|
+
logger.info 'RocketJob Server started'
|
230
233
|
|
234
|
+
stagger = true
|
231
235
|
while running? || paused?
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
236
|
+
SemanticLogger.silence(:info) do
|
237
|
+
find_and_update(
|
238
|
+
'heartbeat.updated_at' => Time.now,
|
239
|
+
'heartbeat.workers' => worker_count
|
240
|
+
)
|
241
|
+
end
|
242
|
+
if paused?
|
243
|
+
workers.each(&:shutdown!)
|
244
|
+
stagger = true
|
245
|
+
end
|
238
246
|
|
239
247
|
# In case number of threads has been modified
|
240
|
-
adjust_workers
|
248
|
+
adjust_workers(stagger)
|
249
|
+
stagger = false
|
241
250
|
|
242
251
|
# Stop server if shutdown indicator was set
|
243
|
-
|
252
|
+
if self.class.shutdown? && may_stop?
|
253
|
+
stop!
|
254
|
+
else
|
255
|
+
sleep Config.instance.heartbeat_seconds
|
256
|
+
end
|
244
257
|
end
|
245
258
|
|
246
259
|
logger.info 'Waiting for workers to stop'
|
@@ -293,7 +306,7 @@ module RocketJob
|
|
293
306
|
# that not all workers poll at the same time
|
294
307
|
# The worker also respond faster than max_poll_seconds when a new
|
295
308
|
# job is added.
|
296
|
-
def adjust_workers(stagger_workers=false)
|
309
|
+
def adjust_workers(stagger_workers = false)
|
297
310
|
count = worker_count
|
298
311
|
# Cleanup workers that have stopped
|
299
312
|
if count != workers.count
|
@@ -301,6 +314,8 @@ module RocketJob
|
|
301
314
|
workers.delete_if { |t| !t.alive? }
|
302
315
|
end
|
303
316
|
|
317
|
+
return unless running?
|
318
|
+
|
304
319
|
# Need to add more workers?
|
305
320
|
if count < max_workers
|
306
321
|
worker_count = max_workers - count
|
@@ -310,7 +325,7 @@ module RocketJob
|
|
310
325
|
return if shutdown?
|
311
326
|
# Start worker
|
312
327
|
begin
|
313
|
-
workers << Worker.new(id: next_worker_id, server_name: name)
|
328
|
+
workers << Worker.new(id: next_worker_id, server_name: name, filter: filter)
|
314
329
|
rescue Exception => exc
|
315
330
|
logger.fatal('Cannot start worker', exc)
|
316
331
|
end
|
data/lib/rocket_job/version.rb
CHANGED
data/lib/rocket_job/worker.rb
CHANGED
@@ -17,7 +17,7 @@ module RocketJob
|
|
17
17
|
|
18
18
|
define_callbacks :running
|
19
19
|
|
20
|
-
attr_accessor :id, :
|
20
|
+
attr_accessor :id, :re_check_seconds, :filter, :current_filter
|
21
21
|
attr_reader :thread, :name
|
22
22
|
|
23
23
|
def self.before_running(*filters, &blk)
|
@@ -32,7 +32,7 @@ module RocketJob
|
|
32
32
|
set_callback(:running, :around, *filters, &blk)
|
33
33
|
end
|
34
34
|
|
35
|
-
def initialize(id: 0, server_name: 'inline', inline: false)
|
35
|
+
def initialize(id: 0, server_name: 'inline', inline: false, re_check_seconds: Config.instance.re_check_seconds, filter: nil)
|
36
36
|
@id = id
|
37
37
|
@server_name = server_name
|
38
38
|
if defined?(Concurrent::JavaAtomicBoolean) || defined?(Concurrent::CAtomicBoolean)
|
@@ -40,8 +40,12 @@ module RocketJob
|
|
40
40
|
else
|
41
41
|
@shutdown = false
|
42
42
|
end
|
43
|
-
@name
|
44
|
-
@thread
|
43
|
+
@name = "#{server_name}:#{id}"
|
44
|
+
@thread = Thread.new { run } unless inline
|
45
|
+
@re_check_seconds = re_check_seconds
|
46
|
+
@re_check_start = Time.now
|
47
|
+
@filter = filter || {}
|
48
|
+
@current_filter = @filter.dup
|
45
49
|
end
|
46
50
|
|
47
51
|
if defined?(Concurrent::JavaAtomicBoolean) || defined?(Concurrent::CAtomicBoolean)
|
@@ -87,21 +91,23 @@ module RocketJob
|
|
87
91
|
rescue Exception => exc
|
88
92
|
logger.fatal('Unhandled exception in job processing thread', exc)
|
89
93
|
ensure
|
90
|
-
# TODO: Move to after_running callback
|
91
94
|
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord::Base)
|
92
95
|
end
|
93
96
|
|
94
97
|
# Process the next available job
|
95
98
|
# Returns [Boolean] whether any job was actually processed
|
96
99
|
def process_available_jobs
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
+
# Only clear out the current_filter after every `re_check_seconds`
|
101
|
+
time = Time.now
|
102
|
+
if (time - @re_check_start) > re_check_seconds
|
103
|
+
@recheck_start = time
|
104
|
+
self.current_filter = filter.dup
|
105
|
+
end
|
106
|
+
|
107
|
+
processed = false
|
108
|
+
while (job = Job.rocket_job_next_job(name, current_filter)) && !shutdown?
|
100
109
|
logger.fast_tag("job:#{job.id}") do
|
101
|
-
|
102
|
-
# Need to skip the specified job due to throttling or no work available
|
103
|
-
skip_job_ids << job.id
|
104
|
-
else
|
110
|
+
unless job.rocket_job_work(self, false, current_filter)
|
105
111
|
processed = true
|
106
112
|
end
|
107
113
|
end
|
data/lib/rocketjob.rb
CHANGED
@@ -25,6 +25,7 @@ module RocketJob
|
|
25
25
|
autoload :Logger, 'rocket_job/plugins/job/logger'
|
26
26
|
autoload :Model, 'rocket_job/plugins/job/model'
|
27
27
|
autoload :Persistence, 'rocket_job/plugins/job/persistence'
|
28
|
+
autoload :Throttle, 'rocket_job/plugins/job/throttle'
|
28
29
|
autoload :Worker, 'rocket_job/plugins/job/worker'
|
29
30
|
end
|
30
31
|
module Rufus
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
# Unit Test for RocketJob::Job
|
4
|
+
module Plugins
|
5
|
+
module Job
|
6
|
+
class ThrottleTest < Minitest::Test
|
7
|
+
|
8
|
+
class ThrottleJob < RocketJob::Job
|
9
|
+
# Only allow one to be processed at a time
|
10
|
+
self.throttle_max_workers = 1
|
11
|
+
|
12
|
+
def perform
|
13
|
+
21
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe RocketJob::Plugins::Job::Logger do
|
18
|
+
before do
|
19
|
+
ThrottleJob.delete_all
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#throttle_exceeded?' do
|
23
|
+
it 'does not exceed throttle when no other jobs are running' do
|
24
|
+
ThrottleJob.create!
|
25
|
+
job = ThrottleJob.new
|
26
|
+
refute job.throttle_exceeded?
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'exceeds throttle when other jobs are running' do
|
30
|
+
job1 = ThrottleJob.new
|
31
|
+
job1.start!
|
32
|
+
job2 = ThrottleJob.new
|
33
|
+
assert job2.throttle_exceeded?
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'excludes paused jobs' do
|
37
|
+
job1 = ThrottleJob.new
|
38
|
+
job1.start
|
39
|
+
job1.pause!
|
40
|
+
job2 = ThrottleJob.new
|
41
|
+
refute job2.throttle_exceeded?
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'excludes failed jobs' do
|
45
|
+
job1 = ThrottleJob.new
|
46
|
+
job1.start
|
47
|
+
job1.fail!
|
48
|
+
job2 = ThrottleJob.new
|
49
|
+
refute job2.throttle_exceeded?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
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.0.0.
|
4
|
+
version: 3.0.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/rocket_job/plugins/job/model.rb
|
102
102
|
- lib/rocket_job/plugins/job/persistence.rb
|
103
103
|
- lib/rocket_job/plugins/job/state_machine.rb
|
104
|
+
- lib/rocket_job/plugins/job/throttle.rb
|
104
105
|
- lib/rocket_job/plugins/job/worker.rb
|
105
106
|
- lib/rocket_job/plugins/processing_window.rb
|
106
107
|
- lib/rocket_job/plugins/restart.rb
|
@@ -127,6 +128,7 @@ files:
|
|
127
128
|
- test/plugins/job/model_test.rb
|
128
129
|
- test/plugins/job/persistence_test.rb
|
129
130
|
- test/plugins/job/state_machine_test.rb
|
131
|
+
- test/plugins/job/throttle_test.rb
|
130
132
|
- test/plugins/job/worker_test.rb
|
131
133
|
- test/plugins/processing_window_test.rb
|
132
134
|
- test/plugins/restart_test.rb
|
@@ -173,6 +175,7 @@ test_files:
|
|
173
175
|
- test/plugins/job/model_test.rb
|
174
176
|
- test/plugins/job/persistence_test.rb
|
175
177
|
- test/plugins/job/state_machine_test.rb
|
178
|
+
- test/plugins/job/throttle_test.rb
|
176
179
|
- test/plugins/job/worker_test.rb
|
177
180
|
- test/plugins/processing_window_test.rb
|
178
181
|
- test/plugins/restart_test.rb
|