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.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +3 -4
  4. data/Dockerfile +9 -0
  5. data/Gemfile +6 -0
  6. data/Rakefile +1 -0
  7. data/doc/pages/Gemfile +1 -0
  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 +1 -0
  12. data/doc/pages/plugins/plantuml.rb +1 -0
  13. data/doc/pages/plugins/play.rb +1 -0
  14. data/doc/pages/plugins/tags.rb +1 -0
  15. data/doc/pages/plugins/toc.rb +1 -0
  16. data/docker-compose.yml +41 -0
  17. data/dynflow.gemspec +1 -0
  18. data/examples/clock_benchmark.rb +1 -0
  19. data/examples/example_helper.rb +19 -2
  20. data/examples/future_execution.rb +2 -1
  21. data/examples/memory_limit_watcher.rb +1 -0
  22. data/examples/orchestrate.rb +4 -5
  23. data/examples/orchestrate_evented.rb +3 -2
  24. data/examples/remote_executor.rb +68 -0
  25. data/examples/singletons.rb +4 -3
  26. data/examples/sub_plan_concurrency_control.rb +2 -1
  27. data/examples/sub_plans.rb +3 -2
  28. data/examples/termination.rb +1 -0
  29. data/lib/dynflow.rb +20 -0
  30. data/lib/dynflow/action.rb +28 -3
  31. data/lib/dynflow/action/cancellable.rb +1 -0
  32. data/lib/dynflow/action/format.rb +1 -0
  33. data/lib/dynflow/action/missing.rb +1 -0
  34. data/lib/dynflow/action/polling.rb +3 -1
  35. data/lib/dynflow/action/progress.rb +1 -0
  36. data/lib/dynflow/action/rescue.rb +1 -0
  37. data/lib/dynflow/action/singleton.rb +1 -0
  38. data/lib/dynflow/action/suspended.rb +9 -2
  39. data/lib/dynflow/action/timeouts.rb +2 -1
  40. data/lib/dynflow/action/with_bulk_sub_plans.rb +2 -1
  41. data/lib/dynflow/action/with_polling_sub_plans.rb +7 -5
  42. data/lib/dynflow/action/with_sub_plans.rb +1 -0
  43. data/lib/dynflow/active_job/queue_adapter.rb +1 -0
  44. data/lib/dynflow/actor.rb +13 -5
  45. data/lib/dynflow/actors.rb +1 -0
  46. data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -0
  47. data/lib/dynflow/clock.rb +27 -47
  48. data/lib/dynflow/config.rb +11 -2
  49. data/lib/dynflow/connectors.rb +1 -0
  50. data/lib/dynflow/connectors/abstract.rb +1 -0
  51. data/lib/dynflow/connectors/database.rb +1 -0
  52. data/lib/dynflow/connectors/direct.rb +1 -0
  53. data/lib/dynflow/coordinator.rb +1 -0
  54. data/lib/dynflow/coordinator_adapters.rb +1 -0
  55. data/lib/dynflow/coordinator_adapters/abstract.rb +1 -0
  56. data/lib/dynflow/coordinator_adapters/sequel.rb +1 -0
  57. data/lib/dynflow/dead_letter_silencer.rb +2 -0
  58. data/lib/dynflow/debug/telemetry/persistence.rb +1 -0
  59. data/lib/dynflow/delayed_executors.rb +1 -0
  60. data/lib/dynflow/delayed_executors/abstract.rb +1 -0
  61. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -0
  62. data/lib/dynflow/delayed_executors/polling.rb +1 -0
  63. data/lib/dynflow/delayed_plan.rb +1 -0
  64. data/lib/dynflow/director.rb +80 -15
  65. data/lib/dynflow/director/execution_plan_manager.rb +17 -3
  66. data/lib/dynflow/director/flow_manager.rb +1 -0
  67. data/lib/dynflow/director/{work_queue.rb → queue_hash.rb} +9 -8
  68. data/lib/dynflow/director/running_steps_manager.rb +55 -18
  69. data/lib/dynflow/director/sequence_cursor.rb +1 -0
  70. data/lib/dynflow/director/sequential_manager.rb +12 -2
  71. data/lib/dynflow/dispatcher.rb +4 -2
  72. data/lib/dynflow/dispatcher/abstract.rb +1 -0
  73. data/lib/dynflow/dispatcher/client_dispatcher.rb +6 -4
  74. data/lib/dynflow/dispatcher/executor_dispatcher.rb +13 -1
  75. data/lib/dynflow/errors.rb +1 -0
  76. data/lib/dynflow/execution_history.rb +1 -0
  77. data/lib/dynflow/execution_plan.rb +3 -2
  78. data/lib/dynflow/execution_plan/dependency_graph.rb +1 -0
  79. data/lib/dynflow/execution_plan/hooks.rb +1 -0
  80. data/lib/dynflow/execution_plan/output_reference.rb +2 -1
  81. data/lib/dynflow/execution_plan/steps.rb +1 -0
  82. data/lib/dynflow/execution_plan/steps/abstract.rb +10 -5
  83. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +2 -0
  84. data/lib/dynflow/execution_plan/steps/error.rb +1 -0
  85. data/lib/dynflow/execution_plan/steps/finalize_step.rb +1 -0
  86. data/lib/dynflow/execution_plan/steps/plan_step.rb +1 -0
  87. data/lib/dynflow/execution_plan/steps/run_step.rb +1 -0
  88. data/lib/dynflow/executors.rb +1 -1
  89. data/lib/dynflow/executors/abstract/core.rb +132 -0
  90. data/lib/dynflow/executors/parallel.rb +24 -11
  91. data/lib/dynflow/executors/parallel/core.rb +10 -91
  92. data/lib/dynflow/executors/parallel/pool.rb +4 -2
  93. data/lib/dynflow/executors/parallel/worker.rb +2 -1
  94. data/lib/dynflow/executors/sidekiq/core.rb +121 -0
  95. data/lib/dynflow/executors/sidekiq/internal_job_base.rb +24 -0
  96. data/lib/dynflow/executors/sidekiq/orchestrator_jobs.rb +60 -0
  97. data/lib/dynflow/executors/sidekiq/redis_locking.rb +69 -0
  98. data/lib/dynflow/executors/sidekiq/serialization.rb +33 -0
  99. data/lib/dynflow/executors/sidekiq/worker_jobs.rb +42 -0
  100. data/lib/dynflow/flows.rb +1 -0
  101. data/lib/dynflow/flows/abstract.rb +1 -0
  102. data/lib/dynflow/flows/abstract_composed.rb +1 -0
  103. data/lib/dynflow/flows/atom.rb +1 -0
  104. data/lib/dynflow/flows/concurrence.rb +1 -0
  105. data/lib/dynflow/flows/sequence.rb +1 -0
  106. data/lib/dynflow/logger_adapters.rb +1 -0
  107. data/lib/dynflow/logger_adapters/abstract.rb +1 -0
  108. data/lib/dynflow/logger_adapters/delegator.rb +1 -0
  109. data/lib/dynflow/logger_adapters/formatters.rb +1 -0
  110. data/lib/dynflow/logger_adapters/formatters/abstract.rb +1 -0
  111. data/lib/dynflow/logger_adapters/formatters/exception.rb +1 -0
  112. data/lib/dynflow/logger_adapters/simple.rb +1 -0
  113. data/lib/dynflow/middleware.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 +1 -0
  118. data/lib/dynflow/middleware/stack.rb +1 -0
  119. data/lib/dynflow/middleware/world.rb +1 -0
  120. data/lib/dynflow/persistence.rb +3 -2
  121. data/lib/dynflow/persistence_adapters.rb +1 -0
  122. data/lib/dynflow/persistence_adapters/abstract.rb +1 -0
  123. data/lib/dynflow/persistence_adapters/sequel.rb +10 -7
  124. data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +1 -0
  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 -0
  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 -0
  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 +1 -0
  142. data/lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb +48 -0
  143. data/lib/dynflow/rails.rb +1 -0
  144. data/lib/dynflow/rails/configuration.rb +6 -3
  145. data/lib/dynflow/rails/daemon.rb +1 -0
  146. data/lib/dynflow/round_robin.rb +1 -0
  147. data/lib/dynflow/semaphores.rb +1 -0
  148. data/lib/dynflow/semaphores/abstract.rb +1 -0
  149. data/lib/dynflow/semaphores/aggregating.rb +1 -0
  150. data/lib/dynflow/semaphores/dummy.rb +1 -0
  151. data/lib/dynflow/semaphores/stateful.rb +1 -0
  152. data/lib/dynflow/serializable.rb +13 -4
  153. data/lib/dynflow/serializer.rb +24 -0
  154. data/lib/dynflow/serializers.rb +1 -0
  155. data/lib/dynflow/serializers/abstract.rb +1 -0
  156. data/lib/dynflow/serializers/noop.rb +1 -0
  157. data/lib/dynflow/stateful.rb +1 -0
  158. data/lib/dynflow/telemetry.rb +1 -0
  159. data/lib/dynflow/telemetry_adapters/abstract.rb +1 -0
  160. data/lib/dynflow/telemetry_adapters/dummy.rb +1 -0
  161. data/lib/dynflow/telemetry_adapters/statsd.rb +1 -0
  162. data/lib/dynflow/testing.rb +1 -0
  163. data/lib/dynflow/testing/assertions.rb +6 -5
  164. data/lib/dynflow/testing/dummy_execution_plan.rb +1 -0
  165. data/lib/dynflow/testing/dummy_executor.rb +19 -2
  166. data/lib/dynflow/testing/dummy_planned_action.rb +1 -0
  167. data/lib/dynflow/testing/dummy_step.rb +3 -1
  168. data/lib/dynflow/testing/dummy_world.rb +9 -0
  169. data/lib/dynflow/testing/factories.rb +6 -1
  170. data/lib/dynflow/testing/in_thread_executor.rb +22 -3
  171. data/lib/dynflow/testing/in_thread_world.rb +9 -0
  172. data/lib/dynflow/testing/managed_clock.rb +1 -0
  173. data/lib/dynflow/testing/mimic.rb +1 -0
  174. data/lib/dynflow/throttle_limiter.rb +1 -0
  175. data/lib/dynflow/transaction_adapters.rb +1 -0
  176. data/lib/dynflow/transaction_adapters/abstract.rb +1 -0
  177. data/lib/dynflow/transaction_adapters/active_record.rb +1 -0
  178. data/lib/dynflow/transaction_adapters/none.rb +1 -0
  179. data/lib/dynflow/utils.rb +1 -0
  180. data/lib/dynflow/utils/indifferent_hash.rb +1 -0
  181. data/lib/dynflow/utils/priority_queue.rb +1 -0
  182. data/lib/dynflow/version.rb +2 -1
  183. data/lib/dynflow/watchers/memory_consumption_watcher.rb +1 -0
  184. data/lib/dynflow/web.rb +1 -0
  185. data/lib/dynflow/web/console.rb +1 -0
  186. data/lib/dynflow/web/console_helpers.rb +1 -0
  187. data/lib/dynflow/web/filtering_helpers.rb +1 -0
  188. data/lib/dynflow/web/world_helpers.rb +1 -0
  189. data/lib/dynflow/web_console.rb +1 -0
  190. data/lib/dynflow/world.rb +11 -1
  191. data/lib/dynflow/world/invalidation.rb +7 -1
  192. data/test/abnormal_states_recovery_test.rb +41 -40
  193. data/test/action_test.rb +160 -110
  194. data/test/activejob_adapter_test.rb +1 -0
  195. data/test/batch_sub_tasks_test.rb +12 -11
  196. data/test/clock_test.rb +2 -1
  197. data/test/concurrency_control_test.rb +20 -19
  198. data/test/coordinator_test.rb +20 -21
  199. data/test/daemon_test.rb +2 -1
  200. data/test/dead_letter_silencer_test.rb +9 -7
  201. data/test/dispatcher_test.rb +2 -1
  202. data/test/execution_plan_cleaner_test.rb +13 -12
  203. data/test/execution_plan_hooks_test.rb +3 -2
  204. data/test/execution_plan_test.rb +33 -32
  205. data/test/executor_test.rb +533 -489
  206. data/test/future_execution_test.rb +45 -44
  207. data/test/memory_cosumption_watcher_test.rb +5 -4
  208. data/test/middleware_test.rb +55 -54
  209. data/test/persistence_test.rb +56 -53
  210. data/test/rescue_test.rb +36 -35
  211. data/test/round_robin_test.rb +13 -12
  212. data/test/semaphores_test.rb +31 -30
  213. data/test/support/code_workflow_example.rb +1 -0
  214. data/test/support/dummy_example.rb +14 -1
  215. data/test/support/middleware_example.rb +2 -1
  216. data/test/support/rails/config/environment.rb +1 -0
  217. data/test/support/rescue_example.rb +1 -0
  218. data/test/support/test_execution_log.rb +1 -0
  219. data/test/test_helper.rb +18 -17
  220. data/test/testing_test.rb +45 -44
  221. data/test/utils_test.rb +18 -17
  222. data/test/web_console_test.rb +1 -0
  223. data/test/world_test.rb +7 -6
  224. metadata +13 -4
  225. data/lib/dynflow/executors/abstract.rb +0 -40
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'example_helper'
4
5
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'apipie-params'
2
3
  require 'algebrick'
