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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -316
  3. data/bin/rocketjob_batch_perf +1 -1
  4. data/lib/rocket_job/batch/categories.rb +25 -25
  5. data/lib/rocket_job/batch/io.rb +21 -15
  6. data/lib/rocket_job/batch/logger.rb +2 -2
  7. data/lib/rocket_job/batch/model.rb +2 -2
  8. data/lib/rocket_job/batch/performance.rb +1 -1
  9. data/lib/rocket_job/batch/statistics.rb +9 -2
  10. data/lib/rocket_job/batch/throttle.rb +7 -2
  11. data/lib/rocket_job/batch/throttle_running_workers.rb +5 -1
  12. data/lib/rocket_job/batch/throttle_windows.rb +4 -2
  13. data/lib/rocket_job/batch/worker.rb +51 -11
  14. data/lib/rocket_job/config.rb +6 -1
  15. data/lib/rocket_job/dirmon_entry.rb +6 -4
  16. data/lib/rocket_job/event.rb +84 -4
  17. data/lib/rocket_job/extensions/mongo/logging.rb +1 -0
  18. data/lib/rocket_job/extensions/mongoid/clients/options.rb +3 -3
  19. data/lib/rocket_job/extensions/mongoid/contextual/mongo.rb +3 -4
  20. data/lib/rocket_job/extensions/mongoid/factory.rb +2 -2
  21. data/lib/rocket_job/extensions/psych/yaml_tree.rb +8 -4
  22. data/lib/rocket_job/extensions/rocket_job_adapter.rb +1 -1
  23. data/lib/rocket_job/jobs/active_job.rb +1 -1
  24. data/lib/rocket_job/jobs/dirmon_job.rb +3 -1
  25. data/lib/rocket_job/jobs/on_demand_batch_job.rb +1 -1
  26. data/lib/rocket_job/jobs/upload_file_job.rb +8 -5
  27. data/lib/rocket_job/lookup_collection.rb +2 -2
  28. data/lib/rocket_job/performance.rb +1 -1
  29. data/lib/rocket_job/plugins/cron.rb +6 -3
  30. data/lib/rocket_job/plugins/document.rb +0 -2
  31. data/lib/rocket_job/plugins/job/logger.rb +2 -2
  32. data/lib/rocket_job/plugins/job/model.rb +1 -1
  33. data/lib/rocket_job/plugins/job/persistence.rb +2 -2
  34. data/lib/rocket_job/plugins/job/throttle.rb +14 -2
  35. data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +4 -1
  36. data/lib/rocket_job/plugins/job/worker.rb +5 -2
  37. data/lib/rocket_job/plugins/retry.rb +1 -1
  38. data/lib/rocket_job/plugins/throttle_dependent_jobs.rb +7 -2
  39. data/lib/rocket_job/plugins/transaction.rb +2 -2
  40. data/lib/rocket_job/sliced/input.rb +17 -12
  41. data/lib/rocket_job/sliced/slice.rb +3 -3
  42. data/lib/rocket_job/sliced/slices.rb +3 -6
  43. data/lib/rocket_job/supervisor.rb +2 -2
  44. data/lib/rocket_job/thread_worker.rb +3 -3
  45. data/lib/rocket_job/throttle_definition.rb +19 -2
  46. data/lib/rocket_job/throttle_definitions.rb +14 -9
  47. data/lib/rocket_job/version.rb +1 -1
  48. data/lib/rocket_job/worker.rb +77 -28
  49. data/lib/rocketjob.rb +1 -2
  50. metadata +33 -27
  51. 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
- define_throttle :dependent_jobs_running?
17
- define_batch_throttle :dependent_jobs_running? if respond_to?(:define_batch_throttle)
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
@@ -41,8 +41,8 @@ module RocketJob
41
41
 
42
42
  private
43
43
 
44
- def rocket_job_transaction(&block)
45
- ActiveRecord::Base.transaction(&block)
44
+ def rocket_job_transaction(&)
45
+ ActiveRecord::Base.transaction(&)
46
46
  end
