dynflow 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +16 -1
  3. data/Gemfile +13 -1
  4. data/doc/pages/source/_drafts/2015-03-01-new-documentation.markdown +10 -0
  5. data/doc/pages/source/_includes/menu.html +1 -0
  6. data/doc/pages/source/_includes/menu_right.html +1 -1
  7. data/doc/pages/source/_sass/_bootstrap-variables.sass +1 -0
  8. data/doc/pages/source/_sass/_style.scss +4 -0
  9. data/doc/pages/source/blog/index.html +12 -0
  10. data/doc/pages/source/documentation/index.md +330 -5
  11. data/dynflow.gemspec +3 -1
  12. data/examples/example_helper.rb +18 -11
  13. data/examples/orchestrate_evented.rb +2 -1
  14. data/examples/remote_executor.rb +53 -20
  15. data/lib/dynflow.rb +16 -6
  16. data/lib/dynflow/action/suspended.rb +1 -1
  17. data/lib/dynflow/action/with_sub_plans.rb +3 -6
  18. data/lib/dynflow/actor.rb +56 -0
  19. data/lib/dynflow/clock.rb +43 -38
  20. data/lib/dynflow/config.rb +107 -0
  21. data/lib/dynflow/connectors.rb +7 -0
  22. data/lib/dynflow/connectors/abstract.rb +41 -0
  23. data/lib/dynflow/connectors/database.rb +175 -0
  24. data/lib/dynflow/connectors/direct.rb +71 -0
  25. data/lib/dynflow/coordinator.rb +280 -0
  26. data/lib/dynflow/coordinator_adapters.rb +8 -0
  27. data/lib/dynflow/coordinator_adapters/abstract.rb +28 -0
  28. data/lib/dynflow/coordinator_adapters/sequel.rb +29 -0
  29. data/lib/dynflow/dispatcher.rb +58 -0
  30. data/lib/dynflow/dispatcher/abstract.rb +14 -0
  31. data/lib/dynflow/dispatcher/client_dispatcher.rb +139 -0
  32. data/lib/dynflow/dispatcher/executor_dispatcher.rb +86 -0
  33. data/lib/dynflow/errors.rb +7 -1
  34. data/lib/dynflow/execution_history.rb +46 -0
  35. data/lib/dynflow/execution_plan.rb +19 -15
  36. data/lib/dynflow/executors.rb +0 -1
  37. data/lib/dynflow/executors/abstract.rb +5 -10
  38. data/lib/dynflow/executors/parallel.rb +16 -13
  39. data/lib/dynflow/executors/parallel/core.rb +76 -78
  40. data/lib/dynflow/executors/parallel/execution_plan_manager.rb +4 -5
  41. data/lib/dynflow/executors/parallel/pool.rb +22 -52
  42. data/lib/dynflow/executors/parallel/running_steps_manager.rb +9 -2
  43. data/lib/dynflow/executors/parallel/worker.rb +5 -10
  44. data/lib/dynflow/persistence.rb +14 -0
  45. data/lib/dynflow/persistence_adapters/abstract.rb +14 -3
  46. data/lib/dynflow/persistence_adapters/sequel.rb +142 -38
  47. data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +14 -0
  48. data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +14 -0
  49. data/lib/dynflow/round_robin.rb +37 -0
  50. data/lib/dynflow/serializable.rb +1 -2
  51. data/lib/dynflow/serializer.rb +46 -0
  52. data/lib/dynflow/testing/dummy_executor.rb +2 -2
  53. data/lib/dynflow/testing/dummy_world.rb +1 -1
  54. data/lib/dynflow/transaction_adapters/abstract.rb +0 -5
  55. data/lib/dynflow/transaction_adapters/active_record.rb +0 -10
  56. data/lib/dynflow/version.rb +1 -1
  57. data/lib/dynflow/web.rb +26 -0
  58. data/lib/dynflow/web/console.rb +108 -0
  59. data/lib/dynflow/web/console_helpers.rb +158 -0
  60. data/lib/dynflow/web/filtering_helpers.rb +85 -0
  61. data/lib/dynflow/web/world_helpers.rb +9 -0
  62. data/lib/dynflow/web_console.rb +3 -310
  63. data/lib/dynflow/world.rb +188 -119
  64. data/test/abnormal_states_recovery_test.rb +152 -0
  65. data/test/action_test.rb +2 -3
  66. data/test/clock_test.rb +1 -5
  67. data/test/coordinator_test.rb +152 -0
  68. data/test/dispatcher_test.rb +146 -0
  69. data/test/execution_plan_test.rb +2 -1
  70. data/test/executor_test.rb +534 -612
  71. data/test/middleware_test.rb +4 -4
  72. data/test/persistence_test.rb +17 -0
  73. data/test/prepare_travis_env.sh +35 -0
  74. data/test/rescue_test.rb +5 -3
  75. data/test/round_robin_test.rb +28 -0
  76. data/test/support/code_workflow_example.rb +0 -73
  77. data/test/support/dummy_example.rb +130 -0
  78. data/test/support/test_execution_log.rb +41 -0
  79. data/test/test_helper.rb +222 -116
  80. data/test/testing_test.rb +10 -10
  81. data/test/web_console_test.rb +3 -3
  82. data/test/world_test.rb +23 -0
  83. data/web/assets/images/logo-square.png +0 -0
  84. data/web/assets/stylesheets/application.css +9 -0
  85. data/web/assets/vendor/bootstrap/config.json +429 -0
  86. data/web/assets/vendor/bootstrap/css/bootstrap-theme.css +479 -0
  87. data/web/assets/vendor/bootstrap/css/bootstrap-theme.min.css +10 -0
  88. data/web/assets/vendor/bootstrap/css/bootstrap.css +5377 -4980
  89. data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -8
  90. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  91. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  92. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  93. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  94. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  95. data/web/assets/vendor/bootstrap/js/bootstrap.js +1674 -1645
  96. data/web/assets/vendor/bootstrap/js/bootstrap.min.js +11 -5
  97. data/web/views/execution_history.erb +17 -0
  98. data/web/views/index.erb +4 -6
  99. data/web/views/layout.erb +44 -8
  100. data/web/views/show.erb +4 -5
  101. data/web/views/worlds.erb +26 -0
  102. metadata +116 -23
  103. checksums.yaml +0 -15
  104. data/lib/dynflow/daemon.rb +0 -30
  105. data/lib/dynflow/executors/remote_via_socket.rb +0 -43
  106. data/lib/dynflow/executors/remote_via_socket/core.rb +0 -184
  107. data/lib/dynflow/future.rb +0 -173
  108. data/lib/dynflow/listeners.rb +0 -7
  109. data/lib/dynflow/listeners/abstract.rb +0 -17
  110. data/lib/dynflow/listeners/serialization.rb +0 -77
  111. data/lib/dynflow/listeners/socket.rb +0 -117
  112. data/lib/dynflow/micro_actor.rb +0 -102
  113. data/lib/dynflow/simple_world.rb +0 -19
  114. data/test/remote_via_socket_test.rb +0 -170
  115. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +0 -1109
  116. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +0 -9
  117. data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
  118. data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
