dynflow 1.8.2 → 1.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/.rubocop.yml +11 -5
  4. data/.rubocop_todo.yml +777 -345
  5. data/Gemfile +4 -3
  6. data/Rakefile +1 -0
  7. data/doc/pages/Gemfile +4 -3
  8. data/doc/pages/Rakefile +1 -0
  9. data/doc/pages/plugins/alert_block.rb +1 -0
  10. data/doc/pages/plugins/div_tag.rb +1 -0
  11. data/doc/pages/plugins/graphviz.rb +6 -6
  12. data/doc/pages/plugins/plantuml.rb +2 -3
  13. data/doc/pages/plugins/play.rb +1 -2
  14. data/doc/pages/plugins/tags.rb +2 -8
  15. data/doc/pages/plugins/toc.rb +1 -1
  16. data/dynflow.gemspec +11 -10
  17. data/examples/clock_benchmark.rb +1 -0
  18. data/examples/example_helper.rb +4 -3
  19. data/examples/future_execution.rb +0 -2
  20. data/examples/memory_limit_watcher.rb +8 -8
  21. data/examples/orchestrate.rb +9 -21
  22. data/examples/orchestrate_evented.rb +18 -33
  23. data/examples/remote_executor.rb +9 -11
  24. data/examples/singletons.rb +1 -0
  25. data/examples/sub_plan_concurrency_control.rb +0 -1
  26. data/examples/sub_plans.rb +1 -0
  27. data/examples/sub_plans_v2.rb +1 -0
  28. data/lib/dynflow/action/cancellable.rb +1 -0
  29. data/lib/dynflow/action/format.rb +1 -4
  30. data/lib/dynflow/action/missing.rb +4 -4
  31. data/lib/dynflow/action/polling.rb +2 -3
  32. data/lib/dynflow/action/progress.rb +1 -4
  33. data/lib/dynflow/action/rescue.rb +1 -2
  34. data/lib/dynflow/action/singleton.rb +1 -0
  35. data/lib/dynflow/action/suspended.rb +1 -0
  36. data/lib/dynflow/action/timeouts.rb +2 -1
  37. data/lib/dynflow/action/with_bulk_sub_plans.rb +1 -0
  38. data/lib/dynflow/action/with_polling_sub_plans.rb +1 -1
  39. data/lib/dynflow/action/with_sub_plans.rb +20 -19
  40. data/lib/dynflow/action.rb +37 -37
  41. data/lib/dynflow/active_job/queue_adapter.rb +2 -1
  42. data/lib/dynflow/actor.rb +2 -2
  43. data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
  44. data/lib/dynflow/actors.rb +1 -0
  45. data/lib/dynflow/clock.rb +3 -4
  46. data/lib/dynflow/config.rb +6 -5
  47. data/lib/dynflow/connectors/abstract.rb +11 -10
  48. data/lib/dynflow/connectors/database.rb +2 -2
  49. data/lib/dynflow/connectors/direct.rb +2 -3
  50. data/lib/dynflow/connectors.rb +1 -0
  51. data/lib/dynflow/coordinator.rb +21 -9
  52. data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
  53. data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
  54. data/lib/dynflow/coordinator_adapters.rb +1 -2
  55. data/lib/dynflow/dead_letter_silencer.rb +1 -0
  56. data/lib/dynflow/debug/telemetry/persistence.rb +3 -2
  57. data/lib/dynflow/delayed_executors/abstract.rb +1 -2
  58. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -1
  59. data/lib/dynflow/delayed_executors/polling.rb +1 -2
  60. data/lib/dynflow/delayed_executors.rb +1 -2
  61. data/lib/dynflow/delayed_plan.rb +6 -6
  62. data/lib/dynflow/director/execution_plan_manager.rb +1 -1
  63. data/lib/dynflow/director/flow_manager.rb +1 -0
  64. data/lib/dynflow/director/queue_hash.rb +2 -1
  65. data/lib/dynflow/director/running_steps_manager.rb +3 -2
  66. data/lib/dynflow/director/sequence_cursor.rb +1 -2
  67. data/lib/dynflow/director/sequential_manager.rb +1 -0
  68. data/lib/dynflow/director.rb +12 -11
  69. data/lib/dynflow/dispatcher/abstract.rb +1 -0
  70. data/lib/dynflow/dispatcher/client_dispatcher.rb +33 -33
  71. data/lib/dynflow/dispatcher/executor_dispatcher.rb +7 -6
  72. data/lib/dynflow/dispatcher.rb +8 -7
  73. data/lib/dynflow/errors.rb +1 -0
  74. data/lib/dynflow/execution_history.rb +2 -1
  75. data/lib/dynflow/execution_plan/dependency_graph.rb +1 -2
  76. data/lib/dynflow/execution_plan/hooks.rb +1 -1
  77. data/lib/dynflow/execution_plan/output_reference.rb +4 -4
  78. data/lib/dynflow/execution_plan/steps/abstract.rb +21 -20
  79. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +1 -1
  80. data/lib/dynflow/execution_plan/steps/error.rb +10 -9
  81. data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -2
  82. data/lib/dynflow/execution_plan/steps/plan_step.rb +12 -11
  83. data/lib/dynflow/execution_plan/steps/run_step.rb +1 -1
  84. data/lib/dynflow/execution_plan/steps.rb +1 -2
  85. data/lib/dynflow/execution_plan.rb +46 -46
  86. data/lib/dynflow/executors/abstract/core.rb +4 -3
  87. data/lib/dynflow/executors/parallel/core.rb +3 -2
  88. data/lib/dynflow/executors/parallel/pool.rb +1 -4
  89. data/lib/dynflow/executors/parallel/worker.rb +1 -0
  90. data/lib/dynflow/executors/parallel.rb +3 -2
  91. data/lib/dynflow/executors/sidekiq/core.rb +2 -0
  92. data/lib/dynflow/executors/sidekiq/internal_job_base.rb +1 -0
  93. data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +1 -0
  94. data/lib/dynflow/executors/sidekiq/redis_locking.rb +1 -0
  95. data/lib/dynflow/executors/sidekiq/serialization.rb +1 -0
  96. data/lib/dynflow/executors/sidekiq/worker_jobs.rb +1 -0
  97. data/lib/dynflow/executors.rb +1 -1
  98. data/lib/dynflow/extensions/msgpack.rb +5 -4
  99. data/lib/dynflow/extensions.rb +1 -0
  100. data/lib/dynflow/flows/abstract.rb +1 -1
  101. data/lib/dynflow/flows/abstract_composed.rb +1 -2
  102. data/lib/dynflow/flows/atom.rb +1 -2
  103. data/lib/dynflow/flows/concurrence.rb +1 -1
  104. data/lib/dynflow/flows/registry.rb +1 -0
  105. data/lib/dynflow/flows/sequence.rb +1 -1
  106. data/lib/dynflow/flows.rb +1 -2
  107. data/lib/dynflow/logger_adapters/abstract.rb +1 -1
  108. data/lib/dynflow/logger_adapters/delegator.rb +1 -1
  109. data/lib/dynflow/logger_adapters/formatters/abstract.rb +1 -0
  110. data/lib/dynflow/logger_adapters/formatters/exception.rb +1 -0
  111. data/lib/dynflow/logger_adapters/formatters.rb +1 -0
  112. data/lib/dynflow/logger_adapters/simple.rb +6 -5
  113. data/lib/dynflow/logger_adapters.rb +1 -0
  114. data/lib/dynflow/middleware/common/singleton.rb +1 -0
  115. data/lib/dynflow/middleware/common/transaction.rb +1 -0
  116. data/lib/dynflow/middleware/register.rb +1 -0
  117. data/lib/dynflow/middleware/resolver.rb +2 -3
  118. data/lib/dynflow/middleware/stack.rb +1 -0
  119. data/lib/dynflow/middleware/world.rb +1 -2
  120. data/lib/dynflow/middleware.rb +1 -0
  121. data/lib/dynflow/persistence.rb +4 -5
  122. data/lib/dynflow/persistence_adapters/abstract.rb +1 -1
  123. data/lib/dynflow/persistence_adapters/sequel.rb +14 -14
  124. data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +2 -1
  125. data/lib/dynflow/persistence_adapters/sequel_migrations/002_incremental_progress.rb +1 -0
  126. data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +1 -0
  127. data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +1 -1
  128. data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +1 -0
  129. data/lib/dynflow/persistence_adapters/sequel_migrations/006_fix_data_length.rb +1 -0
  130. data/lib/dynflow/persistence_adapters/sequel_migrations/007_future_execution.rb +1 -0
  131. data/lib/dynflow/persistence_adapters/sequel_migrations/008_rename_scheduled_plans_to_delayed_plans.rb +1 -0
  132. data/lib/dynflow/persistence_adapters/sequel_migrations/009_fix_mysql_data_length.rb +1 -1
  133. data/lib/dynflow/persistence_adapters/sequel_migrations/010_add_execution_plans_label.rb +1 -0
  134. data/lib/dynflow/persistence_adapters/sequel_migrations/011_placeholder.rb +1 -0
  135. data/lib/dynflow/persistence_adapters/sequel_migrations/012_add_delayed_plans_serialized_args.rb +1 -0
  136. data/lib/dynflow/persistence_adapters/sequel_migrations/013_add_action_columns.rb +1 -0
  137. data/lib/dynflow/persistence_adapters/sequel_migrations/014_add_step_columns.rb +1 -0
  138. data/lib/dynflow/persistence_adapters/sequel_migrations/015_add_execution_plan_columns.rb +1 -0
  139. data/lib/dynflow/persistence_adapters/sequel_migrations/016_add_step_queue.rb +1 -0
  140. data/lib/dynflow/persistence_adapters/sequel_migrations/017_add_delayed_plan_frozen.rb +1 -0
  141. data/lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb +37 -30
  142. data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +1 -0
  143. data/lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb +1 -0
  144. data/lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb +4 -3
  145. data/lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb +1 -0
  146. data/lib/dynflow/persistence_adapters.rb +1 -0
  147. data/lib/dynflow/rails/configuration.rb +1 -0
  148. data/lib/dynflow/rails/daemon.rb +1 -1
  149. data/lib/dynflow/rails.rb +3 -2
  150. data/lib/dynflow/round_robin.rb +2 -2
  151. data/lib/dynflow/semaphores/abstract.rb +1 -1
  152. data/lib/dynflow/semaphores/aggregating.rb +1 -2
  153. data/lib/dynflow/semaphores/dummy.rb +1 -1
  154. data/lib/dynflow/semaphores/stateful.rb +1 -1
  155. data/lib/dynflow/semaphores.rb +1 -0
  156. data/lib/dynflow/serializable.rb +1 -0
  157. data/lib/dynflow/serializer.rb +2 -2
  158. data/lib/dynflow/serializers/abstract.rb +1 -2
  159. data/lib/dynflow/serializers/noop.rb +1 -2
  160. data/lib/dynflow/serializers.rb +1 -2
  161. data/lib/dynflow/stateful.rb +1 -0
  162. data/lib/dynflow/telemetry.rb +11 -10
  163. data/lib/dynflow/telemetry_adapters/abstract.rb +1 -0
  164. data/lib/dynflow/telemetry_adapters/dummy.rb +1 -0
  165. data/lib/dynflow/telemetry_adapters/statsd.rb +2 -1
  166. data/lib/dynflow/testing/assertions.rb +7 -7
  167. data/lib/dynflow/testing/dummy_coordinator.rb +1 -0
  168. data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
  169. data/lib/dynflow/testing/dummy_executor.rb +1 -0
  170. data/lib/dynflow/testing/dummy_planned_action.rb +3 -1
  171. data/lib/dynflow/testing/dummy_step.rb +1 -0
  172. data/lib/dynflow/testing/dummy_world.rb +1 -0
  173. data/lib/dynflow/testing/factories.rb +42 -37
  174. data/lib/dynflow/testing/in_thread_executor.rb +1 -0
  175. data/lib/dynflow/testing/in_thread_world.rb +1 -0
  176. data/lib/dynflow/testing/managed_clock.rb +1 -1
  177. data/lib/dynflow/testing/mimic.rb +4 -4
  178. data/lib/dynflow/testing.rb +1 -0
  179. data/lib/dynflow/throttle_limiter.rb +1 -1
  180. data/lib/dynflow/transaction_adapters/abstract.rb +1 -0
  181. data/lib/dynflow/transaction_adapters/active_record.rb +1 -0
  182. data/lib/dynflow/transaction_adapters/none.rb +1 -0
  183. data/lib/dynflow/transaction_adapters.rb +1 -2
  184. data/lib/dynflow/utils/indifferent_hash.rb +7 -1
  185. data/lib/dynflow/utils/priority_queue.rb +1 -0
  186. data/lib/dynflow/utils.rb +1 -1
  187. data/lib/dynflow/version.rb +2 -1
  188. data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -1
  189. data/lib/dynflow/web/console.rb +1 -3
  190. data/lib/dynflow/web/console_helpers.rb +5 -4
  191. data/lib/dynflow/web/filtering_helpers.rb +1 -0
  192. data/lib/dynflow/web/world_helpers.rb +1 -0
  193. data/lib/dynflow/web.rb +3 -3
  194. data/lib/dynflow/web_console.rb +1 -0
  195. data/lib/dynflow/world/invalidation.rb +9 -1
  196. data/lib/dynflow/world.rb +20 -20
  197. data/lib/dynflow.rb +3 -6
  198. data/test/abnormal_states_recovery_test.rb +4 -8
  199. data/test/action_test.rb +10 -18
  200. data/test/activejob_adapter_test.rb +2 -2
  201. data/test/batch_sub_tasks_test.rb +1 -1
  202. data/test/clock_test.rb +2 -3
  203. data/test/concurrency_control_test.rb +6 -7
  204. data/test/coordinator_test.rb +1 -0
  205. data/test/daemon_test.rb +3 -2
  206. data/test/dead_letter_silencer_test.rb +2 -1
  207. data/test/dispatcher_test.rb +4 -5
  208. data/test/execution_plan_cleaner_test.rb +1 -0
  209. data/test/execution_plan_hooks_test.rb +1 -0
  210. data/test/execution_plan_test.rb +10 -32
  211. data/test/executor_test.rb +20 -37
  212. data/test/extensions_test.rb +1 -0
  213. data/test/flows_test.rb +2 -2
  214. data/test/future_execution_test.rb +2 -3
  215. data/test/memory_cosumption_watcher_test.rb +1 -0
  216. data/test/middleware_test.rb +4 -6
  217. data/test/persistence_test.rb +26 -26
  218. data/test/redis_locking_test.rb +1 -0
  219. data/test/rescue_test.rb +3 -11
  220. data/test/round_robin_test.rb +1 -0
  221. data/test/semaphores_test.rb +5 -7
  222. data/test/support/code_workflow_example.rb +11 -28
  223. data/test/support/dummy_example.rb +20 -19
  224. data/test/support/middleware_example.rb +2 -8
  225. data/test/support/rescue_example.rb +1 -14
  226. data/test/support/test_execution_log.rb +1 -2
  227. data/test/test_helper.rb +3 -7
  228. data/test/testing_test.rb +6 -8
  229. data/test/utils_test.rb +1 -0
  230. data/test/v2_sub_plans_test.rb +1 -0
  231. data/test/web_console_test.rb +4 -4
  232. data/test/world_test.rb +4 -3
  233. 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
