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.
- 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
|
|