3
4
  require 'thread'
@@ -17,8 +18,27 @@ end
17
18
  # TODO profiling, find bottlenecks
18
19
  # FIND change ids to uuid, uuid-<action_id>, uuid-<action_id-(plan, run, finalize)
19
20
  module Dynflow
21
+ class << self
22
+ # Return the world that representing this process - this is mainly used by
23
+ # Sidekiq deployments, where there is a need for a global-level context.
24
+ #
25
+ # @return [Dynflow::World, nil]
26
+ def process_world
27
+ return @process_world if defined? @process_world
28
+ @process_world = Sidekiq.options[:dynflow_world]
29
+ raise "process world is not set" unless @process_world
30
+ @process_world
31
+ end
32
+ end
20
33
 
21
34
  class Error < StandardError
35
+ def to_hash
36
+ { class: self.class.name, message: message, backtrace: backtrace }
37
+ end
38
+
39
+ def self.from_hash(hash)
40
+ self.new(hash[:message]).tap { |e| e.set_backtrace(hash[:backtrace]) }
41
+ end
22
42
  end
23
43
 
24
44
  require 'dynflow/utils'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  # rubocop:disable Metrics/ClassLength
3
4
  class Action < Serializable
@@ -88,6 +89,13 @@ module Dynflow
88
89
  end
