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/.gitignore CHANGED
@@ -5,3 +5,5 @@ Gemfile.lock
5
5
  .bin
6
6
  .bundle
7
7
  .idea
8
+ .ruby-version
9
+ examples/remote_executor_db.sqlite
data/.travis.yml CHANGED
@@ -4,7 +4,22 @@ language:
4
4
  rvm:
5
5
  - "1.9.3"
6
6
  - "2.0.0"
7
- - "2.1.0"
7
+ - "2.1.5"
8
+ - "2.2.0"
9
+
10
+ env:
11
+ global:
12
+ - "TESTOPTS=--verbose"
13
+ matrix:
14
+ - "DB=mysql DB_CONN_STRING=mysql2://root@localhost/travis_ci_test CONCURRENT_RUBY_EXT=true"
15
+ - "DB=postgresql DB_CONN_STRING=postgres://postgres@localhost/travis_ci_test CONCURRENT_RUBY_EXT=true"
16
+ - "DB=sqlite3 CONCURRENT_RUBY_EXT=true"
17
+ - "DB=mysql DB_CONN_STRING=mysql2://root@localhost/travis_ci_test CONCURRENT_RUBY_EXT=false"
18
+ - "DB=postgresql DB_CONN_STRING=postgres://postgres@localhost/travis_ci_test CONCURRENT_RUBY_EXT=false"
19
+ - "DB=sqlite3 CONCURRENT_RUBY_EXT=false"
20
+
21
+ install:
22
+ - test/prepare_travis_env.sh
8
23
 
9
24
  script:
10
25
  - bundle exec rake test
data/Gemfile CHANGED
@@ -2,6 +2,18 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- group :development, :test do
5
+ group :concurrent_ruby_ext do
6
+ gem 'concurrent-ruby-ext', '~> 0.9.0.pre3'
7
+ end
8
+
9
+ group :pry do
6
10
  gem 'pry'
7
11
  end
12
+
13
+ group :postgresql do
14
+ gem "pg"
15
+ end
16
+
17
+ group :mysql do
18
+ gem "mysql2"
19
+ end
@@ -0,0 +1,10 @@
1
+ ---
2
+ layout: post
3
+ title: "New documentation"
4
+ date: 2015-03-01 10:00
5
+ tags:
6
+ - documentation
7
+ ---
8
+
9
+ We have a new Dynflow documentation!
10
+
@@ -1,6 +1,7 @@
1
1
  <!--TODO make active-->
2
2
  <li><a href="/">About</a></li>
3
3
  <li><a href="/documentation/">Documentation</a></li>
4
+ <li><a href="/blog/">Blog</a></li>
4
5
  <li><a href="/faq/">FAQ</a></li>
5
6
  <li><a href="/media/">Media</a></li>
6
7
  <li><a href="/projects/">Related projects</a></li>
@@ -1 +1 @@
1
- <!--<li><a class="extra" href="/atom.xml">RSS</a></li>-->
1
+ <li><a class="extra" href="/atom.xml">RSS</a></li>
@@ -863,3 +863,4 @@ $blockquote-border-color: $gray-light
863
863
  // $dl-horizontal-offset: $component-offset-horizontal
864
864
  //** Horizontal line color.
865
865
  // $hr-border: $gray-lighter
866
+ $hr-border: $gray-light
@@ -55,6 +55,10 @@ body {
55
55
  }
56
56
  }
57
57
 
