dynflow 1.6.10 → 1.7.0
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/.github/workflows/ruby.yml +7 -15
- data/lib/dynflow/action.rb +6 -0
- data/lib/dynflow/persistence.rb +8 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +16 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +16 -2
- data/lib/dynflow/version.rb +1 -1
- data/test/action_test.rb +43 -0
- data/test/persistence_test.rb +75 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1fd4cb9db86befb0bfc96cd5c98f2ca9aa0f6f40249712cbbb8b250b9b9a142
|
4
|
+
data.tar.gz: 218d8a4ce5ec717f24ca4d5d730a5c0620f65ab00e6a35e0595a18876f4b9a31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80b8644418108a107c2a73481fb793647ac6ce40f9789b4efa064970bd6cccd54579ce95ff0b5a4710f033e4defaa7d0fd50ec96d9c51e91666adcd875cc9cc4
|
7
|
+
data.tar.gz: 6a28661cdfeff6555c970df957f10f28d4c23baf977b1212cf3253bac973cef967b1c40be7b77dbaa3fe6811e484be319a04417a0895dd81a32edd534ce58975
|
data/.github/workflows/ruby.yml
CHANGED
@@ -35,10 +35,9 @@ jobs:
|
|
35
35
|
fail-fast: false
|
36
36
|
matrix:
|
37
37
|
ruby_version:
|
38
|
-
- 2.5.0
|
39
|
-
- 2.6.0
|
40
38
|
- 2.7.0
|
41
39
|
- 3.0.0
|
40
|
+
- 3.2.0
|
42
41
|
concurrent_ruby_ext:
|
43
42
|
- 'true'
|
44
43
|
- 'false'
|
@@ -54,30 +53,23 @@ jobs:
|
|
54
53
|
- db: sqlite3
|
55
54
|
conn_string: sqlite:/
|
56
55
|
exclude:
|
57
|
-
- db: mysql
|
58
|
-
ruby_version: 2.5.0
|
59
|
-
- db: mysql
|
60
|
-
ruby_version: 2.6.0
|
61
56
|
- db: mysql
|
62
57
|
ruby_version: 3.0.0
|
58
|
+
- db: mysql
|
59
|
+
ruby_version: 3.2.0
|
63
60
|
- db: mysql
|
64
61
|
concurrent_ruby_ext: 'true'
|
65
|
-
- db: sqlite3
|
66
|
-
ruby_version: 2.5.0
|
67
|
-
- db: sqlite3
|
68
|
-
ruby_version: 2.6.0
|
69
62
|
- db: sqlite3
|
70
63
|
ruby_version: 3.0.0
|
64
|
+
- db: sqlite3
|
65
|
+
ruby_version: 3.2.0
|
71
66
|
- db: sqlite3
|
72
67
|
concurrent_ruby_ext: 'true'
|
73
68
|
- db: postgresql
|
74
|
-
ruby_version:
|
75
|
-
concurrent_ruby_ext: 'true'
|
76
|
-
- db: postgresql
|
77
|
-
ruby_version: 2.6.0
|
69
|
+
ruby_version: 3.0.0
|
78
70
|
concurrent_ruby_ext: 'true'
|
79
71
|
- db: postgresql
|
80
|
-
ruby_version: 3.
|
72
|
+
ruby_version: 3.2.0
|
81
73
|
concurrent_ruby_ext: 'true'
|
82
74
|
|
83
75
|
services:
|
data/lib/dynflow/action.rb
CHANGED
@@ -179,6 +179,12 @@ module Dynflow
|
|
179
179
|
@output_chunks ||= world.persistence.load_output_chunks(@execution_plan_id, @id)
|
180
180
|
end
|
181
181
|
|
182
|
+
def drop_output_chunks!
|
183
|
+
@pending_output_chunks = []
|
184
|
+
@output_chunks = []
|
185
|
+
world.persistence.delete_output_chunks(@execution_plan_id, @id)
|
186
|
+
end
|
187
|
+
|
182
188
|
def caller_action
|
183
189
|
phase! Present
|
184
190
|
return nil if @caller_action_id
|
data/lib/dynflow/persistence.rb
CHANGED
@@ -56,12 +56,20 @@ module Dynflow
|
|
56
56
|
adapter.load_output_chunks(execution_plan_id, action_id)
|
57
57
|
end
|
58
58
|
|
59
|
+
def delete_output_chunks(execution_plan_id, action_id)
|
60
|
+
adapter.delete_output_chunks(execution_plan_id, action_id)
|
61
|
+
end
|
62
|
+
|
59
63
|
def find_execution_plans(options)
|
60
64
|
adapter.find_execution_plans(options).map do |execution_plan_hash|
|
61
65
|
ExecutionPlan.new_from_hash(execution_plan_hash, @world)
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
69
|
+
def find_execution_plan_statuses(options)
|
70
|
+
adapter.find_execution_plan_statuses(options)
|
71
|
+
end
|
72
|
+
|
65
73
|
def find_execution_plan_counts(options)
|
66
74
|
adapter.find_execution_plan_counts(options)
|
67
75
|
end
|
@@ -46,6 +46,10 @@ module Dynflow
|
|
46
46
|
filter(:execution_plan, options[:filters]).count
|
47
47
|
end
|
48
48
|
|
49
|
+
def find_execution_plan_statuses(options)
|
50
|
+
raise NotImplementedError
|
51
|
+
end
|
52
|
+
|
49
53
|
# @param filters [Hash{ String => Object }] filters to determine
|
50
54
|
# what to delete
|
51
55
|
# @param batch_size the size of the chunks to iterate over when
|
@@ -104,6 +108,18 @@ module Dynflow
|
|
104
108
|
raise NotImplementedError
|
105
109
|
end
|
106
110
|
|
111
|
+
def save_output_chunks(execution_plan_id, action_id, chunks)
|
112
|
+
raise NotImplementedError
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_output_chunks(execution_plan_id, action_id)
|
116
|
+
raise NotImplementedError
|
117
|
+
end
|
118
|
+
|
119
|
+
def delete_output_chunks(execution_plan_id, action_id)
|
120
|
+
raise NotImplementedError
|
121
|
+
end
|
122
|
+
|
107
123
|
# for debug purposes
|
108
124
|
def to_hash
|
109
125
|
raise NotImplementedError
|
@@ -78,6 +78,16 @@ module Dynflow
|
|
78
78
|
filter(:execution_plan, table(:execution_plan), options[:filters]).count
|
79
79
|
end
|
80
80
|
|
81
|
+
def find_execution_plan_statuses(options)
|
82
|
+
plans = filter(:execution_plan, table(:execution_plan), options[:filters])
|
83
|
+
.select(:uuid, :state, :result)
|
84
|
+
|
85
|
+
plans.each_with_object({}) do |current, acc|
|
86
|
+
uuid = current.delete(:uuid)
|
87
|
+
acc[uuid] = current
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
81
91
|
def delete_execution_plans(filters, batch_size = 1000, backup_dir = nil)
|
82
92
|
count = 0
|
83
93
|
filter(:execution_plan, table(:execution_plan), filters).each_slice(batch_size) do |plans|
|
@@ -190,6 +200,10 @@ module Dynflow
|
|
190
200
|
load_records :output_chunk, { execution_plan_uuid: execution_plan_id, action_id: action_id }, [:timestamp, :kind, :chunk]
|
191
201
|
end
|
192
202
|
|
203
|
+
def delete_output_chunks(execution_plan_id, action_id)
|
204
|
+
filter(:output_chunk, table(:output_chunk), { execution_plan_uuid: execution_plan_id, action_id: action_id }).delete
|
205
|
+
end
|
206
|
+
|
193
207
|
def connector_feature!
|
194
208
|
unless @additional_responsibilities[:connector]
|
195
209
|
raise "The sequel persistence adapter connector feature used but not enabled in additional_features"
|
@@ -393,11 +407,11 @@ module Dynflow
|
|
393
407
|
hash = if record[:data].nil?
|
394
408
|
SERIALIZABLE_COLUMNS.fetch(what, []).each do |key|
|
395
409
|
key = key.to_sym
|
396
|
-
record[key] = MessagePack.unpack(
|
410
|
+
record[key] = MessagePack.unpack(record[key].to_s) unless record[key].nil?
|
397
411
|
end
|
398
412
|
record
|
399
413
|
else
|
400
|
-
MessagePack.unpack(record[:data])
|
414
|
+
MessagePack.unpack(record[:data].to_s)
|
401
415
|
end
|
402
416
|
Utils.indifferent_hash(hash)
|
403
417
|
end
|
data/lib/dynflow/version.rb
CHANGED
data/test/action_test.rb
CHANGED
@@ -889,5 +889,48 @@ module Dynflow
|
|
889
889
|
end
|
890
890
|
end
|
891
891
|
end
|
892
|
+
|
893
|
+
describe 'output chunks' do
|
894
|
+
include ::Dynflow::Testing::Factories
|
895
|
+
|
896
|
+
class OutputChunkAction < ::Dynflow::Action
|
897
|
+
def run(event = nil)
|
898
|
+
output[:counter] ||= 0
|
899
|
+
case event
|
900
|
+
when nil
|
901
|
+
output_chunk("Chunk #{output[:counter]}")
|
902
|
+
output[:counter] += 1
|
903
|
+
suspend
|
904
|
+
when :exit
|
905
|
+
return
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
909
|
+
def finalize
|
910
|
+
drop_output_chunks!
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
it 'collects and drops output chunks' do
|
915
|
+
action = create_and_plan_action(OutputChunkAction)
|
916
|
+
_(action.pending_output_chunks).must_equal nil
|
917
|
+
|
918
|
+
action = run_action(action)
|
919
|
+
_(action.pending_output_chunks.count).must_equal 1
|
920
|
+
|
921
|
+
action = run_action(action)
|
922
|
+
_(action.pending_output_chunks.count).must_equal 2
|
923
|
+
|
924
|
+
action = run_action(action, :exit)
|
925
|
+
_(action.pending_output_chunks.count).must_equal 2
|
926
|
+
|
927
|
+
persistence = mock()
|
928
|
+
persistence.expects(:delete_output_chunks).with(action.execution_plan_id, action.id)
|
929
|
+
action.world.stubs(:persistence).returns(persistence)
|
930
|
+
|
931
|
+
action = finalize_action(action)
|
932
|
+
_(action.pending_output_chunks.count).must_equal 0
|
933
|
+
end
|
934
|
+
end
|
892
935
|
end
|
893
936
|
end
|
data/test/persistence_test.rb
CHANGED
@@ -159,6 +159,46 @@ module Dynflow
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
+
describe '#def find_execution_plan_statuses' do
|
163
|
+
before do
|
164
|
+
# the tests expect clean field
|
165
|
+
adapter.delete_execution_plans({})
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'supports filtering' do
|
169
|
+
prepare_and_save_plans
|
170
|
+
if adapter.ordering_by.include?('state')
|
171
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { label: ['test1'] })
|
172
|
+
_(loaded_plans).must_equal({ 'plan1' => { state: 'paused', result: nil} })
|
173
|
+
|
174
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { state: ['paused'] })
|
175
|
+
_(loaded_plans).must_equal({"plan1"=>{:state=>"paused", :result=>nil},
|
176
|
+
"plan3"=>{:state=>"paused", :result=>nil},
|
177
|
+
"plan4"=>{:state=>"paused", :result=>nil}})
|
178
|
+
|
179
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { state: ['stopped'] })
|
180
|
+
_(loaded_plans).must_equal({"plan2"=>{:state=>"stopped", :result=>nil}})
|
181
|
+
|
182
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { state: [] })
|
183
|
+
_(loaded_plans).must_equal({})
|
184
|
+
|
185
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { state: ['stopped', 'paused'] })
|
186
|
+
_(loaded_plans).must_equal({"plan1"=>{:state=>"paused", :result=>nil},
|
187
|
+
"plan2"=>{:state=>"stopped", :result=>nil},
|
188
|
+
"plan3"=>{:state=>"paused", :result=>nil}, "plan4"=>{:state=>"paused", :result=>nil}})
|
189
|
+
|
190
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { 'state' => ['stopped', 'paused'] })
|
191
|
+
_(loaded_plans).must_equal({"plan1"=>{:state=>"paused", :result=>nil},
|
192
|
+
"plan2"=>{:state=>"stopped", :result=>nil},
|
193
|
+
"plan3"=>{:state=>"paused", :result=>nil},
|
194
|
+
"plan4"=>{:state=>"paused", :result=>nil}})
|
195
|
+
|
196
|
+
loaded_plans = adapter.find_execution_plan_statuses(filters: { label: ['test1'], :delayed => true })
|
197
|
+
_(loaded_plans).must_equal({})
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
162
202
|
describe '#def find_execution_plan_counts' do
|
163
203
|
before do
|
164
204
|
# the tests expect clean field
|
@@ -331,6 +371,20 @@ module Dynflow
|
|
331
371
|
_(plans.first[:execution_plan_uuid]).must_equal 'plan1'
|
332
372
|
end
|
333
373
|
end
|
374
|
+
|
375
|
+
describe '#delete_output_chunks' do
|
376
|
+
it 'deletes output chunks' do
|
377
|
+
prepare_plans_with_actions
|
378
|
+
|
379
|
+
adapter.save_output_chunks('plan1', 1, [{chunk: "Hello", timestamp: Time.now}, {chunk: "Bye", timestamp: Time.now}])
|
380
|
+
chunks = adapter.load_output_chunks('plan1', 1)
|
381
|
+
_(chunks.length).must_equal 2
|
382
|
+
deleted = adapter.delete_output_chunks('plan1', 1)
|
383
|
+
_(deleted).must_equal 2
|
384
|
+
chunks = adapter.load_output_chunks('plan1', 1)
|
385
|
+
_(chunks.length).must_equal 0
|
386
|
+
end
|
387
|
+
end
|
334
388
|
end
|
335
389
|
|
336
390
|
describe Dynflow::PersistenceAdapters::Sequel do
|
@@ -464,6 +518,27 @@ module Dynflow
|
|
464
518
|
loaded_plan = adapter.load_execution_plan(plan[:id])
|
465
519
|
assert_equal_attributes!(plan_data, loaded_plan)
|
466
520
|
end
|
521
|
+
|
522
|
+
it 'does not leak Sequel blobs' do
|
523
|
+
db = adapter.send(:db)
|
524
|
+
# Prepare records for saving
|
525
|
+
plan = prepare_plans.first
|
526
|
+
|
527
|
+
value = 'a' * 1000
|
528
|
+
|
529
|
+
adata = action_data.merge({:output => { :key => value }})
|
530
|
+
plan_record = adapter.send(:prepare_record, :execution_plan, plan.merge(:uuid => plan[:id]))
|
531
|
+
action_record = adapter.send(:prepare_record, :action, adata.dup)
|
532
|
+
|
533
|
+
# Insert the records
|
534
|
+
db[:dynflow_execution_plans].insert plan_record.merge(:uuid => plan[:id])
|
535
|
+
db[:dynflow_actions].insert action_record.merge(:execution_plan_uuid => plan[:id], :id => adata[:id])
|
536
|
+
|
537
|
+
# Load the saved records
|
538
|
+
loaded_action = adapter.load_action(plan[:id], adata[:id])
|
539
|
+
_(loaded_action[:output][:key].class).must_equal String
|
540
|
+
_(loaded_action[:output][:key]).must_equal value
|
541
|
+
end
|
467
542
|
end
|
468
543
|
end
|
469
544
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Necas
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-05-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -678,7 +678,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
678
678
|
- !ruby/object:Gem::Version
|
679
679
|
version: '0'
|
680
680
|
requirements: []
|
681
|
-
rubygems_version: 3.
|
681
|
+
rubygems_version: 3.4.12
|
682
682
|
signing_key:
|
683
683
|
specification_version: 4
|
684
684
|
summary: DYNamic workFLOW engine
|