data/dynflow.gemspec CHANGED
@@ -21,7 +21,9 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency "activesupport"
22
22
  s.add_dependency "multi_json"
23
23
  s.add_dependency "apipie-params"
24
- s.add_dependency "algebrick", '~> 0.4.0'
24
+ s.add_dependency "algebrick", '~> 0.7.0'
25
+ s.add_dependency "concurrent-ruby", '~> 0.9.0.pre3'
26
+ s.add_dependency "concurrent-ruby-edge"
25
27
 
26
28
  s.add_development_dependency "rack-test"
27
29
  s.add_development_dependency "minitest"
@@ -8,9 +8,15 @@ class ExampleHelper
8
8
  @world ||= create_world
9
9
  end
10
10
 
11
- def create_world(options = {})
12
- options = default_world_options.merge(options)
13
- Dynflow::SimpleWorld.new(options)
11
+ def create_world
12
+ config = Dynflow::Config.new
13
+ config.persistence_adapter = persistence_adapter
14
+ config.logger_adapter = logger_adapter
15
+ config.auto_rescue = false
16
+ yield config if block_given?
17
+ Dynflow::World.new(config).tap do |world|
18
+ puts "World #{world.id} started..."
19
+ end
14
20
  end
15
21
 
16
22
  def persistence_conn_string
@@ -21,22 +27,17 @@ class ExampleHelper
21
27
  Dynflow::PersistenceAdapters::Sequel.new persistence_conn_string
22
28
  end
23
29
 
24
- def default_world_options
25
- { logger_adapter: logger_adapter,
26
- persistence_adapter: persistence_adapter }
27
- end
28
-
29
30
  def logger_adapter
