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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 331deb991c6fe8d4617af49ce8072fe0edceef6f
4
- data.tar.gz: 8de8181ec0b0451fefab469ee496172b8f1938dc
3
+ metadata.gz: 7ff5e3fce545cac6d1d299717c2fa11b54ba9ef6
4
+ data.tar.gz: 90214fe9c441dfae1780f591cca27de7eaa89da3
5
5
  SHA512:
6
- metadata.gz: 90d6626cc3af5a24784985ac6efd3beb029a50524b899f3ba9028248ddd0952cab7d72f46284cb1595a2d8ec74512a4d0247a77e41011306ec5a237d51c18e43
7
- data.tar.gz: 5db089dc8165ea98ad5677e8a4849e369c6329b856023084cdd6cc4b9ef4427a6bd39a59e8921be689ba3b03b7427138647b636ec02c386ff03610d2d5009631
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[:total_count] = total_count
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
- (output[:success_count] + output[:failed_count]).to_f / total_count
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 failed
75
- output[:failed_count] += total_count - output[:planned_count]
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
- total_count - output[:success_count] - output[:pending_count] - output[:failed_count] > 0
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 = sub_plans.count
64
- failed = sub_plans('state' => %w(paused stopped), 'result' => 'error').count
65
- success = sub_plans('state' => 'stopped', 'result' => 'success').count
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 => 0,
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
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module Dynflow
2
- VERSION = '0.8.33'
2
+ VERSION = '0.8.34'
3
3
  end
@@ -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.sub_plans.count == total
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.sub_plans.count == 2 * total
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.sub_plans.count == total &&
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.sub_plans.count == total &&
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.sub_plans.count == total &&
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.sub_plans.count == total }
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.sub_plans.count == total }
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.sub_plans.count == total }
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.sub_plans.count == total }
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.sub_plans.count == total }
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.sub_plans.count == total && plan.sub_plans.all? { |sub| sub.result == :pending } }
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
@@ -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.33
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-11-29 00:00:00.000000000 Z
12
+ date: 2017-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json