47
47
  end
48
48
  end
@@ -1,10 +1,10 @@
1
1
  module RocketJob
2
2
  module Sliced
3
3
  class Input < Slices
4
- def upload(**args, &block)
4
+ def upload(**args, &)
5
5
  # Create indexes before uploading
6
6
  create_indexes
7
- Writer::Input.collect(self, **args, &block)
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
- # Returns the next slice to work on in id order
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
- # If a slice is in queued state it will be started and assigned to this worker
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
- sort("_id" => 1).
129
- find_one_and_update(
130
- {"$set" => {worker_name: worker_name, state: "running", started_at: Time.now}},
131
- return_document: :after
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, &block)
158
- SemanticLogger.named_tagged(slice: id.to_s, &block)
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 = super(*args)
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(&block)
48
- all.sort(id: 1).each(&block)
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, :first, :find, :last, :nor, :not, :or, :to_a, :where
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
  #
@@ -92,8 +92,8 @@ module RocketJob
92
92
  end
93
93
  end
94
94
 
95
- def synchronize(&block)
96
- @mutex.synchronize(&block)
95
+ def synchronize(&)
96
+ @mutex.synchronize(&)
97
97
  end
98
98
  end
99
99
  end
@@ -8,7 +8,7 @@ module RocketJob
8
8
  attr_reader :thread
9
9
 
10
10
  def initialize(id:, server_name:)
11
- super(id: id, server_name: server_name)
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(*args)
25
- @thread.join(*args)
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
- def initialize(method_name, filter)
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 matching filter,
28
+ # Returns [ThrottleDefinition] the first throttle that was triggered,
29
29
  # or nil if no throttles were triggered.
30
- def matching_filter(job, *args)
31
- throttles.each do |throttle|
32
- next unless throttle.throttled?(job, *args)
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
- return throttle.extract_filter(job, *args)
35
- end
36
- nil
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
@@ -1,3 +1,3 @@
1
1
  module RocketJob
2
- VERSION = "6.3.2".freeze
2
+ VERSION = "7.0.0".freeze
3
3
  end
@@ -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
- # Returns true when work was completed, but no other work is available
71
- if job&.rocket_job_work(self, false)
72
- # Return the database connections for this fiber back to the connection pool
73
- if defined?(ActiveRecord::Base)
74
- if ActiveRecord::Base.respond_to?(:connection_handler)
75
- # Rails 7 .2
76
- ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
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
- ActiveRecord::Base.clear_active_connections!
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
- end
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
- if defined?(ActiveRecord::Base)
95
- if ActiveRecord::Base.respond_to?(:connection_handler)
96
- ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
97
- else
98
- ActiveRecord::Base.clear_active_connections!
99
- end
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
- filter = job.rocket_job_throttles.matching_filter(job)
153
- return false unless filter
154
-
155
- add_to_current_filter(filter)
156
- # Restore retrieved job so that other workers can process it later
157
- job.set(worker_name: nil, state: :queued)
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
- update = {"$set" => {"worker_name" => name, "state" => "running"}}
174
- query.sort(priority: 1, _id: 1).find_one_and_update(update, bypass_document_validation: true)
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, "rocket_job/jobs/re_encrypt/relational_job"
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: 6.3.2
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: 2026-01-31 00:00:00.000000000 Z
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: '1.9'
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: '1.9'
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: '7.5'
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: '7.5'
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: '4.7'
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: '4.7'
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.3'
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.3'
111
- description:
112
- email:
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.io
221
+ homepage: https://rocketjob.reidmorrison.com
221
222
  licenses:
222
223
  - Apache-2.0
223
- metadata: {}
224
- post_install_message:
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: '2.7'
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.5.3
240
- signing_key:
245
+ rubygems_version: 3.6.9
241
246
  specification_version: 4
242
- summary: Ruby's missing batch processing system.
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