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
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class Director
|
3
4
|
class SequentialManager
|
@@ -30,13 +31,22 @@ module Dynflow
|
|
30
31
|
@done = true
|
31
32
|
end
|
32
33
|
|
34
|
+
def finalize_steps
|
35
|
+
execution_plan.finalize_flow.all_step_ids.map do |step_id|
|
36
|
+
execution_plan.steps[step_id]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
33
40
|
def reset_finalize_steps
|
34
|
-
|
35
|
-
step = execution_plan.steps[step_id]
|
41
|
+
finalize_steps.each do |step|
|
36
42
|
step.state = :pending if [:success, :error].include? step.state
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
46
|
+
def done!
|
47
|
+
@done = true
|
48
|
+
end
|
49
|
+
|
40
50
|
def done?
|
41
51
|
@done
|
42
52
|
end
|
data/lib/dynflow/dispatcher.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module Dispatcher
|
3
4
|
Request = Algebrick.type do
|
4
5
|
Event = type do
|
5
6
|
fields! execution_plan_id: String,
|
6
7
|
step_id: Integer,
|
7
|
-
event: Object
|
8
|
+
event: Object,
|
9
|
+
time: type { variants Time, NilClass }
|
8
10
|
end
|
9
11
|
|
10
12
|
Execution = type do
|
@@ -33,7 +35,7 @@ module Dynflow
|
|
33
35
|
end
|
34
36
|
|
35
37
|
Envelope = Algebrick.type do
|
36
|
-
fields! request_id:
|
38
|
+
fields! request_id: String,
|
37
39
|
sender_id: String,
|
38
40
|
receiver_id: type { variants String, AnyExecutor = atom, UnknownWorld = atom },
|
39
41
|
message: type { variants Request, Response }
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module Dispatcher
|
3
4
|
class ClientDispatcher < Abstract
|
4
5
|
|
5
6
|
TrackedRequest = Algebrick.type do
|
6
|
-
fields! id:
|
7
|
+
fields! id: String, request: Request,
|
7
8
|
accepted: Concurrent::Promises::ResolvableFuture, finished: Concurrent::Promises::ResolvableFuture
|
8
9
|
end
|
9
10
|
|
@@ -33,7 +34,7 @@ module Dynflow
|
|
33
34
|
# or whether the Ping really needs to be sent.
|
34
35
|
class PingCache
|
35
36
|
# Format string used for formating and parsing times
|
36
|
-
TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
|
37
|
+
TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
|
37
38
|
DEFAULT_MAX_AGE = 60
|
38
39
|
|
39
40
|
# Formats time into a string
|
@@ -105,7 +106,7 @@ module Dynflow
|
|
105
106
|
attr_reader :ping_cache
|
106
107
|
def initialize(world, ping_cache_age)
|
107
108
|
@world = Type! world, World
|
108
|
-
@
|
109
|
+
@last_id_suffix = 0
|
109
110
|
@tracked_requests = {}
|
110
111
|
@terminated = nil
|
111
112
|
@ping_cache = PingCache.new world, ping_cache_age
|
@@ -197,7 +198,8 @@ module Dynflow
|
|
197
198
|
end
|
198
199
|
|
199
200
|
def track_request(finished, request, timeout)
|
200
|
-
|
201
|
+
id_suffix = @last_id_suffix += 1
|
202
|
+
id = "#{@world.id}-#{id_suffix}"
|
201
203
|
tracked_request = TrackedRequest[id, request, Concurrent::Promises.resolvable_future, finished]
|
202
204
|
@tracked_requests[id] = tracked_request
|
203
205
|
@world.clock.ping(self, timeout, [:timeout, id]) if timeout
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module Dispatcher
|
3
4
|
class ExecutorDispatcher < Abstract
|
@@ -50,7 +51,18 @@ module Dynflow
|
|
50
51
|
respond(envelope, Failed[reason.to_s])
|
51
52
|
end
|
52
53
|
end
|
53
|
-
|
54
|
+
if event_request.time.nil? || event_request.time < Time.now
|
55
|
+
@world.executor.event(envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, future)
|
56
|
+
else
|
57
|
+
@world.clock.ping(
|
58
|
+
@world.executor,
|
59
|
+
event_request.time,
|
60
|
+
Director::Event[envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, Concurrent::Promises.resolvable_future],
|
61
|
+
:delayed_event
|
62
|
+
)
|
63
|
+
# resolves the future right away - currently we do not wait for the clock ping
|
64
|
+
future.fulfill true
|
65
|
+
end
|
54
66
|
rescue Dynflow::Error => e
|
55
67
|
future.reject(e) if future && !future.resolved?
|
56
68
|
end
|
data/lib/dynflow/errors.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'securerandom'
|
2
3
|
|
3
4
|
module Dynflow
|
@@ -121,9 +122,9 @@ module Dynflow
|
|
121
122
|
original = self.state
|
122
123
|
case self.state = state
|
123
124
|
when :planning
|
124
|
-
@started_at = Time.now
|
125
|
+
@started_at = Time.now.utc
|
125
126
|
when :stopped
|
126
|
-
@ended_at = Time.now
|
127
|
+
@ended_at = Time.now.utc
|
127
128
|
@real_time = @ended_at - @started_at unless @started_at.nil?
|
128
129
|
@execution_time = compute_execution_time
|
129
130
|
key = failure? ? :failure : :success
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
class ExecutionPlan::OutputReference < Serializable
|
3
4
|
include Algebrick::TypeCheck
|
@@ -59,7 +60,7 @@ module Dynflow
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def to_s
|
62
|
-
"Step(#{step_id}).output".tap do |ret|
|
63
|
+
"Step(#{step_id}).output".dup.tap do |ret|
|
63
64
|
ret << subkeys.map { |k| "[:#{k}]" }.join('') if subkeys.any?
|
64
65
|
end
|
65
66
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module ExecutionPlan::Steps
|
3
4
|
class Abstract < Serializable
|
@@ -5,7 +6,7 @@ module Dynflow
|
|
5
6
|
include Stateful
|
6
7
|
|
7
8
|
attr_reader :execution_plan_id, :id, :state, :action_class, :action_id, :world, :started_at,
|
8
|
-
:ended_at, :execution_time, :real_time, :queue
|
9
|
+
:ended_at, :execution_time, :real_time, :queue, :delayed_events
|
9
10
|
attr_accessor :error
|
10
11
|
|
11
12
|
# rubocop:disable Metrics/ParameterLists
|
@@ -47,6 +48,10 @@ module Dynflow
|
|
47
48
|
end
|
48
49
|
# rubocop:enable Metrics/ParameterLists
|
49
50
|
|
51
|
+
def ==(other)
|
52
|
+
other.class == self.class && other.execution_plan_id == self.execution_plan_id && other.id == self.id
|
53
|
+
end
|
54
|
+
|
50
55
|
def action_logger
|
51
56
|
@world.action_logger
|
52
57
|
end
|
@@ -63,8 +68,8 @@ module Dynflow
|
|
63
68
|
world.persistence
|
64
69
|
end
|
65
70
|
|
66
|
-
def save
|
67
|
-
persistence.save_step(self)
|
71
|
+
def save(conditions = {})
|
72
|
+
persistence.save_step(self, conditions)
|
68
73
|
end
|
69
74
|
|
70
75
|
def self.states
|
@@ -157,12 +162,12 @@ module Dynflow
|
|
157
162
|
private
|
158
163
|
|
159
164
|
def with_meta_calculation(action, &block)
|
160
|
-
start = Time.now
|
165
|
+
start = Time.now.utc
|
161
166
|
@started_at ||= start
|
162
167
|
block.call
|
163
168
|
ensure
|
164
169
|
calculate_progress(action)
|
165
|
-
@ended_at = Time.now
|
170
|
+
@ended_at = Time.now.utc
|
166
171
|
current_execution_time = @ended_at - start
|
167
172
|
@execution_time += current_execution_time
|
168
173
|
@real_time = @ended_at - @started_at
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module ExecutionPlan::Steps
|
3
4
|
class AbstractFlowStep < Abstract
|
@@ -15,6 +16,7 @@ module Dynflow
|
|
15
16
|
open_action do |action|
|
16
17
|
with_meta_calculation(action) do
|
17
18
|
action.execute(*args)
|
19
|
+
@delayed_events = action.delayed_events
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/lib/dynflow/executors.rb
CHANGED
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Dynflow
|
3
|
+
module Executors
|
4
|
+
module Abstract
|
5
|
+
class Core < Actor
|
6
|
+
attr_reader :logger
|
7
|
+
|
8
|
+
def initialize(world, heartbeat_interval, queues_options)
|
9
|
+
@logger = world.logger
|
10
|
+
@world = Type! world, World
|
11
|
+
@pools = {}
|
12
|
+
@terminated = nil
|
13
|
+
@director = Director.new(@world)
|
14
|
+
@heartbeat_interval = heartbeat_interval
|
15
|
+
@queues_options = queues_options
|
16
|
+
|
17
|
+
schedule_heartbeat
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle_execution(execution_plan_id, finished)
|
21
|
+
if terminating?
|
22
|
+
raise Dynflow::Error,
|
23
|
+
"cannot accept execution_plan_id:#{execution_plan_id} core is terminating"
|
24
|
+
end
|
25
|
+
|
26
|
+
handle_work(@director.start_execution(execution_plan_id, finished))
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_event(event)
|
30
|
+
Type! event, Director::Event
|
31
|
+
if terminating?
|
32
|
+
raise Dynflow::Error,
|
33
|
+
"cannot accept event: #{event} core is terminating"
|
34
|
+
end
|
35
|
+
handle_work(@director.handle_event(event))
|
36
|
+
end
|
37
|
+
|
38
|
+
def plan_events(delayed_events)
|
39
|
+
delayed_events.each do |event|
|
40
|
+
@world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def work_finished(work, delayed_events = nil)
|
45
|
+
handle_work(@director.work_finished(work))
|
46
|
+
plan_events(delayed_events) if delayed_events
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_persistence_error(error, work = nil)
|
50
|
+
logger.error "PersistenceError in executor"
|
51
|
+
logger.error error
|
52
|
+
@director.work_failed(work) if work
|
53
|
+
if error.is_a? Errors::FatalPersistenceError
|
54
|
+
logger.fatal "Terminating"
|
55
|
+
@world.terminate
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_termination(*args)
|
60
|
+
logger.info 'shutting down Core ...'
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def finish_termination
|
65
|
+
@director.terminate
|
66
|
+
logger.info '... Dynflow core terminated.'
|
67
|
+
super()
|
68
|
+
end
|
69
|
+
|
70
|
+
def dead_letter_routing
|
71
|
+
@world.dead_letter_handler
|
72
|
+
end
|
73
|
+
|
74
|
+
def execution_status(execution_plan_id = nil)
|
75
|
+
{}
|
76
|
+
end
|
77
|
+
|
78
|
+
def heartbeat
|
79
|
+
@logger.debug('Executor heartbeat')
|
80
|
+
record = @world.coordinator.find_records(:id => @world.id,
|
81
|
+
:class => ['Dynflow::Coordinator::ExecutorWorld', 'Dynflow::Coordinator::ClientWorld']).first
|
82
|
+
unless record
|
83
|
+
logger.error(%{Executor's world record for #{@world.id} missing: terminating})
|
84
|
+
@world.terminate
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
record.data[:meta].update(:last_seen => Dynflow::Dispatcher::ClientDispatcher::PingCache.format_time)
|
89
|
+
@world.coordinator.update_record(record)
|
90
|
+
schedule_heartbeat
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def suggest_queue(work_item)
|
96
|
+
queue = work_item.queue
|
97
|
+
unless @queues_options.key?(queue)
|
98
|
+
logger.debug("Pool is not available for queue #{queue}, falling back to #{fallback_queue}")
|
99
|
+
queue = fallback_queue
|
100
|
+
end
|
101
|
+
queue
|
102
|
+
end
|
103
|
+
|
104
|
+
def fallback_queue
|
105
|
+
:default
|
106
|
+
end
|
107
|
+
|
108
|
+
def schedule_heartbeat
|
109
|
+
@world.clock.ping(self, @heartbeat_interval, :heartbeat)
|
110
|
+
end
|
111
|
+
|
112
|
+
def on_message(message)
|
113
|
+
super
|
114
|
+
rescue Errors::PersistenceError => e
|
115
|
+
handle_persistence_error(e)
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_work(work_items)
|
119
|
+
return if terminating?
|
120
|
+
return if work_items.nil?
|
121
|
+
work_items = [work_items] if work_items.is_a? Director::WorkItem
|
122
|
+
work_items.all? { |i| Type! i, Director::WorkItem }
|
123
|
+
feed_pool(work_items)
|
124
|
+
end
|
125
|
+
|
126
|
+
def feed_pool(work_items)
|
127
|
+
raise NotImplementedError
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -1,15 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Dynflow
|
2
3
|
module Executors
|
3
|
-
class Parallel
|
4
|
+
class Parallel
|
5
|
+
require 'dynflow/executors/abstract/core'
|
4
6
|
require 'dynflow/executors/parallel/core'
|
5
|
-
|
6
|
-
require 'dynflow/executors/
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
# only load Sidekiq pieces when run in Sidekiq runtime (and the Sidekiq module is already loaded)
|
8
|
+
require 'dynflow/executors/sidekiq/core' if defined? ::Sidekiq
|
9
|
+
|
10
|
+
attr_reader :core
|
11
|
+
|
12
|
+
def initialize(world,
|
13
|
+
executor_class:,
|
14
|
+
heartbeat_interval:,
|
15
|
+
queues_options: { :default => { :pool_size => 5 }})
|
16
|
+
@world = world
|
17
|
+
@logger = world.logger
|
18
|
+
@core = executor_class.spawn name: 'parallel-executor-core',
|
19
|
+
args: [world, heartbeat_interval, queues_options],
|
20
|
+
initialized: @core_initialized = Concurrent::Promises.resolvable_future
|
13
21
|
end
|
14
22
|
|
15
23
|
def execute(execution_plan_id, finished = Concurrent::Promises.resolvable_future, wait_for_acceptance = true)
|
@@ -25,11 +33,16 @@ module Dynflow
|
|
25
33
|
raise e
|
26
34
|
end
|
27
35
|
|
28
|
-
def event(execution_plan_id, step_id, event, future =
|
29
|
-
@core.ask([:handle_event, Director::Event[execution_plan_id, step_id, event, future]])
|
36
|
+
def event(request_id, execution_plan_id, step_id, event, future = nil)
|
37
|
+
@core.ask([:handle_event, Director::Event[request_id, execution_plan_id, step_id, event, future]])
|
30
38
|
future
|
31
39
|
end
|
32
40
|
|
41
|
+
def delayed_event(director_event)
|
42
|
+
@core.ask([:handle_event, director_event])
|
43
|
+
director_event.result
|
44
|
+
end
|
45
|
+
|
33
46
|
def terminate(future = Concurrent::Promises.resolvable_future)
|
34
47
|
@core.tell([:start_termination, future])
|
35
48
|
future
|