@@ -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