dynflow 0.8.33 → 0.8.34
Sign up to get free protection for your applications and to get access to all the features.
- 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
|