dynflow 1.3.0 → 1.4.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.travis.yml +3 -4
- data/Dockerfile +9 -0
- data/Gemfile +6 -0
- data/Rakefile +1 -0
- data/doc/pages/Gemfile +1 -0
- data/doc/pages/Rakefile +1 -0
- data/doc/pages/plugins/alert_block.rb +1 -0
- data/doc/pages/plugins/div_tag.rb +1 -0
- data/doc/pages/plugins/graphviz.rb +1 -0
- data/doc/pages/plugins/plantuml.rb +1 -0
- data/doc/pages/plugins/play.rb +1 -0
- data/doc/pages/plugins/tags.rb +1 -0
- data/doc/pages/plugins/toc.rb +1 -0
- data/docker-compose.yml +41 -0
- data/dynflow.gemspec +1 -0
- data/examples/clock_benchmark.rb +1 -0
- data/examples/example_helper.rb +19 -2
- data/examples/future_execution.rb +2 -1
- data/examples/memory_limit_watcher.rb +1 -0
- data/examples/orchestrate.rb +4 -5
- data/examples/orchestrate_evented.rb +3 -2
- data/examples/remote_executor.rb +68 -0
- data/examples/singletons.rb +4 -3
- data/examples/sub_plan_concurrency_control.rb +2 -1
- data/examples/sub_plans.rb +3 -2
- data/examples/termination.rb +1 -0
- data/lib/dynflow.rb +20 -0
- data/lib/dynflow/action.rb +28 -3
- data/lib/dynflow/action/cancellable.rb +1 -0
- data/lib/dynflow/action/format.rb +1 -0
- data/lib/dynflow/action/missing.rb +1 -0
- data/lib/dynflow/action/polling.rb +3 -1
- data/lib/dynflow/action/progress.rb +1 -0
- data/lib/dynflow/action/rescue.rb +1 -0
- data/lib/dynflow/action/singleton.rb +1 -0
- data/lib/dynflow/action/suspended.rb +9 -2
- data/lib/dynflow/action/timeouts.rb +2 -1
- data/lib/dynflow/action/with_bulk_sub_plans.rb +2 -1
- data/lib/dynflow/action/with_polling_sub_plans.rb +7 -5
- data/lib/dynflow/action/with_sub_plans.rb +1 -0
- data/lib/dynflow/active_job/queue_adapter.rb +1 -0
- data/lib/dynflow/actor.rb +13 -5
- data/lib/dynflow/actors.rb +1 -0
- data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
- data/lib/dynflow/clock.rb +27 -47
- data/lib/dynflow/config.rb +11 -2
- data/lib/dynflow/connectors.rb +1 -0
- data/lib/dynflow/connectors/abstract.rb +1 -0
- data/lib/dynflow/connectors/database.rb +1 -0
- data/lib/dynflow/connectors/direct.rb +1 -0
- data/lib/dynflow/coordinator.rb +1 -0
- data/lib/dynflow/coordinator_adapters.rb +1 -0
- data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
- data/lib/dynflow/dead_letter_silencer.rb +2 -0
- data/lib/dynflow/debug/telemetry/persistence.rb +1 -0
- data/lib/dynflow/delayed_executors.rb +1 -0
- data/lib/dynflow/delayed_executors/abstract.rb +1 -0
- data/lib/dynflow/delayed_executors/abstract_core.rb +1 -0
- data/lib/dynflow/delayed_executors/polling.rb +1 -0
- data/lib/dynflow/delayed_plan.rb +1 -0
- data/lib/dynflow/director.rb +80 -15
- data/lib/dynflow/director/execution_plan_manager.rb +17 -3
- data/lib/dynflow/director/flow_manager.rb +1 -0
- data/lib/dynflow/director/{work_queue.rb → queue_hash.rb} +9 -8
- data/lib/dynflow/director/running_steps_manager.rb +55 -18
- data/lib/dynflow/director/sequence_cursor.rb +1 -0
- data/lib/dynflow/director/sequential_manager.rb +12 -2
- data/lib/dynflow/dispatcher.rb +4 -2
- data/lib/dynflow/dispatcher/abstract.rb +1 -0
- data/lib/dynflow/dispatcher/client_dispatcher.rb +6 -4
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +13 -1
- data/lib/dynflow/errors.rb +1 -0
- data/lib/dynflow/execution_history.rb +1 -0
- data/lib/dynflow/execution_plan.rb +3 -2
- data/lib/dynflow/execution_plan/dependency_graph.rb +1 -0
- data/lib/dynflow/execution_plan/hooks.rb +1 -0
- data/lib/dynflow/execution_plan/output_reference.rb +2 -1
- data/lib/dynflow/execution_plan/steps.rb +1 -0
- data/lib/dynflow/execution_plan/steps/abstract.rb +10 -5
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +2 -0
- data/lib/dynflow/execution_plan/steps/error.rb +1 -0
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -0
- data/lib/dynflow/execution_plan/steps/plan_step.rb +1 -0
- data/lib/dynflow/execution_plan/steps/run_step.rb +1 -0
- data/lib/dynflow/executors.rb +1 -1
- data/lib/dynflow/executors/abstract/core.rb +132 -0
- data/lib/dynflow/executors/parallel.rb +24 -11
- data/lib/dynflow/executors/parallel/core.rb +10 -91
- data/lib/dynflow/executors/parallel/pool.rb +4 -2
- data/lib/dynflow/executors/parallel/worker.rb +2 -1
- data/lib/dynflow/executors/sidekiq/core.rb +121 -0
- data/lib/dynflow/executors/sidekiq/internal_job_base.rb +24 -0
- data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +60 -0
- data/lib/dynflow/executors/sidekiq/redis_locking.rb +69 -0
- data/lib/dynflow/executors/sidekiq/serialization.rb +33 -0
- data/lib/dynflow/executors/sidekiq/worker_jobs.rb +42 -0
- data/lib/dynflow/flows.rb +1 -0
- data/lib/dynflow/flows/abstract.rb +1 -0
- data/lib/dynflow/flows/abstract_composed.rb +1 -0
- data/lib/dynflow/flows/atom.rb +1 -0
- data/lib/dynflow/flows/concurrence.rb +1 -0
- data/lib/dynflow/flows/sequence.rb +1 -0
- data/lib/dynflow/logger_adapters.rb +1 -0
- data/lib/dynflow/logger_adapters/abstract.rb +1 -0
- data/lib/dynflow/logger_adapters/delegator.rb +1 -0
- data/lib/dynflow/logger_adapters/formatters.rb +1 -0
- data/lib/dynflow/logger_adapters/formatters/abstract.rb +1 -0
- data/lib/dynflow/logger_adapters/formatters/exception.rb +1 -0
- data/lib/dynflow/logger_adapters/simple.rb +1 -0
- data/lib/dynflow/middleware.rb +1 -0
- data/lib/dynflow/middleware/common/singleton.rb +1 -0
- data/lib/dynflow/middleware/common/transaction.rb +1 -0
- data/lib/dynflow/middleware/register.rb +1 -0
- data/lib/dynflow/middleware/resolver.rb +1 -0
- data/lib/dynflow/middleware/stack.rb +1 -0
- data/lib/dynflow/middleware/world.rb +1 -0
- data/lib/dynflow/persistence.rb +3 -2
- data/lib/dynflow/persistence_adapters.rb +1 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +10 -7
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/002_incremental_progress.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/006_fix_data_length.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/007_future_execution.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/008_rename_scheduled_plans_to_delayed_plans.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/009_fix_mysql_data_length.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/010_add_execution_plans_label.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/011_placeholder.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/012_add_delayed_plans_serialized_args.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/013_add_action_columns.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/014_add_step_columns.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/015_add_execution_plan_columns.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/016_add_step_queue.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/017_add_delayed_plan_frozen.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +48 -0
- data/lib/dynflow/rails.rb +1 -0
- data/lib/dynflow/rails/configuration.rb +6 -3
- data/lib/dynflow/rails/daemon.rb +1 -0
- data/lib/dynflow/round_robin.rb +1 -0
- data/lib/dynflow/semaphores.rb +1 -0
- data/lib/dynflow/semaphores/abstract.rb +1 -0
- data/lib/dynflow/semaphores/aggregating.rb +1 -0
- data/lib/dynflow/semaphores/dummy.rb +1 -0
- data/lib/dynflow/semaphores/stateful.rb +1 -0
- data/lib/dynflow/serializable.rb +13 -4
- data/lib/dynflow/serializer.rb +24 -0
- data/lib/dynflow/serializers.rb +1 -0
- data/lib/dynflow/serializers/abstract.rb +1 -0
- data/lib/dynflow/serializers/noop.rb +1 -0
- data/lib/dynflow/stateful.rb +1 -0
- data/lib/dynflow/telemetry.rb +1 -0
- data/lib/dynflow/telemetry_adapters/abstract.rb +1 -0
- data/lib/dynflow/telemetry_adapters/dummy.rb +1 -0
- data/lib/dynflow/telemetry_adapters/statsd.rb +1 -0
- data/lib/dynflow/testing.rb +1 -0
- data/lib/dynflow/testing/assertions.rb +6 -5
- data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
- data/lib/dynflow/testing/dummy_executor.rb +19 -2
- data/lib/dynflow/testing/dummy_planned_action.rb +1 -0
- data/lib/dynflow/testing/dummy_step.rb +3 -1
- data/lib/dynflow/testing/dummy_world.rb +9 -0
- data/lib/dynflow/testing/factories.rb +6 -1
- data/lib/dynflow/testing/in_thread_executor.rb +22 -3
- data/lib/dynflow/testing/in_thread_world.rb +9 -0
- data/lib/dynflow/testing/managed_clock.rb +1 -0
- data/lib/dynflow/testing/mimic.rb +1 -0
- data/lib/dynflow/throttle_limiter.rb +1 -0
- data/lib/dynflow/transaction_adapters.rb +1 -0
- data/lib/dynflow/transaction_adapters/abstract.rb +1 -0
- data/lib/dynflow/transaction_adapters/active_record.rb +1 -0
- data/lib/dynflow/transaction_adapters/none.rb +1 -0
- data/lib/dynflow/utils.rb +1 -0
- data/lib/dynflow/utils/indifferent_hash.rb +1 -0
- data/lib/dynflow/utils/priority_queue.rb +1 -0
- data/lib/dynflow/version.rb +2 -1
- data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -0
- data/lib/dynflow/web.rb +1 -0
- data/lib/dynflow/web/console.rb +1 -0
- data/lib/dynflow/web/console_helpers.rb +1 -0
- data/lib/dynflow/web/filtering_helpers.rb +1 -0
- data/lib/dynflow/web/world_helpers.rb +1 -0
- data/lib/dynflow/web_console.rb +1 -0
- data/lib/dynflow/world.rb +11 -1
- data/lib/dynflow/world/invalidation.rb +7 -1
- data/test/abnormal_states_recovery_test.rb +41 -40
- data/test/action_test.rb +160 -110
- data/test/activejob_adapter_test.rb +1 -0
- data/test/batch_sub_tasks_test.rb +12 -11
- data/test/clock_test.rb +2 -1
- data/test/concurrency_control_test.rb +20 -19
- data/test/coordinator_test.rb +20 -21
- data/test/daemon_test.rb +2 -1
- data/test/dead_letter_silencer_test.rb +9 -7
- data/test/dispatcher_test.rb +2 -1
- data/test/execution_plan_cleaner_test.rb +13 -12
- data/test/execution_plan_hooks_test.rb +3 -2
- data/test/execution_plan_test.rb +33 -32
- data/test/executor_test.rb +533 -489
- data/test/future_execution_test.rb +45 -44
- data/test/memory_cosumption_watcher_test.rb +5 -4
- data/test/middleware_test.rb +55 -54
- data/test/persistence_test.rb +56 -53
- data/test/rescue_test.rb +36 -35
- data/test/round_robin_test.rb +13 -12
- data/test/semaphores_test.rb +31 -30
- data/test/support/code_workflow_example.rb +1 -0
- data/test/support/dummy_example.rb +14 -1
- data/test/support/middleware_example.rb +2 -1
- data/test/support/rails/config/environment.rb +1 -0
- data/test/support/rescue_example.rb +1 -0
- data/test/support/test_execution_log.rb +1 -0
- data/test/test_helper.rb +18 -17
- data/test/testing_test.rb +45 -44
- data/test/utils_test.rb +18 -17
- data/test/web_console_test.rb +1 -0
- data/test/world_test.rb +7 -6
- metadata +13 -4
- data/lib/dynflow/executors/abstract.rb +0 -40
data/lib/dynflow/connectors.rb
CHANGED
data/lib/dynflow/coordinator.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class DeadLetterSilencer < Concurrent::Actor::DefaultDeadLetterHandler
|
3
4
|
def initialize(matchers)
|
@@ -24,6 +25,7 @@ module Dynflow
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def match?(dead_letter)
|
28
|
+
return unless dead_letter.sender.respond_to?(:actor_class)
|
27
29
|
evaluate(dead_letter.sender.actor_class, @from) &&
|
28
30
|
evaluate(dead_letter.message, @message) &&
|
29
31
|
evaluate(dead_letter.address.actor_class, @to)
|
data/lib/dynflow/delayed_plan.rb
CHANGED
data/lib/dynflow/director.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
# Director is responsible for telling what to do next when:
|
3
4
|
# * new execution starts
|
@@ -10,7 +11,8 @@ module Dynflow
|
|
10
11
|
include Algebrick::TypeCheck
|
11
12
|
|
12
13
|
Event = Algebrick.type do
|
13
|
-
fields!
|
14
|
+
fields! request_id: String,
|
15
|
+
execution_plan_id: String,
|
14
16
|
step_id: Integer,
|
15
17
|
event: Object,
|
16
18
|
result: Concurrent::Promises::ResolvableFuture
|
@@ -18,57 +20,119 @@ module Dynflow
|
|
18
20
|
|
19
21
|
UnprocessableEvent = Class.new(Dynflow::Error)
|
20
22
|
|
21
|
-
class WorkItem
|
22
|
-
attr_reader :execution_plan_id, :queue
|
23
|
+
class WorkItem < Serializable
|
24
|
+
attr_reader :execution_plan_id, :queue, :sender_orchestrator_id
|
23
25
|
|
24
|
-
def initialize(execution_plan_id, queue)
|
26
|
+
def initialize(execution_plan_id, queue, sender_orchestrator_id)
|
25
27
|
@execution_plan_id = execution_plan_id
|
26
28
|
@queue = queue
|
29
|
+
@sender_orchestrator_id = sender_orchestrator_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def world
|
33
|
+
raise "World expected but not set for the work item #{self}" unless @world
|
34
|
+
@world
|
35
|
+
end
|
36
|
+
|
37
|
+
# the world to be used for execution purposes of the step. Setting it separately and explicitly
|
38
|
+
# as the world can't be serialized
|
39
|
+
def world=(world)
|
40
|
+
@world = world
|
27
41
|
end
|
28
42
|
|
29
43
|
def execute
|
30
44
|
raise NotImplementedError
|
31
45
|
end
|
46
|
+
|
47
|
+
def to_hash
|
48
|
+
{ class: self.class.name,
|
49
|
+
execution_plan_id: execution_plan_id,
|
50
|
+
queue: queue,
|
51
|
+
sender_orchestrator_id: sender_orchestrator_id }
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.new_from_hash(hash, *_args)
|
55
|
+
self.new(hash[:execution_plan_id], hash[:queue])
|
56
|
+
end
|
32
57
|
end
|
33
58
|
|
34
59
|
class StepWorkItem < WorkItem
|
35
60
|
attr_reader :step
|
36
61
|
|
37
|
-
def initialize(execution_plan_id, step, queue)
|
38
|
-
super(execution_plan_id, queue)
|
62
|
+
def initialize(execution_plan_id, step, queue, sender_orchestrator_id)
|
63
|
+
super(execution_plan_id, queue, sender_orchestrator_id)
|
39
64
|
@step = step
|
40
65
|
end
|
41
66
|
|
42
67
|
def execute
|
43
68
|
@step.execute(nil)
|
44
69
|
end
|
70
|
+
|
71
|
+
def to_hash
|
72
|
+
super.merge(step: step.to_hash)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.new_from_hash(hash, *_args)
|
76
|
+
self.new(hash[:execution_plan_id],
|
77
|
+
Serializable.from_hash(hash[:step], hash[:execution_plan_id], Dynflow.process_world),
|
78
|
+
hash[:queue],
|
79
|
+
hash[:sender_orchestrator_id])
|
80
|
+
end
|
45
81
|
end
|
46
82
|
|
47
83
|
class EventWorkItem < StepWorkItem
|
48
|
-
attr_reader :event
|
84
|
+
attr_reader :event, :request_id
|
49
85
|
|
50
|
-
def initialize(execution_plan_id, step, event, queue)
|
51
|
-
super(execution_plan_id, step, queue)
|
86
|
+
def initialize(request_id, execution_plan_id, step, event, queue, sender_orchestrator_id)
|
87
|
+
super(execution_plan_id, step, queue, sender_orchestrator_id)
|
52
88
|
@event = event
|
89
|
+
@request_id = request_id
|
53
90
|
end
|
54
91
|
|
55
92
|
def execute
|
56
|
-
@step.execute(@event
|
93
|
+
@step.execute(@event)
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_hash
|
97
|
+
super.merge(request_id: @request_id, event: Dynflow.serializer.dump(@event))
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.new_from_hash(hash, *_args)
|
101
|
+
self.new(hash[:request_id],
|
102
|
+
hash[:execution_plan_id],
|
103
|
+
Serializable.from_hash(hash[:step], hash[:execution_plan_id], Dynflow.process_world),
|
104
|
+
Dynflow.serializer.load(hash[:event]),
|
105
|
+
hash[:queue],
|
106
|
+
hash[:sender_orchestrator_id])
|
57
107
|
end
|
58
108
|
end
|
59
109
|
|
60
110
|
class FinalizeWorkItem < WorkItem
|
61
|
-
|
62
|
-
|
63
|
-
|
111
|
+
attr_reader :finalize_steps_data
|
112
|
+
|
113
|
+
# @param finalize_steps_data - used to pass the result steps from the worker back to orchestrator
|
114
|
+
def initialize(execution_plan_id, queue, sender_orchestrator_id, finalize_steps_data = nil)
|
115
|
+
super(execution_plan_id, queue, sender_orchestrator_id)
|
116
|
+
@finalize_steps_data = finalize_steps_data
|
64
117
|
end
|
65
118
|
|
66
119
|
def execute
|
67
|
-
|
120
|
+
execution_plan = world.persistence.load_execution_plan(execution_plan_id)
|
121
|
+
manager = Director::SequentialManager.new(world, execution_plan)
|
122
|
+
manager.finalize
|
123
|
+
@finalize_steps_data = manager.finalize_steps.map(&:to_hash)
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_hash
|
127
|
+
super.merge(finalize_steps_data: @finalize_steps_data)
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.new_from_hash(hash, *_args)
|
131
|
+
self.new(*hash.values_at(:execution_plan_id, :queue, :sender_orchestrator_id, :finalize_steps_data))
|
68
132
|
end
|
69
133
|
end
|
70
134
|
|
71
|
-
require 'dynflow/director/
|
135
|
+
require 'dynflow/director/queue_hash'
|
72
136
|
require 'dynflow/director/sequence_cursor'
|
73
137
|
require 'dynflow/director/flow_manager'
|
74
138
|
require 'dynflow/director/execution_plan_manager'
|
@@ -109,6 +173,7 @@ module Dynflow
|
|
109
173
|
|
110
174
|
def work_finished(work)
|
111
175
|
manager = @execution_plan_managers[work.execution_plan_id]
|
176
|
+
return [] unless manager # skip case when getting event from execution plan that is not running anymore
|
112
177
|
unless_done(manager, manager.what_is_next(work))
|
113
178
|
end
|
114
179
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class Director
|
3
4
|
class ExecutionPlanManager
|
@@ -30,7 +31,7 @@ module Dynflow
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def prepare_next_step(step)
|
33
|
-
StepWorkItem.new(execution_plan.id, step, step.queue).tap do |work|
|
34
|
+
StepWorkItem.new(execution_plan.id, step, step.queue, @world.id).tap do |work|
|
34
35
|
@running_steps_manager.add(step, work)
|
35
36
|
end
|
36
37
|
end
|
@@ -42,13 +43,22 @@ module Dynflow
|
|
42
43
|
case work
|
43
44
|
when StepWorkItem
|
44
45
|
step = work.step
|
45
|
-
|
46
|
+
update_steps([step])
|
46
47
|
suspended, work = @running_steps_manager.done(step)
|
47
48
|
work = compute_next_from_step(step) unless suspended
|
48
49
|
work
|
49
50
|
when FinalizeWorkItem
|
51
|
+
if work.finalize_steps_data
|
52
|
+
steps = work.finalize_steps_data.map do |step_data|
|
53
|
+
Serializable.from_hash(step_data, execution_plan.id, @world)
|
54
|
+
end
|
55
|
+
update_steps(steps)
|
56
|
+
end
|
50
57
|
raise "Finalize work item without @finalize_manager ready" unless @finalize_manager
|
58
|
+
@finalize_manager.done!
|
51
59
|
finish
|
60
|
+
else
|
61
|
+
raise "Unexpected work #{work}"
|
52
62
|
end
|
53
63
|
end
|
54
64
|
|
@@ -70,6 +80,10 @@ module Dynflow
|
|
70
80
|
|
71
81
|
private
|
72
82
|
|
83
|
+
def update_steps(steps)
|
84
|
+
steps.each { |step| execution_plan.steps[step.id] = step }
|
85
|
+
end
|
86
|
+
|
73
87
|
def compute_next_from_step(step)
|
74
88
|
raise "run manager not set" unless @run_manager
|
75
89
|
raise "run manager already done" if @run_manager.done?
|
@@ -98,7 +112,7 @@ module Dynflow
|
|
98
112
|
return if execution_plan.finalize_flow.empty?
|
99
113
|
raise 'finalize phase already started' if @finalize_manager
|
100
114
|
@finalize_manager = SequentialManager.new(@world, execution_plan)
|
101
|
-
[FinalizeWorkItem.new(execution_plan.id,
|
115
|
+
[FinalizeWorkItem.new(execution_plan.id, execution_plan.finalize_steps.first.queue, @world.id)]
|
102
116
|
end
|
103
117
|
|
104
118
|
def finish
|
@@ -1,23 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class Director
|
3
|
-
class
|
4
|
+
class QueueHash
|
4
5
|
include Algebrick::TypeCheck
|
5
6
|
|
6
|
-
def initialize(key_type = Object,
|
7
|
-
@key_type
|
8
|
-
@
|
7
|
+
def initialize(key_type = Object, value_type = Object)
|
8
|
+
@key_type = key_type
|
9
|
+
@value_type = value_type
|
9
10
|
@stash = Hash.new { |hash, key| hash[key] = [] }
|
10
11
|
end
|
11
12
|
|
12
|
-
def push(key,
|
13
|
+
def push(key, value)
|
13
14
|
Type! key, @key_type
|
14
|
-
Type!
|
15
|
-
@stash[key].push
|
15
|
+
Type! value, @value_type
|
16
|
+
@stash[key].push value
|
16
17
|
end
|
17
18
|
|
18
19
|
def shift(key)
|
19
20
|
return nil unless present? key
|
20
|
-
@stash[key].shift.tap {
|
21
|
+
@stash[key].shift.tap { @stash.delete(key) if @stash[key].empty? }
|
21
22
|
end
|
22
23
|
|
23
24
|
def present?(key)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class Director
|
3
4
|
# Handles the events generated while running actions, makes sure
|
@@ -8,11 +9,16 @@ module Dynflow
|
|
8
9
|
def initialize(world)
|
9
10
|
@world = Type! world, World
|
10
11
|
@running_steps = {}
|
11
|
-
|
12
|
+
# enqueued work items by step id
|
13
|
+
@work_items = QueueHash.new(Integer, WorkItem)
|
14
|
+
# enqueued events by step id - we delay creating work items from events until execution time
|
15
|
+
# to handle potential updates of the step object (that is part of the event)
|
16
|
+
@events = QueueHash.new(Integer, Director::Event)
|
17
|
+
@events_by_request_id = {}
|
12
18
|
end
|
13
19
|
|
14
20
|
def terminate
|
15
|
-
pending_work = @
|
21
|
+
pending_work = @work_items.clear.values.flatten(1)
|
16
22
|
pending_work.each do |w|
|
17
23
|
if EventWorkItem === w
|
18
24
|
w.event.result.reject UnprocessableEvent.new("dropping due to termination")
|
@@ -24,27 +30,38 @@ module Dynflow
|
|
24
30
|
Type! step, ExecutionPlan::Steps::RunStep
|
25
31
|
@running_steps[step.id] = step
|
26
32
|
# we make sure not to run any event when the step is still being executed
|
27
|
-
@
|
33
|
+
@work_items.push(step.id, work)
|
28
34
|
self
|
29
35
|
end
|
30
36
|
|
31
37
|
# @returns [TrueClass|FalseClass, Array<WorkItem>]
|
32
38
|
def done(step)
|
33
39
|
Type! step, ExecutionPlan::Steps::RunStep
|
34
|
-
|
35
|
-
|
40
|
+
# update the step based on the latest finished work
|
41
|
+
@running_steps[step.id] = step
|
42
|
+
|
43
|
+
@work_items.shift(step.id).tap do |work|
|
44
|
+
finish_event_result(work) { |f| f.fulfill true }
|
36
45
|
end
|
37
46
|
|
38
47
|
if step.state == :suspended
|
39
|
-
return true, [
|
48
|
+
return true, [create_next_event_work_item(step)].compact
|
40
49
|
else
|
50
|
+
while (work = @work_items.shift(step.id))
|
51
|
+
@world.logger.debug "step #{step.execution_plan_id}:#{step.id} dropping event #{work.request_id}/#{work.event}"
|
52
|
+
finish_event_result(work) do |f|
|
53
|
+
f.reject UnprocessableEvent.new("Message dropped").tap { |e| e.set_backtrace(caller) }
|
54
|
+
end
|
55
|
+
end
|
41
56
|
while (event = @events.shift(step.id))
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
57
|
+
@world.logger.debug "step #{step.execution_plan_id}:#{step.id} dropping event #{event.request_id}/#{event}"
|
58
|
+
if event.result
|
59
|
+
event.result.reject UnprocessableEvent.new("Message dropped").tap { |e| e.set_backtrace(caller) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
unless @work_items.empty?(step.id) && @events.empty?(step.id)
|
63
|
+
raise "Unexpected item in @work_items (#{@work_items.inspect}) or @events (#{@events.inspect})"
|
46
64
|
end
|
47
|
-
raise 'assert' unless @events.empty?(step.id)
|
48
65
|
@running_steps.delete(step.id)
|
49
66
|
return false, []
|
50
67
|
end
|
@@ -60,19 +77,39 @@ module Dynflow
|
|
60
77
|
# @returns [Array<WorkItem>]
|
61
78
|
def event(event)
|
62
79
|
Type! event, Event
|
63
|
-
next_work_items = []
|
64
80
|
|
65
81
|
step = @running_steps[event.step_id]
|
66
82
|
unless step
|
67
83
|
event.result.reject UnprocessableEvent.new('step is not suspended, it cannot process events')
|
68
|
-
return
|
84
|
+
return []
|
69
85
|
end
|
70
86
|
|
71
|
-
can_run_event = @
|
72
|
-
|
73
|
-
@events.push(step.id,
|
74
|
-
|
75
|
-
|
87
|
+
can_run_event = @work_items.empty?(step.id)
|
88
|
+
@events_by_request_id[event.request_id] = event
|
89
|
+
@events.push(step.id, event)
|
90
|
+
if can_run_event
|
91
|
+
[create_next_event_work_item(step)]
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# turns the first event from the queue to the next work item to work on
|
98
|
+
def create_next_event_work_item(step)
|
99
|
+
event = @events.shift(step.id)
|
100
|
+
return unless event
|
101
|
+
work = EventWorkItem.new(event.request_id, event.execution_plan_id, step, event.event, step.queue, @world.id)
|
102
|
+
@work_items.push(step.id, work)
|
103
|
+
work
|
104
|
+
end
|
105
|
+
|
106
|
+
# @yield [Concurrent.resolvable_future] in case the work item has an result future assigned
|
107
|
+
# and deletes the tracked event
|
108
|
+
def finish_event_result(work_item)
|
109
|
+
return unless EventWorkItem === work_item
|
110
|
+
if event = @events_by_request_id.delete(work_item.request_id)
|
111
|
+
yield event.result if event.result
|
112
|
+
end
|
76
113
|
end
|
77
114
|
end
|
78
115
|
end
|