- "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."
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
- "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."
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
- "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."
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,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Action::Rescue
4
-
5
5
  Strategy = Algebrick.type do
6
6
  variants Skip = atom, Pause = atom, Fail = atom
7
7
  end
@@ -65,4 +65,3 @@ module Dynflow
65
65
  end
66
66
  end
67
67
  end
68
-
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  class Action
4
5
  module Singleton
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  class Action::Suspended
4
5
  attr_reader :execution_plan_id, :step_id
@@ -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
- end
14
+ end
14
15
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Action::WithBulkSubPlans
4
5
  include Dynflow::Action::Cancellable
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Action::WithPollingSubPlans
4
-
5
5
  REFRESH_INTERVAL = 10
6
6
  Poll = Algebrick.atom
7
7
 
@@ -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
- (on nil do
20
- if output[:total_count]
21
- resume
22
- else
23
- initiate
24
- end
25
- end),
26
- (on SubPlanFinished do
27
- mark_as_done(event.execution_plan_id, event.success)
28
- try_to_finish or suspend
29
- end),
30
- (on Action::Cancellable::Cancel do
31
- cancel!
32
- end),
33
- (on Action::Cancellable::Abort do
34
- abort!
35
- end)
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
- :interval => time.to_f / (count * level),
113
- :time_span => time)
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
@@ -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 = atom,
72
- Run = atom,
73
- Finalize = atom
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
- (on Plan, :execute_plan),
82
- (on Run, :execute_run),
83
- (on Finalize, :execute_finalize)
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
- :plan_step_id, :run_step_id, :finalize_step_id,
109
- :caller_execution_plan_id, :caller_action_id,
110
- :pending_output_chunks
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 = Type!(attributes.fetch(:execution_plan), ExecutionPlan) if phase? Present
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 =-> key, required do
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
- planned_steps(execution_plan).
241
- map { |s| s.action(execution_plan) }.
242
- select { |a| a.is_a?(filter) }
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
- select { |a| a.is_a?(filter_class) }
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
- { 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)
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 =-> value do
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
- 'Step', execution_plan_id, @step.id,
368
- self.state, state,
369
- phase.to_s_humanized, self.class)
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
- 'Step', execution_plan_id, @step.id, event) if event
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
- .delay_with_options(id: job.provider_job_id,
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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Actors
4
5
  class ExecutionPlanCleaner
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Actors
4
5
  require 'dynflow/actors/execution_plan_cleaner'
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, -> who { who.respond_to? v.where } }
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 = current_time + time if time.is_a? Numeric
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
@@ -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_#{ name }!") do |value|
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 = "@#{ 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_#{ name }!"
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 # rubocop:disable Lint/HandleExceptions
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
- (on Dispatcher::Ping do
39
- response_envelope = envelope.build_response_envelope(Dispatcher::Pong, world)
40
- send(response_envelope)
41
- end),
42
- (on Dispatcher::Request do
43
- world.executor_dispatcher.tell([:handle_request, envelope])
44
- end),
45
- (on Dispatcher::Response do
46
- world.client_dispatcher.tell([:dispatch_response, envelope])
47
- end))
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:#{ @world_id }", :loop => true) do
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 #{ envelope } not found")
32
+ log(Logger::ERROR, "Receiver for envelope #{envelope} not found")
34
33
  end
35
34
  end
36
35
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module Connectors
4
5
  require 'dynflow/connectors/abstract'
@@ -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 to_s
150
- "#{self.class.name}: #{id} by #{owner_id}"
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
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Dynflow
3
4
  module CoordinatorAdapters
4
5
  class Abstract