89
90
  end
90
91
 
92
+ DelayedEvent = Algebrick.type do
93
+ fields! execution_plan_id: String,
94
+ step_id: Integer,
95
+ event: Object,
96
+ time: type { variants Time, NilClass }
97
+ end
98
+
91
99
  def self.constantize(action_name)
92
100
  super action_name
93
101
  rescue NameError
@@ -322,6 +330,17 @@ module Dynflow
322
330
  def queue
323
331
  end
324
332
 
333
+ # Plan an +event+ to be send to the action defined by +action+, what defaults to be self.
334
+ # if +time+ is not passed, event is sent as soon as possible.
335
+ def plan_event(event, time = nil, execution_plan_id: self.execution_plan_id, step_id: self.run_step_id)
336
+ time = @world.clock.current_time + time if time.is_a?(Numeric)
337
+ delayed_events << DelayedEvent[execution_plan_id, step_id, event, time]
338
+ end
339
+
340
+ def delayed_events
341
+ @delayed_events ||= []
342
+ end
343
+
325
344
  protected
326
345
 
327
346
  def state=(state)
@@ -333,9 +352,15 @@ module Dynflow
333
352
  @step.state = state
334
353
  end
335
354
 
336
- def save_state
355
+ def save_state(conditions = {})
337
356
  phase! Executable
