dynflow 0.8.6 → 0.8.7
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 +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
|
- - ! '>='
|