rocketjob 3.0.0.beta2 → 3.0.0.beta3
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/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
|