338
- @step.save
357
+ # If this save returns an integer, it means it was an update. The number
358
+ # represents the number of updated records. If it is 0, then the step
359
+ # was in an unexpected state and couldn't be updated, in which case we
360
+ # raise an exception and crash hard to prevent the step from being
361
+ # executed twice
362
+ count = @step.save(conditions)
363
+ raise 'Could not save state' if count.kind_of?(Integer) && !count.positive?
339
364
  end
340
365
 
341
366
  def delay(delay_options, *args)
@@ -527,7 +552,7 @@ module Dynflow
527
552
  end
528
553
 
529
554
  self.state = :running unless self.state == :skipping
530
- save_state
555
+ save_state(:state => %w(pending error skipping suspended))
531
556
  with_error_handling do
532
557
  event = Skip if state == :skipping
533
558
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::Cancellable
3
4
  Cancel = Algebrick.atom
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
 
3
4
  # Input/output format validation logic calling
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  # for cases the serialized action was renamed and it's not available
3
4
  # in the code base anymore.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'dynflow/action/timeouts'
2
3
 
3
4
  module Dynflow
@@ -90,7 +91,8 @@ module Dynflow
90
91
  end
91
92
 
92
93
  def suspend_and_ping
93
- suspend { |suspended_action| world.clock.ping suspended_action, poll_interval, Poll }
94
+ plan_event(Poll, poll_interval)
95
+ suspend
94
96
  end
