dynflow 0.7.9 → 0.8.0
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.
- data/.gitignore +2 -0
- data/.travis.yml +16 -1
- data/Gemfile +13 -1
- data/doc/pages/source/_drafts/2015-03-01-new-documentation.markdown +10 -0
- data/doc/pages/source/_includes/menu.html +1 -0
- data/doc/pages/source/_includes/menu_right.html +1 -1
- data/doc/pages/source/_sass/_bootstrap-variables.sass +1 -0
- data/doc/pages/source/_sass/_style.scss +4 -0
- data/doc/pages/source/blog/index.html +12 -0
- data/doc/pages/source/documentation/index.md +330 -5
- data/dynflow.gemspec +3 -1
- data/examples/example_helper.rb +18 -11
- data/examples/orchestrate_evented.rb +2 -1
- data/examples/remote_executor.rb +53 -20
- data/lib/dynflow.rb +16 -6
- data/lib/dynflow/action/suspended.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +3 -6
- data/lib/dynflow/actor.rb +56 -0
- data/lib/dynflow/clock.rb +43 -38
- data/lib/dynflow/config.rb +107 -0
- data/lib/dynflow/connectors.rb +7 -0
- data/lib/dynflow/connectors/abstract.rb +41 -0
- data/lib/dynflow/connectors/database.rb +175 -0
- data/lib/dynflow/connectors/direct.rb +71 -0
- data/lib/dynflow/coordinator.rb +280 -0
- data/lib/dynflow/coordinator_adapters.rb +8 -0
- data/lib/dynflow/coordinator_adapters/abstract.rb +28 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +29 -0
- data/lib/dynflow/dispatcher.rb +58 -0
- data/lib/dynflow/dispatcher/abstract.rb +14 -0
- data/lib/dynflow/dispatcher/client_dispatcher.rb +139 -0
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +86 -0
- data/lib/dynflow/errors.rb +7 -1
- data/lib/dynflow/execution_history.rb +46 -0
- data/lib/dynflow/execution_plan.rb +19 -15
- data/lib/dynflow/executors.rb +0 -1
- data/lib/dynflow/executors/abstract.rb +5 -10
- data/lib/dynflow/executors/parallel.rb +16 -13
- data/lib/dynflow/executors/parallel/core.rb +76 -78
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +4 -5
- data/lib/dynflow/executors/parallel/pool.rb +22 -52
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +9 -2
- data/lib/dynflow/executors/parallel/worker.rb +5 -10
- data/lib/dynflow/persistence.rb +14 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +14 -3
- data/lib/dynflow/persistence_adapters/sequel.rb +142 -38
- data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +14 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +14 -0
- data/lib/dynflow/round_robin.rb +37 -0
- data/lib/dynflow/serializable.rb +1 -2
- data/lib/dynflow/serializer.rb +46 -0
- data/lib/dynflow/testing/dummy_executor.rb +2 -2
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/transaction_adapters/abstract.rb +0 -5
- data/lib/dynflow/transaction_adapters/active_record.rb +0 -10
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web.rb +26 -0
- data/lib/dynflow/web/console.rb +108 -0
- data/lib/dynflow/web/console_helpers.rb +158 -0
- data/lib/dynflow/web/filtering_helpers.rb +85 -0
- data/lib/dynflow/web/world_helpers.rb +9 -0
- data/lib/dynflow/web_console.rb +3 -310
- data/lib/dynflow/world.rb +188 -119
- data/test/abnormal_states_recovery_test.rb +152 -0
- data/test/action_test.rb +2 -3
- data/test/clock_test.rb +1 -5
- data/test/coordinator_test.rb +152 -0
- data/test/dispatcher_test.rb +146 -0
- data/test/execution_plan_test.rb +2 -1
- data/test/executor_test.rb +534 -612
- data/test/middleware_test.rb +4 -4
- data/test/persistence_test.rb +17 -0
- data/test/prepare_travis_env.sh +35 -0
- data/test/rescue_test.rb +5 -3
- data/test/round_robin_test.rb +28 -0
- data/test/support/code_workflow_example.rb +0 -73
- data/test/support/dummy_example.rb +130 -0
- data/test/support/test_execution_log.rb +41 -0
- data/test/test_helper.rb +222 -116
- data/test/testing_test.rb +10 -10
- data/test/web_console_test.rb +3 -3
- data/test/world_test.rb +23 -0
- data/web/assets/images/logo-square.png +0 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/assets/vendor/bootstrap/config.json +429 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.css +479 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.min.css +10 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +5377 -4980
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -8
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +1674 -1645
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +11 -5
- data/web/views/execution_history.erb +17 -0
- data/web/views/index.erb +4 -6
- data/web/views/layout.erb +44 -8
- data/web/views/show.erb +4 -5
- data/web/views/worlds.erb +26 -0
- metadata +116 -23
- checksums.yaml +0 -15
- data/lib/dynflow/daemon.rb +0 -30
- data/lib/dynflow/executors/remote_via_socket.rb +0 -43
- data/lib/dynflow/executors/remote_via_socket/core.rb +0 -184
- data/lib/dynflow/future.rb +0 -173
- data/lib/dynflow/listeners.rb +0 -7
- data/lib/dynflow/listeners/abstract.rb +0 -17
- data/lib/dynflow/listeners/serialization.rb +0 -77
- data/lib/dynflow/listeners/socket.rb +0 -117
- data/lib/dynflow/micro_actor.rb +0 -102
- data/lib/dynflow/simple_world.rb +0 -19
- data/test/remote_via_socket_test.rb +0 -170
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +0 -1109
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +0 -9
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
module Dynflow
|
|
2
2
|
module Executors
|
|
3
3
|
class Parallel < Abstract
|
|
4
|
-
class Worker <
|
|
5
|
-
def initialize(pool, transaction_adapter)
|
|
6
|
-
super(pool.logger, pool, transaction_adapter)
|
|
7
|
-
end
|
|
4
|
+
class Worker < Actor
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def delayed_initialize(pool, transaction_adapter)
|
|
12
|
-
@pool = pool
|
|
6
|
+
def initialize(pool, transaction_adapter)
|
|
7
|
+
@pool = Type! pool, Concurrent::Actor::Reference
|
|
13
8
|
@transaction_adapter = Type! transaction_adapter, TransactionAdapters::Abstract
|
|
14
9
|
end
|
|
15
10
|
|
|
@@ -23,9 +18,9 @@ module Dynflow
|
|
|
23
18
|
sequential_manager.finalize
|
|
24
19
|
end)
|
|
25
20
|
rescue Errors::PersistenceError => e
|
|
26
|
-
@pool
|
|
21
|
+
@pool.tell([:handle_persistence_error, e])
|
|
27
22
|
ensure
|
|
28
|
-
@pool
|
|
23
|
+
@pool.tell([:worker_done, reference, message])
|
|
29
24
|
@transaction_adapter.cleanup
|
|
30
25
|
end
|
|
31
26
|
end
|
data/lib/dynflow/persistence.rb
CHANGED
|
@@ -4,6 +4,8 @@ module Dynflow
|
|
|
4
4
|
|
|
5
5
|
class Persistence
|
|
6
6
|
|
|
7
|
+
include Algebrick::TypeCheck
|
|
8
|
+
|
|
7
9
|
attr_reader :adapter
|
|
8
10
|
|
|
9
11
|
def initialize(world, persistence_adapter)
|
|
@@ -56,5 +58,17 @@ module Dynflow
|
|
|
56
58
|
adapter.save_step(step.execution_plan_id, step.id, step.to_hash)
|
|
57
59
|
end
|
|
58
60
|
|
|
61
|
+
def push_envelope(envelope)
|
|
62
|
+
Type! envelope, Dispatcher::Envelope
|
|
63
|
+
adapter.push_envelope(Dynflow.serializer.dump(envelope))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def pull_envelopes(world_id)
|
|
67
|
+
adapter.pull_envelopes(world_id).map do |data|
|
|
68
|
+
envelope = Dynflow.serializer.load(data)
|
|
69
|
+
Type! envelope, Dispatcher::Envelope
|
|
70
|
+
envelope
|
|
71
|
+
end
|
|
72
|
+
end
|
|
59
73
|
end
|
|
60
74
|
end
|
|
@@ -6,18 +6,21 @@ module Dynflow
|
|
|
6
6
|
attr_accessor :logger
|
|
7
7
|
|
|
8
8
|
def register_world(world)
|
|
9
|
-
@
|
|
10
|
-
@worlds << world
|
|
9
|
+
@logger ||= world.logger
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
def log(level, message)
|
|
14
|
-
|
|
13
|
+
logger.send(level, message) if logger
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def pagination?
|
|
18
17
|
false
|
|
19
18
|
end
|
|
20
19
|
|
|
20
|
+
def transaction
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
|
|
21
24
|
def filtering_by
|
|
22
25
|
[]
|
|
23
26
|
end
|
|
@@ -72,6 +75,14 @@ module Dynflow
|
|
|
72
75
|
def to_hash
|
|
73
76
|
raise NotImplementedError
|
|
74
77
|
end
|
|
78
|
+
|
|
79
|
+
def pull_envelopes(receiver_id)
|
|
80
|
+
raise NotImplementedError
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def push_envelope(envelope)
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
end
|
|
75
86
|
end
|
|
76
87
|
end
|
|
77
88
|
end
|
|
@@ -8,6 +8,7 @@ module Dynflow
|
|
|
8
8
|
|
|
9
9
|
class Sequel < Abstract
|
|
10
10
|
include Algebrick::TypeCheck
|
|
11
|
+
include Algebrick::Matching
|
|
11
12
|
|
|
12
13
|
MAX_RETRIES = 10
|
|
13
14
|
RETRY_DELAY = 1
|
|
@@ -26,26 +27,40 @@ module Dynflow
|
|
|
26
27
|
META_DATA.fetch :execution_plan
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
META_DATA = { execution_plan:
|
|
30
|
-
action:
|
|
31
|
-
step:
|
|
30
|
+
META_DATA = { execution_plan: %w(state result started_at ended_at real_time execution_time),
|
|
31
|
+
action: %w(caller_execution_plan_id caller_action_id),
|
|
32
|
+
step: %w(state started_at ended_at real_time execution_time action_id progress_done progress_weight),
|
|
33
|
+
envelope: %w(receiver_id),
|
|
34
|
+
coordinator_record: %w(id owner_id class) }
|
|
32
35
|
|
|
33
36
|
def initialize(config)
|
|
37
|
+
config = config.dup
|
|
38
|
+
@additional_responsibilities = { coordinator: true, connector: true }
|
|
39
|
+
if config.is_a?(Hash) && config.key?(:additional_responsibilities)
|
|
40
|
+
@additional_responsibilities.merge!(config.delete(:additional_responsibilities))
|
|
41
|
+
end
|
|
34
42
|
@db = initialize_db config
|
|
35
43
|
migrate_db
|
|
36
44
|
end
|
|
37
45
|
|
|
38
|
-
def
|
|
39
|
-
|
|
46
|
+
def transaction(&block)
|
|
47
|
+
db.transaction(&block)
|
|
48
|
+
end
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
def find_execution_plans(options = {})
|
|
51
|
+
options[:order_by] ||= :started_at
|
|
52
|
+
data_set = filter(:execution_plan,
|
|
53
|
+
order(:execution_plan,
|
|
54
|
+
paginate(table(:execution_plan), options),
|
|
55
|
+
options),
|
|
56
|
+
options[:filters])
|
|
57
|
+
|
|
58
|
+
data_set.map { |record| load_data(record) }
|
|
44
59
|
end
|
|
45
60
|
|
|
46
61
|
def delete_execution_plans(filters, batch_size = 1000)
|
|
47
62
|
count = 0
|
|
48
|
-
filter(table(:execution_plan), filters).each_slice(batch_size) do |plans|
|
|
63
|
+
filter(:execution_plan, table(:execution_plan), filters).each_slice(batch_size) do |plans|
|
|
49
64
|
uuids = plans.map { |p| p.fetch(:uuid) }
|
|
50
65
|
@db.transaction do
|
|
51
66
|
table(:step).where(execution_plan_uuid: uuids).delete
|
|
@@ -80,17 +95,76 @@ module Dynflow
|
|
|
80
95
|
save :action, { execution_plan_uuid: execution_plan_id, id: action_id }, value
|
|
81
96
|
end
|
|
82
97
|
|
|
98
|
+
def connector_feature!
|
|
99
|
+
unless @additional_responsibilities[:connector]
|
|
100
|
+
raise "The sequel persistence adapter connector feature used but not enabled in additional_features"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def save_envelope(data)
|
|
105
|
+
connector_feature!
|
|
106
|
+
save :envelope, {}, data
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def pull_envelopes(receiver_id)
|
|
110
|
+
connector_feature!
|
|
111
|
+
db.transaction do
|
|
112
|
+
data_set = table(:envelope).where(receiver_id: receiver_id).to_a
|
|
113
|
+
|
|
114
|
+
envelopes = data_set.map { |record| load_data(record) }
|
|
115
|
+
|
|
116
|
+
table(:envelope).where(id: data_set.map { |d| d[:id] }).delete
|
|
117
|
+
return envelopes
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def push_envelope(envelope)
|
|
122
|
+
connector_feature!
|
|
123
|
+
table(:envelope).insert(prepare_record(:envelope, envelope))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def coordinator_feature!
|
|
127
|
+
unless @additional_responsibilities[:coordinator]
|
|
128
|
+
raise "The sequel persistence adapter coordinator feature used but not enabled in additional_features"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def insert_coordinator_record(value)
|
|
133
|
+
coordinator_feature!
|
|
134
|
+
save :coordinator_record, {}, value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def update_coordinator_record(class_name, record_id, value)
|
|
138
|
+
coordinator_feature!
|
|
139
|
+
save :coordinator_record, {class: class_name, :id => record_id}, value
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def delete_coordinator_record(class_name, record_id)
|
|
143
|
+
coordinator_feature!
|
|
144
|
+
table(:coordinator_record).where(class: class_name, id: record_id).delete
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def find_coordinator_records(options)
|
|
148
|
+
coordinator_feature!
|
|
149
|
+
options = options.dup
|
|
150
|
+
data_set = filter(:coordinator_record, table(:coordinator_record), options[:filters])
|
|
151
|
+
data_set.map { |record| load_data(record) }
|
|
152
|
+
end
|
|
153
|
+
|
|
83
154
|
def to_hash
|
|
84
|
-
{ execution_plans:
|
|
85
|
-
steps:
|
|
86
|
-
actions:
|
|
155
|
+
{ execution_plans: table(:execution_plan).all.to_a,
|
|
156
|
+
steps: table(:step).all.to_a,
|
|
157
|
+
actions: table(:action).all.to_a,
|
|
158
|
+
envelopes: table(:envelope).all.to_a }
|
|
87
159
|
end
|
|
88
160
|
|
|
89
161
|
private
|
|
90
162
|
|
|
91
|
-
TABLES = { execution_plan:
|
|
92
|
-
action:
|
|
93
|
-
step:
|
|
163
|
+
TABLES = { execution_plan: :dynflow_execution_plans,
|
|
164
|
+
action: :dynflow_actions,
|
|
165
|
+
step: :dynflow_steps,
|
|
166
|
+
envelope: :dynflow_envelopes,
|
|
167
|
+
coordinator_record: :dynflow_coordinator_records }
|
|
94
168
|
|
|
95
169
|
def table(which)
|
|
96
170
|
db[TABLES.fetch(which)]
|
|
@@ -108,18 +182,22 @@ module Dynflow
|
|
|
108
182
|
::Sequel::Migrator.run(db, self.class.migrations_path, table: 'dynflow_schema_info')
|
|
109
183
|
end
|
|
110
184
|
|
|
185
|
+
def prepare_record(table_name, value, base = {})
|
|
186
|
+
record = base.dup
|
|
187
|
+
if table(table_name).columns.include?(:data)
|
|
188
|
+
record[:data] = dump_data(value)
|
|
189
|
+
end
|
|
190
|
+
record.merge! extract_metadata(table_name, value)
|
|
191
|
+
record.each { |k, v| record[k] = v.to_s if v.is_a? Symbol }
|
|
192
|
+
record
|
|
193
|
+
end
|
|
194
|
+
|
|
111
195
|
def save(what, condition, value)
|
|
112
196
|
table = table(what)
|
|
113
|
-
existing_record = with_retry { table.first condition }
|
|
197
|
+
existing_record = with_retry { table.first condition } unless condition.empty?
|
|
114
198
|
|
|
115
199
|
if value
|
|
116
|
-
|
|
117
|
-
record = existing_record || condition
|
|
118
|
-
record[:data] = MultiJson.dump Type!(value, Hash)
|
|
119
|
-
meta_data = META_DATA.fetch(what).inject({}) { |h, k| h.update k.to_sym => value.fetch(k) }
|
|
120
|
-
record.merge! meta_data
|
|
121
|
-
record.each { |k, v| record[k] = v.to_s if v.is_a? Symbol }
|
|
122
|
-
|
|
200
|
+
record = prepare_record(what, value, (existing_record || condition))
|
|
123
201
|
if existing_record
|
|
124
202
|
with_retry { table.where(condition).update(record) }
|
|
125
203
|
else
|
|
@@ -135,47 +213,71 @@ module Dynflow
|
|
|
135
213
|
def load(what, condition)
|
|
136
214
|
table = table(what)
|
|
137
215
|
if (record = with_retry { table.first(condition.symbolize_keys) } )
|
|
138
|
-
|
|
216
|
+
load_data(record)
|
|
139
217
|
else
|
|
140
218
|
raise KeyError, "searching: #{what} by: #{condition.inspect}"
|
|
141
219
|
end
|
|
142
220
|
end
|
|
143
221
|
|
|
222
|
+
def load_data(record)
|
|
223
|
+
HashWithIndifferentAccess.new(MultiJson.load(record[:data]))
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def delete(what, condition)
|
|
227
|
+
table(what).where(condition.symbolize_keys).delete
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def extract_metadata(what, value)
|
|
231
|
+
meta_keys = META_DATA.fetch(what)
|
|
232
|
+
value = value.with_indifferent_access
|
|
233
|
+
meta_keys.inject({}) { |h, k| h.update k.to_sym => value[k] }
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def dump_data(value)
|
|
237
|
+
MultiJson.dump Type!(value, Hash)
|
|
238
|
+
end
|
|
239
|
+
|
|
144
240
|
def paginate(data_set, options)
|
|
145
|
-
page = Integer(options[:page]
|
|
146
|
-
per_page = Integer(options[:per_page]
|
|
241
|
+
page = Integer(options[:page]) if options[:page]
|
|
242
|
+
per_page = Integer(options[:per_page]) if options[:per_page]
|
|
147
243
|
|
|
148
244
|
if page
|
|
245
|
+
raise ArgumentError, "page specified without per_page attribute" unless per_page
|
|
149
246
|
data_set.limit per_page, per_page * page
|
|
150
247
|
else
|
|
151
248
|
data_set
|
|
152
249
|
end
|
|
153
250
|
end
|
|
154
251
|
|
|
155
|
-
def order(data_set, options)
|
|
156
|
-
order_by = (options[:order_by]
|
|
157
|
-
|
|
252
|
+
def order(what, data_set, options)
|
|
253
|
+
order_by = (options[:order_by]).to_s
|
|
254
|
+
return data_set if order_by.empty?
|
|
255
|
+
unless META_DATA.fetch(what).include? order_by
|
|
158
256
|
raise ArgumentError, "unknown column #{order_by.inspect}"
|
|
159
257
|
end
|
|
160
258
|
order_by = order_by.to_sym
|
|
161
259
|
data_set.order_by options[:desc] ? ::Sequel.desc(order_by) : order_by
|
|
162
260
|
end
|
|
163
261
|
|
|
164
|
-
def filter(data_set, filters)
|
|
262
|
+
def filter(what, data_set, filters)
|
|
165
263
|
Type! filters, NilClass, Hash
|
|
166
264
|
return data_set if filters.nil?
|
|
167
|
-
unknown = filters.keys - META_DATA.fetch(:execution_plan) - %w[uuid caller_execution_plan_id caller_action_id]
|
|
168
265
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
266
|
+
unknown = filters.keys.map(&:to_s) - META_DATA.fetch(what)
|
|
267
|
+
if what == :execution_plan
|
|
268
|
+
unknown -= %w[uuid caller_execution_plan_id caller_action_id]
|
|
172
269
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
270
|
+
if filters.key?('caller_action_id') && !filters.key?('caller_execution_plan_id')
|
|
271
|
+
raise ArgumentError, "caller_action_id given but caller_execution_plan_id missing"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
if filters.key?('caller_execution_plan_id')
|
|
275
|
+
data_set = data_set.join_table(:inner, TABLES[:action], :execution_plan_uuid => :uuid).
|
|
276
|
+
select_all(TABLES[:execution_plan]).distinct
|
|
277
|
+
end
|
|
176
278
|
end
|
|
177
279
|
|
|
178
|
-
unless
|
|
280
|
+
unless unknown.empty?
|
|
179
281
|
raise ArgumentError, "unkown columns: #{unknown.inspect}"
|
|
180
282
|
end
|
|
181
283
|
|
|
@@ -186,6 +288,8 @@ module Dynflow
|
|
|
186
288
|
attempts = 0
|
|
187
289
|
begin
|
|
188
290
|
yield
|
|
291
|
+
rescue ::Sequel::UniqueConstraintViolation => e
|
|
292
|
+
raise e
|
|
189
293
|
rescue Exception => e
|
|
190
294
|
attempts += 1
|
|
191
295
|
log(:error, e)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
change do
|
|
3
|
+
create_table(:dynflow_coordinator_records) do
|
|
4
|
+
column :id, String
|
|
5
|
+
column :class, String
|
|
6
|
+
primary_key [:id, :class]
|
|
7
|
+
index :class
|
|
8
|
+
column :owner_id, String
|
|
9
|
+
index :owner_id
|
|
10
|
+
column :data, String, text: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
change do
|
|
3
|
+
create_table(:dynflow_envelopes) do
|
|
4
|
+
primary_key :id
|
|
5
|
+
# we don't add a foreign key to worlds here as there might be an envelope created for the world
|
|
6
|
+
# while the world gets terminated, and it would mess the whole thing up:
|
|
7
|
+
# error on the world deletion because some envelopes arrived in the meantime
|
|
8
|
+
# we still do our best to remove the envelopes if we can
|
|
9
|
+
column :receiver_id, String, size: 36, fixed: true
|
|
10
|
+
index :receiver_id
|
|
11
|
+
column :data, String, text: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
# A simple round-robin scheduling implementation used at various
|
|
3
|
+
# places in Dynflow
|
|
4
|
+
class RoundRobin
|
|
5
|
+
def initialize
|
|
6
|
+
@data = []
|
|
7
|
+
@cursor = 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add(item)
|
|
11
|
+
@data.push item
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def delete(item)
|
|
16
|
+
@data.delete item
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def next
|
|
21
|
+
@cursor = 0 if @cursor > @data.size-1
|
|
22
|
+
@data[@cursor]
|
|
23
|
+
ensure
|
|
24
|
+
@cursor += 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def empty?
|
|
28
|
+
@data.empty?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# the `add` and `delete` methods should be preferred, but
|
|
32
|
+
# sometimes the list of things to iterate though can not be owned
|
|
33
|
+
# by the round robin object itself
|
|
34
|
+
attr_writer :data
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
data/lib/dynflow/serializable.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Dynflow
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def self.check_class_key_present(hash)
|
|
26
|
-
raise ArgumentError,
|
|
26
|
+
raise ArgumentError, "missing :class in #{hash.inspect}" unless hash[:class]
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def self.constantize(action_name)
|
|
@@ -74,6 +74,5 @@ module Dynflow
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
private_class_method :string_to_time, :hash_to_error
|
|
77
|
-
|
|
78
77
|
end
|
|
79
78
|
end
|