30
31
  Dynflow::LoggerAdapters::Simple.new $stderr, 4
31
32
  end
32
33
 
33
34
 
34
35
  def run_web_console(world = ExampleHelper.world)
35
- require 'dynflow/web_console'
36
- dynflow_console = Dynflow::WebConsole.setup do
36
+ require 'dynflow/web'
37
+ dynflow_console = Dynflow::Web.setup do
37
38
  set :world, world
38
39
  end
39
- dynflow_console.run!
40
+ Rack::Server.new(:app => dynflow_console, :Port => 4567).start
40
41
  end
41
42
 
42
43
  # for simulation of the execution failing for the first time
@@ -52,5 +53,11 @@ class ExampleHelper
52
53
  def nothing_should_fail!
53
54
  @should_fail = false
54
55
  end
56
+
57
+ def terminate
58
+ @world.terminate.wait if @world
59
+ end
55
60
  end
56
61
  end
62
+
63
+ at_exit { ExampleHelper.terminate }
@@ -153,7 +153,8 @@ module OrchestrateEvented
153
153
  puts <<-MSG.gsub(/^.*\|/, '')
154
154
 
155
155
  | Execution plan #{execution_plan_id} got stuck
156
- | You can cancel the stucked step at http://localhost:4567/#{execution_plan_id}
156
+ | You can cancel the stucked step at
157
+ | http://localhost:4567/#{execution_plan_id}
157
158
 
158
159
  MSG
159
160
  # we suspend the action but don't plan the wakeup event,
@@ -2,6 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  require_relative 'example_helper'
5
+ require_relative 'orchestrate_evented'
5
6
  require 'tmpdir'
6
7
 
7
8
  class SampleAction < Dynflow::Action
@@ -19,37 +20,64 @@ end
19
20
  class RemoteExecutorExample
20
21
  class << self
21
22
 
22
- def run_server
23
- world = ExampleHelper.create_world(persistence_adapter: persistence_adapter)
24
- listener = Dynflow::Listeners::Socket.new world, socket
25
-
26
- Thread.new { Dynflow::Daemon.new(listener, world).run }
27
- ExampleHelper.run_web_console(world)
28
- ensure
29
- File.delete(db_path)
23
+ def run_observer
24
+ world = ExampleHelper.create_world do |config|
25
+ config.persistence_adapter = persistence_adapter
26
+ config.connector = connector
27
+ config.executor = false
28
+ end
29
+ run(world)
30
30
  end
31
31
 
32
- def run_client
33
- executor = ->(world) { Dynflow::Executors::RemoteViaSocket.new(world, socket) }
34
- world = ExampleHelper.create_world(persistence_adapter: persistence_adapter,
35
- executor: executor)
32
+ def run_server
33
+ world = ExampleHelper.create_world do |config|
34
+ config.persistence_adapter = persistence_adapter
35
+ config.connector = connector
36
+ end
37
+ run(world)
38
+ end
36
39
 
37
- loop do
38
- world.trigger(SampleAction).finished.wait
39
- sleep 0.5
40
+ def run(world)
41
+ begin
42
+ ExampleHelper.run_web_console(world)
43
+ rescue Errno::EADDRINUSE
44
+ require 'io/console'
45
+ puts "Running without a web console. Press q<enter> to quit."
46
+ until STDIN.gets.chomp == 'q'
47
+ end
40
48
  end
41
49
  end
42
50
 
43
- def socket
44
- File.join(Dir.tmpdir, 'dynflow_socket')
51
+ def db_path
52
+ File.expand_path("../remote_executor_db.sqlite", __FILE__)
53
+ end
54
+
55
+ def persistence_conn_string
56
+ ENV['DB_CONN_STRING'] || "sqlite://#{db_path}"
45
57
  end
46
58
 
47
59
  def persistence_adapter
48
- Dynflow::PersistenceAdapters::Sequel.new "sqlite://#{db_path}"
60
+ Dynflow::PersistenceAdapters::Sequel.new persistence_conn_string
49
61
  end
50
62
 