95
97
 
96
98
  def poll_external_task_with_rescue
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
 
3
4
  # Methods for specifying the progress of the action
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::Rescue
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  class Action
3
4
  module Singleton
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  class Action::Suspended
3
4
  attr_reader :execution_plan_id, :step_id
@@ -8,8 +9,14 @@ module Dynflow
8
9
  @step_id = action.run_step_id
9
10
  end
10
11
 
11
- def event(event, future = Concurrent::Promises.resolvable_future)
12
- @world.event execution_plan_id, step_id, event, future
12
+ def plan_event(event, time, sent = Concurrent::Promises.resolvable_future)
13
+ @world.plan_event(execution_plan_id, step_id, event, time, sent)
14
+ end
15
+
16
+ def event(event, sent = Concurrent::Promises.resolvable_future)
17
+ # TODO: deprecate 2 levels backtrace (to know it's called from clock or internaly)
18
+ # remove lib/dynflow/clock.rb ClockReference#ping branch condition on removal.
19
+ plan_event(event, nil, sent)
13
20
  end
14
21
 
15
22
  def <<(event = nil)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::Timeouts
3
4
  Timeout = Algebrick.atom
@@ -7,7 +8,7 @@ module Dynflow
7
8
  end
8
9
 
9
10
  def schedule_timeout(seconds)
10
- world.clock.ping suspended_action, seconds, Timeout
11
+ plan_event(Timeout, seconds)
11
12
  end
12
13
  end
13
14
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::WithBulkSubPlans
3
4
  include Dynflow::Action::Cancellable
@@ -69,7 +70,7 @@ module Dynflow
69
70
  def spawn_plans
70
71
  super
71
72
  ensure
72
- suspended_action << PlanNextBatch
73
+ plan_event(PlanNextBatch)
73
74
  end
74
75
 
75
76
  def cancel!(force = false)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::WithPollingSubPlans
3
4
 
@@ -19,7 +20,7 @@ module Dynflow
19
20
  end
20
21
 
21
22
  def initiate
22
- ping suspended_action
23
+ ping
23
24
  super
24
25
  end
25
26
 
@@ -38,7 +39,7 @@ module Dynflow
38
39
  else
