canvas_sync 0.17.23.beta6 → 0.17.24
Sign up to get free protection for your applications and to get access to all the features.
- 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