dynflow 0.8.33 → 0.8.34
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/dynflow/action/with_bulk_sub_plans.rb +10 -5
- data/lib/dynflow/action/with_polling_sub_plans.rb +4 -4
- data/lib/dynflow/action/with_sub_plans.rb +6 -0
- data/lib/dynflow/execution_plan.rb +6 -1
- data/lib/dynflow/persistence.rb +4 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +6 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +4 -0
- data/lib/dynflow/rails/configuration.rb +1 -0
- data/lib/dynflow/version.rb +1 -1
- data/test/action_test.rb +6 -5
- data/test/batch_sub_tasks_test.rb +13 -0
- data/test/concurrency_control_test.rb +6 -6
- data/test/persistence_test.rb +39 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ff5e3fce545cac6d1d299717c2fa11b54ba9ef6
|
4
|
+
data.tar.gz: 90214fe9c441dfae1780f591cca27de7eaa89da3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1084dc1ef5c5d8d923df3fc92d93b1b6e92edd85d29ab4fd5fe045908315a692b2cbf62615d0d34aedbf18b077d522b9eff4b368944c31c5689d1f8484820d2
|
7
|
+
data.tar.gz: eb449ff93ccc6720e13a4cc539dbec6a1296a4d81d58b3a4129a2ef8530fae769c0a68fe870317d0775fe094919de460d4c719c5bcc2a1318ee3b7939c90a9d7
|
@@ -30,7 +30,8 @@ module Dynflow
|
|
30
30
|
|
31
31
|
def initiate
|
32
32
|
output[:planned_count] = 0
|
33
|
-
output[:
|
33
|
+
output[:cancelled_count] = 0
|
34
|
+
output[:total_count] = total_count
|
34
35
|
super
|
35
36
|
end
|
36
37
|
|
@@ -58,7 +59,8 @@ module Dynflow
|
|
58
59
|
# The same logic as in Action::WithSubPlans, but calculated using the expected total count
|
59
60
|
def run_progress
|
60
61
|
if counts_set?
|
61
|
-
(
|
62
|
+
sum = output.values_at(:success_count, :cancelled_count, :failed_count).reduce(:+)
|
63
|
+
sum.to_f / total_count
|
62
64
|
else
|
63
65
|
0.1
|
64
66
|
end
|
@@ -71,8 +73,8 @@ module Dynflow
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def cancel!(force = false)
|
74
|
-
# Count the not-yet-planned tasks as
|
75
|
-
output[:
|
76
|
+
# Count the not-yet-planned tasks as cancelled
|
77
|
+
output[:cancelled_count] = total_count - output[:planned_count]
|
76
78
|
if uses_concurrency_control
|
77
79
|
# Tell the throttle limiter to cancel the tasks its managing
|
78
80
|
world.throttle_limiter.cancel!(execution_plan_id)
|
@@ -92,8 +94,11 @@ module Dynflow
|
|
92
94
|
end
|
93
95
|
|
94
96
|
def can_spawn_next_batch?
|
95
|
-
|
97
|
+
remaining_count > 0
|
96
98
|
end
|
97
99
|
|
100
|
+
def remaining_count
|
101
|
+
total_count - output[:cancelled_count] - output[:planned_count]
|
102
|
+
end
|
98
103
|
end
|
99
104
|
end
|
@@ -60,11 +60,11 @@ module Dynflow
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def recalculate_counts
|
63
|
-
total =
|
64
|
-
failed =
|
65
|
-
success =
|
63
|
+
total = sub_plans_count
|
64
|
+
failed = sub_plans_count('state' => %w(paused stopped), 'result' => 'error')
|
65
|
+
success = sub_plans_count('state' => 'stopped', 'result' => 'success')
|
66
66
|
output.update(:total_count => total - output.fetch(:resumed_count, 0),
|
67
|
-
:pending_count =>
|
67
|
+
:pending_count => total - failed - success,
|
68
68
|
:failed_count => failed - output.fetch(:resumed_count, 0),
|
69
69
|
:success_count => success)
|
70
70
|
end
|
@@ -162,6 +162,12 @@ module Dynflow
|
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
165
|
+
def sub_plans_count(filter = {})
|
166
|
+
filters = { 'caller_execution_plan_id' => execution_plan_id,
|
167
|
+
'caller_action_id' => self.id }
|
168
|
+
world.persistence.find_execution_plan_counts(filters: filters.merge(filter))
|
169
|
+
end
|
170
|
+
|
165
171
|
def notify_on_finish(plans)
|
166
172
|
suspend do |suspended_action|
|
167
173
|
plans.each do |plan|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'securerandom'
|
2
2
|
|
3
3
|
module Dynflow
|
4
|
-
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
5
5
|
# TODO extract planning logic to an extra class ExecutionPlanner
|
6
6
|
class ExecutionPlan < Serializable
|
7
7
|
|
@@ -162,6 +162,10 @@ module Dynflow
|
|
162
162
|
persistence.find_execution_plans(filters: { 'caller_execution_plan_id' => self.id })
|
163
163
|
end
|
164
164
|
|
165
|
+
def sub_plans_count
|
166
|
+
persistence.find_execution_plan_counts(filters: { 'caller_execution_plan_id' => self.id })
|
167
|
+
end
|
168
|
+
|
165
169
|
def rescue_plan_id
|
166
170
|
case rescue_strategy
|
167
171
|
when Action::Rescue::Pause
|
@@ -519,4 +523,5 @@ module Dynflow
|
|
519
523
|
|
520
524
|
private_class_method :steps_from_hash
|
521
525
|
end
|
526
|
+
# rubocop:enable Metrics/ClassLength
|
522
527
|
end
|
data/lib/dynflow/persistence.rb
CHANGED
@@ -40,6 +40,10 @@ module Dynflow
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
def find_execution_plan_counts(options)
|
44
|
+
adapter.find_execution_plan_counts(options)
|
45
|
+
end
|
46
|
+
|
43
47
|
def delete_execution_plans(filters, batch_size = 1000, enforce_backup_dir = nil)
|
44
48
|
backup_dir = enforce_backup_dir || current_backup_dir
|
45
49
|
adapter.delete_execution_plans(filters, batch_size, backup_dir)
|
@@ -39,6 +39,12 @@ module Dynflow
|
|
39
39
|
raise NotImplementedError
|
40
40
|
end
|
41
41
|
|
42
|
+
# @option options [Hash{ String => Object,Array<object> }] filters hash represents
|
43
|
+
# set of allowed values for a given key representing column
|
44
|
+
def find_execution_plan_counts(options = {})
|
45
|
+
filter(:execution_plan, options[:filters]).count
|
46
|
+
end
|
47
|
+
|
42
48
|
# @param filters [Hash{ String => Object }] filters to determine
|
43
49
|
# what to delete
|
44
50
|
# @param batch_size the size of the chunks to iterate over when
|
@@ -60,6 +60,10 @@ module Dynflow
|
|
60
60
|
data_set.all.map { |record| load_data(record) }
|
61
61
|
end
|
62
62
|
|
63
|
+
def find_execution_plan_counts(options = {})
|
64
|
+
filter(:execution_plan, table(:execution_plan), options[:filters]).count
|
65
|
+
end
|
66
|
+
|
63
67
|
def delete_execution_plans(filters, batch_size = 1000, backup_dir = nil)
|
64
68
|
count = 0
|
65
69
|
filter(:execution_plan, table(:execution_plan), filters).each_slice(batch_size) do |plans|
|
@@ -118,6 +118,7 @@ module Dynflow
|
|
118
118
|
|
119
119
|
def default_sequel_adapter_options
|
120
120
|
db_config = ::ActiveRecord::Base.configurations[::Rails.env].dup
|
121
|
+
db_config['adapter'] = db_config['adapter'].gsub(/_?makara_?/, '')
|
121
122
|
db_config['adapter'] = 'postgres' if db_config['adapter'] == 'postgresql'
|
122
123
|
db_config['max_connections'] = db_pool_size if increase_db_pool_size?
|
123
124
|
|
data/lib/dynflow/version.rb
CHANGED
data/test/action_test.rb
CHANGED
@@ -433,6 +433,7 @@ module Dynflow
|
|
433
433
|
specify "the sub-plan stores the information about its parent" do
|
434
434
|
sub_plans = execution_plan.sub_plans
|
435
435
|
sub_plans.size.must_equal 2
|
436
|
+
execution_plan.sub_plans_count.must_equal 2
|
436
437
|
sub_plans.each { |sub_plan| sub_plan.caller_execution_plan_id.must_equal execution_plan.id }
|
437
438
|
end
|
438
439
|
|
@@ -505,7 +506,7 @@ module Dynflow
|
|
505
506
|
|
506
507
|
wait_for do # Waiting for the sub plans to be spawned
|
507
508
|
polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
|
508
|
-
polling_plan.
|
509
|
+
polling_plan.sub_plans_count == total
|
509
510
|
end
|
510
511
|
|
511
512
|
# Moving the clock to make the parent check on sub plans
|
@@ -522,7 +523,7 @@ module Dynflow
|
|
522
523
|
world.execute(polling_plan.id) # The actual resume
|
523
524
|
|
524
525
|
wait_for do # Waiting for new generation of sub plans to be spawned
|
525
|
-
polling_plan.
|
526
|
+
polling_plan.sub_plans_count == 2 * total
|
526
527
|
end
|
527
528
|
|
528
529
|
# Move the clock again
|
@@ -545,7 +546,7 @@ module Dynflow
|
|
545
546
|
|
546
547
|
wait_for do # Waiting for the sub plans to be spawned
|
547
548
|
polling_plan = world.persistence.load_execution_plan(triggered_plan.id)
|
548
|
-
polling_plan.
|
549
|
+
polling_plan.sub_plans_count == total &&
|
549
550
|
polling_plan.sub_plans.all? { |sub| sub.state == :paused }
|
550
551
|
end
|
551
552
|
|
@@ -629,7 +630,7 @@ module Dynflow
|
|
629
630
|
world.execute(plan.id)
|
630
631
|
clock.pending_pings.count.must_equal 0
|
631
632
|
wait_for do
|
632
|
-
plan.
|
633
|
+
plan.sub_plans_count == total &&
|
633
634
|
plan.sub_plans.all? { |sub| sub.result == :success }
|
634
635
|
end
|
635
636
|
clock.pending_pings.count.must_equal 1
|
@@ -658,7 +659,7 @@ module Dynflow
|
|
658
659
|
|
659
660
|
# Wait for the sub plans to finish
|
660
661
|
wait_for do
|
661
|
-
plan.
|
662
|
+
plan.sub_plans_count == total &&
|
662
663
|
plan.sub_plans.all? { |sub| sub.result == :success }
|
663
664
|
end
|
664
665
|
|
@@ -93,6 +93,19 @@ module Dynflow
|
|
93
93
|
action.output[:success_count].must_equal action.total_count
|
94
94
|
end
|
95
95
|
|
96
|
+
it 'is controlled only by total_count and output[:planned_count]' do
|
97
|
+
plan = world.plan(ParentAction, 10)
|
98
|
+
future = world.execute plan.id
|
99
|
+
wait_for { future.completed? }
|
100
|
+
action = plan.entry_action
|
101
|
+
action.send(:can_spawn_next_batch?).must_equal false
|
102
|
+
action.current_batch.must_be :empty?
|
103
|
+
action.output[:pending_count] = 0
|
104
|
+
action.output[:success_count] = 5
|
105
|
+
action.send(:can_spawn_next_batch?).must_equal false
|
106
|
+
action.current_batch.must_be :empty?
|
107
|
+
end
|
108
|
+
|
96
109
|
end
|
97
110
|
end
|
98
111
|
end
|
@@ -143,7 +143,7 @@ module Dynflow
|
|
143
143
|
world.stub :clock, klok do
|
144
144
|
plan = world.plan(ParentAction, total, 0)
|
145
145
|
triggered = world.execute(plan.id)
|
146
|
-
wait_for { plan.
|
146
|
+
wait_for { plan.sub_plans_count == total }
|
147
147
|
world.event(plan.id, plan.steps.values.last.id, ::Dynflow::Action::Cancellable::Cancel)
|
148
148
|
wait_for { triggered.completed? }
|
149
149
|
plan.entry_action.output[:failed_count].must_equal total
|
@@ -158,21 +158,21 @@ module Dynflow
|
|
158
158
|
|
159
159
|
plan = world.plan(ParentAction, total, 1, 10)
|
160
160
|
future = world.execute(plan.id)
|
161
|
-
wait_for { plan.
|
161
|
+
wait_for { plan.sub_plans_count == total }
|
162
162
|
wait_for { klok.progress; plan.sub_plans.all? { |sub| successful? sub } }
|
163
163
|
# 10 tasks over 10 seconds, one task at a time, 1 task every second
|
164
164
|
get_interval.call(plan).must_equal 1.0
|
165
165
|
|
166
166
|
plan = world.plan(ParentAction, total, 4, 10)
|
167
167
|
world.execute(plan.id)
|
168
|
-
wait_for { plan.
|
168
|
+
wait_for { plan.sub_plans_count == total }
|
169
169
|
wait_for { klok.progress; plan.sub_plans.all? { |sub| successful? sub } }
|
170
170
|
# 10 tasks over 10 seconds, four tasks at a time, 1 task every 0.25 second
|
171
171
|
get_interval.call(plan).must_equal 0.25
|
172
172
|
|
173
173
|
plan = world.plan(ParentAction, total, nil, 10)
|
174
174
|
world.execute(plan.id)
|
175
|
-
wait_for { plan.
|
175
|
+
wait_for { plan.sub_plans_count == total }
|
176
176
|
wait_for { klok.progress; plan.sub_plans.all? { |sub| successful? sub } }
|
177
177
|
# 1o tasks over 10 seconds, one task at a time (default), 1 task every second
|
178
178
|
get_interval.call(plan).must_equal 1.0
|
@@ -187,7 +187,7 @@ module Dynflow
|
|
187
187
|
plan = world.plan(ParentAction, total, level, time_span)
|
188
188
|
start_time = klok.current_time
|
189
189
|
world.execute(plan.id)
|
190
|
-
wait_for { plan.
|
190
|
+
wait_for { plan.sub_plans_count == total }
|
191
191
|
wait_for { plan.sub_plans.select { |sub| successful? sub }.count == level }
|
192
192
|
finished = 2
|
193
193
|
check_step(plan, total, finished)
|
@@ -224,7 +224,7 @@ module Dynflow
|
|
224
224
|
total = 10
|
225
225
|
plan = world.plan(ParentAction, total, level, time_span, true)
|
226
226
|
future = world.execute(plan.id)
|
227
|
-
wait_for { plan.
|
227
|
+
wait_for { plan.sub_plans_count == total && plan.sub_plans.all? { |sub| sub.result == :pending } }
|
228
228
|
planned, running = plan.sub_plans.partition { |sub| planned? sub }
|
229
229
|
planned.count.must_equal total - level
|
230
230
|
running.count.must_equal level
|
data/test/persistence_test.rb
CHANGED
@@ -123,6 +123,45 @@ module Dynflow
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
describe '#def find_execution_plan_counts' do
|
127
|
+
before do
|
128
|
+
# the tests expect clean field
|
129
|
+
adapter.delete_execution_plans({})
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'supports filtering' do
|
133
|
+
prepare_plans
|
134
|
+
if adapter.ordering_by.include?('state')
|
135
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { label: ['test1'] })
|
136
|
+
loaded_plans.must_equal 1
|
137
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { state: ['paused'] })
|
138
|
+
loaded_plans.must_equal 3
|
139
|
+
|
140
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { state: ['stopped'] })
|
141
|
+
loaded_plans.must_equal 1
|
142
|
+
|
143
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { state: [] })
|
144
|
+
loaded_plans.must_equal 0
|
145
|
+
|
146
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { state: ['stopped', 'paused'] })
|
147
|
+
loaded_plans.must_equal 4
|
148
|
+
|
149
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { 'state' => ['stopped', 'paused'] })
|
150
|
+
loaded_plans.must_equal 4
|
151
|
+
|
152
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { label: ['test1'], :delayed => true })
|
153
|
+
loaded_plans.must_equal 0
|
154
|
+
|
155
|
+
adapter.save_delayed_plan('plan1',
|
156
|
+
:execution_plan_uuid => 'plan1',
|
157
|
+
:start_at => format_time(Time.now + 60),
|
158
|
+
:start_before => format_time(Time.now - 60))
|
159
|
+
loaded_plans = adapter.find_execution_plan_counts(filters: { label: ['test1'], :delayed => true })
|
160
|
+
loaded_plans.must_equal 1
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
126
165
|
describe '#load_execution_plan and #save_execution_plan' do
|
127
166
|
it 'serializes/deserializes the plan data' do
|
128
167
|
-> { adapter.load_execution_plan('plan1') }.must_raise KeyError
|
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: 0.8.
|
4
|
+
version: 0.8.34
|
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: 2017-
|
12
|
+
date: 2017-12-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|