dynflow 0.8.6 → 0.8.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/dynflow.gemspec +1 -1
- data/lib/dynflow/config.rb +1 -1
- data/lib/dynflow/coordinator.rb +31 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +6 -1
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +14 -2
- data/test/abnormal_states_recovery_test.rb +45 -4
- data/test/test_helper.rb +3 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTRkNzg1OTljZjRkYzMyMzg3OTlmYzc0Y2I1OWI2MTZjNGY4ZmQ1ZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Yzg4NmU2OGNkOWMxM2RmZWUxZWE2ZjQyODYzMWEzNGU4ZGUyNDcyNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZmNiMjdjYTg5ZmI0NTcwYjcyOTRkMDI4OGRhM2I5ZDc2MmRjMjdjOWUxZmVl
|
10
|
+
ZDQxZDM4MzhiZjk1MjljMjFlNTg2ZTA4Njc5ZTY3YmNjOGRkNzQ4NDA4MDRl
|
11
|
+
NjkzOTJmZjBmZDIxYTFjYjJkNjg1ZDcwMGI0ZWFlMjk2NDQ3OTU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTJiZGRjOTY2ODdhZjMzMTBhNDEzMDhjYjQ2ZGZiYmU0MzU5ZGI1ZjBmZDBi
|
14
|
+
ODM4MDJkNDAxYWJkMDhlY2NlMGEzY2RjNzkyNDBlZTI1NmU1MGFkZTMwNDFh
|
15
|
+
ZTQyYzJiODM0Y2Y3NDRlM2M3NmQ0NDhkZmY2MDQ0MDBjOWFmMTc=
|
data/dynflow.gemspec
CHANGED
@@ -23,12 +23,12 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency "algebrick", '~> 0.7.0'
|
24
24
|
s.add_dependency "concurrent-ruby", '~> 0.9.0'
|
25
25
|
s.add_dependency "concurrent-ruby-edge", '~> 0.1.0'
|
26
|
+
s.add_dependency "sequel"
|
26
27
|
|
27
28
|
s.add_development_dependency "rack-test"
|
28
29
|
s.add_development_dependency "minitest"
|
29
30
|
s.add_development_dependency "minitest-reporters"
|
30
31
|
s.add_development_dependency "activerecord"
|
31
|
-
s.add_development_dependency "sequel"
|
32
32
|
s.add_development_dependency "sqlite3"
|
33
33
|
s.add_development_dependency "sinatra"
|
34
34
|
end
|
data/lib/dynflow/config.rb
CHANGED
data/lib/dynflow/coordinator.rb
CHANGED
@@ -155,6 +155,17 @@ module Dynflow
|
|
155
155
|
@data[:world_id]
|
156
156
|
end
|
157
157
|
|
158
|
+
def self.valid_owner_ids(coordinator)
|
159
|
+
coordinator.find_worlds.map { |w| "world:#{w.id}" }
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.valid_classes
|
163
|
+
@valid_classes ||= []
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.inherited(klass)
|
167
|
+
valid_classes << klass
|
168
|
+
end
|
158
169
|
end
|
159
170
|
|
160
171
|
class DelayedExecutorLock < LockByWorld
|
@@ -277,6 +288,7 @@ module Dynflow
|
|
277
288
|
|
278
289
|
def delete_world(world)
|
279
290
|
Type! world, Coordinator::ClientWorld, Coordinator::ExecutorWorld
|
291
|
+
release_by_owner("world:#{world.id}")
|
280
292
|
delete_record(world)
|
281
293
|
end
|
282
294
|
|
@@ -285,5 +297,24 @@ module Dynflow
|
|
285
297
|
world.active = false
|
286
298
|
update_record(world)
|
287
299
|
end
|
300
|
+
|
301
|
+
def clean_orphaned_locks
|
302
|
+
cleanup_classes = [LockByWorld]
|
303
|
+
ret = []
|
304
|
+
cleanup_classes.each do |cleanup_class|
|
305
|
+
valid_owner_ids = cleanup_class.valid_owner_ids(self)
|
306
|
+
valid_classes = cleanup_class.valid_classes.map(&:name)
|
307
|
+
orphaned_locks = find_locks(class: valid_classes, exclude_owner_id: valid_owner_ids)
|
308
|
+
# reloading the valid owner ids to avoid race conditions
|
309
|
+
valid_owner_ids = cleanup_class.valid_owner_ids(self)
|
310
|
+
orphaned_locks.each do |lock|
|
311
|
+
unless valid_owner_ids.include?(lock.owner_id)
|
312
|
+
release(lock)
|
313
|
+
ret << lock
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
return ret
|
318
|
+
end
|
288
319
|
end
|
289
320
|
end
|
@@ -177,7 +177,12 @@ module Dynflow
|
|
177
177
|
def find_coordinator_records(options)
|
178
178
|
coordinator_feature!
|
179
179
|
options = options.dup
|
180
|
-
|
180
|
+
filters = (options[:filters] || {}).dup
|
181
|
+
exclude_owner_id = filters.delete(:exclude_owner_id)
|
182
|
+
data_set = filter(:coordinator_record, table(:coordinator_record), filters)
|
183
|
+
if exclude_owner_id
|
184
|
+
data_set = data_set.exclude(:owner_id => exclude_owner_id)
|
185
|
+
end
|
181
186
|
data_set.map { |record| load_data(record) }
|
182
187
|
end
|
183
188
|
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/world.rb
CHANGED
@@ -34,7 +34,10 @@ module Dynflow
|
|
34
34
|
@executor_dispatcher = spawn_and_wait(Dispatcher::ExecutorDispatcher, "executor-dispatcher", self)
|
35
35
|
executor.initialized.wait
|
36
36
|
end
|
37
|
-
|
37
|
+
if auto_validity_check
|
38
|
+
self.worlds_validity_check
|
39
|
+
self.locks_validity_check
|
40
|
+
end
|
38
41
|
@delayed_executor = try_spawn_delayed_executor(config_for_world)
|
39
42
|
@meta = config_for_world.meta
|
40
43
|
@meta['delayed_executor'] = true if @delayed_executor
|
@@ -229,7 +232,6 @@ module Dynflow
|
|
229
232
|
clock.ask(:terminate!).wait
|
230
233
|
end
|
231
234
|
|
232
|
-
coordinator.release_by_owner("world:#{registered_world.id}")
|
233
235
|
coordinator.delete_world(registered_world)
|
234
236
|
true
|
235
237
|
rescue => e
|
@@ -327,6 +329,16 @@ module Dynflow
|
|
327
329
|
return results
|
328
330
|
end
|
329
331
|
|
332
|
+
def locks_validity_check
|
333
|
+
orphaned_locks = coordinator.clean_orphaned_locks
|
334
|
+
|
335
|
+
unless orphaned_locks.empty?
|
336
|
+
logger.error "invalid coordinator locks found and invalidated: #{orphaned_locks.inspect}"
|
337
|
+
end
|
338
|
+
|
339
|
+
return orphaned_locks
|
340
|
+
end
|
341
|
+
|
330
342
|
# executes plans that are planned/paused and haven't reported any error yet (usually when no executor
|
331
343
|
# was available by the time of planning or terminating)
|
332
344
|
def auto_execute
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require_relative 'test_helper'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
module Dynflow
|
5
6
|
module ConsistencyCheckTest
|
@@ -35,8 +36,8 @@ module Dynflow
|
|
35
36
|
let(:persistence_adapter) { WorldFactory.persistence_adapter }
|
36
37
|
let(:shared_connector) { Connectors::Direct.new }
|
37
38
|
let(:connector) { Proc.new { |world| shared_connector.start_listening(world); shared_connector } }
|
38
|
-
let(:executor_world) { create_world(true) }
|
39
|
-
let(:executor_world_2) { create_world(true) }
|
39
|
+
let(:executor_world) { create_world(true) { |config| config.auto_validity_check = true } }
|
40
|
+
let(:executor_world_2) { create_world(true) { |config| config.auto_validity_check = true } }
|
40
41
|
let(:client_world) { create_world(false) }
|
41
42
|
let(:client_world_2) { create_world(false) }
|
42
43
|
|
@@ -176,8 +177,11 @@ module Dynflow
|
|
176
177
|
end
|
177
178
|
|
178
179
|
it 'by default, the auto_validity_check is enabled only for executor words' do
|
179
|
-
|
180
|
-
|
180
|
+
client_world_config = Config::ForWorld.new(Config.new.tap { |c| c.executor = false }, create_world )
|
181
|
+
client_world_config.auto_validity_check.must_equal false
|
182
|
+
|
183
|
+
executor_world_config = Config::ForWorld.new(Config.new.tap { |c| c.executor = lambda { |w, _| Executors::Parallel.new(w) } }, create_world )
|
184
|
+
executor_world_config.auto_validity_check.must_equal true
|
181
185
|
end
|
182
186
|
|
183
187
|
it 'reports the validation status' do
|
@@ -210,6 +214,43 @@ module Dynflow
|
|
210
214
|
end
|
211
215
|
end
|
212
216
|
|
217
|
+
describe '#coordinator_validity_check' do
|
218
|
+
describe 'the auto_validity_check is enabled' do
|
219
|
+
let :world_with_auto_validity_check do
|
220
|
+
create_world do |config|
|
221
|
+
config.auto_validity_check = true
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
let(:invalid_lock) { Coordinator::DelayedExecutorLock.new(OpenStruct.new(:id => 'invalid-world-id')) }
|
226
|
+
let(:valid_lock) { Coordinator::AutoExecuteLock.new(client_world) }
|
227
|
+
|
228
|
+
def current_locks
|
229
|
+
client_world.coordinator.find_locks(id: [valid_lock.id, invalid_lock.id])
|
230
|
+
end
|
231
|
+
|
232
|
+
before do
|
233
|
+
client_world.coordinator.acquire(valid_lock)
|
234
|
+
client_world.coordinator.acquire(invalid_lock)
|
235
|
+
current_locks.must_include(valid_lock)
|
236
|
+
current_locks.must_include(invalid_lock)
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'performs the validity check on world creation if auto_validity_check enabled' do
|
240
|
+
world_with_auto_validity_check
|
241
|
+
current_locks.must_include(valid_lock)
|
242
|
+
current_locks.wont_include(invalid_lock)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'performs the validity check on world creation if auto_validity_check enabled' do
|
246
|
+
invalid_locks = client_world.locks_validity_check
|
247
|
+
current_locks.must_include(valid_lock)
|
248
|
+
current_locks.wont_include(invalid_lock)
|
249
|
+
invalid_locks.must_include(invalid_lock)
|
250
|
+
invalid_locks.wont_include(valid_lock)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
213
254
|
end
|
214
255
|
end
|
215
256
|
end
|
data/test/test_helper.rb
CHANGED
@@ -88,6 +88,7 @@ module WorldFactory
|
|
88
88
|
config.coordinator_adapter = coordinator_adapter
|
89
89
|
config.delayed_executor = nil
|
90
90
|
config.auto_rescue = false
|
91
|
+
config.auto_validity_check = false
|
91
92
|
config.exit_on_terminate = false
|
92
93
|
config.auto_execute = false
|
93
94
|
config.auto_terminate = false
|
@@ -138,13 +139,14 @@ module TestHelpers
|
|
138
139
|
# allows to create the world inside the tests, using the `connector`
|
139
140
|
# and `persistence adapter` from the test context: usefull to create
|
140
141
|
# multi-world topology for a signle test
|
141
|
-
def create_world(with_executor = true)
|
142
|
+
def create_world(with_executor = true, &block)
|
142
143
|
WorldFactory.create_world do |config|
|
143
144
|
config.connector = connector
|
144
145
|
config.persistence_adapter = persistence_adapter
|
145
146
|
unless with_executor
|
146
147
|
config.executor = false
|
147
148
|
end
|
149
|
+
block.call(config) if block
|
148
150
|
end
|
149
151
|
end
|
150
152
|
|
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.7
|
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: 2015-09
|
12
|
+
date: 2015-10-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -82,13 +82,13 @@ dependencies:
|
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 0.1.0
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
|
-
name:
|
85
|
+
name: sequel
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
88
|
- - ! '>='
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '0'
|
91
|
-
type: :
|
91
|
+
type: :runtime
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
@@ -96,7 +96,7 @@ dependencies:
|
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
|
-
name:
|
99
|
+
name: rack-test
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
102
|
- - ! '>='
|
@@ -110,7 +110,7 @@ dependencies:
|
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
|
-
name: minitest
|
113
|
+
name: minitest
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
115
115
|
requirements:
|
116
116
|
- - ! '>='
|
@@ -124,7 +124,7 @@ dependencies:
|
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
|
-
name:
|
127
|
+
name: minitest-reporters
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
130
|
- - ! '>='
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
140
|
- !ruby/object:Gem::Dependency
|
141
|
-
name:
|
141
|
+
name: activerecord
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - ! '>='
|