58
+ .page p img {
59
+ max-width: 100%;
60
+ }
61
+
58
62
  @media(max-width: ($screen-md - 1)) {
59
63
  .affix {
60
64
  position: static;
@@ -0,0 +1,12 @@
1
+ ---
2
+ layout: page
3
+ title: Posts
4
+ ---
5
+
6
+ {% for post in site.posts %}
7
+ {% include post_item.html %}
8
+ {% endfor %}
9
+
10
+ <p id="tag-cloud">
11
+ All tags: {{ site | tag_cloud }}
12
+ </p>
@@ -92,11 +92,13 @@ experiment).
92
92
 
93
93
  - *include executor description*
94
94
 
95
- ### Development vs production TODO
95
+ ### Development vs production
96
96
 
97
97
  - *In development execution runs in the same process, in production there is an
98
98
  executor process.*
99
99
 
100
+ *TODO*
101
+
100
102
  ### Action anatomy
101
103
 
102
104
  {% digraph %}
@@ -122,7 +124,7 @@ end
122
124
 
123
125
  Note that it does not have to be only other actions that are planned to run.
124
126
  In fact it's very common that the action plan itself, which means it will
125
- put it's own `run` method call in the execution plan. In order to do that
127
+ put its own `run` method call in the execution plan. In order to do that
126
128
  you can use `plan_self`. This could be used in MyActions::File::Destroy
127
129
  used in previous example
128
130
 
@@ -440,7 +442,7 @@ actions with DSL methods `sequence` and `concurrence`. Both methods are taking b
440
442
  and they specify how actions planned inside the block
441
443
  (or inner `sequence` and `concurrence` blocks) should be executed.
442
444
 
443
- By default `plan` considers it's space as inside `concurrence`. Which means
445
+ By default `plan` considers its space as inside `concurrence`. Which means
444
446
 
445
447
  ```ruby
446
448
  def plan
@@ -943,19 +945,342 @@ sorted down with
943
945
  [topological sort](http://ruby-doc.org//stdlib-2.0/libdoc/tsort/rdoc/TSort.html)
944
946
  to the chain of middleware execution.
945
947
 
946
- ### SubTasks TODO
948
+ ### Sub-plans
947
949
 
948
950
  - *when to use?*
949
951
  - *how to use?*
950
952
 
951
953
  ## How it works TODO
952
954
 
955
+ {% info_block %}
956
+ This part is based on the current work-in-progress on [multi-executors
957
+ support](https://github.com/Dynflow/dynflow/pull/139)
958
+ {% endinfo_block %}
959
+
953
960
  ### Action states TODO
954
961
 
955
962
  - *normal phases and Present phase*
956
963
  - *how to walk the execution plan*
957
964
 
958
- ### Inner-world communication and multi-executors TODO
965
+ ### The world anatomy
966
+
967
+ The world represents the Dynflow's run-time and it acts as an external
968
+ interface. It holds all the configuration and sub-components needed for the
969
+ Dynflow to perform its job.
970
+
971
+ The Dynflow worlds is composed of the following sub-components:
972
+
973
+ 1. **persistence** - provides the durability functionality: see
974
+ [persistence](#persistence)
975
+ 1. **coordinator** - provides the coordination between worlds: see [coordinator](#coordinator)
976
+ 1. **connector** - provides messages passing between worlds: see [connector](#connector)
977
+ 1. **executor** - the run-time itself executing the execution plan. Not
978
+ every worlds has to have the executor present (there might be pure
979
+ client worlds: useful in production, see [develpment vs. production](#development-vs-production).
980
+ 1. **client dispatcher** - responsible for communication between
981
+ client requests and other worlds
982
+ 1. **executor dispatcher** - responsible for getting requests from
983
+ other worlds and sending the responses
984
+
985
+ {% plantuml %}
986
+
987
+ actor client
988
+
989
+ frame "world" {
990
+ interface adapter as PersistenceAdapter
991
+ interface adapter as CoordinatorAdapter
992
+ interface adapter as ConnectorAdapter
993
+ [coordinator] -up-> CoordinatorAdapter
994
+ [connector] -up-> ConnectorAdapter
995
+ [persistence] -up-> PersistenceAdapter
996
+ [connector] <-- [client dispatcher]
997
+ [connector] <-- [executor dispatcher]
998
+ [executor] <-- [executor dispatcher]
999
+ [coordinator] <-- [client dispatcher]
1000
+ [coordinator] <-- [executor dispatcher]
1001
+ [persistence] <-- [executor]
1002
+ client -left--> [client dispatcher]
1003
+ client -left--> [persistence]
1004
+ }
1005
+
1006
+ {% endplantuml %}
1007
+
1008
+ The underlying technologies are hidden behind adapters abstraction,
1009
+ which allows choosing the right technology for the job, while keeping
1010
+ the rest of the Dynflow intact.
1011
+
1012
+ ### Client world vs. executor world
1013
+
1014
+ In the simplest case, the world handles both the client requests and
1015
+ the execution itself. This is useful for small all-in-one deployments
1016
+ and development.
1017
+
1018
+ In production, however, one might want to separate
1019
+ the client worlds (often running in the web-server process or client
1020
+ library) and the executor worlds (running as part of standalone
1021
+ service). This setup makes the execution more stable and
1022
+ high-available (in active-active mode).
1023
+
1024
+ There might be multiple client as well as executor worlds in the
1025
+ Dynflow infrastructure.
1026
+
1027
+ The executor world has still its own client dispatcher, so that it
1028
+ can act as a client for triggering other execution plans (useful in
1029
+ [sub-plans](#sub-plans) feature).
1030
+
1031
+ {% plantuml %}
1032
+
1033
+ actor client
1034
+
1035
+ frame "client world" {
1036
+ interface adapter as ClientPersistenceAdapter
1037
+ interface adapter as ClientCoordinatorAdapter
1038
+ interface adapter as ClientConnectorAdapter
1039
+ component [coordinator] as ClientCoordinator
1040
+ component [persistence] as ClientPersistence
1041
+ component [connector] as ClientConnector
1042
+ component [client dispatcher] as ClientClientDispatcher
1043
+ [ClientCoordinator] -down-> ClientCoordinatorAdapter
1044
+ [ClientConnector] -down-> ClientConnectorAdapter
1045
+ [ClientPersistence] -down--> ClientPersistenceAdapter
1046
+ [ClientClientDispatcher] --> [ClientConnector]
1047
+ [ClientClientDispatcher] --> [ClientCoordinator]
1048
+ client -down--> [ClientClientDispatcher]
1049
+ client -down--> [ClientPersistence]
1050
+ }
1051
+
1052
+ frame "executor world" {
1053
+ interface adapter as PersistenceAdapter
1054
+ interface adapter as CoordinatorAdapter
1055
+ interface adapter as ConnectorAdapter
1056
+ [coordinator] -up--> CoordinatorAdapter
1057
+ [connector] -up--> ConnectorAdapter
1058
+ [persistence] -up-> PersistenceAdapter
1059
+ [connector] <-- [client dispatcher]
1060
+ [connector] <-- [executor dispatcher]
1061
+ [executor] <-- [executor dispatcher]
1062
+ [coordinator] <-- [client dispatcher]
1063
+ [coordinator] <-- [executor dispatcher]
1064
+ [persistence] <-- [executor]
1065
+ }
1066
+
1067
+ database "Database" as Database
1068
+ ClientPersistenceAdapter -down- Database
1069
+ Database -down- PersistenceAdapter
1070
+
1071
+ node "Synchronization\nservice" as SyncService
1072
+ ClientCoordinatorAdapter -down- SyncService
1073
+ SyncService -down- CoordinatorAdapter
1074
+
1075
+ node "Message\nbus" as MessageBus
1076
+ ClientConnectorAdapter -down- MessageBus
1077
+ MessageBus -down- ConnectorAdapter
1078
+
1079
+ {% endplantuml %}
1080
+
1081
+ ### Single-database model
1082
+
1083
+ Dynflow recognizes different expectations from the underlying
1084
+ technologies from the persistence (durability), coordinator (real-time
1085
+ synchronization) and connector (transport).
1086
+
1087
+ However, we also realize that for many use-cases, a single shared
1088
+ database is just enough infrastructure the user needs for the job.
1089
+ Forcing using different technologies would mean just useless overhead.
1090
+
1091
+ Therefore, it's possible to use a single shared SQL database to do
1092
+ all the work.
1093
+
1094
+ {% plantuml %}
1095
+
1096
+ actor client
1097
+
1098
+ frame "client world" {
1099
+ interface adapter as ClientPersistenceAdapter
1100
+ interface adapter as ClientCoordinatorAdapter
1101
+ interface adapter as ClientConnectorAdapter
1102
+ component [coordinator] as ClientCoordinator
1103
+ component [persistence] as ClientPersistence
1104
+ component [connector] as ClientConnector
1105
+ component [client dispatcher] as ClientClientDispatcher
1106
+ [ClientCoordinator] -down-> ClientCoordinatorAdapter
1107
+ [ClientConnector] -down-> ClientConnectorAdapter
1108
+ [ClientPersistence] -down--> ClientPersistenceAdapter
1109
+ [ClientClientDispatcher] --> [ClientConnector]
1110
+ [ClientClientDispatcher] --> [ClientCoordinator]
1111
+ client -down--> [ClientClientDispatcher]
1112
+ client -down--> [ClientPersistence]
1113
+ }
1114
+
1115
+ frame "executor world" {
1116
+ interface adapter as PersistenceAdapter
1117
+ interface adapter as CoordinatorAdapter
1118
+ interface adapter as ConnectorAdapter
1119
+ [coordinator] -up--> CoordinatorAdapter
1120
+ [connector] -up--> ConnectorAdapter
1121
+ [persistence] -up-> PersistenceAdapter
1122
+ [connector] <-- [client dispatcher]
1123
+ [connector] <-- [executor dispatcher]
1124
+ [executor] <-- [executor dispatcher]
1125
+ [coordinator] <-- [client dispatcher]
1126
+ [coordinator] <-- [executor dispatcher]
1127
+ [persistence] <-- [executor]
1128
+ }
1129
+
1130
+ database "Database" as Database
1131
+ ClientPersistenceAdapter -down- Database
1132
+ Database -down- PersistenceAdapter
1133
+
1134
+ ClientCoordinatorAdapter -down- Database
1135
+ Database -down- CoordinatorAdapter
1136
+
1137
+ ClientConnectorAdapter -down- Database
1138
+ Database -down- ConnectorAdapter
1139
+
1140
+ {% endplantuml %}
1141
+
1142
+ {% info_block %}
1143
+
1144
+ Something as simple as sqlite is enough for getting the Dynlfow
1145
+ up-and-running and getting something done.
1146
+
1147
+ For the best connector results, it's recommended to use PostgreSQL, as
1148
+ Dynflow can utilize the
1149
+ [listen/notify](http://www.postgresql.org/docs/9.0/static/sql-notify.html)
1150
+ feature for better response times.
1151
+
1152
+ {% endinfo_block %}
1153
+
1154
+ ### Inner-world communication
1155
+
1156
+ {% plantuml %}
1157
+ participant "Connector" as ClientConnector
1158
+ participant "Connector" as ExecutorConnector
1159
+
1160
+ box "Client World"
1161
+ participant Client
1162
+ participant "Client Dispatcher"
1163
+ participant "ClientConnector"
1164
+ end box
1165
+
1166
+ box "Executor World"
1167
+ participant "ExecutorConnector"
1168
+ participant "Executor Dispatcher"
1169
+ participant Executor
1170
+ end box
1171
+
1172
+ autonumber
1173
+ Client -> "Client Dispatcher" : publish_request\nexecution_plan_id
1174
+ "Client Dispatcher" --> Client : IVar
1175
+ "Client Dispatcher" -> "ClientConnector" : send\nEnvelope[Execution]
1176
+ "ClientConnector" -> "ExecutorConnector" : handle_envelope\nEnvelope[Execution]
1177
+ "ExecutorConnector" -> "Executor Dispatcher" : handle_request\nEnvelope[Execution]
1178
+ "Executor Dispatcher" -> Executor : execute\nexecution_plan_id
1179
+ note over Executor: executing…
1180
+ activate Executor
1181
+ "Executor Dispatcher" -> ExecutorConnector : send\nEnvelope[Accepted]
1182
+ "ExecutorConnector" -> "ClientConnector" : handle_envelope\nEnvelope[Accepted]
1183
+ "ClientConnector" -> "Client Dispatcher" : dispatch_response\nEnvelope[Accepted]
1184
+ Executor -> "Executor Dispatcher" : finished
1185
+ deactivate Executor
1186
+ "Executor Dispatcher" -> ExecutorConnector : send\nEnvelope[Finished]
1187
+ "ExecutorConnector" -> "ClientConnector" : handle_envelope\nEnvelope[Finished]
1188
+ "ClientConnector" -> "Client Dispatcher" : dispatch_response\nEnvelope[Finished]
1189
+ note over "Client Dispatcher": IVar fullfilled
1190
+ {% endplantuml %}
1191
+
1192
+ 1) the client prepares an execution plan, saves it into persistence
1193
+ and passes its id to the client dispatcher
1194
+
1195
+ 2) the client dispatcher creates an
1196
+ [IVar](http://www.rubydoc.info/github/ruby-concurrency/concurrent-ruby/Concurrent/IVar)
1197
+ that represents the future value of the execution plan after it
1198
+ finishes execution. The client can use this value to wait for the
1199
+ execution plan to finish or hook other procedures on the finish-time.
1200
+
1201
+ 3) the client dispatcher creates an envelope (see
1202
+ [connector](#connector) for more details), containing the request for
1203
+ execution, with ``receiver id`` set to ``AnyExecutor``
1204
+
1205
+ 4) the connector uses some scheduling algorithm to choose what executor to send the request to and
1206
+ replaces the ``AnyExecutor`` with it. It sends the envelope the the
1207
+ chosen executor.
1208
+
1209
+ 5) the connector on the executor side receives the envelope and asks
1210
+ the executor dispatcher to handle it
1211
+
1212
+ 6) the executor dispatcher acquires the lock on the execution plan and
1213
+ initiates the execution. In the mean-time, it lets the client know
1214
+ that the work was accepted (there might be additional logic on the
1215
+ client to handle a timeout after the work was not accepted)
1216
+
1217
+ 7 - 9) the connector layer propagates the ``Accepted`` response back
1218
+ to client
1219
+
1220
+ 10) the executor finishes execution
1221
+
1222
+ 11 - 12) the connector layer propagates the ``Finished`` response
1223
+
1224
+ 13) the client dispatcher resolves the original ``IVar``, so that the
1225
+ client is able to find out about the finished execution.
1226
+
1227
+ The behavior is the same even in the "one world" scenario: in that
1228
+ case this world is participating both on the client and the executor
1229
+ side, connected through a direct in-memory connector.
1230
+
1231
+ ### Persistence
1232
+
1233
+ The persistence making sure that the serialized states of the
1234
+ execution plans are persisted for recovery and status tracking. The
1235
+ execution plan data are stored in it, with the actual state.
1236
+
1237
+ Unlike coordinator, the all the persisted data don't have to be
1238
+ available for all the worlds at the same time: every world needs just
1239
+ the data that it is actively working on. Also, all the data don't have to
1240
+ be fully synchronized between worlds (as long as the up-to-date data
1241
+ about relevant execution plans are available for the world).
1242
+
1243
+ ### Connector
1244
+
1245
+ Provides messages passing between worlds. The message has a form of
1246
+ envelope of the following structure:
1247
+
1248
+ * **sender id** - the id of the world that produced the envelope
1249
+ * **receiver id** - the id of the world that should receive the
1250
+ message, or ``AnyExecutor``, when load-balancing
1251
+ * **request id** - the client-unique id used for pairing the
1252
+ request - response at the client
1253
+ * **message** - the body of the message: the connector doesn't care
1254
+ about that as long as it's serializable
1255
+
1256
+ #### Load-balancing
1257
+
1258
+ The connector is responsible for spreading the load across the
1259
+ executor worlds. It's determined by the ``AnyExecutor`` value at the
1260
+ ``request id`` field. The implementation available in the
1261
+ current version uses a simple round-robin algorithm for this purpose.
1262
+
1263
+ ### Coordinator
1264
+
1265
+ This component (especially important in a multi-executor setup): Makes
1266
+ sure no two executors are executing the same execution plan at the
1267
+ same time (a.k.a locking). It also provides the information
1268
+ about the worlds available in the system (the worlds register). Unlike
1269
+ the persistence, it's not required to persist the data (could be
1270
+ recalculated), but it needs to provide a globally shared state.
1271
+
1272
+ The main type of objects the coordinator works with is a record which
1273
+ consists of:
1274
+
1275
+ * type - the type of the record
1276
+ * id - the id of the record (unique in scope of a type)
1277
+ * data - data in arbitrary format, based on the type
1278
+
1279
+ There is a special type of record called ``lock``, that keeps the owner
1280
+ information as well. It's used for keeping information about what
1281
+ executor is actively working on what execution plan: the executor is
1282
+ not allowed to start executing the unless it has successfully acquired
1283
+ a lock for it.
959
1284
 
960
1285
  ### Thread-pools TODO
961
1286