39
40
  if self.is_a?(::Dynflow::Action::WithBulkSubPlans) && can_spawn_next_batch?
40
41
  # Not everything was spawned
41
- ping suspended_action
42
+ ping
42
43
  spawn_plans
43
44
  suspend
44
45
  else
@@ -52,11 +53,12 @@ module Dynflow
52
53
  end
53
54
 
54
55
  def suspend_and_ping
55
- suspend { |suspended_action| ping suspended_action }
56
+ ping
57
+ suspend
56
58
  end
57
59
 
58
- def ping(suspended_action)
59
- world.clock.ping suspended_action, REFRESH_INTERVAL, Poll
60
+ def ping(_suspended_action = nil)
61
+ plan_event(Poll, REFRESH_INTERVAL)
60
62
  end
61
63
 
62
64
  def recalculate_counts
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Action::WithSubPlans
3
4
  include Dynflow::Action::Cancellable
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module ActiveJob
3
4
  module QueueAdapters
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
 
3
4
  module MethodicActor
@@ -48,7 +49,8 @@ module Dynflow
48
49
  end
49
50
 
50
51
  class BacktraceCollector
51
- CONCURRENT_RUBY_LINE = '[ concurrent-ruby ]'.freeze
52
+ CONCURRENT_RUBY_LINE = '[ concurrent-ruby ]'
53
+ SIDEKIQ_LINE = '[ sidekiq ]'
52
54
 
53
55
  class << self
54
56
  def with_backtrace(backtrace)
@@ -69,14 +71,20 @@ module Dynflow
69
71
 
70
72
  private
71
73
 
72
- def filter_line?(line)
73
- %w[concurrent-ruby gems/logging actor.rb].any? { |pattern| line.include?(pattern) }
74
+ def filter_line(line)
75
+ if %w[concurrent-ruby gems/logging actor.rb].any? { |pattern| line.include?(pattern) }
76
+ CONCURRENT_RUBY_LINE
77
+ elsif line.include?('lib/sidekiq')
78
+ SIDEKIQ_LINE
79
+ else
80
+ line
81
+ end
74
82
  end
75
83
 
76
84
  # takes an array of backtrace lines and replaces each chunk
77
85
  def filter_backtrace(backtrace)
78
- backtrace.map { |line| filter_line?(line) ? CONCURRENT_RUBY_LINE : line }
79
- .chunk_while { |l1, l2| l1.equal?(CONCURRENT_RUBY_LINE) && l2.equal?(CONCURRENT_RUBY_LINE) }
86
+ backtrace.map { |line| filter_line(line) }
87
+ .chunk_while { |l1, l2| l1 == l2}
80
88
  .map(&:first)
81
89
  end
82
90
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Actors
3
4
  require 'dynflow/actors/execution_plan_cleaner'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  module Actors
3
4
  class ExecutionPlanCleaner
@@ -1,9 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  module Dynflow
2
3
  class Clock < Actor
3
4
 
4
5
  include Algebrick::Types
5
6
 
6
- Tick = Algebrick.atom
7
7
  Timer = Algebrick.type do
8
8
  fields! who: Object, # to ping back
9
9
  when: Time, # to deliver
@@ -40,19 +40,9 @@ module Dynflow
40
40
  end
41
41
  end
42
42
 
43
- Pills = Algebrick.type do
44
- variants None = atom,
45
- Took = atom,
46
- Pill = type { fields Float }
47
- end
48
-
49
43
  def initialize(logger = nil)
50
- @logger = logger
51
- @timers = Utils::PriorityQueue.new { |a, b| b <=> a }
52
- @sleeping_pill = None
53
- @sleep_barrier = Mutex.new
54
- @sleeper = Thread.new { sleeping }
55
- Thread.pass until @sleep_barrier.locked? || @sleeper.status == 'sleep'
44
+ @logger = logger
45
+ @timers = Utils::PriorityQueue.new { |a, b| b <=> a }
56
46
  end
57
47
 
