burstflow 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +29 -21
- data/burstflow.gemspec +4 -4
- data/db/schema.rb +11 -13
- data/lib/burstflow/job.rb +23 -26
- data/lib/burstflow/job/callbacks.rb +4 -9
- data/lib/burstflow/job/exception.rb +3 -1
- data/lib/burstflow/job/initialization.rb +3 -6
- data/lib/burstflow/job/state.rb +17 -13
- data/lib/burstflow/manager.rb +85 -86
- data/lib/burstflow/railtie.rb +5 -2
- data/lib/burstflow/version.rb +3 -1
- data/lib/burstflow/worker.rb +21 -21
- data/lib/burstflow/workflow.rb +164 -156
- data/lib/burstflow/workflow/builder.rb +67 -64
- data/lib/burstflow/workflow/callbacks.rb +10 -16
- data/lib/burstflow/workflow/configuration.rb +37 -36
- data/lib/burstflow/workflow/exception.rb +3 -1
- data/lib/generators/burstflow/install/install_generator.rb +11 -5
- data/spec/builder_spec.rb +26 -27
- data/spec/generators/install_generator_spec.rb +11 -7
- data/spec/job_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/workflow_spec.rb +128 -101
- metadata +4 -4
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require
|
2
|
+
require 'generator_spec'
|
3
3
|
require 'tmpdir'
|
4
4
|
|
5
5
|
require 'generators/burstflow/install/install_generator'
|
6
6
|
|
7
7
|
module Burstflow
|
8
|
+
|
8
9
|
module Generators
|
10
|
+
|
9
11
|
describe InstallGenerator, type: :generator do
|
10
|
-
root_dir = File.expand_path(Dir.tmpdir
|
12
|
+
root_dir = File.expand_path(Dir.tmpdir, __FILE__)
|
11
13
|
destination root_dir
|
12
14
|
|
13
15
|
before :all do
|
@@ -15,13 +17,15 @@ module Burstflow
|
|
15
17
|
run_generator
|
16
18
|
end
|
17
19
|
|
18
|
-
it
|
19
|
-
migration_file =
|
20
|
+
it 'creates the installation db migration' do
|
21
|
+
migration_file =
|
20
22
|
Dir.glob("#{root_dir}/db/migrate/*create_workflow.rb")
|
21
23
|
|
22
|
-
assert_file migration_file[0],
|
23
|
-
|
24
|
+
assert_file migration_file[0],
|
25
|
+
/CreateWorkflow < ActiveRecord::Migration/
|
24
26
|
end
|
25
27
|
end
|
28
|
+
|
26
29
|
end
|
27
|
-
|
30
|
+
|
31
|
+
end
|
data/spec/job_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -13,7 +13,7 @@ ActiveJob::Base.logger = nil
|
|
13
13
|
$root = File.join(File.dirname(__dir__), 'spec')
|
14
14
|
Dir[File.join($root, 'support', '**', '*.rb')].each {|f| require f }
|
15
15
|
|
16
|
-
#ActiveJob::Base.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
16
|
+
# ActiveJob::Base.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
17
17
|
ActiveJob::Base.logger = ActiveSupport::Logger.new('/dev/null')
|
18
18
|
|
19
19
|
|
data/spec/workflow_spec.rb
CHANGED
@@ -6,32 +6,36 @@ describe Burstflow::Workflow do
|
|
6
6
|
WfJob3 = Class.new(Burstflow::Job)
|
7
7
|
|
8
8
|
class Workflow1 < Burstflow::Workflow
|
9
|
+
|
9
10
|
$conf1 = configure do |arg1, arg2|
|
10
|
-
$jobid1 = run WfJob1, params: { param1: true, arg: arg1}
|
11
|
-
$jobid2 = run WfJob2, params: { param2: true, arg: arg2}, after: WfJob1
|
11
|
+
$jobid1 = run WfJob1, params: { param1: true, arg: arg1 }
|
12
|
+
$jobid2 = run WfJob2, params: { param2: true, arg: arg2 }, after: WfJob1
|
12
13
|
$jobid3 = run WfJob3, before: WfJob2, after: $jobid1
|
13
14
|
$jobid4 = run WfJob3, after: $jobid3
|
14
15
|
end
|
16
|
+
|
15
17
|
end
|
16
18
|
|
17
19
|
class Workflow2 < Burstflow::Workflow
|
20
|
+
|
18
21
|
$conf2 = configure do |arg1, arg2|
|
19
|
-
$jobid1 = run WfJob1, params: { param1: true, arg: arg1}
|
20
|
-
$jobid2 = run WfJob2, params: { param2: true, arg: arg2}, after: WfJob1
|
22
|
+
$jobid1 = run WfJob1, params: { param1: true, arg: arg1 }
|
23
|
+
$jobid2 = run WfJob2, params: { param2: true, arg: arg2 }, after: WfJob1
|
21
24
|
$jobid3 = run WfJob3, before: WfJob2, after: $jobid1
|
22
25
|
$jobid4 = run WfJob3, after: $jobid3
|
23
26
|
end
|
27
|
+
|
24
28
|
end
|
25
29
|
|
26
|
-
describe
|
27
|
-
it
|
30
|
+
describe 'initializing' do
|
31
|
+
it 'class level configuration' do
|
28
32
|
expect(Burstflow::Workflow.configuration).to eq nil
|
29
33
|
expect(Workflow1.configuration).to eq $conf1
|
30
34
|
expect(Workflow2.configuration).to eq $conf2
|
31
35
|
expect($conf1).not_to eq $conf2
|
32
36
|
end
|
33
37
|
|
34
|
-
it
|
38
|
+
it 'build' do
|
35
39
|
Workflow1.build(:arg1, :arg2).save!
|
36
40
|
wf1 = Workflow1.first
|
37
41
|
jobs = wf1.flow['jobs_config']
|
@@ -39,58 +43,53 @@ describe Burstflow::Workflow do
|
|
39
43
|
expect(jobs.count).to eq 4
|
40
44
|
expect(wf1.initial_jobs.count).to eq 1
|
41
45
|
|
42
|
-
expect(jobs[$jobid1]).to include(:id,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
expect(jobs[$jobid1]).to include(:id,
|
47
|
+
klass: WfJob1.to_s,
|
48
|
+
incoming: [],
|
49
|
+
outgoing: [$jobid2, $jobid3],
|
50
|
+
workflow_id: wf1.id,
|
51
|
+
params: { 'param1' => true, 'arg' => 'arg1' })
|
48
52
|
|
49
53
|
expect(jobs[$jobid2]).to include(:id,
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
klass: WfJob2.to_s,
|
55
|
+
incoming: [$jobid1, $jobid3],
|
56
|
+
outgoing: [],
|
57
|
+
workflow_id: wf1.id,
|
58
|
+
params: { 'param2' => true, 'arg' => 'arg2' })
|
55
59
|
|
56
60
|
expect(jobs[$jobid3]).to include(:id,
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
klass: WfJob3.to_s,
|
62
|
+
incoming: [$jobid1],
|
63
|
+
outgoing: [$jobid2, $jobid4],
|
64
|
+
workflow_id: wf1.id,
|
65
|
+
params: nil)
|
62
66
|
|
63
67
|
expect(jobs[$jobid4]).to include(:id,
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
klass: WfJob3.to_s,
|
69
|
+
incoming: [$jobid3],
|
70
|
+
outgoing: [],
|
71
|
+
workflow_id: wf1.id,
|
72
|
+
params: nil)
|
69
73
|
end
|
70
|
-
|
71
74
|
end
|
72
75
|
|
73
|
-
describe
|
74
|
-
|
75
|
-
def perform_enqueued_job wf, enqueued_job
|
76
|
+
describe 'executing' do
|
77
|
+
def perform_enqueued_job(wf, enqueued_job)
|
76
78
|
enqueued_job[:job].new(*enqueued_job[:args]).perform_now
|
77
79
|
|
78
80
|
queue_adapter.performed_jobs << enqueued_job
|
79
81
|
queue_adapter.enqueued_jobs.delete(enqueued_job)
|
80
|
-
|
82
|
+
|
81
83
|
wf.reload
|
82
84
|
end
|
83
85
|
|
84
86
|
def perform_enqueued_jobs_async
|
85
|
-
|
86
87
|
jobs = Enumerator.new do |y|
|
87
|
-
while queue_adapter.enqueued_jobs.count > 0
|
88
|
-
y << queue_adapter.enqueued_jobs.shift
|
89
|
-
end
|
88
|
+
y << queue_adapter.enqueued_jobs.shift while queue_adapter.enqueued_jobs.count > 0
|
90
89
|
end
|
91
90
|
|
92
91
|
result = yield
|
93
|
-
while queue_adapter.enqueued_jobs.count > 0
|
92
|
+
while queue_adapter.enqueued_jobs.count > 0
|
94
93
|
|
95
94
|
threads = jobs.map do |job|
|
96
95
|
Thread.new(job) do |job|
|
@@ -104,14 +103,14 @@ describe Burstflow::Workflow do
|
|
104
103
|
result
|
105
104
|
end
|
106
105
|
|
107
|
-
describe
|
108
|
-
let(:wf){Workflow1.build(:arg1, :arg2)}
|
106
|
+
describe 'complex' do
|
107
|
+
let(:wf){ Workflow1.build(:arg1, :arg2) }
|
109
108
|
|
110
109
|
before do
|
111
110
|
wf.start!
|
112
111
|
end
|
113
112
|
|
114
|
-
it
|
113
|
+
it 'success story one by one' do
|
115
114
|
expect(Burstflow::Worker).to have_jobs(wf.id, [$jobid1])
|
116
115
|
|
117
116
|
expect(queue_adapter.enqueued_jobs.count).to eq 1
|
@@ -128,7 +127,7 @@ describe Burstflow::Workflow do
|
|
128
127
|
expect(wf.jobs.count(&:started?)).to eq 1
|
129
128
|
expect(wf.jobs.count(&:finished?)).to eq 1
|
130
129
|
expect(wf.jobs.count(&:ready_to_start?)).to eq 0
|
131
|
-
|
130
|
+
|
132
131
|
perform_enqueued_job(wf, queue_adapter.enqueued_jobs.first)
|
133
132
|
expect(queue_adapter.enqueued_jobs.count).to eq 2
|
134
133
|
expect(queue_adapter.enqueued_jobs.first).to include(args: [wf.id, $jobid2])
|
@@ -145,17 +144,16 @@ describe Burstflow::Workflow do
|
|
145
144
|
expect(wf.jobs.count(&:started?)).to eq 3
|
146
145
|
expect(wf.jobs.count(&:finished?)).to eq 3
|
147
146
|
expect(wf.jobs.count(&:ready_to_start?)).to eq 0
|
148
|
-
|
147
|
+
|
149
148
|
perform_enqueued_job(wf, queue_adapter.enqueued_jobs.first)
|
150
149
|
expect(queue_adapter.enqueued_jobs.count).to eq 0
|
151
150
|
expect(wf.jobs.count(&:enqueued?)).to eq 4
|
152
151
|
expect(wf.jobs.count(&:started?)).to eq 4
|
153
152
|
expect(wf.jobs.count(&:finished?)).to eq 4
|
154
153
|
expect(wf.jobs.count(&:ready_to_start?)).to eq 0
|
155
|
-
|
156
154
|
end
|
157
155
|
|
158
|
-
it
|
156
|
+
it 'success story all at once sync' do
|
159
157
|
perform_enqueued_jobs do
|
160
158
|
perform_enqueued_job(wf, queue_adapter.enqueued_jobs.first)
|
161
159
|
end
|
@@ -167,7 +165,7 @@ describe Burstflow::Workflow do
|
|
167
165
|
expect(wf.jobs.count(&:ready_to_start?)).to eq 0
|
168
166
|
end
|
169
167
|
|
170
|
-
it
|
168
|
+
it 'success story all at once async', threads: true do
|
171
169
|
perform_enqueued_jobs_async do
|
172
170
|
perform_enqueued_job(wf, queue_adapter.enqueued_jobs.first)
|
173
171
|
end
|
@@ -181,73 +179,81 @@ describe Burstflow::Workflow do
|
|
181
179
|
end
|
182
180
|
end
|
183
181
|
|
184
|
-
describe
|
182
|
+
describe 'concurrency' do
|
185
183
|
class MapJob1 < Burstflow::Job
|
184
|
+
|
186
185
|
def perform
|
187
186
|
set_output(params['i'])
|
188
187
|
end
|
188
|
+
|
189
189
|
end
|
190
190
|
|
191
191
|
class ReduceJob1 < Burstflow::Job
|
192
|
+
|
192
193
|
def perform
|
193
|
-
set_output(payloads.map{|p| p[:value]}.sum)
|
194
|
+
set_output(payloads.map{|p| p[:value] }.sum)
|
194
195
|
end
|
196
|
+
|
195
197
|
end
|
196
198
|
|
197
199
|
class ConcWorkflow < Burstflow::Workflow
|
200
|
+
|
198
201
|
configure do |size|
|
199
202
|
size.to_i.times.to_a.shuffle.each do |i|
|
200
|
-
run MapJob1, params: {i: i}, before: ReduceJob1
|
203
|
+
run MapJob1, params: { i: i }, before: ReduceJob1
|
201
204
|
end
|
202
205
|
|
203
206
|
$jobid = run ReduceJob1
|
204
207
|
end
|
205
|
-
end
|
206
208
|
|
207
|
-
|
208
|
-
let(:expected_result){count.times.sum}
|
209
|
+
end
|
209
210
|
|
210
|
-
|
211
|
+
let(:count){ 50 }
|
212
|
+
let(:expected_result){ count.times.sum }
|
213
|
+
|
214
|
+
it 'sync' do
|
211
215
|
wf = perform_enqueued_jobs do
|
212
216
|
ConcWorkflow.build(count).start!
|
213
217
|
end.reload
|
214
|
-
|
218
|
+
|
215
219
|
expect(wf.job($jobid).output).to eq expected_result
|
216
220
|
end
|
217
221
|
|
218
|
-
it
|
222
|
+
it 'threaded', threads: true do
|
219
223
|
wf = perform_enqueued_jobs_async do
|
220
224
|
ConcWorkflow.build(count).start!
|
221
225
|
end.reload
|
222
|
-
|
226
|
+
|
223
227
|
expect(wf.job($jobid).output).to eq expected_result
|
224
228
|
end
|
225
229
|
end
|
226
230
|
|
227
|
-
describe
|
231
|
+
describe 'simple' do
|
228
232
|
WfSimpleJob = Class.new(Burstflow::Job)
|
229
233
|
WfFailureJob = Class.new(Burstflow::Job) do
|
230
|
-
def perform
|
231
|
-
raise
|
234
|
+
def perform
|
235
|
+
raise 'ex'
|
232
236
|
end
|
233
237
|
end
|
234
238
|
|
235
239
|
WfSuspendJob = Class.new(Burstflow::Job) do
|
236
|
-
def perform
|
237
|
-
|
240
|
+
def perform
|
241
|
+
Burstflow::Job::SUSPEND
|
238
242
|
end
|
239
243
|
end
|
240
244
|
|
241
|
-
describe
|
245
|
+
describe 'parallel failure' do
|
242
246
|
class Workflow3 < Burstflow::Workflow
|
243
|
-
|
247
|
+
|
248
|
+
configure do |_arg1, _arg2|
|
244
249
|
$jobid1 = run WfSimpleJob
|
245
250
|
$jobid2 = run WfFailureJob
|
246
251
|
$jobid3 = run WfSimpleJob, after: $jobid2
|
247
252
|
end
|
253
|
+
|
248
254
|
end
|
249
255
|
|
250
|
-
it
|
256
|
+
it 'run' do
|
251
257
|
wf = perform_enqueued_jobs do
|
252
258
|
Workflow3.build.start!
|
253
259
|
end.reload
|
@@ -265,16 +271,18 @@ describe Burstflow::Workflow do
|
|
265
271
|
end
|
266
272
|
end
|
267
273
|
|
268
|
-
describe
|
274
|
+
describe 'parallel suspend' do
|
269
275
|
class Workflow4 < Burstflow::Workflow
|
270
|
-
|
276
|
+
|
277
|
+
configure do |_arg1, _arg2|
|
271
278
|
$jobid1 = run WfSimpleJob
|
272
279
|
$jobid2 = run WfSuspendJob
|
273
280
|
$jobid3 = run WfSimpleJob, after: $jobid2
|
274
281
|
end
|
282
|
+
|
275
283
|
end
|
276
284
|
|
277
|
-
it
|
285
|
+
it 'run' do
|
278
286
|
wf = perform_enqueued_jobs do
|
279
287
|
Workflow4.build.start!
|
280
288
|
end.reload
|
@@ -286,7 +294,7 @@ describe Burstflow::Workflow do
|
|
286
294
|
expect(wf.job($jobid3).enqueued?).to eq false
|
287
295
|
|
288
296
|
wf = perform_enqueued_jobs do
|
289
|
-
wf.resume!($jobid2,
|
297
|
+
wf.resume!($jobid2, 'gogogo')
|
290
298
|
end.reload
|
291
299
|
|
292
300
|
expect(wf.status).to eq Burstflow::Workflow::FINISHED
|
@@ -297,22 +305,22 @@ describe Burstflow::Workflow do
|
|
297
305
|
end
|
298
306
|
end
|
299
307
|
|
300
|
-
describe
|
301
|
-
let(:count) {10}
|
302
|
-
let(:expected_result) {(count + 1).times.sum}
|
308
|
+
describe 'dynamic job creation' do
|
309
|
+
let(:count) { 10 }
|
310
|
+
let(:expected_result) { (count + 1).times.sum }
|
303
311
|
|
304
312
|
WfDynamicJob = Class.new(Burstflow::Job) do
|
305
|
-
def perform
|
313
|
+
def perform
|
306
314
|
if params['i'] > 0
|
307
|
-
configure(
|
308
|
-
$lasjobid = run WfDynamicJob, params: {i: params['i'] - 1}, after: id
|
315
|
+
configure(id, params) do |id, params|
|
316
|
+
$lasjobid = run WfDynamicJob, params: { i: params['i'] - 1 }, after: id
|
309
317
|
end
|
310
318
|
end
|
311
319
|
|
312
320
|
output = if payload = payloads.first
|
313
|
-
|
314
|
-
|
315
|
-
|
321
|
+
payload[:value] + params['i']
|
322
|
+
else
|
323
|
+
params['i']
|
316
324
|
end
|
317
325
|
|
318
326
|
set_output(output)
|
@@ -320,12 +328,14 @@ describe Burstflow::Workflow do
|
|
320
328
|
end
|
321
329
|
|
322
330
|
class WorkflowDynamic < Burstflow::Workflow
|
331
|
+
|
323
332
|
configure do |count|
|
324
|
-
run WfDynamicJob, params: {i: count}
|
333
|
+
run WfDynamicJob, params: { i: count }
|
325
334
|
end
|
335
|
+
|
326
336
|
end
|
327
337
|
|
328
|
-
it
|
338
|
+
it 'run' do
|
329
339
|
wf = WorkflowDynamic.build(count)
|
330
340
|
wf.save!
|
331
341
|
|
@@ -338,11 +348,9 @@ describe Burstflow::Workflow do
|
|
338
348
|
expect(wf.jobs.count).to eq 11
|
339
349
|
expect(wf.job($lasjobid).output).to eq expected_result
|
340
350
|
end
|
341
|
-
|
342
351
|
end
|
343
352
|
|
344
|
-
describe
|
345
|
-
|
353
|
+
describe 'callbacks' do
|
346
354
|
WfCallJob = Class.new(Burstflow::Job) do
|
347
355
|
before_enqueue :be
|
348
356
|
before_perform :bp
|
@@ -350,36 +358,60 @@ describe Burstflow::Workflow do
|
|
350
358
|
before_resume :br
|
351
359
|
before_failure :bf
|
352
360
|
|
353
|
-
def be
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
def
|
361
|
+
def be
|
362
|
+
$be = true
|
363
|
+
end
|
364
|
+
|
365
|
+
def bp
|
366
|
+
$bp = true
|
367
|
+
end
|
368
|
+
|
369
|
+
def bs
|
370
|
+
$bs = true
|
371
|
+
end
|
372
|
+
|
373
|
+
def br
|
374
|
+
$br = true
|
375
|
+
end
|
376
|
+
|
377
|
+
def bf
|
378
|
+
$bf = true
|
379
|
+
end
|
358
380
|
|
359
381
|
def perform
|
360
|
-
suspend
|
382
|
+
suspend
|
361
383
|
end
|
362
384
|
|
363
|
-
def resume
|
364
|
-
raise
|
385
|
+
def resume(_data)
|
386
|
+
raise 'ex'
|
365
387
|
end
|
366
388
|
end
|
367
|
-
|
389
|
+
|
368
390
|
class WorkflowCall < Burstflow::Workflow
|
391
|
+
|
369
392
|
before_suspend :wbs
|
370
393
|
before_resume :wbr
|
371
394
|
before_failure :wbf
|
372
395
|
|
373
|
-
def wbs
|
374
|
-
|
375
|
-
|
396
|
+
def wbs
|
397
|
+
$wbs = true
|
398
|
+
end
|
399
|
+
|
400
|
+
def wbr
|
401
|
+
$wbr = true
|
402
|
+
end
|
403
|
+
|
404
|
+
def wbf
|
405
|
+
$wbf = true
|
406
|
+
end
|
376
407
|
|
377
408
|
configure do
|
378
409
|
$jobid1 = run WfCallJob
|
379
410
|
end
|
411
|
+
|
380
412
|
end
|
381
413
|
|
382
|
-
it
|
414
|
+
it 'run' do
|
383
415
|
expect([$be, $bp, $bs, $br, $bf].any?).to eq false
|
384
416
|
expect([$wbs, $wbr, $wbf].any?).to eq false
|
385
417
|
|
@@ -402,12 +434,9 @@ describe Burstflow::Workflow do
|
|
402
434
|
expect([$wbs, $wbr, $wbf].all?).to eq true
|
403
435
|
end
|
404
436
|
end
|
405
|
-
|
406
|
-
|
407
437
|
end
|
408
|
-
|
409
438
|
end
|
410
|
-
|
439
|
+
|
411
440
|
context 'model' do
|
412
441
|
it 'default store' do
|
413
442
|
w = Burstflow::Workflow.new
|
@@ -429,7 +458,5 @@ describe Burstflow::Workflow do
|
|
429
458
|
w2 = Burstflow::Workflow.find(w.id)
|
430
459
|
expect(w2.attributes).to include(w.attributes)
|
431
460
|
end
|
432
|
-
|
433
461
|
end
|
434
|
-
|
435
462
|
end
|