dynflow 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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|