58
48
  def default_reference_class
@@ -60,9 +50,7 @@ module Dynflow
60
50
  end
61
51
 
62
52
  def on_event(event)
63
- if event == :terminated
64
- @sleeper.kill
65
- end
53
+ wakeup if event == :terminated
66
54
  end
67
55
 
68
56
  def tick
@@ -97,52 +85,44 @@ module Dynflow
97
85
  end
98
86
 
99
87
  def wakeup
100
- while @sleep_barrier.synchronize { Pill === @sleeping_pill }
101
- Thread.pass
102
- end
103
- @sleep_barrier.synchronize do
104
- @sleeper.wakeup if Took === @sleeping_pill
88
+ if @timer
89
+ @timer.cancel
90
+ tick unless terminating?
105
91
  end
106
92
  end
107
93
 
108
94
  def sleep_to(timer)
109
95
  return unless timer
110
- sec = [timer.when - Time.now, 0.0].max
111
- @sleep_barrier.synchronize do
112
- @sleeping_pill = Pill[sec]
113
- @sleeper.wakeup
114
- end
96
+ schedule(timer.when - Time.now) { reference.tell(:tick) unless terminating? }
97
+ nil
115
98
  end
116
99
 
117
- def sleeping
118
- @sleep_barrier.synchronize do
119
- loop do
120
- @sleeping_pill = None
121
- @sleep_barrier.sleep
122
- pill = @sleeping_pill
123
- @sleeping_pill = Took
124
- @sleep_barrier.sleep pill.value
125
- reference.tell(:tick)
126
- end
127
- end
100
+ def schedule(delay, &block)
101
+ @timer = if delay.positive?
102
+ Concurrent::ScheduledTask.execute(delay, &block)
103
+ else
104
+ yield
105
+ nil
106
+ end
128
107
  end
129
108
  end
130
109
 
131
110
  class ClockReference < Concurrent::Actor::Reference
132
111
  include Algebrick::Types
133
112
 
113
+ def current_time
114
+ Time.now
115
+ end
116
+
134
117
  def ping(who, time, with_what = nil, where = :<<)
135
118
  Type! time, Time, Numeric
136
- time = Time.now + time if time.is_a? Numeric
137
- timer = Clock::Timer[who, time, with_what.nil? ? Algebrick::Types::None : Some[Object][with_what], where]
138
- # if self.ask!(:terminated?) # FIXME not thread safe
139
- # Thread.new do
140
- # sleep [timer.when - Time.now, 0].max
141
- # timer.apply
142
- # end
143
- # else
144
- self.tell([:add_timer, timer])
145
- # end
119
+ time = current_time + time if time.is_a? Numeric
120
+ if who.is_a?(Action::Suspended)
121
+ who.plan_event(with_what, time)
122
+ else
123
+ timer = Clock::Timer[who, time, with_what.nil? ? Algebrick::Types::None : Some[Object][with_what], where]
124
+ self.tell([:add_timer, timer])
125
+ end
146
126
  end
147
127
  end
148
128
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'socket'
2
3
 
3
4
  module Dynflow
@@ -97,8 +98,16 @@ module Dynflow
97
98
  5
98
99
  end
99
100
 
100
- config_attr :executor, Executors::Abstract, FalseClass do |world, config|
101
- Executors::Parallel.new(world, config.executor_heartbeat_interval, config.queues)
101
+ config_attr :executor do |world, config|
102
+ Executors::Parallel::Core
103
+ end
104
+
105
+ def validate_executor!(value)
106
+ accepted_executors = [Executors::Parallel::Core]
107
+ accepted_executors << Executors::Sidekiq::Core if defined? Executors::Sidekiq::Core
108
+ if value && !accepted_executors.include?(value)
109
+ raise ArgumentError, "Executor #{value} is expected to be one of #{accepted_executors.inspect}"
110
+ end
102
111
  end
103
112
 
104
113
  config_attr :executor_semaphore, Semaphores::Abstract, FalseClass do |world, config|