canvas_sync 0.17.3.beta3 → 0.17.6.beta1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54feabc8f9154b633a70a11d15bfeebaf54ab9d9eb24b9a636c27844728ca1a3
4
- data.tar.gz: 0c8d2692a83ebb37a518f61d11fb2970b3204f3d68c990c721fa23a7365a91b6
3
+ metadata.gz: ce407ae1dd566665cb8ae02856b61f033001cb25aadb903b5e9261846ad9e0c0
4
+ data.tar.gz: 349fa1f9e033101be253f2d5e5b8212f243a9ea28d143e683d6b2b6a877fcac6
5
5
  SHA512:
6
- metadata.gz: 50efdad1e463d363463b72fa2fcc5581f877bdeb2c2440d688b10f451321996d95c7acd610d822fcc943d0220ed5520f4963de12341c1adb4238d131c5d8df04
7
- data.tar.gz: 51d40bc898a58a0aab65a85d4c1820ca5da72bf22b4c11e0ee631ad2e815dc344dc4824a2ade24e7485ace7ad0d12692d71cc93054afcfc05269ac22a4f7b4c9
6
+ metadata.gz: 32f2b3b973fdeec88c0a231112c3b82a275baa97937d3788b865d4eb50147a973022e77271970c3c58eab8a4ddbc4bdf10108d7f4f2df2f13c098d007d3bab67
7
+ data.tar.gz: 854ab10f0092ca6edfb280429c6cb600d8192a5c1189078ecca45a19ec3301c7e6afc3fdceec638ead82e17eab523e5669da010a42cd34761c7753dd06a0d7fb
@@ -99,7 +99,7 @@ module CanvasSync
99
99
  if parent_bid
100
100
  r.hincrby("BID-#{parent_bid}", "children", 1)
101
101
  r.expire("BID-#{parent_bid}", BID_EXPIRE_TTL)
102
- r.sadd("BID-#{parent_bid}-bids", bid)
102
+ r.zadd("BID-#{parent_bid}-bids", created_at, bid)
103
103
  end
104
104
  end
105
105
  end
@@ -151,6 +151,42 @@ module CanvasSync
151
151
  batch.parent ? valid && valid?(batch.parent) : valid
152
152
  end
153
153
 
154
+ def keep_open!
155
+ if block_given?
156
+ begin
157
+ keep_open!
158
+ yield
159
+ ensure
160
+ let_close!
161
+ end
162
+ else
163
+ redis do |r|
164
+ r.hset(@bidkey, 'keep_open', true)
165
+ end
166
+ end
167
+ end
168
+
169
+ def let_close!
170
+ _, failed, pending, children, complete, success = redis do |r|
171
+ r.multi do
172
+ r.hset(@bidkey, 'keep_open', false)
173
+
174
+ r.scard("BID-#{bid}-failed")
175
+ r.hincrby("BID-#{bid}", "pending", 0)
176
+ r.hincrby("BID-#{bid}", "children", 0)
177
+ r.scard("BID-#{bid}-batches-complete")
178
+ r.scard("BID-#{bid}-batches-success")
179
+ end
180
+ end
181
+
182
+ all_success = pending.to_i.zero? && children == success
183
+ # if complete or successfull call complete callback (the complete callback may then call successful)
184
+ if (pending.to_i == failed.to_i && children == complete) || all_success
185
+ self.class.enqueue_callbacks(:complete, bid)
186
+ self.class.enqueue_callbacks(:success, bid) if all_success
187
+ end
188
+ end
189
+
154
190
  def self.with_batch(batch)
155
191
  batch = self.new(batch) if batch.is_a?(String)
156
192
  parent = Thread.current[:batch]
@@ -171,6 +207,13 @@ module CanvasSync
171
207
  @bidkey
172
208
  end
173
209
 
210
+ def flush_pending_attrs
211
+ super
212
+ redis do |r|
213
+ r.zadd("batches", created_at, bid)
214
+ end
215
+ end
216
+
174
217
  private
175
218
 
176
219
  def assert_batch_is_open
@@ -184,15 +227,16 @@ module CanvasSync
184
227
 
185
228
  def append_jobs(jids)
186
229
  jids = jids.uniq
230
+ return unless jids.size > 0
231
+
187
232
  redis do |r|
233
+ tme = Time.now.utc.to_f
234
+ added = r.zadd(@bidkey + "-jids", jids.map{|jid| [tme, jid] }, nx: true)
188
235
  r.multi do
189
- r.hincrby(@bidkey, "pending", jids.size)
236
+ r.hincrby(@bidkey, "pending", added)
237
+ r.hincrby(@bidkey, "job_count", added)
190
238
  r.expire(@bidkey, BID_EXPIRE_TTL)