51
- def db_path
52
- File.expand_path("../remote_executor_db.sqlite", __FILE__)
63
+ def connector
64
+ Proc.new { |world| Dynflow::Connectors::Database.new(world) }
65
+ end
66
+
67
+ def run_client
68
+ world = ExampleHelper.create_world do |config|
69
+ config.persistence_adapter = persistence_adapter
70
+ config.executor = false
71
+ config.connector = connector
72
+ end
73
+
74
+ world.trigger(OrchestrateEvented::CreateInfrastructure)
75
+ world.trigger(OrchestrateEvented::CreateInfrastructure, true)
76
+
77
+ loop do
78
+ world.trigger(SampleAction).finished.wait
79
+ sleep 0.5
80
+ end
53
81
  end
54
82
 
55
83
  end
@@ -59,6 +87,11 @@ command = ARGV.first || 'server'
59
87
 
60
88
  if $0 == __FILE__
61
89
  case command
90
+ when 'observer'
91
+ puts <<MSG
92
+ The observer starting…. You can see what's going on there
93
+ MSG
94
+ RemoteExecutorExample.run_observer
62
95
  when 'server'
63
96
  puts <<MSG
64
97
  The server is starting…. You can send the work to it by running:
data/lib/dynflow.rb CHANGED
@@ -4,6 +4,14 @@ require 'thread'
4
4
  require 'set'
5
5
  require 'active_support/core_ext/hash/indifferent_access'
6
6
  require 'base64'
7
+ require 'concurrent'
8
+ require 'concurrent-edge'
9
+
10
+ logger = Logger.new($stderr)
11
+ logger.level = Logger::INFO
12
+ Concurrent.configuration.logger = lambda do |level, progname, message = nil, &block|
13
+ logger.add level, message, progname, &block
14
+ end
7
15
 
8
16
  # TODO validate in/output, also validate unknown keys
9
17
  # TODO performance testing, how many actions will it handle?
@@ -14,23 +22,25 @@ module Dynflow
14
22
  class Error < StandardError
15
23
  end
16
24
 
25
+ require 'dynflow/round_robin'
26
+ require 'dynflow/actor'
17
27
  require 'dynflow/errors'
18
- require 'dynflow/future'
19
- require 'dynflow/micro_actor'
28
+ require 'dynflow/serializer'
20
29
  require 'dynflow/serializable'
21
30
  require 'dynflow/clock'
22
31
  require 'dynflow/stateful'
23
32
  require 'dynflow/transaction_adapters'
33
+ require 'dynflow/coordinator'
24
34
  require 'dynflow/persistence'
25
35
  require 'dynflow/middleware'
26
36
  require 'dynflow/flows'
37
+ require 'dynflow/execution_history'
27
38
  require 'dynflow/execution_plan'
28
39
  require 'dynflow/action'
29
- require 'dynflow/listeners'
30
40
  require 'dynflow/executors'
31
41
  require 'dynflow/logger_adapters'
32
42
  require 'dynflow/world'
33
- require 'dynflow/simple_world'
34
- require 'dynflow/daemon'
35
-
43
+ require 'dynflow/connectors'
44
+ require 'dynflow/dispatcher'
45
+ require 'dynflow/config'
36
46
  end
@@ -8,7 +8,7 @@ module Dynflow
8
8
  @step_id = action.run_step_id
9
9
  end
10
10
 
11
- def event(event, future = Future.new)
11
+ def event(event, future = Concurrent.future)
12
12
  @world.event execution_plan_id, step_id, event, future
13
13
  end
14
14
 
@@ -51,9 +51,7 @@ module Dynflow
51
51
 
52
52
  # Helper for creating sub plans
53
53
  def trigger(*args)
54
- world.trigger do
55
- world.plan_with_caller(self, *args)
56
- end
54
+ world.trigger { world.plan_with_caller(self, *args) }
57
55
  end
58
56
 
59
57
  def wait_for_sub_plans(sub_plans)
@@ -102,9 +100,8 @@ module Dynflow
102
100
  def notify_on_finish(plans)
103
101
  suspend do |suspended_action|
104
102
  plans.each do |plan|
105
- plan.finished.do_then do |value|
106
- suspended_action << SubPlanFinished[plan.execution_plan_id,
107
- value.result == :success]
103
+ plan.finished.on_completion! do |success, value|
104
+ suspended_action << SubPlanFinished[plan.id, success && (value.result == :success)]
108
105
  end
109
106
  end
110
107
  end
