canvas_sync 0.21.1 → 0.22.0.beta1
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/concerns/auto_relations.rb +11 -0
- data/lib/canvas_sync/config.rb +3 -5
- data/lib/canvas_sync/generators/templates/models/rubric.rb +2 -1
- data/lib/canvas_sync/job_batches/batch.rb +432 -402
- data/lib/canvas_sync/job_batches/callback.rb +100 -114
- data/lib/canvas_sync/job_batches/chain_builder.rb +194 -196
- data/lib/canvas_sync/job_batches/{active_job.rb → compat/active_job.rb} +2 -2
- data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/helpers.rb +1 -1
- data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web.rb +3 -3
- data/lib/canvas_sync/job_batches/{sidekiq.rb → compat/sidekiq.rb} +35 -22
- data/lib/canvas_sync/job_batches/compat.rb +20 -0
- data/lib/canvas_sync/job_batches/context_hash.rb +124 -126
- data/lib/canvas_sync/job_batches/jobs/base_job.rb +2 -4
- data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +14 -16
- data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +125 -127
- data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +14 -16
- data/lib/canvas_sync/job_batches/pool.rb +193 -195
- data/lib/canvas_sync/job_batches/redis_model.rb +50 -52
- data/lib/canvas_sync/job_batches/redis_script.rb +129 -131
- data/lib/canvas_sync/job_batches/status.rb +85 -87
- data/lib/canvas_sync/job_uniqueness/compat/active_job.rb +75 -0
- data/lib/canvas_sync/job_uniqueness/compat/sidekiq.rb +135 -0
- data/lib/canvas_sync/job_uniqueness/compat.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/configuration.rb +25 -0
- data/lib/canvas_sync/job_uniqueness/job_uniqueness.rb +47 -0
- data/lib/canvas_sync/job_uniqueness/lock_context.rb +171 -0
- data/lib/canvas_sync/job_uniqueness/locksmith.rb +92 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/base.rb +32 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/log.rb +13 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/null_strategy.rb +9 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/raise.rb +11 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/reject.rb +21 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/reschedule.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict.rb +41 -0
- data/lib/canvas_sync/job_uniqueness/strategy/base.rb +104 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_and_while_executing.rb +35 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_executed.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_executing.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_expired.rb +16 -0
- data/lib/canvas_sync/job_uniqueness/strategy/while_executing.rb +26 -0
- data/lib/canvas_sync/job_uniqueness/strategy.rb +27 -0
- data/lib/canvas_sync/job_uniqueness/unique_job_common.rb +79 -0
- data/lib/canvas_sync/misc_helper.rb +1 -1
- data/lib/canvas_sync/version.rb +1 -1
- data/lib/canvas_sync.rb +4 -3
- data/spec/dummy/app/models/rubric.rb +2 -1
- data/spec/dummy/config/environments/test.rb +1 -1
- data/spec/job_batching/batch_spec.rb +49 -7
- data/spec/job_batching/{active_job_spec.rb → compat/active_job_spec.rb} +2 -2
- data/spec/job_batching/{sidekiq_spec.rb → compat/sidekiq_spec.rb} +14 -12
- data/spec/job_batching/flow_spec.rb +1 -1
- data/spec/job_batching/integration_helper.rb +1 -1
- data/spec/job_batching/status_spec.rb +2 -2
- data/spec/job_uniqueness/compat/active_job_spec.rb +49 -0
- data/spec/job_uniqueness/compat/sidekiq_spec.rb +68 -0
- data/spec/job_uniqueness/lock_context_spec.rb +95 -0
- data/spec/job_uniqueness/on_conflict/log_spec.rb +11 -0
- data/spec/job_uniqueness/on_conflict/raise_spec.rb +10 -0
- data/spec/job_uniqueness/on_conflict/reschedule_spec.rb +24 -0
- data/spec/job_uniqueness/on_conflict_spec.rb +16 -0
- data/spec/job_uniqueness/spec_helper.rb +14 -0
- data/spec/job_uniqueness/strategy/base_spec.rb +100 -0
- data/spec/job_uniqueness/strategy/until_and_while_executing_spec.rb +48 -0
- data/spec/job_uniqueness/strategy/until_executed_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/until_executing_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/until_expired_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/while_executing_spec.rb +33 -0
- data/spec/job_uniqueness/support/lock_strategy.rb +28 -0
- data/spec/job_uniqueness/support/on_conflict.rb +24 -0
- data/spec/job_uniqueness/support/test_worker.rb +19 -0
- data/spec/job_uniqueness/unique_job_common_spec.rb +45 -0
- data/spec/spec_helper.rb +1 -1
- metadata +278 -204
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/css/styles.less +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/batch_tree.js +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/util.js +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batch_tree.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batches_table.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_common.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_jobs_table.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_pagination.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batch.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batches.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pool.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pools.erb +0 -0
@@ -1,159 +1,157 @@
|
|
1
|
-
module CanvasSync
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
# Local is "the nearest batch with a context value"
|
15
|
-
# This allows for, for example, SerialBatchJob to have a modifiable context stored on it's main Batch
|
16
|
-
# that can be accessed transparently from one of it's internal, context-less Batches
|
17
|
-
def local_bid
|
18
|
-
bid = @bid_stack[-1]
|
19
|
-
while bid.present?
|
20
|
-
bhash = resolve_hash(bid)
|
21
|
-
return bid if bhash
|
22
|
-
bid = get_parent_bid(bid)
|
23
|
-
end
|
24
|
-
nil
|
25
|
-
end
|
1
|
+
module CanvasSync::JobBatches
|
2
|
+
class ContextHash
|
3
|
+
delegate_missing_to :flatten
|
4
|
+
|
5
|
+
def initialize(bid, hash = nil)
|
6
|
+
@bid_stack = [bid]
|
7
|
+
@hash_map = {}
|
8
|
+
@dirty = false
|
9
|
+
@flattened = nil
|
10
|
+
@hash_map[bid] = hash.with_indifferent_access if hash
|
11
|
+
end
|
26
12
|
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
# Local is "the nearest batch with a context value"
|
14
|
+
# This allows for, for example, SerialBatchJob to have a modifiable context stored on it's main Batch
|
15
|
+
# that can be accessed transparently from one of it's internal, context-less Batches
|
16
|
+
def local_bid
|
17
|
+
bid = @bid_stack[-1]
|
18
|
+
while bid.present?
|
19
|
+
bhash = resolve_hash(bid)
|
20
|
+
return bid if bhash
|
21
|
+
bid = get_parent_bid(bid)
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
|
26
|
+
def local
|
27
|
+
@hash_map[local_bid]
|
28
|
+
end
|
34
29
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
30
|
+
def own
|
31
|
+
resolve_hash(@bid_stack[-1]) || {}
|
32
|
+
end
|
39
33
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
self
|
45
|
-
end
|
34
|
+
def set_local(new_hash)
|
35
|
+
@dirty = true
|
36
|
+
local.clear.merge!(new_hash)
|
37
|
+
end
|
46
38
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
39
|
+
def clear
|
40
|
+
local.clear
|
41
|
+
@flattened = nil
|
42
|
+
@dirty = true
|
43
|
+
self
|
44
|
+
end
|
52
45
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
bid = get_parent_bid(bid)
|
59
|
-
end
|
60
|
-
nil
|
61
|
-
end
|
46
|
+
def []=(key, value)
|
47
|
+
@flattened = nil
|
48
|
+
@dirty = true
|
49
|
+
local[key] = value
|
50
|
+
end
|
62
51
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
52
|
+
def [](key)
|
53
|
+
bid = @bid_stack[-1]
|
54
|
+
while bid.present?
|
55
|
+
bhash = resolve_hash(bid)
|
56
|
+
return bhash[key] if bhash&.key?(key)
|
57
|
+
bid = get_parent_bid(bid)
|
67
58
|
end
|
59
|
+
nil
|
60
|
+
end
|
68
61
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
62
|
+
def reload!
|
63
|
+
@dirty = false
|
64
|
+
@hash_map = {}
|
65
|
+
self
|
66
|
+
end
|
75
67
|
|
76
|
-
|
77
|
-
|
68
|
+
def save!(force: false)
|
69
|
+
return unless dirty? || force
|
70
|
+
Batch.redis do |r|
|
71
|
+
r.hset("BID-#{local_bid}", 'context', JSON.unparse(local))
|
78
72
|
end
|
73
|
+
end
|
79
74
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
75
|
+
def dirty?
|
76
|
+
@dirty
|
77
|
+
end
|
84
78
|
|
85
|
-
|
86
|
-
|
79
|
+
def is_a?(arg)
|
80
|
+
return true if Hash <= arg
|
81
|
+
super
|
82
|
+
end
|
87
83
|
|
88
|
-
|
89
|
-
|
90
|
-
@bid_stack.compact.each do |bid|
|
91
|
-
flattened.merge!(@hash_map[bid]) if @hash_map[bid]
|
92
|
-
end
|
93
|
-
flattened.freeze
|
84
|
+
def flatten
|
85
|
+
return @flattened if @flattened
|
94
86
|
|
95
|
-
|
87
|
+
load_all
|
88
|
+
flattened = {}
|
89
|
+
@bid_stack.compact.each do |bid|
|
90
|
+
flattened.merge!(@hash_map[bid]) if @hash_map[bid]
|
96
91
|
end
|
92
|
+
flattened.freeze
|
97
93
|
|
98
|
-
|
99
|
-
|
100
|
-
end
|
94
|
+
@flattened = flattened.with_indifferent_access
|
95
|
+
end
|
101
96
|
|
102
|
-
|
97
|
+
def to_h
|
98
|
+
flatten
|
99
|
+
end
|
103
100
|
|
104
|
-
|
105
|
-
resolve_hash(get_parent_bid(bid)).freeze
|
106
|
-
end
|
101
|
+
private
|
107
102
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
103
|
+
def get_parent_hash(bid)
|
104
|
+
resolve_hash(get_parent_bid(bid)).freeze
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_parent_bid(bid)
|
108
|
+
index = @bid_stack.index(bid)
|
109
|
+
raise "Invalid BID #{bid}" if index.nil? # Sanity Check - this shouldn't happen
|
110
|
+
|
111
|
+
index -= 1
|
112
|
+
if index >= 0
|
113
|
+
@bid_stack[index]
|
114
|
+
else
|
115
|
+
pbid = Batch.redis do |r|
|
116
|
+
callback_params = JSON.parse(r.hget("BID-#{bid}", "callback_params") || "{}")
|
117
|
+
callback_params['for_bid'] || r.hget("BID-#{bid}", "parent_bid")
|
122
118
|
end
|
119
|
+
@bid_stack.unshift(pbid)
|
120
|
+
pbid
|
123
121
|
end
|
122
|
+
end
|
124
123
|
|
125
|
-
|
126
|
-
|
127
|
-
|
124
|
+
def resolve_hash(bid)
|
125
|
+
return nil unless bid.present?
|
126
|
+
return @hash_map[bid] if @hash_map.key?(bid)
|
128
127
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
128
|
+
context_json, editable = Batch.redis do |r|
|
129
|
+
r.multi do |r|
|
130
|
+
r.hget("BID-#{bid}", "context")
|
131
|
+
r.hget("BID-#{bid}", "allow_context_changes")
|
134
132
|
end
|
133
|
+
end
|
135
134
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
142
|
-
context_hash.freeze unless editable
|
143
|
-
|
144
|
-
@hash_map[bid] = context_hash
|
145
|
-
else
|
146
|
-
@hash_map[bid] = nil
|
135
|
+
if context_json.present?
|
136
|
+
context_hash = JSON.parse(context_json)
|
137
|
+
context_hash = context_hash.with_indifferent_access
|
138
|
+
context_hash.each do |k, v|
|
139
|
+
v.freeze
|
147
140
|
end
|
141
|
+
context_hash.freeze unless editable
|
142
|
+
|
143
|
+
@hash_map[bid] = context_hash
|
144
|
+
else
|
145
|
+
@hash_map[bid] = nil
|
148
146
|
end
|
147
|
+
end
|
149
148
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
155
|
-
@hash_map
|
149
|
+
def load_all
|
150
|
+
resolve_hash(@bid_stack[0]).freeze
|
151
|
+
while @bid_stack[0].present?
|
152
|
+
get_parent_hash(@bid_stack[0])
|
156
153
|
end
|
154
|
+
@hash_map
|
157
155
|
end
|
158
156
|
end
|
159
157
|
end
|
@@ -1,22 +1,20 @@
|
|
1
1
|
require_relative './base_job'
|
2
2
|
|
3
|
-
module CanvasSync
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
3
|
+
module CanvasSync::JobBatches
|
4
|
+
class ConcurrentBatchJob < BaseJob
|
5
|
+
def self.make_batch(sub_jobs, **kwargs, &blk)
|
6
|
+
ManagedBatchJob.make_batch(
|
7
|
+
sub_jobs,
|
8
|
+
**kwargs,
|
9
|
+
ordered: false,
|
10
|
+
concurrency: true,
|
11
|
+
desc_prefix: 'ConcurrentBatchJob: ',
|
12
|
+
&blk
|
13
|
+
)
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
16
|
+
def perform(sub_jobs, **kwargs)
|
17
|
+
self.class.make_batch(sub_jobs, **kwargs)
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -1,170 +1,168 @@
|
|
1
1
|
require_relative './base_job'
|
2
2
|
|
3
|
-
module CanvasSync
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
module CanvasSync::JobBatches
|
4
|
+
class ManagedBatchJob < BaseJob
|
5
|
+
def self.make_batch(sub_jobs, ordered: true, concurrency: nil, context: nil, preflight_check: nil, desc_prefix: nil, &blk)
|
6
|
+
desc_prefix ||= ''
|
7
|
+
|
8
|
+
if concurrency == 0 || concurrency == nil || concurrency == true
|
9
|
+
concurrency = sub_jobs.count
|
10
|
+
elsif concurrency == false
|
11
|
+
concurrency = 1
|
12
|
+
end
|
13
|
+
|
14
|
+
root_batch = Batch.new
|
15
|
+
man_batch_id = nil
|
16
|
+
|
17
|
+
if concurrency < sub_jobs.count
|
18
|
+
man_batch_id = SecureRandom.urlsafe_base64(10)
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
r.expire("MNGBID-#{man_batch_id}", Batch::BID_EXPIRE_TTL)
|
28
|
-
|
29
|
-
mapped_sub_jobs = sub_jobs.each_with_index.map do |j, i|
|
30
|
-
j['_mngbid_index_'] = i # This allows duplicate jobs when a Redis Set is used
|
31
|
-
j = ::ActiveJob::Arguments.serialize([j])
|
32
|
-
JSON.unparse(j)
|
33
|
-
end
|
34
|
-
if ordered
|
35
|
-
r.rpush("MNGBID-#{man_batch_id}-jobs", mapped_sub_jobs)
|
36
|
-
else
|
37
|
-
r.sadd("MNGBID-#{man_batch_id}-jobs", mapped_sub_jobs)
|
38
|
-
end
|
39
|
-
r.expire("MNGBID-#{man_batch_id}-jobs", Batch::BID_EXPIRE_TTL)
|
20
|
+
Batch.redis do |r|
|
21
|
+
r.multi do |r|
|
22
|
+
r.hset("MNGBID-#{man_batch_id}", "root_bid", root_batch.bid)
|
23
|
+
r.hset("MNGBID-#{man_batch_id}", "ordered", ordered ? 1 : 0)
|
24
|
+
r.hset("MNGBID-#{man_batch_id}", "concurrency", concurrency)
|
25
|
+
r.hset("MNGBID-#{man_batch_id}", "preflight_check", preflight_check) if preflight_check.present?
|
26
|
+
r.expire("MNGBID-#{man_batch_id}", Batch::BID_EXPIRE_TTL)
|
27
|
+
|
28
|
+
mapped_sub_jobs = sub_jobs.each_with_index.map do |j, i|
|
29
|
+
j['_mngbid_index_'] = i # This allows duplicate jobs when a Redis Set is used
|
30
|
+
j = ::ActiveJob::Arguments.serialize([j])
|
31
|
+
JSON.unparse(j)
|
40
32
|
end
|
33
|
+
if ordered
|
34
|
+
r.rpush("MNGBID-#{man_batch_id}-jobs", mapped_sub_jobs)
|
35
|
+
else
|
36
|
+
r.sadd("MNGBID-#{man_batch_id}-jobs", mapped_sub_jobs)
|
37
|
+
end
|
38
|
+
r.expire("MNGBID-#{man_batch_id}-jobs", Batch::BID_EXPIRE_TTL)
|
41
39
|
end
|
40
|
+
end
|
42
41
|
|
43
|
-
|
44
|
-
|
42
|
+
root_batch.allow_context_changes = (concurrency == 1)
|
43
|
+
root_batch.on(:success, "#{to_s}.cleanup_redis", managed_batch_id: man_batch_id)
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
desc_prefix = "MGD(#{man_batch_id}): #{desc_prefix}"
|
46
|
+
end
|
48
47
|
|
49
|
-
|
48
|
+
root_batch.context = context
|
50
49
|
|
51
|
-
|
50
|
+
blk.call(ManagedBatchProxy.new(root_batch)) if blk.present?
|
52
51
|
|
53
|
-
|
52
|
+
root_batch.description = "#{desc_prefix}#{root_batch.description || 'Root'}"
|
54
53
|
|
55
|
-
|
54
|
+
root_batch.context["managed_batch_bid"] = man_batch_id if man_batch_id
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
56
|
+
if concurrency < sub_jobs.count
|
57
|
+
root_batch.jobs {}
|
58
|
+
concurrency.times do
|
59
|
+
perform_next_sequence_job(man_batch_id, skip_preflight: true)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
root_batch.jobs do
|
63
|
+
sub_jobs.each do |j|
|
64
|
+
ChainBuilder.enqueue_job(j)
|
67
65
|
end
|
68
66
|
end
|
69
|
-
|
70
|
-
root_batch
|
71
67
|
end
|
72
68
|
|
73
|
-
|
74
|
-
|
75
|
-
end
|
69
|
+
root_batch
|
70
|
+
end
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
r.del(
|
81
|
-
"MNGBID-#{man_batch_id}",
|
82
|
-
"MNGBID-#{man_batch_id}-jobs",
|
83
|
-
)
|
84
|
-
end
|
85
|
-
end
|
72
|
+
def perform(sub_jobs, **kwargs)
|
73
|
+
self.class.make_batch(sub_jobs, **kwargs)
|
74
|
+
end
|
86
75
|
|
87
|
-
|
88
|
-
|
89
|
-
|
76
|
+
def self.cleanup_redis(status, options)
|
77
|
+
man_batch_id = options['managed_batch_id']
|
78
|
+
Batch.redis do |r|
|
79
|
+
r.del(
|
80
|
+
"MNGBID-#{man_batch_id}",
|
81
|
+
"MNGBID-#{man_batch_id}-jobs",
|
82
|
+
)
|
90
83
|
end
|
84
|
+
end
|
91
85
|
|
92
|
-
|
86
|
+
def self.job_succeeded_callback(status, options)
|
87
|
+
man_batch_id = options['managed_batch_id']
|
88
|
+
perform_next_sequence_job(man_batch_id)
|
89
|
+
end
|
93
90
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
91
|
+
protected
|
92
|
+
|
93
|
+
def self.perform_next_sequence_job(man_batch_id, skip_preflight: false)
|
94
|
+
root_bid, ordered, preflight_check = Batch.redis do |r|
|
95
|
+
r.multi do |r|
|
96
|
+
r.hget("MNGBID-#{man_batch_id}", "root_bid")
|
97
|
+
r.hget("MNGBID-#{man_batch_id}", "ordered")
|
98
|
+
r.hget("MNGBID-#{man_batch_id}", "preflight_check")
|
101
99
|
end
|
100
|
+
end
|
102
101
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
else
|
108
|
-
clazz = Object
|
109
|
-
method_name = preflight_check
|
110
|
-
end
|
111
|
-
preflight_check = ->(*args) { clazz.send(method_name, *args) }
|
102
|
+
if !skip_preflight && preflight_check.present?
|
103
|
+
if preflight_check.include?(".")
|
104
|
+
clazz, method_name = preflight_check.split('.')
|
105
|
+
clazz = clazz.constantize
|
112
106
|
else
|
113
|
-
|
107
|
+
clazz = Object
|
108
|
+
method_name = preflight_check
|
114
109
|
end
|
110
|
+
preflight_check = ->(*args) { clazz.send(method_name, *args) }
|
111
|
+
else
|
112
|
+
preflight_check = ->(*args) { true }
|
113
|
+
end
|
115
114
|
|
116
|
-
|
115
|
+
ordered = CanvasSync::MiscHelper.to_boolean(ordered)
|
117
116
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
117
|
+
loop do
|
118
|
+
next_job_json = Batch.redis do |r|
|
119
|
+
if ordered
|
120
|
+
r.lpop("MNGBID-#{man_batch_id}-jobs")
|
121
|
+
else
|
122
|
+
r.spop("MNGBID-#{man_batch_id}-jobs")
|
125
123
|
end
|
124
|
+
end
|
126
125
|
|
127
|
-
|
126
|
+
break unless next_job_json.present?
|
128
127
|
|
129
|
-
|
130
|
-
|
128
|
+
next_job = JSON.parse(next_job_json)
|
129
|
+
next_job = ::ActiveJob::Arguments.deserialize(next_job)[0]
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
131
|
+
preflight_result = preflight_check.call(next_job)
|
132
|
+
if preflight_result == :abort
|
133
|
+
cleanup_redis(nil, { "managed_batch_id" => man_batch_id })
|
134
|
+
break
|
135
|
+
elsif !preflight_check
|
136
|
+
next
|
137
|
+
end
|
139
138
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
139
|
+
Batch.new(root_bid).jobs do
|
140
|
+
Batch.new.tap do |batch|
|
141
|
+
batch.description = "Managed Batch Fiber (#{man_batch_id})"
|
142
|
+
batch.on(:success, "#{self.to_s}.job_succeeded_callback", managed_batch_id: man_batch_id)
|
143
|
+
batch.jobs do
|
144
|
+
ChainBuilder.enqueue_job(next_job)
|
147
145
|
end
|
148
146
|
end
|
149
|
-
|
150
|
-
break
|
151
147
|
end
|
152
|
-
end
|
153
148
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
end
|
149
|
+
break
|
150
|
+
end
|
151
|
+
end
|
158
152
|
|
159
|
-
|
153
|
+
class ManagedBatchProxy
|
154
|
+
def initialize(real_batch)
|
155
|
+
@real_batch = real_batch
|
156
|
+
end
|
160
157
|
|
161
|
-
|
162
|
-
raise "Managed Batches do not support calling .jobs directly!"
|
163
|
-
end
|
158
|
+
delegate_missing_to :real_batch
|
164
159
|
|
165
|
-
|
166
|
-
|
160
|
+
def jobs
|
161
|
+
raise "Managed Batches do not support calling .jobs directly!"
|
167
162
|
end
|
163
|
+
|
164
|
+
private
|
165
|
+
attr_reader :real_batch
|
168
166
|
end
|
169
167
|
end
|
170
168
|
end
|
@@ -1,22 +1,20 @@
|
|
1
1
|
require_relative './base_job'
|
2
2
|
|
3
|
-
module CanvasSync
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
3
|
+
module CanvasSync::JobBatches
|
4
|
+
class SerialBatchJob < BaseJob
|
5
|
+
def self.make_batch(sub_jobs, **kwargs, &blk)
|
6
|
+
ManagedBatchJob.make_batch(
|
7
|
+
sub_jobs,
|
8
|
+
**kwargs,
|
9
|
+
ordered: true,
|
10
|
+
concurrency: false,
|
11
|
+
desc_prefix: 'SerialBatchJob: ',
|
12
|
+
&blk
|
13
|
+
)
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
16
|
+
def perform(sub_jobs, **kwargs)
|
17
|
+
self.class.make_batch(sub_jobs, **kwargs)
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|