rocketjob 6.3.2 → 7.0.0
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 +38 -316
- data/bin/rocketjob_batch_perf +1 -1
- data/lib/rocket_job/batch/categories.rb +25 -25
- data/lib/rocket_job/batch/io.rb +21 -15
- data/lib/rocket_job/batch/logger.rb +2 -2
- data/lib/rocket_job/batch/model.rb +2 -2
- data/lib/rocket_job/batch/performance.rb +1 -1
- data/lib/rocket_job/batch/statistics.rb +9 -2
- data/lib/rocket_job/batch/throttle.rb +7 -2
- data/lib/rocket_job/batch/throttle_running_workers.rb +5 -1
- data/lib/rocket_job/batch/throttle_windows.rb +4 -2
- data/lib/rocket_job/batch/worker.rb +51 -11
- data/lib/rocket_job/config.rb +6 -1
- data/lib/rocket_job/dirmon_entry.rb +6 -4
- data/lib/rocket_job/event.rb +84 -4
- data/lib/rocket_job/extensions/mongo/logging.rb +1 -0
- data/lib/rocket_job/extensions/mongoid/clients/options.rb +3 -3
- data/lib/rocket_job/extensions/mongoid/contextual/mongo.rb +3 -4
- data/lib/rocket_job/extensions/mongoid/factory.rb +2 -2
- data/lib/rocket_job/extensions/psych/yaml_tree.rb +8 -4
- data/lib/rocket_job/extensions/rocket_job_adapter.rb +1 -1
- data/lib/rocket_job/jobs/active_job.rb +1 -1
- data/lib/rocket_job/jobs/dirmon_job.rb +3 -1
- data/lib/rocket_job/jobs/on_demand_batch_job.rb +1 -1
- data/lib/rocket_job/jobs/upload_file_job.rb +8 -5
- data/lib/rocket_job/lookup_collection.rb +2 -2
- data/lib/rocket_job/performance.rb +1 -1
- data/lib/rocket_job/plugins/cron.rb +6 -3
- data/lib/rocket_job/plugins/document.rb +0 -2
- data/lib/rocket_job/plugins/job/logger.rb +2 -2
- data/lib/rocket_job/plugins/job/model.rb +1 -1
- data/lib/rocket_job/plugins/job/persistence.rb +2 -2
- data/lib/rocket_job/plugins/job/throttle.rb +14 -2
- data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +4 -1
- data/lib/rocket_job/plugins/job/worker.rb +5 -2
- data/lib/rocket_job/plugins/retry.rb +1 -1
- data/lib/rocket_job/plugins/throttle_dependent_jobs.rb +7 -2
- data/lib/rocket_job/plugins/transaction.rb +2 -2
- data/lib/rocket_job/sliced/input.rb +17 -12
- data/lib/rocket_job/sliced/slice.rb +3 -3
- data/lib/rocket_job/sliced/slices.rb +3 -6
- data/lib/rocket_job/supervisor.rb +2 -2
- data/lib/rocket_job/thread_worker.rb +3 -3
- data/lib/rocket_job/throttle_definition.rb +19 -2
- data/lib/rocket_job/throttle_definitions.rb +14 -9
- data/lib/rocket_job/version.rb +1 -1
- data/lib/rocket_job/worker.rb +77 -28
- data/lib/rocketjob.rb +1 -2
- metadata +33 -27
- data/lib/rocket_job/ractor_worker.rb +0 -42
|
@@ -13,8 +13,13 @@ module RocketJob
|
|
|
13
13
|
included do
|
|
14
14
|
field :dependent_jobs, type: Array, class_attribute: true, user_editable: true, copy_on_restart: true
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
dependent_jobs_description = lambda { |job, *|
|
|
17
|
+
"Throttled: dependent jobs are running (#{Array(job.dependent_jobs).join(', ')})"
|
|
18
|
+
}
|
|
19
|
+
define_throttle :dependent_jobs_running?, description: dependent_jobs_description
|
|
20
|
+
if respond_to?(:define_batch_throttle)
|
|
21
|
+
define_batch_throttle :dependent_jobs_running?, description: dependent_jobs_description
|
|
22
|
+
end
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
class_methods do
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module RocketJob
|
|
2
2
|
module Sliced
|
|
3
3
|
class Input < Slices
|
|
4
|
-
def upload(**args, &
|
|
4
|
+
def upload(**args, &)
|
|
5
5
|
# Create indexes before uploading
|
|
6
6
|
create_indexes
|
|
7
|
-
Writer::Input.collect(self, **args, &
|
|
7
|
+
Writer::Input.collect(self, **args, &)
|
|
8
8
|
rescue Exception => e
|
|
9
9
|
drop
|
|
10
10
|
raise(e)
|
|
@@ -117,19 +117,24 @@ module RocketJob
|
|
|
117
117
|
)
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
-
#
|
|
121
|
-
# Returns nil if there are currently no queued slices
|
|
120
|
+
# Claims and returns the next queued slice for this worker.
|
|
121
|
+
# Returns nil if there are currently no queued slices.
|
|
122
122
|
#
|
|
123
|
-
#
|
|
123
|
+
# No explicit sort is applied. Forcing the global minimum `_id` (a
|
|
124
|
+
# `sort("_id" => 1)`) makes every worker target the same document, so under
|
|
125
|
+
# concurrency they collide on the atomic claim: one wins and the rest hit a
|
|
126
|
+
# WriteConflict ("Document no longer matches the predicate") and retry,
|
|
127
|
+
# which throttles a large batch job as workers are added. Without the sort,
|
|
128
|
+
# the `{state: 1, _id: 1}` index still yields queued slices in roughly `_id`
|
|
129
|
+
# (upload) order, but concurrent workers land on different documents instead
|
|
130
|
+
# of contending for one. Output ordering is unaffected: each output slice
|
|
131
|
+
# inherits its input slice's `_id` and downloads read in `_id` order.
|
|
124
132
|
def next_slice(worker_name)
|
|
125
|
-
# TODO: Will it perform faster without the id sort?
|
|
126
|
-
# I.e. Just process on a FIFO basis?
|
|
127
133
|
document = all.queued.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
134
|
+
find_one_and_update(
|
|
135
|
+
{"$set" => {worker_name: worker_name, state: "running", started_at: Time.now}},
|
|
136
|
+
return_document: :after
|
|
137
|
+
)
|
|
133
138
|
document.collection_name = collection_name if document
|
|
134
139
|
document
|
|
135
140
|
end
|
|
@@ -154,8 +154,8 @@ module RocketJob
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
# Fail this slice if an exception occurs during processing.
|
|
157
|
-
def fail_on_exception!(re_raise_exceptions = false, &
|
|
158
|
-
SemanticLogger.named_tagged(slice: id.to_s, &
|
|
157
|
+
def fail_on_exception!(re_raise_exceptions = false, &)
|
|
158
|
+
SemanticLogger.named_tagged(slice: id.to_s, &)
|
|
159
159
|
rescue Exception => e
|
|
160
160
|
SemanticLogger.named_tagged(slice: id.to_s) do
|
|
161
161
|
if failed? || !may_fail?
|
|
@@ -175,7 +175,7 @@ module RocketJob
|
|
|
175
175
|
|
|
176
176
|
# Always add records to any updates.
|
|
177
177
|
def atomic_updates(*args)
|
|
178
|
-
r
|
|
178
|
+
r = super
|
|
179
179
|
(r["$set"] ||= {})["records"] = serialize_records if @records
|
|
180
180
|
r
|
|
181
181
|
end
|
|
@@ -44,8 +44,8 @@ module RocketJob
|
|
|
44
44
|
|
|
45
45
|
# Returns output slices in the order of their id
|
|
46
46
|
# which is usually the order in which they were written.
|
|
47
|
-
def each(&
|
|
48
|
-
all.sort(id: 1).each(&
|
|
47
|
+
def each(&)
|
|
48
|
+
all.sort(id: 1).each(&)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Insert a new slice into the collection
|
|
@@ -120,7 +120,7 @@ module RocketJob
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# Forward additional methods.
|
|
123
|
-
def_instance_delegators :@all, :collection, :count, :delete_all, :
|
|
123
|
+
def_instance_delegators :@all, :collection, :count, :delete_all, :find, :nor, :not, :or, :to_a, :where
|
|
124
124
|
|
|
125
125
|
# Drop this collection when it is no longer needed
|
|
126
126
|
def drop
|
|
@@ -145,7 +145,6 @@ module RocketJob
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
# Mongoid does not apply ordering, add sort
|
|
148
|
-
# rubocop:disable Style/RedundantSort
|
|
149
148
|
def first
|
|
150
149
|
all.sort("_id" => 1).first
|
|
151
150
|
end
|
|
@@ -154,8 +153,6 @@ module RocketJob
|
|
|
154
153
|
all.sort("_id" => -1).first
|
|
155
154
|
end
|
|
156
155
|
|
|
157
|
-
# rubocop:enable Style/RedundantSort
|
|
158
|
-
|
|
159
156
|
# Returns [Array<Struct>] grouped exceptions by class name,
|
|
160
157
|
# and unique exception messages by exception class.
|
|
161
158
|
#
|
|
@@ -8,7 +8,7 @@ module RocketJob
|
|
|
8
8
|
attr_reader :thread
|
|
9
9
|
|
|
10
10
|
def initialize(id:, server_name:)
|
|
11
|
-
super
|
|
11
|
+
super
|
|
12
12
|
@shutdown = Concurrent::Event.new
|
|
13
13
|
@thread = Thread.new { run }
|
|
14
14
|
end
|
|
@@ -21,8 +21,8 @@ module RocketJob
|
|
|
21
21
|
@thread.backtrace
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def join(*
|
|
25
|
-
@thread.join(*
|
|
24
|
+
def join(*)
|
|
25
|
+
@thread.join(*)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Send each active worker the RocketJob::ShutdownException so that stops processing immediately.
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
module RocketJob
|
|
2
2
|
class ThrottleDefinition
|
|
3
|
-
attr_reader :method_name, :filter
|
|
3
|
+
attr_reader :method_name, :filter, :description
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Parameters:
|
|
6
|
+
# description: [String|Proc|nil]
|
|
7
|
+
# Human readable reason why the job is throttled, persisted to the job as
|
|
8
|
+
# `throttled_by` and surfaced in Mission Control.
|
|
9
|
+
# When a Proc, it is called with the same arguments as the throttle and must
|
|
10
|
+
# return a String, allowing the reason to include runtime detail.
|
|
11
|
+
# When nil, a humanized version of the method name is used.
|
|
12
|
+
def initialize(method_name, filter, description = nil)
|
|
6
13
|
@method_name = method_name.to_sym
|
|
7
14
|
@filter = filter
|
|
15
|
+
@description = description
|
|
8
16
|
end
|
|
9
17
|
|
|
10
18
|
# Returns [true|false] whether the throttle was triggered.
|
|
@@ -33,5 +41,14 @@ module RocketJob
|
|
|
33
41
|
job.send(filter)
|
|
34
42
|
end
|
|
35
43
|
end
|
|
44
|
+
|
|
45
|
+
# Returns [String] the human readable reason why the job is throttled.
|
|
46
|
+
def extract_description(job, *)
|
|
47
|
+
return description.call(job, *) if description.is_a?(Proc)
|
|
48
|
+
return description if description
|
|
49
|
+
|
|
50
|
+
# Default: humanize the method name, dropping a trailing `?` or `_exceeded`.
|
|
51
|
+
method_name.to_s.sub(/_exceeded\?\z/, "").sub(/\?\z/, "").tr("_", " ").capitalize
|
|
52
|
+
end
|
|
36
53
|
end
|
|
37
54
|
end
|
|
@@ -6,13 +6,13 @@ module RocketJob
|
|
|
6
6
|
@throttles = []
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def add(method_name, filter)
|
|
9
|
+
def add(method_name, filter, description = nil)
|
|
10
10
|
unless filter.is_a?(Symbol) || filter.is_a?(Proc)
|
|
11
11
|
raise(ArgumentError, "Filter for #{method_name} must be a Symbol or Proc")
|
|
12
12
|
end
|
|
13
13
|
raise(ArgumentError, "Cannot define #{method_name} twice, undefine previous throttle first") if exist?(method_name)
|
|
14
14
|
|
|
15
|
-
@throttles += [ThrottleDefinition.new(method_name, filter)]
|
|
15
|
+
@throttles += [ThrottleDefinition.new(method_name, filter, description)]
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Undefine a previously defined throttle
|
|
@@ -25,15 +25,20 @@ module RocketJob
|
|
|
25
25
|
throttles.any? { |throttle| throttle.method_name == method_name }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# Returns the
|
|
28
|
+
# Returns [ThrottleDefinition] the first throttle that was triggered,
|
|
29
29
|
# or nil if no throttles were triggered.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
#
|
|
31
|
+
# The returned definition exposes both the filter (`extract_filter`) and the
|
|
32
|
+
# human readable reason (`extract_description`) so callers can apply the filter
|
|
33
|
+
# and persist why the job was throttled.
|
|
34
|
+
def matching_throttle(job, *args)
|
|
35
|
+
throttles.find { |throttle| throttle.throttled?(job, *args) }
|
|
36
|
+
end
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
# Returns the matching filter,
|
|
39
|
+
# or nil if no throttles were triggered.
|
|
40
|
+
def matching_filter(job, *)
|
|
41
|
+
matching_throttle(job, *)&.extract_filter(job, *)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def deep_dup
|
data/lib/rocket_job/version.rb
CHANGED
data/lib/rocket_job/worker.rb
CHANGED
|
@@ -63,25 +63,29 @@ module RocketJob
|
|
|
63
63
|
logger.info "Started"
|
|
64
64
|
|
|
65
65
|
until shutdown?
|
|
66
|
-
sleep_seconds = Config.max_poll_seconds
|
|
67
66
|
reset_filter_if_expired
|
|
68
67
|
job = next_available_job
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
if job
|
|
70
|
+
# Returns true when this job has no more work immediately available,
|
|
71
|
+
# for example a batch job that has run out of slices, or was throttled.
|
|
72
|
+
no_immediate_work = job.rocket_job_work(self, false)
|
|
73
|
+
|
|
74
|
+
# Return the database connections for this thread back to the connection pool.
|
|
75
|
+
release_active_record_connections
|
|
76
|
+
|
|
77
|
+
sleep_seconds =
|
|
78
|
+
if no_immediate_work
|
|
79
|
+
# Stagger workers so that they don't all poll at the same time.
|
|
80
|
+
random_wait_interval
|
|
77
81
|
else
|
|
78
|
-
|
|
82
|
+
# Work was performed and more queued work is likely available,
|
|
83
|
+
# so poll again immediately rather than waiting a full interval.
|
|
84
|
+
0
|
|
79
85
|
end
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# Stagger workers so that they don't all poll at the same time.
|
|
84
|
-
sleep_seconds = random_wait_interval
|
|
86
|
+
else
|
|
87
|
+
# No work was found, so wait the full poll interval before checking again.
|
|
88
|
+
sleep_seconds = Config.max_poll_seconds
|
|
85
89
|
end
|
|
86
90
|
|
|
87
91
|
wait_for_shutdown?(sleep_seconds)
|
|
@@ -91,12 +95,18 @@ module RocketJob
|
|
|
91
95
|
rescue Exception => e
|
|
92
96
|
logger.fatal("Unhandled exception in job processing thread", e)
|
|
93
97
|
ensure
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
release_active_record_connections
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return the database connections for this thread back to the connection pool.
|
|
102
|
+
def release_active_record_connections
|
|
103
|
+
return unless defined?(ActiveRecord::Base)
|
|
104
|
+
|
|
105
|
+
if ActiveRecord::Base.respond_to?(:connection_handler)
|
|
106
|
+
# Rails 7.2+
|
|
107
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
|
108
|
+
else
|
|
109
|
+
ActiveRecord::Base.clear_active_connections!
|
|
100
110
|
end
|
|
101
111
|
end
|
|
102
112
|
|
|
@@ -149,12 +159,19 @@ module RocketJob
|
|
|
149
159
|
# Whether the supplied job has been throttled and should be ignored.
|
|
150
160
|
def throttled_job?(job)
|
|
151
161
|
# Evaluate job throttles, if any.
|
|
152
|
-
|
|
153
|
-
return false unless
|
|
154
|
-
|
|
155
|
-
add_to_current_filter(
|
|
156
|
-
# Restore retrieved job so that other workers can process it later
|
|
157
|
-
|
|
162
|
+
throttle = job.rocket_job_throttles.matching_throttle(job)
|
|
163
|
+
return false unless throttle
|
|
164
|
+
|
|
165
|
+
add_to_current_filter(throttle.extract_filter(job))
|
|
166
|
+
# Restore retrieved job so that other workers can process it later, recording
|
|
167
|
+
# why it was throttled so it is visible in Mission Control. This reuses the
|
|
168
|
+
# write that requeues the job, so it adds no extra database round trip.
|
|
169
|
+
job.set(
|
|
170
|
+
worker_name: nil,
|
|
171
|
+
state: :queued,
|
|
172
|
+
throttled_by: throttle.extract_description(job),
|
|
173
|
+
throttled_at: Time.now
|
|
174
|
+
)
|
|
158
175
|
true
|
|
159
176
|
end
|
|
160
177
|
|
|
@@ -164,14 +181,46 @@ module RocketJob
|
|
|
164
181
|
# Applies the current filter to exclude filtered jobs.
|
|
165
182
|
#
|
|
166
183
|
# Returns nil if no jobs are available for processing.
|
|
184
|
+
#
|
|
185
|
+
# Notes:
|
|
186
|
+
# - An already running batch job is _joined_ with a read-only query, since
|
|
187
|
+
# concurrency for a batch job is coordinated per-slice (see
|
|
188
|
+
# `Sliced::Input#next_slice`), not on the job document. Writing
|
|
189
|
+
# `worker_name`/`state` to the shared job document on every poll turns it
|
|
190
|
+
# into a write-contention hotspot: with many workers MongoDB serializes the
|
|
191
|
+
# updates and retries the write conflicts (server log id 46404), which is
|
|
192
|
+
# what makes a large batch job slow down as workers are added.
|
|
193
|
+
# - Only a queued job is claimed with a write, transitioning it to running.
|
|
194
|
+
# That write is naturally distributed: each queued job is claimed once.
|
|
167
195
|
def find_and_assign_job
|
|
168
196
|
SemanticLogger.silence(:info) do
|
|
169
197
|
scheduled = RocketJob::Job.where(run_at: nil).or(:run_at.lte => Time.now)
|
|
170
198
|
working = RocketJob::Job.queued.or(state: "running", sub_state: "processing")
|
|
171
199
|
query = RocketJob::Job.and(working, scheduled)
|
|
172
200
|
query = query.and(current_filter) unless current_filter.blank?
|
|
173
|
-
|
|
174
|
-
|
|
201
|
+
query = query.sort(priority: 1, _id: 1)
|
|
202
|
+
|
|
203
|
+
# Retry only when a queued job was claimed by another worker first.
|
|
204
|
+
loop do
|
|
205
|
+
job = query.first
|
|
206
|
+
return nil unless job
|
|
207
|
+
|
|
208
|
+
# Already running batch job: join it without writing to the job document.
|
|
209
|
+
return job if job.running?
|
|
210
|
+
|
|
211
|
+
# Queued job: claim it atomically, guarding on it still being queued.
|
|
212
|
+
# find_one_and_update returns the pre-image (state: queued) so that the
|
|
213
|
+
# caller still runs throttles and `start!`.
|
|
214
|
+
# Clear any stale throttle reason from a previous throttled poll so a job
|
|
215
|
+
# that is now allowed to run no longer reports as throttled in Mission Control.
|
|
216
|
+
update = {
|
|
217
|
+
"$set" => {"worker_name" => name, "state" => "running"},
|
|
218
|
+
"$unset" => {"throttled_by" => "", "throttled_at" => ""}
|
|
219
|
+
}
|
|
220
|
+
claimed = RocketJob::Job.queued.where(id: job.id).
|
|
221
|
+
find_one_and_update(update, bypass_document_validation: true)
|
|
222
|
+
return claimed if claimed
|
|
223
|
+
end
|
|
175
224
|
end
|
|
176
225
|
end
|
|
177
226
|
|
data/lib/rocketjob.rb
CHANGED
|
@@ -31,7 +31,6 @@ module RocketJob
|
|
|
31
31
|
autoload :LookupCollection, "rocket_job/lookup_collection"
|
|
32
32
|
autoload :Worker, "rocket_job/worker"
|
|
33
33
|
autoload :Performance, "rocket_job/performance"
|
|
34
|
-
autoload :RactorWorker, "rocket_job/ractor_worker"
|
|
35
34
|
autoload :Server, "rocket_job/server"
|
|
36
35
|
autoload :Sliced, "rocket_job/sliced"
|
|
37
36
|
autoload :Subscriber, "rocket_job/subscriber"
|
|
@@ -83,7 +82,7 @@ module RocketJob
|
|
|
83
82
|
autoload :UploadFileJob, "rocket_job/jobs/upload_file_job"
|
|
84
83
|
|
|
85
84
|
module ReEncrypt
|
|
86
|
-
autoload :RelationalJob,
|
|
85
|
+
autoload :RelationalJob, "rocket_job/jobs/re_encrypt/relational_job"
|
|
87
86
|
end
|
|
88
87
|
end
|
|
89
88
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rocketjob
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 7.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Reid Morrison
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: aasm
|
|
@@ -56,62 +55,65 @@ dependencies:
|
|
|
56
55
|
name: iostreams
|
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
|
58
57
|
requirements:
|
|
59
|
-
- - "
|
|
58
|
+
- - "~>"
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
60
|
+
version: '2.0'
|
|
62
61
|
type: :runtime
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
|
-
- - "
|
|
65
|
+
- - "~>"
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
67
|
+
version: '2.0'
|
|
69
68
|
- !ruby/object:Gem::Dependency
|
|
70
69
|
name: mongoid
|
|
71
70
|
requirement: !ruby/object:Gem::Requirement
|
|
72
71
|
requirements:
|
|
73
72
|
- - ">="
|
|
74
73
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '
|
|
74
|
+
version: '8.1'
|
|
76
75
|
type: :runtime
|
|
77
76
|
prerelease: false
|
|
78
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
78
|
requirements:
|
|
80
79
|
- - ">="
|
|
81
80
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
81
|
+
version: '8.1'
|
|
83
82
|
- !ruby/object:Gem::Dependency
|
|
84
83
|
name: semantic_logger
|
|
85
84
|
requirement: !ruby/object:Gem::Requirement
|
|
86
85
|
requirements:
|
|
87
|
-
- - "
|
|
86
|
+
- - "~>"
|
|
88
87
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
88
|
+
version: '5.0'
|
|
90
89
|
type: :runtime
|
|
91
90
|
prerelease: false
|
|
92
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
92
|
requirements:
|
|
94
|
-
- - "
|
|
93
|
+
- - "~>"
|
|
95
94
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
95
|
+
version: '5.0'
|
|
97
96
|
- !ruby/object:Gem::Dependency
|
|
98
97
|
name: symmetric-encryption
|
|
99
98
|
requirement: !ruby/object:Gem::Requirement
|
|
100
99
|
requirements:
|
|
101
|
-
- - "
|
|
100
|
+
- - "~>"
|
|
102
101
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '4.
|
|
102
|
+
version: '4.6'
|
|
104
103
|
type: :runtime
|
|
105
104
|
prerelease: false
|
|
106
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
106
|
requirements:
|
|
108
|
-
- - "
|
|
107
|
+
- - "~>"
|
|
109
108
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '4.
|
|
111
|
-
description:
|
|
112
|
-
|
|
109
|
+
version: '4.6'
|
|
110
|
+
description: Rocket Job is a distributed, priority-based, MongoDB-backed batch processing
|
|
111
|
+
system for Ruby. Run conventional background jobs, or split a single job's input
|
|
112
|
+
into slices and process it concurrently across thousands of workers, spilling from
|
|
113
|
+
memory to disk so very large files never fall over.
|
|
113
114
|
executables:
|
|
114
115
|
- rocketjob
|
|
116
|
+
- rocketjob_batch_perf
|
|
115
117
|
- rocketjob_perf
|
|
116
118
|
extensions: []
|
|
117
119
|
extra_rdoc_files: []
|
|
@@ -186,7 +188,6 @@ files:
|
|
|
186
188
|
- lib/rocket_job/plugins/state_machine.rb
|
|
187
189
|
- lib/rocket_job/plugins/throttle_dependent_jobs.rb
|
|
188
190
|
- lib/rocket_job/plugins/transaction.rb
|
|
189
|
-
- lib/rocket_job/ractor_worker.rb
|
|
190
191
|
- lib/rocket_job/railtie.rb
|
|
191
192
|
- lib/rocket_job/rocket_job.rb
|
|
192
193
|
- lib/rocket_job/server.rb
|
|
@@ -217,11 +218,16 @@ files:
|
|
|
217
218
|
- lib/rocket_job/worker.rb
|
|
218
219
|
- lib/rocket_job/worker_pool.rb
|
|
219
220
|
- lib/rocketjob.rb
|
|
220
|
-
homepage: https://rocketjob.
|
|
221
|
+
homepage: https://rocketjob.reidmorrison.com
|
|
221
222
|
licenses:
|
|
222
223
|
- Apache-2.0
|
|
223
|
-
metadata:
|
|
224
|
-
|
|
224
|
+
metadata:
|
|
225
|
+
bug_tracker_uri: https://github.com/reidmorrison/rocketjob/issues
|
|
226
|
+
changelog_uri: https://github.com/reidmorrison/rocketjob/blob/main/CHANGELOG.md
|
|
227
|
+
documentation_uri: https://rocketjob.reidmorrison.com
|
|
228
|
+
homepage_uri: https://rocketjob.reidmorrison.com
|
|
229
|
+
source_code_uri: https://github.com/reidmorrison/rocketjob/tree/v7.0.0
|
|
230
|
+
rubygems_mfa_required: 'true'
|
|
225
231
|
rdoc_options: []
|
|
226
232
|
require_paths:
|
|
227
233
|
- lib
|
|
@@ -229,15 +235,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
229
235
|
requirements:
|
|
230
236
|
- - ">="
|
|
231
237
|
- !ruby/object:Gem::Version
|
|
232
|
-
version:
|
|
238
|
+
version: 3.2.0
|
|
233
239
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
240
|
requirements:
|
|
235
241
|
- - ">="
|
|
236
242
|
- !ruby/object:Gem::Version
|
|
237
243
|
version: '0'
|
|
238
244
|
requirements: []
|
|
239
|
-
rubygems_version: 3.
|
|
240
|
-
signing_key:
|
|
245
|
+
rubygems_version: 3.6.9
|
|
241
246
|
specification_version: 4
|
|
242
|
-
summary:
|
|
247
|
+
summary: Process millions of records across thousands of workers. A distributed, MongoDB-backed
|
|
248
|
+
batch processing system for Ruby.
|
|
243
249
|
test_files: []
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
module RocketJob
|
|
2
|
-
# Run each worker in its own "Ractor".
|
|
3
|
-
class RactorWorker < Worker
|
|
4
|
-
attr_reader :thread
|
|
5
|
-
|
|
6
|
-
def initialize(id:, server_name:)
|
|
7
|
-
super(id: id, server_name: server_name)
|
|
8
|
-
@shutdown = Concurrent::Event.new
|
|
9
|
-
@thread = Ractor.new(name: "rocketjob-#{id}") { run }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def alive?
|
|
13
|
-
@thread.alive?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def backtrace
|
|
17
|
-
@thread.backtrace
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def join(*args)
|
|
21
|
-
@thread.join(*args)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Send each active worker the RocketJob::ShutdownException so that stops processing immediately.
|
|
25
|
-
def kill
|
|
26
|
-
@thread.raise(Shutdown, "Shutdown due to kill request for worker: #{name}") if @thread.alive?
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def shutdown?
|
|
30
|
-
@shutdown.set?
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def shutdown!
|
|
34
|
-
@shutdown.set
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Returns [true|false] whether the shutdown indicator was set
|
|
38
|
-
def wait_for_shutdown?(timeout = nil)
|
|
39
|
-
@shutdown.wait(timeout)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|