@@ -0,0 +1,56 @@
1
+ module Dynflow
2
+
3
+ module MethodicActor
4
+ def on_message(message)
5
+ method, *args = message
6
+ self.send(method, *args)
7
+ end
8
+ end
9
+
10
+ # Common parent for all the Dynflow actors defining some defaults
11
+ # that we preffer here.
12
+ class Actor < Concurrent::Actor::Context
13
+
14
+ include MethodicActor
15
+
16
+ # Behaviour that watches for polite asking for termination
17
+ # and calls corresponding method on the context to do so
18
+ class PoliteTermination < Concurrent::Actor::Behaviour::Abstract
19
+ def on_envelope(envelope)
20
+ message, terminated_future = envelope
21
+ if :start_termination == message
22
+ context.start_termination(terminated_future)
23
+ envelope.future.success true if !envelope.future.nil?
24
+ Concurrent::Actor::Behaviour::MESSAGE_PROCESSED
25
+ else
26
+ pass envelope
27
+ end
28
+ end
29
+ end
30
+
31
+ include Algebrick::Matching
32
+
33
+ def start_termination(future)
34
+ @terminated = future
35
+ end
36
+
37
+ def finish_termination
38
+ @terminated.success(true)
39
+ reference.tell(:terminate!)
40
+ end
41
+
42
+ def terminating?
43
+ !!@terminated
44
+ end
45
+
46
+ def behaviour_definition
47
+ [*Concurrent::Actor::Behaviour.base(:just_log),
48
+ Concurrent::Actor::Behaviour::Buffer,
49
+ [Concurrent::Actor::Behaviour::SetResults, :just_log],
50
+ Concurrent::Actor::Behaviour::Awaits,
51
+ PoliteTermination,
52
+ Concurrent::Actor::Behaviour::ExecutesContext,
53
+ Concurrent::Actor::Behaviour::ErrorsOnUnknownMessage]
54
+ end
55
+ end
56
+ end
data/lib/dynflow/clock.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Dynflow
2
2
  require 'set'
3
3
 
4
- class Clock < MicroActor
4
+ class Clock < Actor
5
5
 
6
6
  include Algebrick::Types
7
7
 
@@ -40,23 +40,7 @@ module Dynflow
40
40
  Pill = type { fields Float }
41
41
  end
42
42
 
43
- def ping(who, time, with_what = nil, where = :<<)
44
- Type! time, Time, Numeric
45
- time = Time.now + time if time.is_a? Numeric
46
- timer = Timer[who, time, with_what.nil? ? Algebrick::Types::None : Some[Object][with_what], where]
47
- if terminated?
48
- Thread.new do
49
- sleep [timer.when - Time.now, 0].max
50
- timer.apply
51
- end
52
- else
53
- self << timer
54
- end
55
- end
56
-
57
- private
58
-
59
- def delayed_initialize
43
+ def initialize
60
44
  @timers = SortedSet.new
61
45
  @sleeping_pill = None
62
46
  @sleep_barrier = Mutex.new
@@ -64,27 +48,32 @@ module Dynflow
64
48
  Thread.pass until @sleep_barrier.locked? || @sleeper.status == 'sleep'
65
49
  end
66
50
 
67
- def termination
68
- @sleeper.kill
69
- super
51
+ def default_reference_class
52
+ ClockReference
70
53
  end
71
54
 
72
- def on_message(message)
73
- match message,
74
- Tick >-> do
75
- run_ready_timers
76
- sleep_to first_timer
77
- end,
78
- ~Timer >-> timer do
79
- @timers.add timer
80
- if @timers.size == 1
81
- sleep_to timer
82
- else
83
- wakeup if timer == first_timer
84
- end
85
- end
55
+ def on_event(event)
56
+ if event == :terminated
57
+ @sleeper.kill
58
+ end
86
59
  end
87
60
 
61
+ def tick
62
+ run_ready_timers
63
+ sleep_to first_timer
64
+ end
65
+
66
+ def add_timer(timer)
67
+ @timers.add timer
68
+ if @timers.size == 1
69
+ sleep_to timer
70
+ else
71
+ wakeup if timer == first_timer
72
+ end
73
+ end
74
+
75
+ private
76
+
88
77
  def run_ready_timers
89
78
  while first_timer && first_timer.when <= Time.now
90
79
  first_timer.apply
@@ -122,12 +111,28 @@ module Dynflow
122
111
  pill = @sleeping_pill
123
112
  @sleeping_pill = Took
124
113
  @sleep_barrier.sleep pill.value
125
- self << Tick
114
+ reference.tell(:tick)
126
115
  end
127
116
  end
128
117
  end
129
-
130
118
  end