191
-
192
- if jids.size > 0
193
- r.sadd(@bidkey + "-jids", jids)
194
- r.expire(@bidkey + "-jids", BID_EXPIRE_TTL)
195
- end
239
+ r.expire(@bidkey + "-jids", BID_EXPIRE_TTL)
196
240
  end
197
241
  end
198
242
  end
@@ -200,6 +244,8 @@ module CanvasSync
200
244
  class << self
201
245
  def process_failed_job(bid, jid)
202
246
  _, pending, failed, children, complete, parent_bid = redis do |r|
247
+ return unless r.exists?("BID-#{bid}")
248
+
203
249
  r.multi do
204
250
  r.sadd("BID-#{bid}-failed", jid)
205
251
 
@@ -220,6 +266,8 @@ module CanvasSync
220
266
 
221
267
  def process_dead_job(bid, jid)
222
268
  _, failed, children, complete, parent_bid = redis do |r|
269
+ return unless r.exists?("BID-#{bid}")
270
+
223
271
  r.multi do
224
272
  r.sadd("BID-#{bid}-dead", jid)
225
273
 
@@ -245,7 +293,9 @@ module CanvasSync
245
293
  end
246
294
 
247
295
  def process_successful_job(bid, jid)
248
- _, failed, pending, children, complete, success, parent_bid = redis do |r|
296
+ _, failed, pending, children, complete, success, parent_bid, keep_open = redis do |r|
297
+ return unless r.exists?("BID-#{bid}")
298
+
249
299
  r.multi do
250
300
  r.srem("BID-#{bid}-failed", jid)
251
301
 
@@ -255,15 +305,14 @@ module CanvasSync
255
305
  r.scard("BID-#{bid}-batches-complete")
256
306
  r.scard("BID-#{bid}-batches-success")
257
307
  r.hget("BID-#{bid}", "parent_bid")
308
+ r.hget("BID-#{bid}", "keep_open")
258
309
 
259
310
  r.hincrby("BID-#{bid}", "successful-jobs", 1)
260
- r.srem("BID-#{bid}-jids", jid)
311
+ r.zrem("BID-#{bid}-jids", jid)
261
312
  r.expire("BID-#{bid}", BID_EXPIRE_TTL)
262
313
  end
263
314
  end
264
315
 
265
- # TODO - There seems to be an issue where a :complete callback batch will occasionally run its job, but won't enqueue_callbacks()
266
-
267
316
  all_success = pending.to_i.zero? && children == success
268
317
  # if complete or successfull call complete callback (the complete callback may then call successful)
269
318
  if (pending.to_i == failed.to_i && children == complete) || all_success
@@ -277,6 +326,7 @@ module CanvasSync
277
326
  callback_key = "#{batch_key}-callbacks-#{event}"
278
327
  already_processed, _, callbacks, queue, parent_bid, callback_params = redis do |r|
279
328
  return unless r.exists?(batch_key)
329
+ return if r.hget(batch_key, 'keep_open') == 'true'
280
330
  r.multi do
281
331
  r.hget(batch_key, event)
282
332
  r.hset(batch_key, event, true)
@@ -302,6 +352,11 @@ module CanvasSync
302
352
  if callback_args.present? && !callback_params.present?
303
353
  logger.debug {"Enqueue callback bid: #{bid} event: #{event} args: #{callback_args.inspect}"}
304
354
 
355
+ redis do |r|
356
+ r.sadd("#{batch_key}-pending_callbacks", event)
357
+ r.expire("#{batch_key}-pending_callbacks", BID_EXPIRE_TTL)
358
+ end
359
+
305
360
  with_batch(parent_bid) do
306
361
  cb_batch = self.new
