dynflow 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/dynflow.gemspec +1 -1
- data/lib/dynflow/coordinator.rb +13 -1
- data/lib/dynflow/coordinator_adapters/abstract.rb +4 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +4 -0
- data/lib/dynflow/rails.rb +1 -0
- data/lib/dynflow/rails/configuration.rb +11 -2
- data/lib/dynflow/rails/daemon.rb +2 -2
- data/lib/dynflow/testing/assertions.rb +1 -1
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world/invalidation.rb +2 -2
- data/test/abnormal_states_recovery_test.rb +45 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f162fe5b456a60898fad5ea4f3eec20ee1301f4adc8e4939946b03962b0ce12d
|
4
|
+
data.tar.gz: 591f53a8eafe2f94f61c5988fdc02d39ee8405726522a458394cfaa5dd3a260e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41345060fa6037bd1d10f9f6c3515ffe495b131e71744a8820d112f0547f21426b3829de522d1d4571cc4beecfde12f350f080f83fa62ec1be67652a976ef49d
|
7
|
+
data.tar.gz: 7ecec4743683c885c4e98caf978d1cd75d0647da11d88baab5b051fffa0192ee727d6feef1d61fbc1906f874f412f5486a9eb516e5a7aeded9c343de2f6df25d
|
data/dynflow.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency "multi_json"
|
22
22
|
s.add_dependency "apipie-params"
|
23
23
|
s.add_dependency "algebrick", '~> 0.7.0'
|
24
|
-
s.add_dependency "concurrent-ruby", '~> 1.0'
|
24
|
+
s.add_dependency "concurrent-ruby", '~> 1.0.0'
|
25
25
|
s.add_dependency "concurrent-ruby-edge", '~> 0.2.0'
|
26
26
|
s.add_dependency "sequel", '>= 4.0.0'
|
27
27
|
|
data/lib/dynflow/coordinator.rb
CHANGED
@@ -251,6 +251,18 @@ module Dynflow
|
|
251
251
|
def self.lock_id(action_class)
|
252
252
|
'singleton-action:' + action_class
|
253
253
|
end
|
254
|
+
|
255
|
+
def self.valid_owner_ids(coordinator)
|
256
|
+
lock_ids = coordinator.find_locks(:class => self.name).map(&:owner_id)
|
257
|
+
plans = coordinator.adapter.find_execution_plans(:uuid => lock_ids)
|
258
|
+
plans = Hash[*plans.map { |plan| [plan[:uuid], plan[:state]] }.flatten]
|
259
|
+
lock_ids.select { |id| plans.key?(id) && !%w(stopped paused).include?(plans[id]) }
|
260
|
+
.map { |id| 'execution-plan:' + id }
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.valid_classes
|
264
|
+
[self]
|
265
|
+
end
|
254
266
|
end
|
255
267
|
|
256
268
|
class ExecutionLock < LockByWorld
|
@@ -367,7 +379,7 @@ module Dynflow
|
|
367
379
|
end
|
368
380
|
|
369
381
|
def clean_orphaned_locks
|
370
|
-
cleanup_classes = [LockByWorld]
|
382
|
+
cleanup_classes = [LockByWorld, SingletonActionLock]
|
371
383
|
ret = []
|
372
384
|
cleanup_classes.each do |cleanup_class|
|
373
385
|
valid_owner_ids = cleanup_class.valid_owner_ids(self)
|
@@ -28,6 +28,10 @@ module Dynflow
|
|
28
28
|
def find_records(filter_options)
|
29
29
|
@sequel_adapter.find_coordinator_records(filters: filter_options)
|
30
30
|
end
|
31
|
+
|
32
|
+
def find_execution_plans(filter_options)
|
33
|
+
@sequel_adapter.find_execution_plans(filters: filter_options)
|
34
|
+
end
|
31
35
|
end
|
32
36
|
end
|
33
37
|
end
|
data/lib/dynflow/rails.rb
CHANGED
@@ -39,8 +39,9 @@ module Dynflow
|
|
39
39
|
self.lazy_initialization = !::Rails.env.production?
|
40
40
|
self.rake_tasks_with_executor = %w(db:migrate db:seed)
|
41
41
|
|
42
|
-
@on_init
|
43
|
-
@on_executor_init
|
42
|
+
@on_init = []
|
43
|
+
@on_executor_init = []
|
44
|
+
@post_executor_init = []
|
44
45
|
end
|
45
46
|
|
46
47
|
# Action related info such as exceptions raised inside the actions' methods
|
@@ -65,6 +66,14 @@ module Dynflow
|
|
65
66
|
source.each { |init| init.call(world) }
|
66
67
|
end
|
67
68
|
|
69
|
+
def post_executor_init(&block)
|
70
|
+
@post_executor_init << block
|
71
|
+
end
|
72
|
+
|
73
|
+
def run_post_executor_init_hooks(world)
|
74
|
+
@post_executor_init.each { |init| init.call(world) }
|
75
|
+
end
|
76
|
+
|
68
77
|
def initialize_world(world_class = ::Dynflow::World)
|
69
78
|
world_class.new(world_config)
|
70
79
|
end
|
data/lib/dynflow/rails/daemon.rb
CHANGED
@@ -142,12 +142,12 @@ module Dynflow
|
|
142
142
|
|
143
143
|
def log_memory_limit_exceeded(current_memory, memory_limit)
|
144
144
|
message = "Memory level exceeded, registered #{current_memory} bytes, which is greater than #{memory_limit} limit."
|
145
|
-
world.
|
145
|
+
world.logger.error(message)
|
146
146
|
end
|
147
147
|
|
148
148
|
def log_memory_within_limit(current_memory, memory_limit)
|
149
149
|
message = "Memory level OK, registered #{current_memory} bytes, which is less than #{memory_limit} limit."
|
150
|
-
world.
|
150
|
+
world.logger.debug(message)
|
151
151
|
end
|
152
152
|
|
153
153
|
private
|
@@ -4,7 +4,7 @@ module Dynflow
|
|
4
4
|
# assert that +assert_actioned_plan+ was planned by +action+ with arguments +plan_input+
|
5
5
|
# alternatively plan-input can be asserted with +block+
|
6
6
|
def assert_action_planned_with(action, planned_action_class, *plan_input, &block)
|
7
|
-
found_classes =
|
7
|
+
found_classes = assert_action_planned(action, planned_action_class)
|
8
8
|
found = found_classes.select do |a|
|
9
9
|
if plan_input.empty?
|
10
10
|
block.call a.plan_input
|
data/lib/dynflow/version.rb
CHANGED
@@ -82,13 +82,13 @@ module Dynflow
|
|
82
82
|
logger.error "unexpected error when invalidating execution plan #{execution_lock.execution_plan_id}, skipping"
|
83
83
|
end
|
84
84
|
coordinator.release(execution_lock)
|
85
|
-
coordinator.release_by_owner(execution_lock.
|
85
|
+
coordinator.release_by_owner(execution_lock.id)
|
86
86
|
return
|
87
87
|
end
|
88
88
|
unless plan.valid?
|
89
89
|
logger.error "invalid plan #{plan.id}, skipping"
|
90
90
|
coordinator.release(execution_lock)
|
91
|
-
coordinator.release_by_owner(execution_lock.
|
91
|
+
coordinator.release_by_owner(execution_lock.id)
|
92
92
|
return
|
93
93
|
end
|
94
94
|
yield plan
|
@@ -122,6 +122,20 @@ module Dynflow
|
|
122
122
|
"unlock world-invalidation:#{executor_world.id}"]
|
123
123
|
client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
|
124
124
|
end
|
125
|
+
|
126
|
+
it 'releases singleton locks belonging to missing execution plan' do
|
127
|
+
execution_plan_id = 'missing'
|
128
|
+
action_class = 'ActionClass'
|
129
|
+
locks = [Coordinator::ExecutionLock.new(executor_world, "missing", nil, nil),
|
130
|
+
Coordinator::SingletonActionLock.new(action_class, execution_plan_id)]
|
131
|
+
locks.each { |lock| executor_world.coordinator.acquire lock }
|
132
|
+
client_world.invalidate(executor_world.registered_world)
|
133
|
+
expected_locks = ["lock world-invalidation:#{executor_world.id}",
|
134
|
+
"unlock execution-plan:#{execution_plan_id}",
|
135
|
+
"unlock singleton-action:#{action_class}",
|
136
|
+
"unlock world-invalidation:#{executor_world.id}"]
|
137
|
+
client_world.coordinator.adapter.lock_log.must_equal(expected_locks)
|
138
|
+
end
|
125
139
|
end
|
126
140
|
end
|
127
141
|
|
@@ -291,6 +305,37 @@ module Dynflow
|
|
291
305
|
invalid_locks.wont_include(valid_lock)
|
292
306
|
end
|
293
307
|
end
|
308
|
+
|
309
|
+
describe 'with singleton action locks' do
|
310
|
+
def plan_in_state(state)
|
311
|
+
plan = executor_world.persistence.load_execution_plan(trigger_waiting_action.id)
|
312
|
+
step = plan.steps.values.last
|
313
|
+
wait_for do
|
314
|
+
executor_world.persistence.load_step(step.execution_plan_id, step.id, executor_world).state == :suspended
|
315
|
+
end
|
316
|
+
plan.state = state if plan.state != state
|
317
|
+
plan.save
|
318
|
+
plan
|
319
|
+
end
|
320
|
+
|
321
|
+
let(:valid_plan) { plan_in_state :running }
|
322
|
+
let(:invalid_plan) { plan_in_state :stopped }
|
323
|
+
let(:valid_lock) { Coordinator::SingletonActionLock.new('MyClass1', valid_plan.id) }
|
324
|
+
let(:invalid_lock) { Coordinator::SingletonActionLock.new('MyClass2', 'plan-id') }
|
325
|
+
let(:invalid_lock2) { Coordinator::SingletonActionLock.new('MyClass3', invalid_plan.id) }
|
326
|
+
|
327
|
+
it 'unlocks orphaned singleton action locks' do
|
328
|
+
executor_world
|
329
|
+
client_world.coordinator.acquire(valid_lock)
|
330
|
+
client_world.coordinator.acquire(invalid_lock)
|
331
|
+
client_world.coordinator.acquire(invalid_lock2)
|
332
|
+
invalid_locks = client_world.coordinator.clean_orphaned_locks
|
333
|
+
# It must invalidate locks which are missing or in paused/stopped
|
334
|
+
invalid_locks.must_include(invalid_lock)
|
335
|
+
invalid_locks.must_include(invalid_lock2)
|
336
|
+
invalid_locks.wont_include(valid_lock)
|
337
|
+
end
|
338
|
+
end
|
294
339
|
end
|
295
340
|
end
|
296
341
|
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.1.
|
4
|
+
version: 1.1.3
|
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: 2018-11-
|
12
|
+
date: 2018-11-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
62
|
+
version: 1.0.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 1.0.0
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: concurrent-ruby-edge
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|