131
- end
132
119
 
120
+ class ClockReference < Concurrent::Actor::Reference
121
+ include Algebrick::Types
122
+
123
+ def ping(who, time, with_what = nil, where = :<<)
124
+ Type! time, Time, Numeric
125
+ time = Time.now + time if time.is_a? Numeric
126
+ timer = Clock::Timer[who, time, with_what.nil? ? Algebrick::Types::None : Some[Object][with_what], where]
127
+ # if self.ask!(:terminated?) # FIXME not thread safe
128
+ # Thread.new do
129
+ # sleep [timer.when - Time.now, 0].max
130
+ # timer.apply
131
+ # end
132
+ # else
133
+ self.tell([:add_timer, timer])
134
+ # end
135
+ end
136
+ end
133
137
 
138
+ end
@@ -0,0 +1,107 @@
1
+ require 'socket'
2
+
3
+ module Dynflow
4
+ class Config
5
+ include Algebrick::TypeCheck
6
+
7
+ def self.config_attr(name, *types, &default)
8
+ self.send(:define_method, "validate_#{ name }!") do |value|
9
+ Type! value, *types unless types.empty?
10
+ end
11
+ self.send(:define_method, name) do
12
+ var_name = "@#{ name }"
13
+ if instance_variable_defined?(var_name)
14
+ return instance_variable_get(var_name)
15
+ else
16
+ return default
17
+ end
18
+ end
19
+ self.send(:attr_writer, name)
20
+ end
21
+
22
+ class ForWorld
23
+ attr_reader :world, :config
24
+
25
+ def initialize(config, world)
26
+ @config = config
27
+ @world = world
28
+ @cache = {}
29
+ end
30
+
31
+ def validate
32
+ @config.validate(self)
33
+ end
34
+
35
+ def method_missing(name)
36
+ return @cache[name] if @cache.key?(name)
37
+ value = @config.send(name)
38
+ value = value.call(@world, self) if value.is_a? Proc
39
+ @config.send("validate_#{ name }!", value)
40
+ @cache[name] = value
41
+ end
42
+ end
43
+
44
+ config_attr :logger_adapter, LoggerAdapters::Abstract do
45
+ LoggerAdapters::Simple.new
46
+ end
47
+
48
+ config_attr :transaction_adapter, TransactionAdapters::Abstract do
49
+ TransactionAdapters::None.new
50
+ end
51
+
52
+ config_attr :persistence_adapter, PersistenceAdapters::Abstract do
53
+ PersistenceAdapters::Sequel.new('sqlite:/')
54
+ end
55
+
56
+ config_attr :coordinator_adapter, CoordinatorAdapters::Abstract do |world|
57
+ CoordinatorAdapters::Sequel.new(world)
58
+ end
59
+
60
+ config_attr :pool_size, Fixnum do
61
+ 5
62
+ end
63
+
64
+ config_attr :executor, Executors::Abstract, FalseClass do |world, config|
65
+ Executors::Parallel.new(world, config.pool_size)
66
+ end
67
+
68
+ config_attr :connector, Connectors::Abstract do |world|
69
+ Connectors::Direct.new(world)
70
+ end
71
+
72
+ config_attr :auto_rescue, Algebrick::Types::Boolean do
73
+ false
74
+ end
75
+
76
+ config_attr :exit_on_terminate, Algebrick::Types::Boolean do
77
+ true
78
+ end
79
+
80
+ config_attr :auto_terminate, Algebrick::Types::Boolean do
81
+ true
82
+ end
83
+
84
+ config_attr :auto_execute, Algebrick::Types::Boolean do
85
+ true
86
+ end
87
+
88
+ config_attr :action_classes do
89
+ Action.all_children
90
+ end
91
+
92
+ config_attr :meta do
93
+ { 'hostname' => Socket.gethostname, 'pid' => Process.pid }
94
+ end
95
+
96
+ def validate(config_for_world)
97
+ if defined? ::ActiveRecord::Base
98
+ ar_pool_size = ::ActiveRecord::Base.connection_pool.instance_variable_get(:@size)
99
+ if (config_for_world.pool_size / 2.0) > ar_pool_size
100
+ config_for_world.world.logger.warn 'Consider increasing ActiveRecord::Base.connection_pool size, ' +
101
+ "it's #{ar_pool_size} but there is #{config_for_world.pool_size} " +
102
+ 'threads in Dynflow pool.'
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end