canvas_sync 0.17.23.beta6 → 0.17.24
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 +4 -4
- data/lib/canvas_sync/job_batches/batch.rb +99 -58
- data/lib/canvas_sync/job_batches/batch_aware_job.rb +7 -7
- data/lib/canvas_sync/job_batches/pool.rb +42 -52
- data/lib/canvas_sync/job_batches/sidekiq.rb +7 -7
- data/lib/canvas_sync/jobs/sync_terms_job.rb +1 -1
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/dummy/log/development.log +540 -0
- data/spec/dummy/log/test.log +43585 -0
- data/spec/job_batching/batch_aware_job_spec.rb +3 -3
- data/spec/job_batching/batch_spec.rb +40 -1
- data/spec/job_batching/sidekiq_spec.rb +2 -2
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff1d01575608d114952bb12803cae01cc2f4ff61c8ffeed78f18b247973926fa
|
4
|
+
data.tar.gz: ba3629041f491bb7bac12e03da3c896563c9595c77b58cec0d0c728f97168fcf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73ef3c21b2bb55276da6d5bd96a13b089117e47a04a117d4533822dc790eb2a62b2f8ac88fbfe122a4292e801542c49facef12554fd9a7c80426f699125d42b2
|
7
|
+
data.tar.gz: 29be22e24cb562c9decae8986b5e8a2b7f09da39a80e019edfe6f3cc994a90a2d53da1c9bae4e3d56640d630d7fbd8abca43e8bf946d35c211dae0a00833ef91
|
@@ -19,6 +19,8 @@ require_relative "./chain_builder"
|
|
19
19
|
|
20
20
|
module CanvasSync
|
21
21
|
module JobBatches
|
22
|
+
CURRENT_BATCH_THREAD_KEY = :job_batches_batch
|
23
|
+
|
22
24
|
class Batch
|
23
25
|
include RedisModel
|
24
26
|
|
@@ -76,14 +78,12 @@ module CanvasSync
|
|
76
78
|
def on(event, callback, options = {})
|
77
79
|
return unless Callback::VALID_CALLBACKS.include?(event.to_s)
|
78
80
|
callback_key = "#{@bidkey}-callbacks-#{event}"
|
79
|
-
redis do |r|
|
80
|
-
r.
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
r.expire(callback_key, BID_EXPIRE_TTL)
|
86
|
-
end
|
81
|
+
redis.multi do |r|
|
82
|
+
r.sadd(callback_key, JSON.unparse({
|
83
|
+
callback: callback,
|
84
|
+
opts: options
|
85
|
+
}))
|
86
|
+
r.expire(callback_key, BID_EXPIRE_TTL)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
@@ -91,18 +91,16 @@ module CanvasSync
|
|
91
91
|
raise NoBlockGivenError unless block_given?
|
92
92
|
|
93
93
|
if !@existing && !@initialized
|
94
|
-
parent_bid = Thread.current[
|
94
|
+
parent_bid = Thread.current[CURRENT_BATCH_THREAD_KEY]&.bid
|
95
95
|
|
96
|
-
redis do |r|
|
97
|
-
r.
|
98
|
-
|
99
|
-
r.expire(@bidkey, BID_EXPIRE_TTL)
|
96
|
+
redis.multi do |r|
|
97
|
+
r.hset(@bidkey, "parent_bid", parent_bid.to_s) if parent_bid
|
98
|
+
r.expire(@bidkey, BID_EXPIRE_TTL)
|
100
99
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
100
|
+
if parent_bid
|
101
|
+
r.hincrby("BID-#{parent_bid}", "children", 1)
|
102
|
+
r.expire("BID-#{parent_bid}", BID_EXPIRE_TTL)
|
103
|
+
r.zadd("BID-#{parent_bid}-bids", created_at, bid)
|
106
104
|
end
|
107
105
|
end
|
108
106
|
|
@@ -115,11 +113,11 @@ module CanvasSync
|
|
115
113
|
end
|
116
114
|
|
117
115
|
begin
|
118
|
-
parent = Thread.current[
|
119
|
-
Thread.current[
|
116
|
+
parent = Thread.current[CURRENT_BATCH_THREAD_KEY]
|
117
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = self
|
120
118
|
yield
|
121
119
|
ensure
|
122
|
-
Thread.current[
|
120
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = parent
|
123
121
|
end
|
124
122
|
|
125
123
|
nil
|
@@ -131,15 +129,11 @@ module CanvasSync
|
|
131
129
|
end
|
132
130
|
|
133
131
|
def invalidate_all
|
134
|
-
redis
|
135
|
-
r.setex("invalidated-bid-#{bid}", BID_EXPIRE_TTL, 1)
|
136
|
-
end
|
132
|
+
redis.setex("invalidated-bid-#{bid}", BID_EXPIRE_TTL, 1)
|
137
133
|
end
|
138
134
|
|
139
135
|
def parent_bid
|
140
|
-
redis
|
141
|
-
r.hget(@bidkey, "parent_bid")
|
142
|
-
end
|
136
|
+
redis.hget(@bidkey, "parent_bid")
|
143
137
|
end
|
144
138
|
|
145
139
|
def parent
|
@@ -149,7 +143,7 @@ module CanvasSync
|
|
149
143
|
end
|
150
144
|
|
151
145
|
def valid?(batch = self)
|
152
|
-
valid = !redis
|
146
|
+
valid = !redis.exists?("invalidated-bid-#{batch.bid}")
|
153
147
|
batch.parent ? valid && valid?(batch.parent) : valid
|
154
148
|
end
|
155
149
|
|
@@ -162,23 +156,19 @@ module CanvasSync
|
|
162
156
|
let_close!
|
163
157
|
end
|
164
158
|
else
|
165
|
-
redis
|
166
|
-
r.hset(@bidkey, 'keep_open', true)
|
167
|
-
end
|
159
|
+
redis.hset(@bidkey, 'keep_open', true)
|
168
160
|
end
|
169
161
|
end
|
170
162
|
|
171
163
|
def let_close!
|
172
|
-
_, failed, pending, children, complete, success = redis do |r|
|
173
|
-
r.
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
r.scard("BID-#{bid}-batches-success")
|
181
|
-
end
|
164
|
+
_, failed, pending, children, complete, success = redis.multi do |r|
|
165
|
+
r.hset(@bidkey, 'keep_open', false)
|
166
|
+
|
167
|
+
r.scard("BID-#{bid}-failed")
|
168
|
+
r.hincrby("BID-#{bid}", "pending", 0)
|
169
|
+
r.hincrby("BID-#{bid}", "children", 0)
|
170
|
+
r.scard("BID-#{bid}-batches-complete")
|
171
|
+
r.scard("BID-#{bid}-batches-success")
|
182
172
|
end
|
183
173
|
|
184
174
|
all_success = pending.to_i.zero? && children == success
|
@@ -191,11 +181,11 @@ module CanvasSync
|
|
191
181
|
|
192
182
|
def self.with_batch(batch)
|
193
183
|
batch = self.new(batch) if batch.is_a?(String)
|
194
|
-
parent = Thread.current[
|
195
|
-
Thread.current[
|
184
|
+
parent = Thread.current[CURRENT_BATCH_THREAD_KEY]
|
185
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = batch
|
196
186
|
yield
|
197
187
|
ensure
|
198
|
-
Thread.current[
|
188
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = parent
|
199
189
|
end
|
200
190
|
|
201
191
|
# Any Batches or Jobs created in the given block won't be assocaiated to the current batch
|
@@ -211,18 +201,14 @@ module CanvasSync
|
|
211
201
|
|
212
202
|
def flush_pending_attrs
|
213
203
|
super
|
214
|
-
redis
|
215
|
-
r.zadd("batches", created_at, bid)
|
216
|
-
end
|
204
|
+
redis.zadd("batches", created_at, bid)
|
217
205
|
end
|
218
206
|
|
219
207
|
private
|
220
208
|
|
221
209
|
def assert_batch_is_open
|
222
210
|
unless defined?(@closed)
|
223
|
-
redis
|
224
|
-
@closed = r.hget(@bidkey, 'success') == 'true'
|
225
|
-
end
|
211
|
+
@closed = redis.hget(@bidkey, 'success') == 'true'
|
226
212
|
end
|
227
213
|
raise "Cannot add jobs to Batch #{} bid - it has already entered the callback-stage" if @closed
|
228
214
|
end
|
@@ -234,7 +220,7 @@ module CanvasSync
|
|
234
220
|
redis do |r|
|
235
221
|
tme = Time.now.utc.to_f
|
236
222
|
added = r.zadd(@bidkey + "-jids", jids.map{|jid| [tme, jid] }, nx: true)
|
237
|
-
r.multi do
|
223
|
+
r.multi do |r|
|
238
224
|
r.hincrby(@bidkey, "pending", added)
|
239
225
|
r.hincrby(@bidkey, "job_count", added)
|
240
226
|
r.expire(@bidkey, BID_EXPIRE_TTL)
|
@@ -244,6 +230,14 @@ module CanvasSync
|
|
244
230
|
end
|
245
231
|
|
246
232
|
class << self
|
233
|
+
def current
|
234
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY]
|
235
|
+
end
|
236
|
+
|
237
|
+
def current_context
|
238
|
+
current&.context || {}
|
239
|
+
end
|
240
|
+
|
247
241
|
def process_failed_job(bid, jid)
|
248
242
|
_, pending, failed, children, complete, parent_bid = redis do |r|
|
249
243
|
return unless r.exists?("BID-#{bid}")
|
@@ -283,11 +277,9 @@ module CanvasSync
|
|
283
277
|
end
|
284
278
|
|
285
279
|
if parent_bid
|
286
|
-
redis do
|
287
|
-
r.
|
288
|
-
|
289
|
-
r.expire("BID-#{parent_bid}-dead", BID_EXPIRE_TTL)
|
290
|
-
end
|
280
|
+
redis.multi do
|
281
|
+
r.sadd("BID-#{parent_bid}-dead", jid)
|
282
|
+
r.expire("BID-#{parent_bid}-dead", BID_EXPIRE_TTL)
|
291
283
|
end
|
292
284
|
end
|
293
285
|
|
@@ -413,8 +405,21 @@ module CanvasSync
|
|
413
405
|
cleanup_redis(bid)
|
414
406
|
end
|
415
407
|
|
416
|
-
def redis(
|
417
|
-
|
408
|
+
def redis(&blk)
|
409
|
+
return RedisProxy.new unless block_given?
|
410
|
+
|
411
|
+
if Thread.current[:job_batches_redis]
|
412
|
+
yield Thread.current[:job_batches_redis]
|
413
|
+
elsif defined?(::Sidekiq)
|
414
|
+
::Sidekiq.redis do |r|
|
415
|
+
Thread.current[:job_batches_redis] = r
|
416
|
+
yield r
|
417
|
+
ensure
|
418
|
+
Thread.current[:job_batches_redis] = nil
|
419
|
+
end
|
420
|
+
else
|
421
|
+
# TODO
|
422
|
+
end
|
418
423
|
end
|
419
424
|
|
420
425
|
def logger
|
@@ -433,6 +438,42 @@ module CanvasSync
|
|
433
438
|
end
|
434
439
|
end
|
435
440
|
end
|
441
|
+
|
442
|
+
class RedisProxy
|
443
|
+
def multi(*args, &block)
|
444
|
+
Batch.redis do |r|
|
445
|
+
if block.arity == 1
|
446
|
+
r.multi(*args) do
|
447
|
+
block.call(r)
|
448
|
+
end
|
449
|
+
else
|
450
|
+
r.multi(*args, &block)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def pipelined(*args, &block)
|
456
|
+
Batch.redis do |r|
|
457
|
+
if block.arity == 1
|
458
|
+
r.pipelined(*args) do
|
459
|
+
block.call(r)
|
460
|
+
end
|
461
|
+
else
|
462
|
+
r.pipelined(*args, &block)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def method_missing(method_name, *arguments, &block)
|
468
|
+
Batch.redis do |r|
|
469
|
+
r.send(method_name, *arguments, &block)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def respond_to_missing?(method_name, include_private = false)
|
474
|
+
super || Redis.method_defined?(method_name)
|
475
|
+
end
|
476
|
+
end
|
436
477
|
end
|
437
478
|
|
438
479
|
ActiveJob::Base.include BatchAwareJob
|
@@ -6,17 +6,17 @@ module CanvasSync
|
|
6
6
|
included do
|
7
7
|
around_perform do |job, block|
|
8
8
|
if (@bid) # This _must_ be @bid - not just bid
|
9
|
-
prev_batch = Thread.current[
|
9
|
+
prev_batch = Thread.current[CURRENT_BATCH_THREAD_KEY]
|
10
10
|
begin
|
11
|
-
Thread.current[
|
11
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = Batch.new(@bid)
|
12
12
|
block.call
|
13
|
-
Thread.current[
|
13
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY].save_context_changes
|
14
14
|
Batch.process_successful_job(@bid, job_id)
|
15
15
|
rescue
|
16
16
|
Batch.process_failed_job(@bid, job_id)
|
17
17
|
raise
|
18
18
|
ensure
|
19
|
-
Thread.current[
|
19
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = prev_batch
|
20
20
|
end
|
21
21
|
else
|
22
22
|
block.call
|
@@ -24,7 +24,7 @@ module CanvasSync
|
|
24
24
|
end
|
25
25
|
|
26
26
|
around_enqueue do |job, block|
|
27
|
-
if (batch = Thread.current[
|
27
|
+
if (batch = Thread.current[CURRENT_BATCH_THREAD_KEY])
|
28
28
|
@bid = batch.bid
|
29
29
|
batch.increment_job_queue(job_id) if @bid
|
30
30
|
end
|
@@ -33,11 +33,11 @@ module CanvasSync
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def bid
|
36
|
-
@bid || Thread.current[
|
36
|
+
@bid || Thread.current[CURRENT_BATCH_THREAD_KEY]&.bid
|
37
37
|
end
|
38
38
|
|
39
39
|
def batch
|
40
|
-
Thread.current[
|
40
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY]
|
41
41
|
end
|
42
42
|
|
43
43
|
def batch_context
|
@@ -64,18 +64,14 @@ module CanvasSync
|
|
64
64
|
let_close!
|
65
65
|
end
|
66
66
|
else
|
67
|
-
redis
|
68
|
-
r.hset(redis_key, 'keep_open', true)
|
69
|
-
end
|
67
|
+
redis.hset(redis_key, 'keep_open', true)
|
70
68
|
end
|
71
69
|
end
|
72
70
|
|
73
71
|
def let_close!
|
74
|
-
_, active_count = redis do |r|
|
75
|
-
r.
|
76
|
-
|
77
|
-
r.hincrby(redis_key, "active_count", 0)
|
78
|
-
end
|
72
|
+
_, active_count = redis.multi do |r|
|
73
|
+
r.hset(redis_key, 'keep_open', false)
|
74
|
+
r.hincrby(redis_key, "active_count", 0)
|
79
75
|
end
|
80
76
|
|
81
77
|
if active_count == 0 && pending_count == 0
|
@@ -95,35 +91,38 @@ module CanvasSync
|
|
95
91
|
end
|
96
92
|
|
97
93
|
def active_count
|
98
|
-
redis
|
99
|
-
r.hincrby(redis_key, "active_count", 0)
|
100
|
-
end
|
94
|
+
redis.hincrby(redis_key, "active_count", 0)
|
101
95
|
end
|
102
96
|
|
103
97
|
def pending_count
|
104
98
|
jobs_key = "#{redis_key}-jobs"
|
105
99
|
order = self.order || 'fifo'
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
r.zcard(jobs_key)
|
114
|
-
end
|
100
|
+
case order.to_sym
|
101
|
+
when :fifo, :lifo
|
102
|
+
redis.llen(jobs_key)
|
103
|
+
when :random
|
104
|
+
redis.scard(jobs_key)
|
105
|
+
when :priority
|
106
|
+
redis.zcard(jobs_key)
|
115
107
|
end
|
116
108
|
end
|
117
109
|
|
118
110
|
def job_checked_in(status, options)
|
119
|
-
active_count = redis do |r|
|
111
|
+
active_count, pending_count = redis do |r|
|
120
112
|
return unless r.exists?(redis_key)
|
121
|
-
|
113
|
+
|
114
|
+
# Make sure this is loaded outside of the pipeline
|
115
|
+
self.order
|
116
|
+
|
117
|
+
redis.multi do
|
118
|
+
r.hincrby(redis_key, "active_count", -1)
|
119
|
+
self.pending_count
|
120
|
+
end
|
122
121
|
end
|
123
122
|
|
124
123
|
added_count = refill_allotment
|
125
|
-
if active_count == 0 && added_count == 0
|
126
|
-
if clean_when_empty && redis
|
124
|
+
if active_count == 0 && added_count == 0 && pending_count == 0
|
125
|
+
if clean_when_empty && redis.hget(redis_key, 'keep_open') != 'true'
|
127
126
|
cleanup_redis
|
128
127
|
end
|
129
128
|
end
|
@@ -175,18 +174,16 @@ module CanvasSync
|
|
175
174
|
job_json = JSON.unparse(ActiveJob::Arguments.serialize([job_desc]))
|
176
175
|
order = self.order
|
177
176
|
|
178
|
-
redis do |r|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
r.zadd(jobs_key, job_desc[:priority] || 0, job_json)
|
187
|
-
end
|
188
|
-
r.expire(jobs_key, Batch::BID_EXPIRE_TTL)
|
177
|
+
redis.multi do |r|
|
178
|
+
case order.to_sym
|
179
|
+
when :fifo, :lifo
|
180
|
+
r.rpush(jobs_key, job_json)
|
181
|
+
when :random
|
182
|
+
r.sadd(jobs_key, job_json)
|
183
|
+
when :priority
|
184
|
+
r.zadd(jobs_key, job_desc[:priority] || 0, job_json)
|
189
185
|
end
|
186
|
+
r.expire(jobs_key, Batch::BID_EXPIRE_TTL)
|
190
187
|
end
|
191
188
|
end
|
192
189
|
|
@@ -194,18 +191,15 @@ module CanvasSync
|
|
194
191
|
jobs_key = "#{redis_key}-jobs"
|
195
192
|
order = self.order
|
196
193
|
|
197
|
-
job_json =
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
when :priority
|
207
|
-
r.zpopmax(jobs_key)
|
208
|
-
end
|
194
|
+
job_json = case order.to_sym
|
195
|
+
when :fifo
|
196
|
+
redis.lpop(jobs_key)
|
197
|
+
when :lifo
|
198
|
+
redis.rpop(jobs_key)
|
199
|
+
when :random
|
200
|
+
redis.spop(jobs_key)
|
201
|
+
when :priority
|
202
|
+
redis.zpopmax(jobs_key)
|
209
203
|
end
|
210
204
|
|
211
205
|
return nil unless job_json.present?
|
@@ -218,13 +212,9 @@ module CanvasSync
|
|
218
212
|
end
|
219
213
|
delegate :redis, to: :class
|
220
214
|
|
221
|
-
protected
|
222
|
-
|
223
215
|
def flush_pending_attrs
|
224
216
|
super
|
225
|
-
redis
|
226
|
-
r.zadd("pools", created_at, pid)
|
227
|
-
end
|
217
|
+
redis.zadd("pools", created_at, pid)
|
228
218
|
end
|
229
219
|
|
230
220
|
private
|
@@ -8,11 +8,11 @@ module CanvasSync
|
|
8
8
|
module Sidekiq
|
9
9
|
module WorkerExtension
|
10
10
|
def bid
|
11
|
-
Thread.current[
|
11
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY].bid
|
12
12
|
end
|
13
13
|
|
14
14
|
def batch
|
15
|
-
Thread.current[
|
15
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY]
|
16
16
|
end
|
17
17
|
|
18
18
|
def batch_context
|
@@ -42,7 +42,7 @@ module CanvasSync
|
|
42
42
|
|
43
43
|
class ClientMiddleware
|
44
44
|
def call(_worker, msg, _queue, _redis_pool = nil)
|
45
|
-
if (batch = Thread.current[
|
45
|
+
if (batch = Thread.current[CURRENT_BATCH_THREAD_KEY]) && should_handle_batch?(msg)
|
46
46
|
batch.increment_job_queue(msg['jid']) if (msg[:bid] = batch.bid)
|
47
47
|
end
|
48
48
|
yield
|
@@ -57,17 +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[
|
60
|
+
prev_batch = Thread.current[CURRENT_BATCH_THREAD_KEY]
|
61
61
|
begin
|
62
|
-
Thread.current[
|
62
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = Batch.new(bid)
|
63
63
|
yield
|
64
|
-
Thread.current[
|
64
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY].save_context_changes
|
65
65
|
Batch.process_successful_job(bid, msg['jid'])
|
66
66
|
rescue
|
67
67
|
Batch.process_failed_job(bid, msg['jid'])
|
68
68
|
raise
|
69
69
|
ensure
|
70
|
-
Thread.current[
|
70
|
+
Thread.current[CURRENT_BATCH_THREAD_KEY] = prev_batch
|
71
71
|
end
|
72
72
|
else
|
73
73
|
yield
|
@@ -9,7 +9,7 @@ module CanvasSync
|
|
9
9
|
# will be started for each term in that scope. :models should be an array of
|
10
10
|
# models to sync.
|
11
11
|
def perform(options)
|
12
|
-
CanvasSync.get_canvas_sync_client(batch_context).terms("self").all_pages!.each do |term_params|
|
12
|
+
CanvasSync.get_canvas_sync_client(batch_context).terms("self", workflow_state: ['all']).all_pages!.each do |term_params|
|
13
13
|
if account_id = batch_context[:account_id]
|
14
14
|
# These branches are primarily to support Legacy apps
|
15
15
|
if Term.respond_to?(:create_or_update) && Term.method(:create_or_update).arity.abs == 2
|
data/lib/canvas_sync/version.rb
CHANGED