Skip to content

Commit d01a9b2

Browse files
committed
Support nested batches
* Parent batches will not complete until all child batches have been completed
1 parent 7d951b3 commit d01a9b2

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

app/models/solid_queue/job_batch.rb

+29-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class JobBatch < Record
55
belongs_to :job, foreign_key: :job_id, optional: true
66
belongs_to :parent_job_batch, foreign_key: :parent_job_batch_id, class_name: "SolidQueue::JobBatch", optional: true
77
has_many :jobs, foreign_key: :batch_id
8+
has_many :children, foreign_key: :parent_job_batch_id, class_name: "SolidQueue::JobBatch"
89

910
serialize :on_finish_active_job, coder: JSON
1011
serialize :on_success_active_job, coder: JSON
@@ -21,28 +22,33 @@ def current_batch_id
2122
end
2223

2324
def enqueue(attributes = {})
24-
previous_batch_id = current_batch_id.presence || nil
25-
2625
job_batch = nil
2726
transaction do
2827
job_batch = create!(batch_attributes(attributes))
29-
ActiveSupport::IsolatedExecutionState[:current_batch_id] = job_batch.id
30-
yield job_batch
28+
wrap_in_batch_context(job_batch.id) do
29+
yield job_batch
30+
end
3131
end
3232

3333
job_batch
34-
ensure
35-
ActiveSupport::IsolatedExecutionState[:current_batch_id] = previous_batch_id
3634
end
3735

3836
def dispatch_finished_batches
3937
incomplete.order(:id).pluck(:id).each do |id|
4038
transaction do
41-
where(id: id).non_blocking_lock.each(&:finish)
39+
where(id: id).includes(:children, :jobs).non_blocking_lock.each(&:finish)
4240
end
4341
end
4442
end
4543

44+
def wrap_in_batch_context(batch_id)
45+
previous_batch_id = current_batch_id.presence || nil
46+
ActiveSupport::IsolatedExecutionState[:current_batch_id] = batch_id
47+
yield
48+
ensure
49+
ActiveSupport::IsolatedExecutionState[:current_batch_id] = previous_batch_id
50+
end
51+
4652
private
4753

4854
def batch_attributes(attributes)
@@ -62,6 +68,8 @@ def batch_attributes(attributes)
6268
attributes[:on_failure_active_job] = as_active_job(on_failure_klass).serialize
6369
end
6470

71+
attributes[:parent_job_batch_id] = current_batch_id if current_batch_id.present?
72+
6573
attributes
6674
end
6775

@@ -74,16 +82,13 @@ def as_active_job(active_job_klass)
7482
def enqueue(attributes = {})
7583
raise "You cannot enqueue a batch that is already finished" if finished?
7684

77-
previous_batch_id = self.class.current_batch_id.presence || nil
78-
7985
transaction do
80-
ActiveSupport::IsolatedExecutionState[:current_batch_id] = id
81-
yield self
86+
self.class.wrap_in_batch_context(id) do
87+
yield self
88+
end
8289
end
8390

8491
self
85-
ensure
86-
ActiveSupport::IsolatedExecutionState[:current_batch_id] = previous_batch_id
8792
end
8893

8994
def finished?
@@ -110,6 +115,10 @@ def finish
110115
return unless status.in?([ :finished, :failed ])
111116
end
112117

118+
children.find_each do |child|
119+
return unless child.finished?
120+
end
121+
113122
if on_finish_active_job.present?
114123
perform_completion_job(:on_finish_active_job, attrs)
115124
end
@@ -118,7 +127,10 @@ def finish
118127
perform_completion_job(:on_success_active_job, attrs)
119128
end
120129

121-
update!({ finished_at: Time.zone.now }.merge(attrs))
130+
transaction do
131+
parent_job_batch.touch(:changed_at, :last_changed_at) if parent_job_batch_id.present?
132+
update!({ finished_at: Time.zone.now }.merge(attrs))
133+
end
122134
end
123135

124136
private
@@ -133,7 +145,9 @@ def perform_completion_job(job_field, attrs)
133145
active_job = ActiveJob::Base.deserialize(send(job_field))
134146
active_job.send(:deserialize_arguments_if_needed)
135147
active_job.arguments = [ self ] + Array.wrap(active_job.arguments)
136-
ActiveJob.perform_all_later([ active_job ])
148+
self.class.wrap_in_batch_context(id) do
149+
ActiveJob.perform_all_later([ active_job ])
150+
end
137151
active_job.provider_job_id = Job.find_by(active_job_id: active_job.job_id).id
138152
attrs[job_field] = active_job.serialize
139153
end

0 commit comments

Comments
 (0)