dynflow 0.7.9 → 0.8.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 (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