canvas_sync 0.17.0.beta15 → 0.17.3.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +58 -0
  3. data/lib/canvas_sync/importers/bulk_importer.rb +7 -4
  4. data/lib/canvas_sync/job_batches/batch.rb +81 -107
  5. data/lib/canvas_sync/job_batches/callback.rb +20 -29
  6. data/lib/canvas_sync/job_batches/context_hash.rb +8 -5
  7. data/lib/canvas_sync/job_batches/hincr_max.lua +5 -0
  8. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +99 -0
  9. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +6 -65
  10. data/lib/canvas_sync/job_batches/pool.rb +193 -0
  11. data/lib/canvas_sync/job_batches/redis_model.rb +67 -0
  12. data/lib/canvas_sync/job_batches/redis_script.rb +163 -0
  13. data/lib/canvas_sync/job_batches/sidekiq.rb +22 -1
  14. data/lib/canvas_sync/job_batches/status.rb +0 -5
  15. data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +4 -2
  16. data/lib/canvas_sync/jobs/report_starter.rb +1 -0
  17. data/lib/canvas_sync/processors/assignment_groups_processor.rb +3 -2
  18. data/lib/canvas_sync/processors/assignments_processor.rb +3 -2
  19. data/lib/canvas_sync/processors/context_module_items_processor.rb +3 -2
  20. data/lib/canvas_sync/processors/context_modules_processor.rb +3 -2
  21. data/lib/canvas_sync/processors/normal_processor.rb +2 -1
  22. data/lib/canvas_sync/processors/provisioning_report_processor.rb +10 -2
  23. data/lib/canvas_sync/processors/submissions_processor.rb +3 -2
  24. data/lib/canvas_sync/version.rb +1 -1
  25. data/spec/dummy/log/test.log +78907 -0
  26. data/spec/job_batching/batch_aware_job_spec.rb +1 -0
  27. data/spec/job_batching/batch_spec.rb +72 -15
  28. data/spec/job_batching/callback_spec.rb +1 -1
  29. data/spec/job_batching/flow_spec.rb +5 -11
  30. data/spec/job_batching/integration/fail_then_succeed.rb +42 -0
  31. data/spec/job_batching/integration_helper.rb +6 -4
  32. data/spec/job_batching/sidekiq_spec.rb +1 -0
  33. data/spec/job_batching/status_spec.rb +1 -17
  34. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f242a2d6641d3d51bff9b73eaf2165588405a468a1806183d20e68d7c324d976
4
- data.tar.gz: ca2dc6bf47c7c861c42f6550707028d15d1b54469e1a12e3ce7fedead6204102
3
+ metadata.gz: 416545a03622711eba78ef420fd07aff9eb1e56ee9536b9e12369406885079c5
4
+ data.tar.gz: e9cabdc870f0903bcffc60f711c0ed34a5b471d1bbdf1d8a2a4d83ce9ac745a4
5
5
  SHA512:
6
- metadata.gz: 4d25fa9c0d9155780a087ac94b9a72cadddd9805c60760f5bdf719870245b8f1b8c23e14650646565b7a03b6a7983e329049fb1057ccfd990f662ee479322ffd
7
- data.tar.gz: f551da63e671e43194c8a2bd2cfe8f95499417d74599d14307f1470ed678a712492f7ad6a70d7208f57a6b3a22e9fb5e5ced209afb292b93b2f0ce88db02d609
6
+ metadata.gz: 020ba0e260823c40a21273c3bda767c9774207fa36bf7e639d0927db19d544a27b1fefcebfdd64e2b4445ba5ca526098229d95e4c3ee7f5f6e336ef42da8a1f6
7
+ data.tar.gz: 1840797de37257526d002507fe1849c84cd5cc71ed5bbb176a93061dce8d4c25e85528ec7312a3215e9520bcf4f708ac8cf5834539fdac4a7810919700844d33
data/README.md CHANGED
@@ -246,6 +246,64 @@ A batch can be created using `Sidekiq::Batch` or `CanvasSync::JobBatching::Batch
246
246
 
247
247
  Also see `canvas_sync/jobs/begin_sync_chain_job`, `canvas_sync/Job_batches/jobs/serial_batch_job`, or `canvas_sync/Job_batches/jobs/concurrent_batch_job` for example usage.
248
248
 
249
+ Example:
250
+ ```ruby
251
+ batch = CanvasSync::JobBatches::Batch.new
252
+ batch.description = "Some Batch" # Optional, but can be useful for debugging
253
+
254
+ batch.on(:complete, "SomeClass.on_complete", kw_param: 1)
255
+ batch.on(:success, "SomeClass.on_success", some_param: 'foo')
256
+
257
+ # Add context to the batch. Can be accessed as batch_context on any jobs within the batch.
258
+ # Nested Batches will have their contexts merged
259
+ batch.context = {
260
+ some_value: 'blah',
261
+ }
262
+
263
+ batch.jobs do
264
+ # Enqueue jobs like normal
265
+ end
266
+ ```
267
+
268
+ #### Job Pools
269
+ A job pool is like a custom Sidekiq Queue. You can add jobs to it and it will empty itself out into one of the actual queues.
270
+ However, it adds some options for tweaking the logic:
271
+ - `concurrency` (default: `nil`) - Define how many jobs from the pool can run at once.
272
+ - `order` (default: `fifo`) - Define how the pool will empty itself
273
+ - `fifo` - First-In First-Out, a traditional queue
274
+ - `lifo` - Last-In First-Out
275
+ - `random` - Pluck and run jobs in random order
276
+ - `priority` - Execute jobs in a priority order (NB: Due to Redis, this priority-random, meaning that items with the same priority will be run in random order, not fifo)
277
+ - `clean_when_empty` (default: `true`) - Automatically clean the pool when it is empty
278
+ - `on_failed_job` (default `:wait`) - If a Job fails, should the pool `:continue` and still enqueue the next job or `:wait` for the job to succeed
279
+
280
+ Example:
281
+ ```ruby
282
+ pool = CanvasSync::JobBatches::Pool.new(concurrency: 4, order: :priority, clean_when_empty: false)
283
+ pool_id = pool.pid
284
+
285
+ # Add a job to the pool
286
+ pool << {
287
+ job: SomeJob, # The Class of a ActiveJob Job or Sidekiq Worker
288
+ parameters: [1, 2, 3], # Array of params to pass th e Job
289
+ priority: 100, # Only effective if order=:priority, higher is higher
290
+ }
291
+
292
+ # Add many jobs to the pool
293
+ pool.add_jobs([
294
+ {
295
+ job: SomeJob, # The Class of a ActiveJob Job or Sidekiq Worker
296
+ parameters: [1, 2, 3], # Array of params to pass th e Job
297
+ priority: 100, # Only effective if order=:priority, higher is higher
298
+ },
299
+ # ...
300
+ ])
301
+
302
+ # ...Later
303
+ CanvasSync::JobBatches::Pool.from_pid(pool_id).cleanup_redis
304
+
305
+ ```
306
+
249
307
  ## Legacy Support
250
308
 
251
309
  ### Legacy Mappings
@@ -64,13 +64,12 @@ module CanvasSync
64
64
  columns = columns.dup
65
65
 
66
66
  update_conditions = {
67
- condition: condition_sql(klass, columns),
67
+ condition: condition_sql(klass, columns, import_args[:sync_start_time]),
68
68
  columns: columns
69
69
  }
70
70
  update_conditions[:conflict_target] = conflict_target if conflict_target
71
71
 
72
72
  options = { validate: false, on_duplicate_key_update: update_conditions }.merge(import_args)
73
-
74
73
  options.delete(:on_duplicate_key_update) if options.key?(:on_duplicate_key_ignore)
75
74
  klass.import(columns, rows, options)
76
75
  end
@@ -85,10 +84,14 @@ module CanvasSync
85
84
  # started_at = Time.now
86
85
  # run_the_users_sync!
87
86
  # changed = User.where("updated_at >= ?", started_at)
88
- def self.condition_sql(klass, columns)
87
+ def self.condition_sql(klass, columns, report_start)
89
88
  columns_str = columns.map { |c| "#{klass.quoted_table_name}.#{c}" }.join(", ")
90
89
  excluded_str = columns.map { |c| "EXCLUDED.#{c}" }.join(", ")
91
- "(#{columns_str}) IS DISTINCT FROM (#{excluded_str})"
90
+ condition_sql = "(#{columns_str}) IS DISTINCT FROM (#{excluded_str})"
91
+ if klass.column_names.include?("updated_at") && report_start
92
+ condition_sql += " AND #{klass.quoted_table_name}.updated_at < '#{report_start}'"
93
+ end
94
+ condition_sql
92
95
  end
93
96
 
94
97
  def self.batch_size
@@ -4,10 +4,13 @@ begin
4
4
  rescue LoadError
5
5
  end
6
6
 
7
+ require_relative './redis_model'
8
+ require_relative './redis_script'
7
9
  require_relative './batch_aware_job'
8
10
  require_relative "./callback"
9
11
  require_relative "./context_hash"
10
12
  require_relative "./status"
13
+ require_relative "./pool"
11
14
  Dir[File.dirname(__FILE__) + "/jobs/*.rb"].each { |file| require file }
12
15
  require_relative "./chain_builder"
13
16
 
@@ -17,24 +20,9 @@ require_relative "./chain_builder"
17
20
  module CanvasSync
18
21
  module JobBatches
19
22
  class Batch
20
- class NoBlockGivenError < StandardError; end
21
-
22
- def self.batch_attr(key, read_only: true)
23
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
24
- def #{key}=(value)
25
- raise "#{key} is read-only once the batch has been started" if #{read_only.to_s} && (@initialized || @existing)
26
- @#{key} = value
27
- persist_bid_attr('#{key}', value)
28
- end
23
+ include RedisModel
29
24
 
30
- def #{key}
31
- return @#{key} if defined?(@#{key})
32
- if (@initialized || @existing)
33
- @#{key} = read_bid_attr('#{key}')
34
- end
35
- end
36
- RUBY
37
- end
25
+ class NoBlockGivenError < StandardError; end
38
26
 
39
27
  delegate :redis, to: :class
40
28
 
@@ -47,16 +35,14 @@ module CanvasSync
47
35
  @existing = !(!existing_bid || existing_bid.empty?) # Basically existing_bid.present?
48
36
  @initialized = false
49
37
  @bidkey = "BID-" + @bid.to_s
50
- @pending_attrs = {}
51
- @ready_to_queue = nil
52
38
  self.created_at = Time.now.utc.to_f unless @existing
53
39
  end
54
40
 
55
- batch_attr :description
56
- batch_attr :created_at
57
- batch_attr :callback_queue, read_only: false
58
- batch_attr :callback_batch, read_only: false
59
- batch_attr :allow_context_changes
41
+ redis_attr :description
42
+ redis_attr :created_at
43
+ redis_attr :callback_queue, read_only: false
44
+ redis_attr :callback_params, :json
45
+ redis_attr :allow_context_changes
60
46
 
61
47
  def context
62
48
  return @context if defined?(@context)
@@ -122,29 +108,24 @@ module CanvasSync
122
108
  @context&.save!
123
109
 
124
110
  @initialized = true
111
+ else
112
+ assert_batch_is_open
125
113
  end
126
114
 
127
- job_queue = @ready_to_queue = []
128
-
129
115
  begin
130
116
  parent = Thread.current[:batch]
131
117
  Thread.current[:batch] = self
132
118
  yield
133
119
  ensure
134
- @ready_to_queue = nil
135
- append_jobs(job_queue, parent_bid)
136
120
  Thread.current[:batch] = parent
137
121
  end
138
122
 
139
- job_queue
123
+ nil
140
124
  end
141
125
 
142
126
  def increment_job_queue(jid)
143
- if @ready_to_queue
144
- @ready_to_queue << jid
145
- else
146
- append_jobs([jid])
147
- end
127
+ assert_batch_is_open
128
+ append_jobs([jid])
148
129
  end
149
130
 
150
131
  def invalidate_all
@@ -170,52 +151,42 @@ module CanvasSync
170
151
  batch.parent ? valid && valid?(batch.parent) : valid
171
152
  end
172
153
 
173
- # Any Batches or Jobs created in the given block won't be assocaiated to the current batch
174
- def self.without_batch
154
+ def self.with_batch(batch)
155
+ batch = self.new(batch) if batch.is_a?(String)
175
156
  parent = Thread.current[:batch]
176
- Thread.current[:batch] = nil
157
+ Thread.current[:batch] = batch
177
158
  yield
178
159
  ensure
179
160
  Thread.current[:batch] = parent
180
161
  end
181
162
 
182
- private
183
-
184
- def persist_bid_attr(attribute, value)
185
- if @initialized || @existing
186
- redis do |r|
187
- r.multi do
188
- r.hset(@bidkey, attribute, value)
189
- r.expire(@bidkey, BID_EXPIRE_TTL)
190
- end
191
- end
192
- else
193
- @pending_attrs[attribute] = value
194
- end
163
+ # Any Batches or Jobs created in the given block won't be assocaiated to the current batch
164
+ def self.without_batch(&blk)
165
+ with_batch(nil, &blk)
195
166
  end
196
167
 
197
- def read_bid_attr(attribute)
198
- redis do |r|
199
- r.hget(@bidkey, attribute)
200
- end
168
+ protected
169
+
170
+ def redis_key
171
+ @bidkey
201
172
  end
202
173
 
203
- def flush_pending_attrs
204
- redis do |r|
205
- r.mapped_hmset(@bidkey, @pending_attrs)
174
+ private
175
+
176
+ def assert_batch_is_open
177
+ unless defined?(@closed)
178
+ redis do |r|
179
+ @closed = r.hget(@bidkey, 'closed') == 'true'
180
+ end
206
181
  end
207
- @pending_attrs = {}
182
+ raise "Cannot add jobs to Batch #{} bid - it has already entered the callback-stage" if @closed
208
183
  end
209
184
 
210
- def append_jobs(jids, parent_bid = self.parent_bid)
185
+ def append_jobs(jids)
186
+ jids = jids.uniq
211
187
  redis do |r|
212
188
  r.multi do
213
- if parent_bid
214
- r.hincrby("BID-#{parent_bid}", "total", jids.size)
215
- end
216
-
217
189
  r.hincrby(@bidkey, "pending", jids.size)
218
- r.hincrby(@bidkey, "total", jids.size)
219
190
  r.expire(@bidkey, BID_EXPIRE_TTL)
220
191
 
221
192
  if jids.size > 0
@@ -242,17 +213,6 @@ module CanvasSync
242
213
  end
243
214
  end
244
215
 
245
- # if the batch failed, and has a parent, update the parent to show one pending and failed job
246
- if parent_bid
247
- redis do |r|
248
- r.multi do
249
- r.hincrby("BID-#{parent_bid}", "pending", 1)
250
- r.sadd("BID-#{parent_bid}-failed", jid)
251
- r.expire("BID-#{parent_bid}-failed", BID_EXPIRE_TTL)
252
- end
253
- end
254
- end
255
-
256
216
  if pending.to_i == failed.to_i && children == complete
257
217
  enqueue_callbacks(:complete, bid)
258
218
  end
@@ -285,7 +245,7 @@ module CanvasSync
285
245
  end
286
246
 
287
247
  def process_successful_job(bid, jid)
288
- _, failed, pending, children, complete, success, total, parent_bid = redis do |r|
248
+ _, failed, pending, children, complete, success, parent_bid = redis do |r|
289
249
  r.multi do
290
250
  r.srem("BID-#{bid}-failed", jid)
291
251
 
@@ -294,7 +254,6 @@ module CanvasSync
294
254
  r.hincrby("BID-#{bid}", "children", 0)
295
255
  r.scard("BID-#{bid}-batches-complete")
296
256
  r.scard("BID-#{bid}-batches-success")
297
- r.hget("BID-#{bid}", "total")
298
257
  r.hget("BID-#{bid}", "parent_bid")
299
258
 
300
259
  r.srem("BID-#{bid}-jids", jid)
@@ -302,6 +261,8 @@ module CanvasSync
302
261
  end
303
262
  end
304
263
 
264
+ # TODO - There seems to be an issue where a :complete callback batch will occasionally run its job, but won't enqueue_callbacks()
265
+
305
266
  all_success = pending.to_i.zero? && children == success
306
267
  # if complete or successfull call complete callback (the complete callback may then call successful)
307
268
  if (pending.to_i == failed.to_i && children == complete) || all_success
@@ -310,17 +271,34 @@ module CanvasSync
310
271
  end
311
272
  end
312
273
 
274
+ def possibly_enqueue_callbacks(bid, types: [:complete, :success])
275
+ pending_jobs, failed_jobs, total_batches, completed_batches, successful_batches = redis do |r|
276
+ r.multi do
277
+
278
+ end
279
+ end
280
+
281
+ if (types.include?(:complete)) && (pending_jobs.to_i == failed_jobs.to_i && total_batches.to_i == completed_batches.to_i)
282
+ enqueue_callbacks(:complete, bid)
283
+ end
284
+ if (types.include?(:success)) && (pending_jobs.to_i.zero? && total_batches.to_i == successful_batches.to_i)
285
+ enqueue_callbacks(:success, bid)
286
+ end
287
+ end
288
+
313
289
  def enqueue_callbacks(event, bid)
314
290
  batch_key = "BID-#{bid}"
315
291
  callback_key = "#{batch_key}-callbacks-#{event}"
316
- already_processed, _, callbacks, queue, parent_bid, callback_batch = redis do |r|
292
+ already_processed, _, _, callbacks, queue, parent_bid, callback_params = redis do |r|
293
+ return unless r.exists?(batch_key)
317
294
  r.multi do
318
295
  r.hget(batch_key, event)
296
+ r.hset(batch_key, "closed", true)
319
297
  r.hset(batch_key, event, true)
320
298
  r.smembers(callback_key)
321
299
  r.hget(batch_key, "callback_queue")
322
300
  r.hget(batch_key, "parent_bid")
323
- r.hget(batch_key, "callback_batch")
301
+ r.hget(batch_key, "callback_params")
324
302
  end
325
303
  end
326
304
 
@@ -328,6 +306,7 @@ module CanvasSync
328
306
 
329
307
  queue ||= "default"
330
308
  parent_bid = !parent_bid || parent_bid.empty? ? nil : parent_bid # Basically parent_bid.blank?
309
+ callback_params = JSON.parse(callback_params) if callback_params.present?
331
310
  callback_args = callbacks.reduce([]) do |memo, jcb|
332
311
  cb = JSON.load(jcb)
333
312
  memo << [cb['callback'], event.to_s, cb['opts'], bid, parent_bid]
@@ -335,38 +314,32 @@ module CanvasSync
335
314
 
336
315
  opts = {"bid" => bid, "event" => event}
337
316
 
338
- # Run callback batch finalize synchronously
339
- if callback_batch
340
- # Extract opts from cb_args or use current
341
- # Pass in stored event as callback finalize is processed on complete event
342
- cb_opts = callback_args.first&.at(2) || opts
317
+ if callback_args.present? && !callback_params.present?
318
+ logger.debug {"Enqueue callback bid: #{bid} event: #{event} args: #{callback_args.inspect}"}
343
319
 
344
- logger.debug {"Run callback batch bid: #{bid} event: #{event} args: #{callback_args.inspect}"}
345
- # Finalize now
346
- finalizer = Batch::Callback::Finalize.new
347
- status = Status.new bid
348
- finalizer.dispatch(status, cb_opts)
320
+ with_batch(parent_bid) do
321
+ cb_batch = self.new
322
+ cb_batch.callback_params = {
323
+ for_bid: bid,
324
+ event: event,
325
+ }
326
+ opts['callback_bid'] = cb_batch.bid
349
327
 
350
- return
328
+ logger.debug {"Adding callback batch: #{cb_batch.bid} for batch: #{bid}"}
329
+ cb_batch.jobs do
330
+ push_callbacks callback_args, queue
331
+ end
332
+ end
351
333
  end
352
334
 
353
- logger.debug {"Enqueue callback bid: #{bid} event: #{event} args: #{callback_args.inspect}"}
354
-
355
- if callback_args.empty?
356
- # Finalize now
357
- finalizer = Batch::Callback::Finalize.new
358
- status = Status.new bid
359
- finalizer.dispatch(status, opts)
360
- else
361
- # Otherwise finalize in sub batch complete callback
362
- cb_batch = self.new
363
- cb_batch.callback_batch = true
364
- logger.debug {"Adding callback batch: #{cb_batch.bid} for batch: #{bid}"}
365
- cb_batch.on(:complete, "#{Batch::Callback::Finalize.to_s}#dispatch", opts)
366
- cb_batch.jobs do
367
- push_callbacks callback_args, queue
368
- end
335
+ if callback_params.present?
336
+ opts['origin'] = callback_params
369
337
  end
338
+
339
+ logger.debug {"Run batch finalizer bid: #{bid} event: #{event} args: #{callback_args.inspect}"}
340
+ finalizer = Batch::Callback::Finalize.new
341
+ status = Status.new bid
342
+ finalizer.dispatch(status, opts)
370
343
  end
371
344
 
372
345
  def cleanup_redis(bid)
@@ -381,6 +354,7 @@ module CanvasSync
381
354
  "BID-#{bid}-batches-success",
382
355
  "BID-#{bid}-batches-complete",
383
356
  "BID-#{bid}-batches-failed",
357
+ "BID-#{bid}-bids",
384
358
  "BID-#{bid}-jids",
385
359
  )
386
360
  end
@@ -397,7 +371,7 @@ module CanvasSync
397
371
  private
398
372
 
399
373
  def push_callbacks(args, queue)
400
- Batch::Callback::Worker.enqueue_all(args, queue)
374
+ Batch::Callback::worker_class.enqueue_all(args, queue)
401
375
  end
402
376
  end
403
377
  end
@@ -2,6 +2,7 @@ module CanvasSync
2
2
  module JobBatches
3
3
  class Batch
4
4
  module Callback
5
+ mattr_accessor :worker_class
5
6
 
6
7
  VALID_CALLBACKS = %w[success complete dead].freeze
7
8
 
@@ -47,41 +48,28 @@ module CanvasSync
47
48
  end
48
49
  end
49
50
 
50
- if defined?(::Sidekiq)
51
- class SidekiqCallbackWorker
52
- include ::Sidekiq::Worker
53
- include CallbackWorkerCommon
54
-
55
- def self.enqueue_all(args, queue)
56
- return if args.empty?
57
-
58
- ::Sidekiq::Client.push_bulk(
59
- 'class' => self,
60
- 'args' => args,
61
- 'queue' => queue
62
- )
63
- end
64
- end
65
- Worker = SidekiqCallbackWorker
66
- else
67
- Worker = ActiveJobCallbackWorker
68
- end
51
+ worker_class = ActiveJobCallbackWorker
69
52
 
70
53
  class Finalize
71
- def dispatch status, opts
54
+ def dispatch(status, opts)
55
+ is_callback_batch = opts['origin'].present?
56
+ has_callback_batch = opts['callback_bid'].present?
57
+
72
58
  bid = opts["bid"]
73
- callback_bid = status.bid
74
59
  event = opts["event"].to_sym
75
- callback_batch = bid != callback_bid
76
60
 
77
- Batch.logger.debug {"Finalize #{event} batch id: #{opts["bid"]}, callback batch id: #{callback_bid} callback_batch #{callback_batch}"}
61
+ Batch.logger.debug {"Finalize #{event} batch id: #{opts["bid"]}, callback batch id: #{callback_bid} callback_batch #{is_callback_batch}"}
78
62
 
79
63
  batch_status = Status.new bid
80
64
  send(event, bid, batch_status, batch_status.parent_bid)
81
65
 
82
- # Different events are run in different callback batches
83
- Batch.cleanup_redis callback_bid if callback_batch
84
- Batch.cleanup_redis bid if event == :success
66
+ if event == :success && !has_callback_batch
67
+ Batch.cleanup_redis(bid)
68
+ end
69
+
70
+ if event == :success && is_callback_batch && opts['origin']['event'].to_sym == :success
71
+ Batch.cleanup_redis(opts['origin']['for_bid'])
72
+ end
85
73
  end
86
74
 
87
75
  def success(bid, status, parent_bid)
@@ -102,8 +90,9 @@ module CanvasSync
102
90
  r.scard("BID-#{parent_bid}-failed")
103
91
  end
104
92
  end
105
- # if job finished successfully and parent batch completed call parent complete callback
106
- # Success callback is called after complete callback
93
+
94
+ # If the job finished successfully and parent batch is completed, call parent :complete callback
95
+ # Parent :success callback will be called by its :complete callback
107
96
  if complete == children && pending == failure
108
97
  Batch.logger.debug {"Finalize parent complete bid: #{parent_bid}"}
109
98
  Batch.enqueue_callbacks(:complete, parent_bid)
@@ -119,10 +108,12 @@ module CanvasSync
119
108
  end
120
109
  end
121
110
 
122
- # if we batch was successful run success callback
111
+ # If the batch was successful run :success callback, which will call the parent's :complete callback (if necessary)
112
+ # Also, only trigger the success callback if the :complete callback_batch was successful
123
113
  if pending.to_i.zero? && children == success
124
114
  Batch.enqueue_callbacks(:success, bid)
125
115
 
116
+ # otherwise check for a parent and call its :complete if needed
126
117
  elsif parent_bid
127
118
  # if batch was not successfull check and see if its parent is complete
128
119
  # if the parent is complete we trigger the complete callback