dynflow 0.7.9 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +16 -1
- data/Gemfile +13 -1
- data/doc/pages/source/_drafts/2015-03-01-new-documentation.markdown +10 -0
- data/doc/pages/source/_includes/menu.html +1 -0
- data/doc/pages/source/_includes/menu_right.html +1 -1
- data/doc/pages/source/_sass/_bootstrap-variables.sass +1 -0
- data/doc/pages/source/_sass/_style.scss +4 -0
- data/doc/pages/source/blog/index.html +12 -0
- data/doc/pages/source/documentation/index.md +330 -5
- data/dynflow.gemspec +3 -1
- data/examples/example_helper.rb +18 -11
- data/examples/orchestrate_evented.rb +2 -1
- data/examples/remote_executor.rb +53 -20
- data/lib/dynflow.rb +16 -6
- data/lib/dynflow/action/suspended.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +3 -6
- data/lib/dynflow/actor.rb +56 -0
- data/lib/dynflow/clock.rb +43 -38
- data/lib/dynflow/config.rb +107 -0
- data/lib/dynflow/connectors.rb +7 -0
- data/lib/dynflow/connectors/abstract.rb +41 -0
- data/lib/dynflow/connectors/database.rb +175 -0
- data/lib/dynflow/connectors/direct.rb +71 -0
- data/lib/dynflow/coordinator.rb +280 -0
- data/lib/dynflow/coordinator_adapters.rb +8 -0
- data/lib/dynflow/coordinator_adapters/abstract.rb +28 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +29 -0
- data/lib/dynflow/dispatcher.rb +58 -0
- data/lib/dynflow/dispatcher/abstract.rb +14 -0
- data/lib/dynflow/dispatcher/client_dispatcher.rb +139 -0
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +86 -0
- data/lib/dynflow/errors.rb +7 -1
- data/lib/dynflow/execution_history.rb +46 -0
- data/lib/dynflow/execution_plan.rb +19 -15
- data/lib/dynflow/executors.rb +0 -1
- data/lib/dynflow/executors/abstract.rb +5 -10
- data/lib/dynflow/executors/parallel.rb +16 -13
- data/lib/dynflow/executors/parallel/core.rb +76 -78
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +4 -5
- data/lib/dynflow/executors/parallel/pool.rb +22 -52
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +9 -2
- data/lib/dynflow/executors/parallel/worker.rb +5 -10
- data/lib/dynflow/persistence.rb +14 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +14 -3
- data/lib/dynflow/persistence_adapters/sequel.rb +142 -38
- data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +14 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +14 -0
- data/lib/dynflow/round_robin.rb +37 -0
- data/lib/dynflow/serializable.rb +1 -2
- data/lib/dynflow/serializer.rb +46 -0
- data/lib/dynflow/testing/dummy_executor.rb +2 -2
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/transaction_adapters/abstract.rb +0 -5
- data/lib/dynflow/transaction_adapters/active_record.rb +0 -10
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web.rb +26 -0
- data/lib/dynflow/web/console.rb +108 -0
- data/lib/dynflow/web/console_helpers.rb +158 -0
- data/lib/dynflow/web/filtering_helpers.rb +85 -0
- data/lib/dynflow/web/world_helpers.rb +9 -0
- data/lib/dynflow/web_console.rb +3 -310
- data/lib/dynflow/world.rb +188 -119
- data/test/abnormal_states_recovery_test.rb +152 -0
- data/test/action_test.rb +2 -3
- data/test/clock_test.rb +1 -5
- data/test/coordinator_test.rb +152 -0
- data/test/dispatcher_test.rb +146 -0
- data/test/execution_plan_test.rb +2 -1
- data/test/executor_test.rb +534 -612
- data/test/middleware_test.rb +4 -4
- data/test/persistence_test.rb +17 -0
- data/test/prepare_travis_env.sh +35 -0
- data/test/rescue_test.rb +5 -3
- data/test/round_robin_test.rb +28 -0
- data/test/support/code_workflow_example.rb +0 -73
- data/test/support/dummy_example.rb +130 -0
- data/test/support/test_execution_log.rb +41 -0
- data/test/test_helper.rb +222 -116
- data/test/testing_test.rb +10 -10
- data/test/web_console_test.rb +3 -3
- data/test/world_test.rb +23 -0
- data/web/assets/images/logo-square.png +0 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/assets/vendor/bootstrap/config.json +429 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.css +479 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.min.css +10 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +5377 -4980
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -8
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +1674 -1645
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +11 -5
- data/web/views/execution_history.erb +17 -0
- data/web/views/index.erb +4 -6
- data/web/views/layout.erb +44 -8
- data/web/views/show.erb +4 -5
- data/web/views/worlds.erb +26 -0
- metadata +116 -23
- checksums.yaml +0 -15
- data/lib/dynflow/daemon.rb +0 -30
- data/lib/dynflow/executors/remote_via_socket.rb +0 -43
- data/lib/dynflow/executors/remote_via_socket/core.rb +0 -184
- data/lib/dynflow/future.rb +0 -173
- data/lib/dynflow/listeners.rb +0 -7
- data/lib/dynflow/listeners/abstract.rb +0 -17
- data/lib/dynflow/listeners/serialization.rb +0 -77
- data/lib/dynflow/listeners/socket.rb +0 -117
- data/lib/dynflow/micro_actor.rb +0 -102
- data/lib/dynflow/simple_world.rb +0 -19
- data/test/remote_via_socket_test.rb +0 -170
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +0 -1109
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +0 -9
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
data/.gitignore
CHANGED
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.
|
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 :
|
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
|
@@ -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
|
-
|
1
|
+
<li><a class="extra" href="/atom.xml">RSS</a></li>
|
@@ -92,11 +92,13 @@ experiment).
|
|
92
92
|
|
93
93
|
- *include executor description*
|
94
94
|
|
95
|
-
### Development vs production
|
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
|
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
|
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
|
-
###
|
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
|
-
###
|
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
|
|