dynflow 1.8.2 → 1.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.rubocop.yml +11 -5
- data/.rubocop_todo.yml +777 -345
- data/Gemfile +4 -3
- data/Rakefile +1 -0
- data/doc/pages/Gemfile +4 -3
- 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 +6 -6
- data/doc/pages/plugins/plantuml.rb +2 -3
- data/doc/pages/plugins/play.rb +1 -2
- data/doc/pages/plugins/tags.rb +2 -8
- data/doc/pages/plugins/toc.rb +1 -1
- data/dynflow.gemspec +11 -10
- data/examples/clock_benchmark.rb +1 -0
- data/examples/example_helper.rb +4 -3
- data/examples/future_execution.rb +0 -2
- data/examples/memory_limit_watcher.rb +8 -8
- data/examples/orchestrate.rb +9 -21
- data/examples/orchestrate_evented.rb +18 -33
- data/examples/remote_executor.rb +9 -11
- data/examples/singletons.rb +1 -0
- data/examples/sub_plan_concurrency_control.rb +0 -1
- data/examples/sub_plans.rb +1 -0
- data/examples/sub_plans_v2.rb +1 -0
- data/lib/dynflow/action/cancellable.rb +1 -0
- data/lib/dynflow/action/format.rb +1 -4
- data/lib/dynflow/action/missing.rb +4 -4
- data/lib/dynflow/action/polling.rb +2 -3
- data/lib/dynflow/action/progress.rb +1 -4
- data/lib/dynflow/action/rescue.rb +1 -2
- data/lib/dynflow/action/singleton.rb +1 -0
- data/lib/dynflow/action/suspended.rb +1 -0
- data/lib/dynflow/action/timeouts.rb +2 -1
- data/lib/dynflow/action/with_bulk_sub_plans.rb +1 -0
- data/lib/dynflow/action/with_polling_sub_plans.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +20 -19
- data/lib/dynflow/action.rb +37 -37
- data/lib/dynflow/active_job/queue_adapter.rb +2 -1
- data/lib/dynflow/actor.rb +2 -2
- data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
- data/lib/dynflow/actors.rb +1 -0
- data/lib/dynflow/clock.rb +3 -4
- data/lib/dynflow/config.rb +6 -5
- data/lib/dynflow/connectors/abstract.rb +11 -10
- data/lib/dynflow/connectors/database.rb +2 -2
- data/lib/dynflow/connectors/direct.rb +2 -3
- data/lib/dynflow/connectors.rb +1 -0
- data/lib/dynflow/coordinator.rb +21 -9
- data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
- data/lib/dynflow/coordinator_adapters.rb +1 -2
- data/lib/dynflow/dead_letter_silencer.rb +1 -0
- data/lib/dynflow/debug/telemetry/persistence.rb +3 -2
- data/lib/dynflow/delayed_executors/abstract.rb +1 -2
- data/lib/dynflow/delayed_executors/abstract_core.rb +1 -1
- data/lib/dynflow/delayed_executors/polling.rb +1 -2
- data/lib/dynflow/delayed_executors.rb +1 -2
- data/lib/dynflow/delayed_plan.rb +6 -6
- data/lib/dynflow/director/execution_plan_manager.rb +1 -1
- data/lib/dynflow/director/flow_manager.rb +1 -0
- data/lib/dynflow/director/queue_hash.rb +2 -1
- data/lib/dynflow/director/running_steps_manager.rb +3 -2
- data/lib/dynflow/director/sequence_cursor.rb +1 -2
- data/lib/dynflow/director/sequential_manager.rb +1 -0
- data/lib/dynflow/director.rb +12 -11
- data/lib/dynflow/dispatcher/abstract.rb +1 -0
- data/lib/dynflow/dispatcher/client_dispatcher.rb +33 -33
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +7 -6
- data/lib/dynflow/dispatcher.rb +8 -7
- data/lib/dynflow/errors.rb +1 -0
- data/lib/dynflow/execution_history.rb +2 -1
- data/lib/dynflow/execution_plan/dependency_graph.rb +1 -2
- data/lib/dynflow/execution_plan/hooks.rb +1 -1
- data/lib/dynflow/execution_plan/output_reference.rb +4 -4
- data/lib/dynflow/execution_plan/steps/abstract.rb +21 -20
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +1 -1
- data/lib/dynflow/execution_plan/steps/error.rb +10 -9
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -2
- data/lib/dynflow/execution_plan/steps/plan_step.rb +12 -11
- data/lib/dynflow/execution_plan/steps/run_step.rb +1 -1
- data/lib/dynflow/execution_plan/steps.rb +1 -2
- data/lib/dynflow/execution_plan.rb +46 -46
- data/lib/dynflow/executors/abstract/core.rb +4 -3
- data/lib/dynflow/executors/parallel/core.rb +3 -2
- data/lib/dynflow/executors/parallel/pool.rb +1 -4
- data/lib/dynflow/executors/parallel/worker.rb +1 -0
- data/lib/dynflow/executors/parallel.rb +3 -2
- data/lib/dynflow/executors/sidekiq/core.rb +2 -0
- data/lib/dynflow/executors/sidekiq/internal_job_base.rb +1 -0
- data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +1 -0
- data/lib/dynflow/executors/sidekiq/redis_locking.rb +1 -0
- data/lib/dynflow/executors/sidekiq/serialization.rb +1 -0
- data/lib/dynflow/executors/sidekiq/worker_jobs.rb +1 -0
- data/lib/dynflow/executors.rb +1 -1
- data/lib/dynflow/extensions/msgpack.rb +5 -4
- data/lib/dynflow/extensions.rb +1 -0
- data/lib/dynflow/flows/abstract.rb +1 -1
- data/lib/dynflow/flows/abstract_composed.rb +1 -2
- data/lib/dynflow/flows/atom.rb +1 -2
- data/lib/dynflow/flows/concurrence.rb +1 -1
- data/lib/dynflow/flows/registry.rb +1 -0
- data/lib/dynflow/flows/sequence.rb +1 -1
- data/lib/dynflow/flows.rb +1 -2
- data/lib/dynflow/logger_adapters/abstract.rb +1 -1
- data/lib/dynflow/logger_adapters/delegator.rb +1 -1
- 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/formatters.rb +1 -0
- data/lib/dynflow/logger_adapters/simple.rb +6 -5
- data/lib/dynflow/logger_adapters.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 +2 -3
- data/lib/dynflow/middleware/stack.rb +1 -0
- data/lib/dynflow/middleware/world.rb +1 -2
- data/lib/dynflow/middleware.rb +1 -0
- data/lib/dynflow/persistence.rb +4 -5
- data/lib/dynflow/persistence_adapters/abstract.rb +1 -1
- data/lib/dynflow/persistence_adapters/sequel.rb +14 -14
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +2 -1
- 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 -1
- 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 -1
- 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 +37 -30
- data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb +1 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb +4 -3
- data/lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb +1 -0
- data/lib/dynflow/persistence_adapters.rb +1 -0
- data/lib/dynflow/rails/configuration.rb +1 -0
- data/lib/dynflow/rails/daemon.rb +1 -1
- data/lib/dynflow/rails.rb +3 -2
- data/lib/dynflow/round_robin.rb +2 -2
- data/lib/dynflow/semaphores/abstract.rb +1 -1
- data/lib/dynflow/semaphores/aggregating.rb +1 -2
- data/lib/dynflow/semaphores/dummy.rb +1 -1
- data/lib/dynflow/semaphores/stateful.rb +1 -1
- data/lib/dynflow/semaphores.rb +1 -0
- data/lib/dynflow/serializable.rb +1 -0
- data/lib/dynflow/serializer.rb +2 -2
- data/lib/dynflow/serializers/abstract.rb +1 -2
- data/lib/dynflow/serializers/noop.rb +1 -2
- data/lib/dynflow/serializers.rb +1 -2
- data/lib/dynflow/stateful.rb +1 -0
- data/lib/dynflow/telemetry.rb +11 -10
- 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 +2 -1
- data/lib/dynflow/testing/assertions.rb +7 -7
- data/lib/dynflow/testing/dummy_coordinator.rb +1 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
- data/lib/dynflow/testing/dummy_executor.rb +1 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +3 -1
- data/lib/dynflow/testing/dummy_step.rb +1 -0
- data/lib/dynflow/testing/dummy_world.rb +1 -0
- data/lib/dynflow/testing/factories.rb +42 -37
- data/lib/dynflow/testing/in_thread_executor.rb +1 -0
- data/lib/dynflow/testing/in_thread_world.rb +1 -0
- data/lib/dynflow/testing/managed_clock.rb +1 -1
- data/lib/dynflow/testing/mimic.rb +4 -4
- data/lib/dynflow/testing.rb +1 -0
- data/lib/dynflow/throttle_limiter.rb +1 -1
- 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/transaction_adapters.rb +1 -2
- data/lib/dynflow/utils/indifferent_hash.rb +7 -1
- data/lib/dynflow/utils/priority_queue.rb +1 -0
- data/lib/dynflow/utils.rb +1 -1
- data/lib/dynflow/version.rb +2 -1
- data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -1
- data/lib/dynflow/web/console.rb +1 -3
- data/lib/dynflow/web/console_helpers.rb +5 -4
- data/lib/dynflow/web/filtering_helpers.rb +1 -0
- data/lib/dynflow/web/world_helpers.rb +1 -0
- data/lib/dynflow/web.rb +3 -3
- data/lib/dynflow/web_console.rb +1 -0
- data/lib/dynflow/world/invalidation.rb +9 -1
- data/lib/dynflow/world.rb +20 -20
- data/lib/dynflow.rb +3 -6
- data/test/abnormal_states_recovery_test.rb +4 -8
- data/test/action_test.rb +10 -18
- data/test/activejob_adapter_test.rb +2 -2
- data/test/batch_sub_tasks_test.rb +1 -1
- data/test/clock_test.rb +2 -3
- data/test/concurrency_control_test.rb +6 -7
- data/test/coordinator_test.rb +1 -0
- data/test/daemon_test.rb +3 -2
- data/test/dead_letter_silencer_test.rb +2 -1
- data/test/dispatcher_test.rb +4 -5
- data/test/execution_plan_cleaner_test.rb +1 -0
- data/test/execution_plan_hooks_test.rb +1 -0
- data/test/execution_plan_test.rb +10 -32
- data/test/executor_test.rb +20 -37
- data/test/extensions_test.rb +1 -0
- data/test/flows_test.rb +2 -2
- data/test/future_execution_test.rb +2 -3
- data/test/memory_cosumption_watcher_test.rb +1 -0
- data/test/middleware_test.rb +4 -6
- data/test/persistence_test.rb +26 -26
- data/test/redis_locking_test.rb +1 -0
- data/test/rescue_test.rb +3 -11
- data/test/round_robin_test.rb +1 -0
- data/test/semaphores_test.rb +5 -7
- data/test/support/code_workflow_example.rb +11 -28
- data/test/support/dummy_example.rb +20 -19
- data/test/support/middleware_example.rb +2 -8
- data/test/support/rescue_example.rb +1 -14
- data/test/support/test_execution_log.rb +1 -2
- data/test/test_helper.rb +3 -7
- data/test/testing_test.rb +6 -8
- data/test/utils_test.rb +1 -0
- data/test/v2_sub_plans_test.rb +1 -0
- data/test/web_console_test.rb +4 -4
- data/test/world_test.rb +4 -3
- metadata +43 -43
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
# for cases the serialized action was renamed and it's not available
|
4
5
|
# in the code base anymore.
|
5
6
|
class Action::Missing < Dynflow::Action
|
6
|
-
|
7
7
|
def self.generate(action_name)
|
8
8
|
Class.new(self).tap do |klass|
|
9
9
|
klass.singleton_class.send(:define_method, :name) do
|
@@ -14,17 +14,17 @@ module Dynflow
|
|
14
14
|
|
15
15
|
def plan(*args)
|
16
16
|
raise StandardError,
|
17
|
-
|
17
|
+
"The action class was not found and therefore plan phase failed, this can happen if the action was added/renamed but the executor was not restarted."
|
18
18
|
end
|
19
19
|
|
20
20
|
def run
|
21
21
|
raise StandardError,
|
22
|
-
|
22
|
+
"The action class was not found and therefore run phase failed, this can happen if the action was added/renamed but the executor was not restarted."
|
23
23
|
end
|
24
24
|
|
25
25
|
def finalize
|
26
26
|
raise StandardError,
|
27
|
-
|
27
|
+
"The action class was not found and therefore finalize phase failed, this can happen if the action was added/renamed but the executor was not restarted."
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'dynflow/action/timeouts'
|
3
4
|
|
4
5
|
module Dynflow
|
5
6
|
module Action::Polling
|
6
|
-
|
7
7
|
def self.included(base)
|
8
8
|
base.send :include, Action::Timeouts
|
9
9
|
end
|
@@ -71,7 +71,7 @@ module Dynflow
|
|
71
71
|
|
72
72
|
# Returns the time to wait between two polling intervals.
|
73
73
|
def poll_interval
|
74
|
-
interval_level = poll_attempts[:total]/attempts_before_next_interval
|
74
|
+
interval_level = poll_attempts[:total] / attempts_before_next_interval
|
75
75
|
poll_intervals[interval_level] || poll_intervals.last
|
76
76
|
end
|
77
77
|
|
@@ -116,6 +116,5 @@ module Dynflow
|
|
116
116
|
raise error
|
117
117
|
end
|
118
118
|
end
|
119
|
-
|
120
119
|
end
|
121
120
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Dynflow
|
3
2
|
|
3
|
+
module Dynflow
|
4
4
|
# Methods for specifying the progress of the action
|
5
5
|
# the +*_progress+ methods should return number in 0..1.
|
6
6
|
# The weight is there to increase/decrease the portion of this task
|
@@ -10,9 +10,7 @@ module Dynflow
|
|
10
10
|
# The +*_progress+ is run only when the action is in running/suspend state. Otherwise
|
11
11
|
# the progress is 1 for success/skipped actions and 0 for errorneous ones.
|
12
12
|
module Action::Progress
|
13
|
-
|
14
13
|
class Calculate < Middleware
|
15
|
-
|
16
14
|
def run(*args)
|
17
15
|
with_progress_calculation(*args) do
|
18
16
|
[action.run_progress, action.run_progress_weight]
|
@@ -61,4 +59,3 @@ module Dynflow
|
|
61
59
|
attr_accessor :calculated_progress
|
62
60
|
end
|
63
61
|
end
|
64
|
-
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module Action::Timeouts
|
4
5
|
Timeout = Algebrick.atom
|
@@ -10,5 +11,5 @@ module Dynflow
|
|
10
11
|
def schedule_timeout(seconds, optional: false)
|
11
12
|
plan_event(Timeout, seconds, optional: optional)
|
12
13
|
end
|
13
|
-
|
14
|
+
end
|
14
15
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module Action::WithSubPlans
|
4
5
|
include Dynflow::Action::Cancellable
|
@@ -16,23 +17,23 @@ module Dynflow
|
|
16
17
|
|
17
18
|
def run(event = nil)
|
18
19
|
match event,
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
20
|
+
(on nil do
|
21
|
+
if output[:total_count]
|
22
|
+
resume
|
23
|
+
else
|
24
|
+
initiate
|
25
|
+
end
|
26
|
+
end),
|
27
|
+
(on SubPlanFinished do
|
28
|
+
mark_as_done(event.execution_plan_id, event.success)
|
29
|
+
try_to_finish or suspend
|
30
|
+
end),
|
31
|
+
(on Action::Cancellable::Cancel do
|
32
|
+
cancel!
|
33
|
+
end),
|
34
|
+
(on Action::Cancellable::Abort do
|
35
|
+
abort!
|
36
|
+
end)
|
36
37
|
end
|
37
38
|
|
38
39
|
def initiate
|
@@ -109,8 +110,8 @@ module Dynflow
|
|
109
110
|
# Assume concurrency level 1 unless stated otherwise
|
110
111
|
level = input[:concurrency_control].fetch(:level, {}).fetch(:free, 1)
|
111
112
|
semaphore = ::Dynflow::Semaphores::Stateful.new(nil, level,
|
112
|
-
|
113
|
-
|
113
|
+
:interval => time.to_f / (count * level),
|
114
|
+
:time_span => time)
|
114
115
|
input[:concurrency_control][:time] = semaphore.to_hash
|
115
116
|
end
|
116
117
|
end
|
data/lib/dynflow/action.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
# rubocop:disable Metrics/ClassLength
|
4
5
|
class Action < Serializable
|
5
|
-
|
6
6
|
OutputReference = ExecutionPlan::OutputReference
|
7
7
|
|
8
8
|
include Algebrick::TypeCheck
|
@@ -68,9 +68,9 @@ module Dynflow
|
|
68
68
|
Skip = Algebrick.atom
|
69
69
|
Phase = Algebrick.type do
|
70
70
|
Executable = type do
|
71
|
-
variants Plan
|
72
|
-
|
73
|
-
|
71
|
+
variants Plan = atom,
|
72
|
+
Run = atom,
|
73
|
+
Finalize = atom
|
74
74
|
end
|
75
75
|
variants Executable, Present = atom
|
76
76
|
end
|
@@ -78,9 +78,9 @@ module Dynflow
|
|
78
78
|
module Executable
|
79
79
|
def execute_method_name
|
80
80
|
match self,
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
(on Plan, :execute_plan),
|
82
|
+
(on Run, :execute_run),
|
83
|
+
(on Finalize, :execute_finalize)
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -105,9 +105,9 @@ module Dynflow
|
|
105
105
|
end
|
106
106
|
|
107
107
|
attr_reader :world, :phase, :execution_plan_id, :id, :input,
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
:plan_step_id, :run_step_id, :finalize_step_id,
|
109
|
+
:caller_execution_plan_id, :caller_action_id,
|
110
|
+
:pending_output_chunks
|
111
111
|
|
112
112
|
middleware.use Action::Progress::Calculate
|
113
113
|
|
@@ -124,12 +124,12 @@ module Dynflow
|
|
124
124
|
@run_step_id = Type! attributes.fetch(:run_step_id), Integer, NilClass
|
125
125
|
@finalize_step_id = Type! attributes.fetch(:finalize_step_id), Integer, NilClass
|
126
126
|
|
127
|
-
@execution_plan
|
127
|
+
@execution_plan = Type!(attributes.fetch(:execution_plan), ExecutionPlan) if phase? Present
|
128
128
|
|
129
129
|
@caller_execution_plan_id = Type!(attributes.fetch(:caller_execution_plan_id, nil), String, NilClass)
|
130
130
|
@caller_action_id = Type!(attributes.fetch(:caller_action_id, nil), Integer, NilClass)
|
131
131
|
|
132
|
-
getter
|
132
|
+
getter = ->key, required do
|
133
133
|
required ? attributes.fetch(key) : attributes.fetch(key, {})
|
134
134
|
end
|
135
135
|
|
@@ -236,10 +236,10 @@ module Dynflow
|
|
236
236
|
# returned actions are in Present phase
|
237
237
|
def planned_actions(filter = Action)
|
238
238
|
phase! Present
|
239
|
-
plan_step
|
240
|
-
|
241
|
-
|
242
|
-
|
239
|
+
plan_step
|
240
|
+
.planned_steps(execution_plan)
|
241
|
+
.map { |s| s.action(execution_plan) }
|
242
|
+
.select { |a| a.is_a?(filter) }
|
243
243
|
end
|
244
244
|
|
245
245
|
# @param [Class] filter_class return only actions which are kind of `filter_class`
|
@@ -248,8 +248,8 @@ module Dynflow
|
|
248
248
|
def all_planned_actions(filter_class = Action)
|
249
249
|
phase! Present
|
250
250
|
mine = planned_actions
|
251
|
-
(mine + mine.reduce([]) { |arr, action| arr + action.all_planned_actions })
|
252
|
-
|
251
|
+
(mine + mine.reduce([]) { |arr, action| arr + action.all_planned_actions })
|
252
|
+
.select { |a| a.is_a?(filter_class) }
|
253
253
|
end
|
254
254
|
|
255
255
|
def run_step
|
@@ -268,18 +268,19 @@ module Dynflow
|
|
268
268
|
|
269
269
|
def to_hash
|
270
270
|
recursive_to_hash(
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
271
|
+
{ class: self.class.name,
|
272
|
+
execution_plan_id: execution_plan_id,
|
273
|
+
id: id,
|
274
|
+
plan_step_id: plan_step_id,
|
275
|
+
run_step_id: run_step_id,
|
276
|
+
finalize_step_id: finalize_step_id,
|
277
|
+
caller_execution_plan_id: caller_execution_plan_id,
|
278
|
+
caller_action_id: caller_action_id,
|
279
|
+
input: input },
|
280
|
+
if phase? Run, Finalize, Present
|
281
|
+
{ output: output }
|
282
|
+
end
|
283
|
+
)
|
283
284
|
end
|
284
285
|
|
285
286
|
def state
|
@@ -307,7 +308,7 @@ module Dynflow
|
|
307
308
|
# @return [Array<Integer>] - ids of steps referenced from action
|
308
309
|
def required_step_ids(input = self.input)
|
309
310
|
results = []
|
310
|
-
recursion
|
311
|
+
recursion = ->value do
|
311
312
|
case value
|
312
313
|
when Hash
|
313
314
|
value.values.each { |v| recursion.(v) }
|
@@ -364,9 +365,9 @@ module Dynflow
|
|
364
365
|
def state=(state)
|
365
366
|
phase! Executable
|
366
367
|
@world.logger.debug format('%13s %s:%2d %9s >> %9s in phase %8s %s',
|
367
|
-
|
368
|
-
|
369
|
-
|
368
|
+
'Step', execution_plan_id, @step.id,
|
369
|
+
self.state, state,
|
370
|
+
phase.to_s_humanized, self.class)
|
370
371
|
@step.state = state
|
371
372
|
end
|
372
373
|
|
@@ -502,6 +503,7 @@ module Dynflow
|
|
502
503
|
when :skipping
|
503
504
|
self.state = :skipped
|
504
505
|
when :suspended, :error
|
506
|
+
# Do nothing
|
505
507
|
else
|
506
508
|
raise "wrong state #{self.state}"
|
507
509
|
end
|
@@ -551,11 +553,10 @@ module Dynflow
|
|
551
553
|
end
|
552
554
|
|
553
555
|
# TODO: This is getting out of hand, refactoring needed
|
554
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
555
556
|
def execute_run(event)
|
556
557
|
phase! Run
|
557
558
|
@world.logger.debug format('%13s %s:%2d got event %s',
|
558
|
-
|
559
|
+
'Step', execution_plan_id, @step.id, event) if event
|
559
560
|
|
560
561
|
case
|
561
562
|
when state == :running
|
@@ -599,7 +600,6 @@ module Dynflow
|
|
599
600
|
raise "wrong state #{state} when event:#{event}"
|
600
601
|
end
|
601
602
|
end
|
602
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
603
603
|
|
604
604
|
def execute_finalize
|
605
605
|
phase! Finalize
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module ActiveJob
|
4
5
|
module QueueAdapters
|
@@ -13,7 +14,7 @@ module Dynflow
|
|
13
14
|
def enqueue_at(job, timestamp)
|
14
15
|
job.provider_job_id = job.job_id
|
15
16
|
::Rails.application.dynflow.world
|
16
|
-
|
17
|
+
.delay_with_options(id: job.provider_job_id,
|
17
18
|
action_class: JobWrapper,
|
18
19
|
delay_options: { :start_at => Time.at(timestamp) },
|
19
20
|
args: [job.serialize])
|
data/lib/dynflow/actor.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Dynflow
|
3
2
|
|
3
|
+
module Dynflow
|
4
4
|
FULL_BACKTRACE = %w[1 y yes].include?((ENV['DYNFLOW_FULL_BACKTRACE'] || '').downcase)
|
5
5
|
BACKTRACE_LIMIT = begin
|
6
6
|
limit = ENV['DYNFLOW_BACKTRACE_LIMIT'].to_i
|
@@ -94,7 +94,7 @@ module Dynflow
|
|
94
94
|
# takes an array of backtrace lines and replaces each chunk
|
95
95
|
def filter_backtrace(backtrace)
|
96
96
|
trace = backtrace.map { |line| filter_line(line) }
|
97
|
-
.chunk_while { |l1, l2| l1 == l2}
|
97
|
+
.chunk_while { |l1, l2| l1 == l2 }
|
98
98
|
.map(&:first)
|
99
99
|
if BACKTRACE_LIMIT
|
100
100
|
count = trace.count
|
data/lib/dynflow/actors.rb
CHANGED
data/lib/dynflow/clock.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
class Clock < Actor
|
4
|
-
|
5
5
|
include Algebrick::Types
|
6
6
|
|
7
7
|
Timer = Algebrick.type do
|
@@ -13,7 +13,7 @@ module Dynflow
|
|
13
13
|
|
14
14
|
module Timer
|
15
15
|
def self.[](*fields)
|
16
|
-
super(*fields).tap { |v| Match! v.who, ->
|
16
|
+
super(*fields).tap { |v| Match! v.who, ->who { who.respond_to? v.where } }
|
17
17
|
end
|
18
18
|
|
19
19
|
include Comparable
|
@@ -116,7 +116,7 @@ module Dynflow
|
|
116
116
|
|
117
117
|
def ping(who, time, with_what = nil, where = :<<, optional: false)
|
118
118
|
Type! time, Time, Numeric
|
119
|
-
time
|
119
|
+
time = current_time + time if time.is_a? Numeric
|
120
120
|
if who.is_a?(Action::Suspended)
|
121
121
|
who.plan_event(with_what, time, optional: optional)
|
122
122
|
else
|
@@ -125,5 +125,4 @@ module Dynflow
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
128
|
-
|
129
128
|
end
|
data/lib/dynflow/config.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'socket'
|
3
4
|
|
4
5
|
module Dynflow
|
@@ -6,11 +7,11 @@ module Dynflow
|
|
6
7
|
include Algebrick::TypeCheck
|
7
8
|
|
8
9
|
def self.config_attr(name, *types, &default)
|
9
|
-
self.send(:define_method, "validate_#{
|
10
|
+
self.send(:define_method, "validate_#{name}!") do |value|
|
10
11
|
Type! value, *types unless types.empty?
|
11
12
|
end
|
12
13
|
self.send(:define_method, name) do
|
13
|
-
var_name = "@#{
|
14
|
+
var_name = "@#{name}"
|
14
15
|
if instance_variable_defined?(var_name)
|
15
16
|
return instance_variable_get(var_name)
|
16
17
|
else
|
@@ -41,7 +42,7 @@ module Dynflow
|
|
41
42
|
return @cache[name] if @cache.key?(name)
|
42
43
|
value = @config.send(name)
|
43
44
|
value = value.call(@world, self) if value.is_a? Proc
|
44
|
-
validation_method = "validate_#{
|
45
|
+
validation_method = "validate_#{name}!"
|
45
46
|
@config.send(validation_method, value) if @config.respond_to?(validation_method)
|
46
47
|
@cache[name] = value
|
47
48
|
end
|
@@ -51,7 +52,7 @@ module Dynflow
|
|
51
52
|
attr_reader :queues
|
52
53
|
|
53
54
|
def initialize
|
54
|
-
@queues = {:default => {}}
|
55
|
+
@queues = { :default => {} }
|
55
56
|
end
|
56
57
|
|
57
58
|
# Add a new queue to the configuration
|
@@ -204,7 +205,7 @@ module Dynflow
|
|
204
205
|
"it's #{ar_pool_size} but there is #{config_for_world.pool_size} " +
|
205
206
|
'threads in Dynflow pool.'
|
206
207
|
end
|
207
|
-
rescue ActiveRecord::ConnectionNotEstablished
|
208
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
208
209
|
# If in tests or in an environment where ActiveRecord doesn't have a
|
209
210
|
# real DB connection, we want to skip AR configuration altogether
|
210
211
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module Connectors
|
4
5
|
class Abstract
|
@@ -35,16 +36,16 @@ module Dynflow
|
|
35
36
|
Type! envelope, Dispatcher::Envelope
|
36
37
|
Telemetry.with_instance { |t| t.increment_counter(:dynflow_connector_envelopes, 1, :world => world.id, :direction => 'incoming') }
|
37
38
|
match(envelope.message,
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
(on Dispatcher::Ping do
|
40
|
+
response_envelope = envelope.build_response_envelope(Dispatcher::Pong, world)
|
41
|
+
send(response_envelope)
|
42
|
+
end),
|
43
|
+
(on Dispatcher::Request do
|
44
|
+
world.executor_dispatcher.tell([:handle_request, envelope])
|
45
|
+
end),
|
46
|
+
(on Dispatcher::Response do
|
47
|
+
world.client_dispatcher.tell([:dispatch_response, envelope])
|
48
|
+
end))
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module Connectors
|
4
5
|
class Database < Abstract
|
5
|
-
|
6
6
|
class PostgresListerner
|
7
7
|
def initialize(core, world_id, db)
|
8
8
|
@core = core
|
@@ -22,7 +22,7 @@ module Dynflow
|
|
22
22
|
def start
|
23
23
|
@started.set true
|
24
24
|
@thread = Thread.new do
|
25
|
-
@db.listen("world:#{
|
25
|
+
@db.listen("world:#{@world_id}", :loop => true) do
|
26
26
|
if started?
|
27
27
|
@core << :check_inbox
|
28
28
|
else
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Dynflow
|
3
4
|
module Connectors
|
4
5
|
class Direct < Abstract
|
5
|
-
|
6
6
|
class Core < Actor
|
7
|
-
|
8
7
|
def initialize(connector)
|
9
8
|
@connector = connector
|
10
9
|
@worlds = {}
|
@@ -30,7 +29,7 @@ module Dynflow
|
|
30
29
|
if world = find_receiver(envelope)
|
31
30
|
@connector.receive(world, envelope)
|
32
31
|
else
|
33
|
-
log(Logger::ERROR, "Receiver for envelope #{
|
32
|
+
log(Logger::ERROR, "Receiver for envelope #{envelope} not found")
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
data/lib/dynflow/connectors.rb
CHANGED
data/lib/dynflow/coordinator.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'dynflow/coordinator_adapters'
|
3
4
|
|
4
5
|
module Dynflow
|
5
6
|
class Coordinator
|
6
|
-
|
7
7
|
include Algebrick::TypeCheck
|
8
8
|
|
9
9
|
class DuplicateRecordError < Dynflow::Error
|
@@ -41,7 +41,7 @@ module Dynflow
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def initialize(*args)
|
44
|
-
@data
|
44
|
+
@data = {}
|
45
45
|
@data = Utils.indifferent_hash(@data.merge(class: self.class.name))
|
46
46
|
end
|
47
47
|
|
@@ -146,8 +146,8 @@ module Dynflow
|
|
146
146
|
Type! owner_id, String
|
147
147
|
end
|
148
148
|
|
149
|
-
def
|
150
|
-
|
149
|
+
def unlock_on_shutdown?
|
150
|
+
true
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
@@ -293,6 +293,10 @@ module Dynflow
|
|
293
293
|
def request_id
|
294
294
|
@data[:request_id]
|
295
295
|
end
|
296
|
+
|
297
|
+
def unlock_on_shutdown?
|
298
|
+
false
|
299
|
+
end
|
296
300
|
end
|
297
301
|
|
298
302
|
class PlanningLock < LockByWorld
|
@@ -309,6 +313,10 @@ module Dynflow
|
|
309
313
|
def execution_plan_id
|
310
314
|
@data[:execution_plan_id]
|
311
315
|
end
|
316
|
+
|
317
|
+
def unlock_on_shutdown?
|
318
|
+
false
|
319
|
+
end
|
312
320
|
end
|
313
321
|
|
314
322
|
attr_reader :adapter
|
@@ -324,8 +332,12 @@ module Dynflow
|
|
324
332
|
if block
|
325
333
|
begin
|
326
334
|
block.call
|
335
|
+
# We are looking for ::Sidekiq::Shutdown, but that may not be defined. We rely on it being a subclass of Interrupt
|
336
|
+
# We don't really want to rescue it, but we need to bind it somehow so that we can check it in ensure
|
337
|
+
rescue Interrupt => e
|
338
|
+
raise e
|
327
339
|
ensure
|
328
|
-
release(lock)
|
340
|
+
release(lock) if !(defined?(::Sidekiq) && e.is_a?(::Sidekiq::Shutdown)) || lock.unlock_on_shutdown?
|
329
341
|
end
|
330
342
|
end
|
331
343
|
rescue DuplicateRecordError => e
|
@@ -337,8 +349,8 @@ module Dynflow
|
|
337
349
|
adapter.delete_record(lock)
|
338
350
|
end
|
339
351
|
|
340
|
-
def release_by_owner(owner_id)
|
341
|
-
find_locks(owner_id: owner_id).map { |lock| release(lock) }
|
352
|
+
def release_by_owner(owner_id, on_termination = false)
|
353
|
+
find_locks(owner_id: owner_id).map { |lock| release(lock) if !on_termination || lock.unlock_on_shutdown? }
|
342
354
|
end
|
343
355
|
|
344
356
|
def find_locks(filter_options)
|
@@ -383,9 +395,9 @@ module Dynflow
|
|
383
395
|
create_record(world)
|
384
396
|
end
|
385
397
|
|
386
|
-
def delete_world(world)
|
398
|
+
def delete_world(world, on_termination = false)
|
387
399
|
Type! world, Coordinator::ClientWorld, Coordinator::ExecutorWorld
|
388
|
-
release_by_owner("world:#{world.id}")
|
400
|
+
release_by_owner("world:#{world.id}", on_termination)
|
389
401
|
delete_record(world)
|
390
402
|
end
|
391
403
|
|