307
362
  cb_batch.callback_params = {
@@ -330,7 +385,8 @@ module CanvasSync
330
385
  def cleanup_redis(bid)
331
386
  logger.debug {"Cleaning redis of batch #{bid}"}
332
387
  redis do |r|
333
- r.del(
388
+ r.zrem("batches", bid)
389
+ r.unlink(
334
390
  "BID-#{bid}",
335
391
  "BID-#{bid}-callbacks-complete",
336
392
  "BID-#{bid}-callbacks-success",
@@ -341,10 +397,21 @@ module CanvasSync
341
397
  "BID-#{bid}-batches-failed",
342
398
  "BID-#{bid}-bids",
343
399
  "BID-#{bid}-jids",
400
+ "BID-#{bid}-pending_callbacks",
344
401
  )
345
402
  end
346
403
  end
347
404
 
405
+ def delete_prematurely!(bid)
406
+ child_bids = redis do |r|
407
+ r.zrange("BID-#{bid}-bids", 0, -1)
408
+ end
409
+ child_bids.each do |cbid|
410
+ delete_prematurely!(cbid)
411
+ end
412
+ cleanup_redis(bid)
413
+ end
414
+
348
415
  def redis(*args, &blk)
349
416
  defined?(::Sidekiq) ? ::Sidekiq.redis(*args, &blk) : nil # TODO
350
417
  end
@@ -353,8 +420,6 @@ module CanvasSync
353
420
  defined?(::Sidekiq) ? ::Sidekiq.logger : Rails.logger
354
421
  end
355
422
 
356
- private
357
-
358
423
  def push_callbacks(args, queue)
359
424
  Batch::Callback::worker_class.enqueue_all(args, queue)
360
425
  end
@@ -10,7 +10,7 @@ module CanvasSync
10
10
  begin
11
11
  Thread.current[:batch] = Batch.new(@bid)
12
12
  block.call
13
- batch&.save_context_changes
13
+ Thread.current[:batch].save_context_changes
14
14
  Batch.process_successful_job(@bid, job_id)
15
15
  rescue
16
16
  Batch.process_failed_job(@bid, job_id)
@@ -52,9 +52,6 @@ module CanvasSync
52
52
 
53
53
  class Finalize
54
54
  def dispatch(status, opts)
55
- is_callback_batch = opts['origin'].present?
56
- has_callback_batch = opts['callback_bid'].present?
57
-
58
55
  bid = opts["bid"]
59
56
  event = opts["event"].to_sym
60
57
 
@@ -63,12 +60,21 @@ module CanvasSync
63
60
  batch_status = Status.new bid
64
61
  send(event, bid, batch_status, batch_status.parent_bid)
65
62
 
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'])
63
+ if event == :success
64
+ if opts['origin'].present?
65
+ origin_bid = opts['origin']['for_bid']
66
+ _, pending, success_ran = Batch.redis do |r|
67
+ r.multi do
68
+ r.srem("BID-#{origin_bid}-pending_callbacks", opts['origin']['event'])
69
+ r.scard("BID-#{origin_bid}-pending_callbacks")
70
+ r.hget("BID-#{origin_bid}", "success")
71
+ end
72
+ end
73
+ Batch.cleanup_redis(origin_bid) if pending == 0 && success_ran == 'true'
74
+ end
75
+ if (Batch.redis {|r| r.scard("BID-#{bid}-pending_callbacks") }) == 0
76
+ Batch.cleanup_redis(bid)
77
+ end
72
78
  end
73
79
  end
74
80
 
@@ -113,13 +119,7 @@ module CanvasSync
113
119
  end
114
120
  end
115
121
 
116
- # If the batch was successful run :success callback, which will call the parent's :complete callback (if necessary)
117
- # Also, only trigger the success callback if the :complete callback_batch was successful
118
- if pending.to_i.zero? && children == success
119
- # Batch.enqueue_callbacks(:success, bid)
120
-
121
- # otherwise check for a parent and call its :complete if needed
122
- elsif parent_bid
122
+ if parent_bid && !(pending.to_i.zero? && children == success)
123
123
  # if batch was not successfull check and see if its parent is complete
124
124
  # if the parent is complete we trigger the complete callback
125
125
  # We don't want to run this if the batch was successfull because the success
@@ -28,6 +28,10 @@ module CanvasSync
28
28
  @hash_map[local_bid]
29
29
  end
30
30
 
31
+ def own
32
+ resolve_hash(@bid_stack[-1]) || {}
33
+ end
34
+
31
35
  def set_local(new_hash)
32
36
  @dirty = true
33
37
  local.clear.merge!(new_hash)
@@ -106,7 +110,8 @@ module CanvasSync
106
110
  @bid_stack[index]
107
111
  else
108
112
  pbid = Batch.redis do |r|
109
- r.hget("BID-#{bid}", "parent_bid") || r.hget("BID-#{bid}", "callback_for")
113
+ callback_params = JSON.parse(r.hget("BID-#{bid}", "callback_params") || "{}")
114
+ callback_params['for_bid'] || r.hget("BID-#{bid}", "parent_bid")
110
115
  end
111
116
  @bid_stack.unshift(pbid)
112
117
  pbid
@@ -24,6 +24,7 @@ module CanvasSync
24
24
  end
25
25
 
26
26
  def self.from_pid(pid)
27
+ raise "PID must be given" unless pid.present?
27
28
  new(pid)
28
29
  end
29
30
 
@@ -38,7 +39,7 @@ module CanvasSync
38
39
  def add_jobs(job_descs)
39
40
  job_descs.each do |job_desc|
40
41
  wrapper = Batch.new
41
- wrapper.description = "Pool Job Wrapper"
42
+ wrapper.description = "Pool Job Wrapper (PID: #{pid})"
42
43
  checkin_event = (on_failed_job == :wait) ? :success : :complete
43
44
  wrapper.on(checkin_event, "#{self.class.to_s}.job_checked_in", pool_id: pid)
44
45
  wrapper.jobs {}
@@ -54,24 +55,77 @@ module CanvasSync
54
55
  refill_allotment
55
56
  end
56
57
 
58
+ def keep_open!
59
+ if block_given?
60
+ begin
61
+ keep_open!
62
+ yield
63
+ ensure
64
+ let_close!
65
+ end
66
+ else
67
+ redis do |r|
68
+ r.hset(redis_key, 'keep_open', true)
69
+ end
70
+ end
71
+ end
72
+
73
+ def let_close!
74
+ _, active_count = redis do |r|
75
+ r.multi do
76
+ r.hset(redis_key, 'keep_open', false)
77
+ r.hincrby(redis_key, "active_count", 0)
78
+ end
79
+ end
80
+
81
+ if active_count == 0 && pending_count == 0
82
+ cleanup_redis if clean_when_empty
83
+ end
84
+ end
85
+
57
86
  def cleanup_redis
58
87
  Batch.logger.debug {"Cleaning redis of pool #{pid}"}
59
88
  redis do |r|
60
- r.del(
89
+ r.zrem("pools", pid)
90
+ r.unlink(
61
91
  "#{redis_key}",
62
92
  "#{redis_key}-jobs",
63
93
  )
64
94
  end
65
95
  end
66
96
 
97
+ def active_count
98
+ redis do |r|
99
+ r.hincrby(redis_key, "active_count", 0)
100
+ end
101
+ end
102
+
103
+ def pending_count
104
+ jobs_key = "#{redis_key}-jobs"
105
+ order = self.order || 'fifo'
106
+ redis do |r|
107
+ case order.to_sym
108
+ when :fifo, :lifo
109
+ r.llen(jobs_key)
110
+ when :random
111
+ r.scard(jobs_key)
112
+ when :priority
113
+ r.zcard(jobs_key)
114
+ end
115
+ end
116
+ end
117
+
67
118
  def job_checked_in(status, options)
68
119
  active_count = redis do |r|
120
+ return unless r.exists?(redis_key)
69
121
  r.hincrby(redis_key, "active_count", -1)
70
122
  end
71
- added_count = refill_allotment
72
123
 
124
+ added_count = refill_allotment
73
125
  if active_count == 0 && added_count == 0
74
- cleanup_redis if clean_when_empty
126
+ if clean_when_empty && redis {|r| r.hget(redis_key, 'keep_open') } != 'true'
127
+ cleanup_redis
128
+ end
75
129
  end
76
130
  end
77
131
 
@@ -159,29 +213,25 @@ module CanvasSync
159
213
  ActiveJob::Arguments.deserialize(JSON.parse(job_json))[0]
160
214
  end
161
215
 
162
- def pending_count
163
- order = self.order
164
- redis do |r|
165
- case order.to_sym
166
- when :fifo, :lifo
167
- r.llen(jobs_key)
168
- when :random
169
- r.scard(jobs_key)
170
- when :priority
171
- r.zcard(jobs_key)
172
- end
173
- end
174
- end
175
-
176
216
  def self.redis(&blk)
177
217
  Batch.redis &blk
178
218
  end
179
219
  delegate :redis, to: :class
180
220
 
221
+ protected
222
+
223
+ def flush_pending_attrs
224
+ super
225
+ redis do |r|
226
+ r.zadd("pools", created_at, pid)
227
+ end
228
+ end
229
+
181
230
  private
182
231
 
183
- def initialize_new(concurrency: nil, order: :fifo, clean_when_empty: true, on_failed_job: :wait)
232
+ def initialize_new(concurrency: nil, order: :fifo, clean_when_empty: true, on_failed_job: :wait, description: nil)
184
233
  self.created_at = Time.now.utc.to_f
234
+ self.description = description
185
235
  self.order = order
186
236
  self.concurrency = concurrency
187
237
  self.clean_when_empty = clean_when_empty
@@ -57,16 +57,17 @@ module CanvasSync
57
57
  class ServerMiddleware
58
58
  def call(_worker, msg, _queue)
59
59
  if (bid = msg['bid'])
60
+ prev_batch = Thread.current[:batch]
60
61
  begin
61
62
  Thread.current[:batch] = Batch.new(bid)
62
63
  yield
63
- Thread.current[:batch] = nil
64
+ Thread.current[:batch].save_context_changes
64
65
  Batch.process_successful_job(bid, msg['jid'])
65
66
  rescue
66
67
  Batch.process_failed_job(bid, msg['jid'])
67
68
  raise
68
69
  ensure
69
- Thread.current[:batch] = nil
70
+ Thread.current[:batch] = prev_batch
70
71
  end
71
72
  else
72
73
  yield
@@ -117,3 +118,5 @@ module CanvasSync
117
118
  end
118
119
  end
119
120
  end
121
+
122
+ require_relative 'sidekiq/web'