temporalio 0.0.0 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (327) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +301 -0
  3. data/bridge/Cargo.lock +2888 -0
  4. data/bridge/Cargo.toml +27 -0
  5. data/bridge/sdk-core/ARCHITECTURE.md +76 -0
  6. data/bridge/sdk-core/Cargo.lock +2606 -0
  7. data/bridge/sdk-core/Cargo.toml +2 -0
  8. data/bridge/sdk-core/LICENSE.txt +23 -0
  9. data/bridge/sdk-core/README.md +104 -0
  10. data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
  11. data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
  12. data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  13. data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
  14. data/bridge/sdk-core/client/Cargo.toml +40 -0
  15. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  16. data/bridge/sdk-core/client/src/lib.rs +1286 -0
  17. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  18. data/bridge/sdk-core/client/src/raw.rs +932 -0
  19. data/bridge/sdk-core/client/src/retry.rs +751 -0
  20. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  21. data/bridge/sdk-core/core/Cargo.toml +116 -0
  22. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  23. data/bridge/sdk-core/core/benches/workflow_replay.rs +76 -0
  24. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  25. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +1014 -0
  26. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  27. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  28. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +925 -0
  29. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  30. data/bridge/sdk-core/core/src/core_tests/queries.rs +894 -0
  31. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  32. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  33. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  34. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2090 -0
  35. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  36. data/bridge/sdk-core/core/src/lib.rs +282 -0
  37. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  38. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  39. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  40. data/bridge/sdk-core/core/src/replay/mod.rs +215 -0
  41. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  42. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  43. data/bridge/sdk-core/core/src/telemetry/metrics.rs +428 -0
  44. data/bridge/sdk-core/core/src/telemetry/mod.rs +407 -0
  45. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +78 -0
  46. data/bridge/sdk-core/core/src/test_help/mod.rs +889 -0
  47. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  48. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1048 -0
  49. data/bridge/sdk-core/core/src/worker/activities.rs +481 -0
  50. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  51. data/bridge/sdk-core/core/src/worker/client.rs +373 -0
  52. data/bridge/sdk-core/core/src/worker/mod.rs +570 -0
  53. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  54. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +101 -0
  55. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +532 -0
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +907 -0
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +294 -0
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +167 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +858 -0
  60. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +136 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +157 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +129 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1450 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +316 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +708 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +439 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +435 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +175 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +242 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1200 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +272 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +655 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1200 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +985 -0
  80. data/bridge/sdk-core/core-api/Cargo.toml +32 -0
  81. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  82. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  83. data/bridge/sdk-core/core-api/src/lib.rs +109 -0
  84. data/bridge/sdk-core/core-api/src/telemetry.rs +147 -0
  85. data/bridge/sdk-core/core-api/src/worker.rs +148 -0
  86. data/bridge/sdk-core/etc/deps.svg +162 -0
  87. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  88. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  89. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  90. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  91. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  92. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  93. data/bridge/sdk-core/fsm/README.md +3 -0
  94. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  95. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  96. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  97. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  98. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  99. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  100. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  115. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  116. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  117. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  118. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  119. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  120. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  121. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  122. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  123. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  124. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  125. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  126. data/bridge/sdk-core/protos/api_upstream/buf.yaml +9 -0
  127. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  128. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  129. data/bridge/sdk-core/protos/api_upstream/build/tools.go +29 -0
  130. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  131. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  132. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +89 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +260 -0
  134. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
  135. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +47 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +56 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +170 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +118 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/interaction_type.proto +39 -0
  141. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
  142. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
  143. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
  144. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  145. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
  146. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +40 -0
  147. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
  148. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
  149. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
  150. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
  151. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +758 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +121 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +80 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +379 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  160. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
  161. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +146 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1168 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +415 -0
  164. data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  165. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  166. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
  167. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +263 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +304 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  174. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  175. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  176. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  179. data/bridge/sdk-core/rustfmt.toml +1 -0
  180. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  181. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  182. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  183. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  184. data/bridge/sdk-core/sdk/src/interceptors.rs +50 -0
  185. data/bridge/sdk-core/sdk/src/lib.rs +794 -0
  186. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  187. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  188. data/bridge/sdk-core/sdk/src/workflow_context.rs +694 -0
  189. data/bridge/sdk-core/sdk/src/workflow_future.rs +499 -0
  190. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  191. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  192. data/bridge/sdk-core/sdk-core-protos/build.rs +107 -0
  193. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  194. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +544 -0
  195. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  196. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1970 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  199. data/bridge/sdk-core/test-utils/Cargo.toml +36 -0
  200. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  201. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  202. data/bridge/sdk-core/test-utils/src/lib.rs +650 -0
  203. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  204. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  205. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +221 -0
  206. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  207. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +133 -0
  208. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  209. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  210. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  211. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  212. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +788 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  219. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
  220. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +223 -0
  221. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
  222. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
  223. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
  224. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
  225. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
  226. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +597 -0
  227. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  228. data/bridge/sdk-core/tests/main.rs +113 -0
  229. data/bridge/sdk-core/tests/runner.rs +93 -0
  230. data/bridge/src/connection.rs +186 -0
  231. data/bridge/src/lib.rs +239 -0
  232. data/bridge/src/runtime.rs +54 -0
  233. data/bridge/src/worker.rs +124 -0
  234. data/ext/Rakefile +9 -0
  235. data/lib/bridge.so +0 -0
  236. data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
  237. data/lib/gen/temporal/api/batch/v1/message_pb.rb +50 -0
  238. data/lib/gen/temporal/api/command/v1/message_pb.rb +174 -0
  239. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  240. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +33 -0
  241. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +39 -0
  242. data/lib/gen/temporal/api/enums/v1/common_pb.rb +42 -0
  243. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +68 -0
  244. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +77 -0
  245. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +25 -0
  246. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  247. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  248. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  249. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  250. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  251. data/lib/gen/temporal/api/enums/v1/update_pb.rb +23 -0
  252. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  253. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  254. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  255. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  256. data/lib/gen/temporal/api/history/v1/message_pb.rb +490 -0
  257. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +49 -0
  258. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
  259. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +85 -0
  260. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
  261. data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
  262. data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
  263. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +149 -0
  264. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  265. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  266. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +111 -0
  267. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +788 -0
  268. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  269. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  270. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  271. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  272. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  273. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  274. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  275. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  276. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +165 -0
  277. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +196 -0
  278. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  279. data/lib/temporalio/activity/context.rb +97 -0
  280. data/lib/temporalio/activity/info.rb +67 -0
  281. data/lib/temporalio/activity.rb +85 -0
  282. data/lib/temporalio/bridge/error.rb +8 -0
  283. data/lib/temporalio/bridge.rb +14 -0
  284. data/lib/temporalio/client/implementation.rb +340 -0
  285. data/lib/temporalio/client/workflow_handle.rb +243 -0
  286. data/lib/temporalio/client.rb +131 -0
  287. data/lib/temporalio/connection.rb +751 -0
  288. data/lib/temporalio/data_converter.rb +191 -0
  289. data/lib/temporalio/error/failure.rb +194 -0
  290. data/lib/temporalio/error/workflow_failure.rb +19 -0
  291. data/lib/temporalio/errors.rb +40 -0
  292. data/lib/temporalio/failure_converter/base.rb +26 -0
  293. data/lib/temporalio/failure_converter/basic.rb +319 -0
  294. data/lib/temporalio/failure_converter.rb +7 -0
  295. data/lib/temporalio/interceptor/chain.rb +28 -0
  296. data/lib/temporalio/interceptor/client.rb +123 -0
  297. data/lib/temporalio/payload_codec/base.rb +32 -0
  298. data/lib/temporalio/payload_converter/base.rb +24 -0
  299. data/lib/temporalio/payload_converter/bytes.rb +27 -0
  300. data/lib/temporalio/payload_converter/composite.rb +49 -0
  301. data/lib/temporalio/payload_converter/encoding_base.rb +35 -0
  302. data/lib/temporalio/payload_converter/json.rb +26 -0
  303. data/lib/temporalio/payload_converter/nil.rb +26 -0
  304. data/lib/temporalio/payload_converter.rb +14 -0
  305. data/lib/temporalio/retry_policy.rb +82 -0
  306. data/lib/temporalio/retry_state.rb +35 -0
  307. data/lib/temporalio/runtime.rb +25 -0
  308. data/lib/temporalio/timeout_type.rb +29 -0
  309. data/lib/temporalio/version.rb +3 -0
  310. data/lib/temporalio/worker/activity_runner.rb +92 -0
  311. data/lib/temporalio/worker/activity_worker.rb +138 -0
  312. data/lib/temporalio/worker/reactor.rb +46 -0
  313. data/lib/temporalio/worker/runner.rb +63 -0
  314. data/lib/temporalio/worker/sync_worker.rb +88 -0
  315. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  316. data/lib/temporalio/worker.rb +198 -0
  317. data/lib/temporalio/workflow/execution_info.rb +54 -0
  318. data/lib/temporalio/workflow/execution_status.rb +36 -0
  319. data/lib/temporalio/workflow/id_reuse_policy.rb +36 -0
  320. data/lib/temporalio/workflow/query_reject_condition.rb +33 -0
  321. data/lib/temporalio.rb +12 -1
  322. data/lib/thermite_patch.rb +23 -0
  323. data/temporalio.gemspec +45 -0
  324. metadata +566 -9
  325. data/lib/temporal/version.rb +0 -3
  326. data/lib/temporal.rb +0 -4
  327. data/temporal.gemspec +0 -20
@@ -0,0 +1,1200 @@
1
+ mod local_acts;
2
+
3
+ pub(crate) use temporal_sdk_core_api::errors::WFMachinesError;
4
+
5
+ use super::{
6
+ activity_state_machine::new_activity, cancel_external_state_machine::new_external_cancel,
7
+ cancel_workflow_state_machine::cancel_workflow,
8
+ child_workflow_state_machine::new_child_workflow,
9
+ complete_workflow_state_machine::complete_workflow,
10
+ continue_as_new_workflow_state_machine::continue_as_new,
11
+ fail_workflow_state_machine::fail_workflow, local_activity_state_machine::new_local_activity,
12
+ patch_state_machine::has_change, signal_external_state_machine::new_external_signal,
13
+ timer_state_machine::new_timer, upsert_search_attributes_state_machine::upsert_search_attrs,
14
+ workflow_machines::local_acts::LocalActivityData,
15
+ workflow_task_state_machine::WorkflowTaskMachine, Machines, NewMachineWithCommand,
16
+ TemporalStateMachine,
17
+ };
18
+ use crate::{
19
+ protosext::{HistoryEventExt, ValidScheduleLA},
20
+ telemetry::{metrics::MetricsContext, VecDisplayer},
21
+ worker::{
22
+ workflow::{
23
+ machines::modify_workflow_properties_state_machine::modify_workflow_properties,
24
+ CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, OutgoingJob, WFCommand,
25
+ WorkflowFetcher, WorkflowStartedInfo,
26
+ },
27
+ ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
28
+ },
29
+ };
30
+ use siphasher::sip::SipHasher13;
31
+ use slotmap::{SlotMap, SparseSecondaryMap};
32
+ use std::{
33
+ borrow::{Borrow, BorrowMut},
34
+ collections::{HashMap, VecDeque},
35
+ convert::TryInto,
36
+ hash::{Hash, Hasher},
37
+ time::{Duration, Instant, SystemTime},
38
+ };
39
+ use temporal_sdk_core_protos::{
40
+ coresdk::{
41
+ common::NamespacedWorkflowExecution,
42
+ workflow_activation,
43
+ workflow_activation::{
44
+ workflow_activation_job, NotifyHasPatch, UpdateRandomSeed, WorkflowActivation,
45
+ },
46
+ workflow_commands::{
47
+ request_cancel_external_workflow_execution as cancel_we, ContinueAsNewWorkflowExecution,
48
+ },
49
+ },
50
+ temporal::api::{
51
+ command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
52
+ enums::v1::EventType,
53
+ history::v1::{history_event, HistoryEvent},
54
+ },
55
+ };
56
+
57
+ type Result<T, E = WFMachinesError> = std::result::Result<T, E>;
58
+
59
+ slotmap::new_key_type! { struct MachineKey; }
60
+ /// Handles all the logic for driving a workflow. It orchestrates many state machines that together
61
+ /// comprise the logic of an executing workflow. One instance will exist per currently executing
62
+ /// (or cached) workflow on the worker.
63
+ pub(crate) struct WorkflowMachines {
64
+ /// The last recorded history we received from the server for this workflow run. This must be
65
+ /// kept because the lang side polls & completes for every workflow task, but we do not need
66
+ /// to poll the server that often during replay.
67
+ last_history_from_server: HistoryUpdate,
68
+ /// EventId of the last handled WorkflowTaskStarted event
69
+ current_started_event_id: i64,
70
+ /// The event id of the next workflow task started event that the machines need to process.
71
+ /// Eventually, this number should reach the started id in the latest history update, but
72
+ /// we must incrementally apply the history while communicating with lang.
73
+ next_started_event_id: i64,
74
+ /// The event id of the most recent event processed. It's possible in some situations (ex legacy
75
+ /// queries) to receive a history with no new workflow tasks. If the last history we processed
76
+ /// also had no new tasks, we need a way to know not to apply the same events over again.
77
+ pub last_processed_event: i64,
78
+ /// True if the workflow is replaying from history
79
+ pub replaying: bool,
80
+ /// Namespace this workflow exists in
81
+ pub namespace: String,
82
+ /// Workflow identifier
83
+ pub workflow_id: String,
84
+ /// Workflow type identifier. (Function name, class, etc)
85
+ pub workflow_type: String,
86
+ /// Identifies the current run
87
+ pub run_id: String,
88
+ /// The time the workflow execution began, as told by the WEStarted event
89
+ workflow_start_time: Option<SystemTime>,
90
+ /// The time the workflow execution finished, as determined by when the machines handled
91
+ /// a terminal workflow command. If this is `Some`, you know the workflow is ended.
92
+ workflow_end_time: Option<SystemTime>,
93
+ /// The WFT start time if it has been established
94
+ wft_start_time: Option<SystemTime>,
95
+ /// The current workflow time if it has been established. This may differ from the WFT start
96
+ /// time since local activities may advance the clock
97
+ current_wf_time: Option<SystemTime>,
98
+
99
+ all_machines: SlotMap<MachineKey, Machines>,
100
+ /// If a machine key is in this map, that machine was created internally by core, not as a
101
+ /// command from lang.
102
+ machine_is_core_created: SparseSecondaryMap<MachineKey, ()>,
103
+
104
+ /// A mapping for accessing machines associated to a particular event, where the key is the id
105
+ /// of the initiating event for that machine.
106
+ machines_by_event_id: HashMap<i64, MachineKey>,
107
+
108
+ /// Maps command ids as created by workflow authors to their associated machines.
109
+ id_to_machine: HashMap<CommandID, MachineKey>,
110
+
111
+ /// Queued commands which have been produced by machines and await processing / being sent to
112
+ /// the server.
113
+ commands: VecDeque<CommandAndMachine>,
114
+ /// Commands generated by the currently processing workflow task, which will eventually be
115
+ /// transferred to `commands` (and hence eventually sent to the server)
116
+ current_wf_task_commands: VecDeque<CommandAndMachine>,
117
+
118
+ /// Information about patch markers we have already seen while replaying history
119
+ encountered_change_markers: HashMap<String, ChangeInfo>,
120
+
121
+ /// Contains extra local-activity related data
122
+ local_activity_data: LocalActivityData,
123
+
124
+ /// The workflow that is being driven by this instance of the machines
125
+ drive_me: DrivenWorkflow,
126
+
127
+ /// Is set to true once we've seen the final event in workflow history, to avoid accidentally
128
+ /// re-applying the final workflow task.
129
+ pub have_seen_terminal_event: bool,
130
+
131
+ /// Metrics context
132
+ pub metrics: MetricsContext,
133
+ }
134
+
135
+ #[derive(Debug, derive_more::Display)]
136
+ #[display(fmt = "Cmd&Machine({})", "command")]
137
+ struct CommandAndMachine {
138
+ command: MachineAssociatedCommand,
139
+ machine: MachineKey,
140
+ }
141
+
142
+ #[derive(Debug, derive_more::Display)]
143
+ enum MachineAssociatedCommand {
144
+ Real(Box<ProtoCommand>),
145
+ #[display(fmt = "FakeLocalActivityMarker({})", "_0")]
146
+ FakeLocalActivityMarker(u32),
147
+ }
148
+
149
+ #[derive(Debug, Clone, Copy)]
150
+ struct ChangeInfo {
151
+ created_command: bool,
152
+ }
153
+
154
+ /// Returned by [TemporalStateMachine]s when handling events
155
+ #[derive(Debug, derive_more::Display)]
156
+ #[must_use]
157
+ #[allow(clippy::large_enum_variant)]
158
+ pub(super) enum MachineResponse {
159
+ #[display(fmt = "PushWFJob({})", "_0")]
160
+ PushWFJob(OutgoingJob),
161
+
162
+ /// Pushes a new command into the list that will be sent to server once we respond with the
163
+ /// workflow task completion
164
+ IssueNewCommand(ProtoCommand),
165
+ /// The machine requests the creation of another *different* machine. This acts as if lang
166
+ /// had replied to the activation with a command, but we use a special set of IDs to avoid
167
+ /// collisions.
168
+ #[display(fmt = "NewCoreOriginatedCommand({:?})", "_0")]
169
+ NewCoreOriginatedCommand(ProtoCmdAttrs),
170
+ #[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
171
+ IssueFakeLocalActivityMarker(u32),
172
+ #[display(fmt = "TriggerWFTaskStarted")]
173
+ TriggerWFTaskStarted {
174
+ task_started_event_id: i64,
175
+ time: SystemTime,
176
+ },
177
+ #[display(fmt = "UpdateRunIdOnWorkflowReset({})", run_id)]
178
+ UpdateRunIdOnWorkflowReset { run_id: String },
179
+
180
+ /// Queue a local activity to be processed by the worker
181
+ #[display(fmt = "QueueLocalActivity")]
182
+ QueueLocalActivity(ValidScheduleLA),
183
+ /// Request cancellation of an executing local activity
184
+ #[display(fmt = "RequestCancelLocalActivity({})", "_0")]
185
+ RequestCancelLocalActivity(u32),
186
+ /// Indicates we are abandoning the indicated LA, so we can remove it from "outstanding" LAs
187
+ /// and we will not try to WFT heartbeat because of it.
188
+ #[display(fmt = "AbandonLocalActivity({:?})", "_0")]
189
+ AbandonLocalActivity(u32),
190
+
191
+ /// Set the workflow time to the provided time
192
+ #[display(fmt = "UpdateWFTime({:?})", "_0")]
193
+ UpdateWFTime(Option<SystemTime>),
194
+ }
195
+
196
+ impl<T> From<T> for MachineResponse
197
+ where
198
+ T: Into<workflow_activation_job::Variant>,
199
+ {
200
+ fn from(v: T) -> Self {
201
+ Self::PushWFJob(v.into().into())
202
+ }
203
+ }
204
+
205
+ impl WorkflowMachines {
206
+ pub(crate) fn new(
207
+ namespace: String,
208
+ workflow_id: String,
209
+ workflow_type: String,
210
+ run_id: String,
211
+ history: HistoryUpdate,
212
+ driven_wf: DrivenWorkflow,
213
+ metrics: MetricsContext,
214
+ ) -> Self {
215
+ let replaying = history.previous_started_event_id > 0;
216
+ Self {
217
+ last_history_from_server: history,
218
+ namespace,
219
+ workflow_id,
220
+ workflow_type,
221
+ run_id,
222
+ drive_me: driven_wf,
223
+ replaying,
224
+ metrics,
225
+ // In an ideal world one could say ..Default::default() here and it'd still work.
226
+ current_started_event_id: 0,
227
+ next_started_event_id: 0,
228
+ last_processed_event: 0,
229
+ workflow_start_time: None,
230
+ workflow_end_time: None,
231
+ wft_start_time: None,
232
+ current_wf_time: None,
233
+ all_machines: Default::default(),
234
+ machine_is_core_created: Default::default(),
235
+ machines_by_event_id: Default::default(),
236
+ id_to_machine: Default::default(),
237
+ commands: Default::default(),
238
+ current_wf_task_commands: Default::default(),
239
+ encountered_change_markers: Default::default(),
240
+ local_activity_data: LocalActivityData::default(),
241
+ have_seen_terminal_event: false,
242
+ }
243
+ }
244
+
245
+ /// Returns true if workflow has seen a terminal command
246
+ pub(crate) const fn workflow_is_finished(&self) -> bool {
247
+ self.workflow_end_time.is_some()
248
+ }
249
+
250
+ /// Returns the total time it took to execute the workflow. Returns `None` if workflow is
251
+ /// incomplete, or time went backwards.
252
+ pub(crate) fn total_runtime(&self) -> Option<Duration> {
253
+ self.workflow_start_time
254
+ .zip(self.workflow_end_time)
255
+ .and_then(|(st, et)| et.duration_since(st).ok())
256
+ }
257
+
258
+ pub(crate) async fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
259
+ self.last_history_from_server = update;
260
+ self.replaying = self.last_history_from_server.previous_started_event_id > 0;
261
+ self.apply_next_wft_from_history().await?;
262
+ Ok(())
263
+ }
264
+
265
+ /// Let this workflow know that something we've been waiting locally on has resolved, like a
266
+ /// local activity or side effect
267
+ ///
268
+ /// Returns true if the resolution did anything. EX: If the activity is already canceled and
269
+ /// used the TryCancel or Abandon modes, the resolution is uninteresting.
270
+ pub(crate) fn local_resolution(&mut self, resolution: LocalResolution) -> Result<bool> {
271
+ let mut result_important = true;
272
+ match resolution {
273
+ LocalResolution::LocalActivity(LocalActivityResolution {
274
+ seq,
275
+ result,
276
+ runtime,
277
+ attempt,
278
+ backoff,
279
+ original_schedule_time,
280
+ }) => {
281
+ let act_id = CommandID::LocalActivity(seq);
282
+ let mk = self.get_machine_key(act_id)?;
283
+ let mach = self.machine_mut(mk);
284
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
285
+ let resps =
286
+ lam.try_resolve(result, runtime, attempt, backoff, original_schedule_time)?;
287
+ if resps.is_empty() {
288
+ result_important = false;
289
+ }
290
+ self.process_machine_responses(mk, resps)?;
291
+ } else {
292
+ return Err(WFMachinesError::Nondeterminism(format!(
293
+ "Command matching activity with seq num {} existed but was not a \
294
+ local activity!",
295
+ seq
296
+ )));
297
+ }
298
+ self.local_activity_data.done_executing(seq);
299
+ }
300
+ }
301
+ Ok(result_important)
302
+ }
303
+
304
+ /// Drain all queued local activities that need executing or cancellation
305
+ pub(crate) fn drain_queued_local_activities(&mut self) -> Vec<LocalActRequest> {
306
+ self.local_activity_data
307
+ .take_all_reqs(&self.workflow_type, &self.workflow_id, &self.run_id)
308
+ }
309
+
310
+ /// Returns the number of local activities we know we need to execute but have not yet finished
311
+ pub(crate) fn outstanding_local_activity_count(&self) -> usize {
312
+ self.local_activity_data.outstanding_la_count()
313
+ }
314
+
315
+ /// Returns start info for the workflow if it has started
316
+ pub(crate) fn get_started_info(&self) -> Option<&WorkflowStartedInfo> {
317
+ self.drive_me.get_started_info()
318
+ }
319
+
320
+ /// Fetches commands which are ready for processing from the state machines, generally to be
321
+ /// sent off to the server. They are not removed from the internal queue, that happens when
322
+ /// corresponding history events from the server are being handled.
323
+ pub(crate) fn get_commands(&self) -> Vec<ProtoCommand> {
324
+ self.commands
325
+ .iter()
326
+ .filter_map(|c| {
327
+ if !self.machine(c.machine).is_final_state() {
328
+ match &c.command {
329
+ MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
330
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
331
+ }
332
+ } else {
333
+ None
334
+ }
335
+ })
336
+ .collect()
337
+ }
338
+
339
+ /// Returns the next activation that needs to be performed by the lang sdk. Things like unblock
340
+ /// timer, etc. This does *not* cause any advancement of the state machines, it merely drains
341
+ /// from the outgoing queue of activation jobs.
342
+ ///
343
+ /// The job list may be empty, in which case it is expected the caller handles what to do in a
344
+ /// "no work" situation. Possibly, it may know about some work the machines don't, like queries.
345
+ pub(crate) fn get_wf_activation(&mut self) -> WorkflowActivation {
346
+ let jobs = self.drive_me.drain_jobs();
347
+ WorkflowActivation {
348
+ timestamp: self.current_wf_time.map(Into::into),
349
+ is_replaying: self.replaying,
350
+ run_id: self.run_id.clone(),
351
+ history_length: self.last_processed_event as u32,
352
+ jobs,
353
+ }
354
+ }
355
+
356
+ pub(crate) fn has_pending_jobs(&self) -> bool {
357
+ !self.drive_me.peek_pending_jobs().is_empty()
358
+ }
359
+
360
+ pub(crate) fn has_pending_la_resolutions(&self) -> bool {
361
+ self.drive_me
362
+ .peek_pending_jobs()
363
+ .iter()
364
+ .any(|v| v.is_la_resolution)
365
+ }
366
+
367
+ /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
368
+ /// the workflow code, handling them, and preparing them to be sent off to the server.
369
+ pub(crate) async fn iterate_machines(&mut self) -> Result<()> {
370
+ let results = self.drive_me.fetch_workflow_iteration_output().await;
371
+ self.handle_driven_results(results)?;
372
+ self.prepare_commands()?;
373
+ if self.workflow_is_finished() {
374
+ if let Some(rt) = self.total_runtime() {
375
+ self.metrics.wf_e2e_latency(rt);
376
+ }
377
+ }
378
+ Ok(())
379
+ }
380
+
381
+ /// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
382
+ /// any events that need to be replayed until caught up to the newest WFT. May also fetch
383
+ /// history from server if needed.
384
+ pub(crate) async fn apply_next_wft_from_history(&mut self) -> Result<usize> {
385
+ // If we have already seen the terminal event for the entire workflow in a previous WFT,
386
+ // then we don't need to do anything here, and in fact we need to avoid re-applying the
387
+ // final WFT.
388
+ if self.have_seen_terminal_event {
389
+ return Ok(0);
390
+ }
391
+
392
+ let last_handled_wft_started_id = self.current_started_event_id;
393
+ let events = {
394
+ let mut evts = self
395
+ .last_history_from_server
396
+ .take_next_wft_sequence(last_handled_wft_started_id)
397
+ .await
398
+ .map_err(WFMachinesError::HistoryFetchingError)?;
399
+ // Do not re-process events we have already processed
400
+ evts.retain(|e| e.event_id > self.last_processed_event);
401
+ evts
402
+ };
403
+ let num_events_to_process = events.len();
404
+
405
+ // We're caught up on reply if there are no new events to process
406
+ if events.is_empty() {
407
+ self.replaying = false;
408
+ }
409
+ let replay_start = Instant::now();
410
+
411
+ if let Some(last_event) = events.last() {
412
+ if last_event.event_type == EventType::WorkflowTaskStarted as i32 {
413
+ self.next_started_event_id = last_event.event_id;
414
+ }
415
+ }
416
+
417
+ let mut history = events.into_iter().peekable();
418
+ while let Some(event) = history.next() {
419
+ if event.event_id != self.last_processed_event + 1 {
420
+ return Err(WFMachinesError::Fatal(format!(
421
+ "History is out of order. Last processed event: {}, event id: {}",
422
+ self.last_processed_event, event.event_id
423
+ )));
424
+ }
425
+ let next_event = history.peek();
426
+ let eid = event.event_id;
427
+ let etype = event.event_type();
428
+ self.handle_event(event, next_event.is_some())?;
429
+ self.last_processed_event = eid;
430
+ if etype == EventType::WorkflowTaskStarted && next_event.is_none() {
431
+ break;
432
+ }
433
+ }
434
+
435
+ // Scan through to the next WFT, searching for any patch / la markers, so that we can
436
+ // pre-resolve them.
437
+ for e in self.last_history_from_server.peek_next_wft_sequence() {
438
+ if let Some((patch_id, _)) = e.get_patch_marker_details() {
439
+ self.encountered_change_markers.insert(
440
+ patch_id.clone(),
441
+ ChangeInfo {
442
+ created_command: false,
443
+ },
444
+ );
445
+ // Found a patch marker
446
+ self.drive_me.send_job(
447
+ workflow_activation_job::Variant::NotifyHasPatch(NotifyHasPatch { patch_id })
448
+ .into(),
449
+ );
450
+ } else if e.is_local_activity_marker() {
451
+ self.local_activity_data.process_peekahead_marker(e)?;
452
+ }
453
+ }
454
+
455
+ if !self.replaying {
456
+ self.metrics.wf_task_replay_latency(replay_start.elapsed());
457
+ }
458
+
459
+ Ok(num_events_to_process)
460
+ }
461
+
462
+ /// Handle a single event from the workflow history. `has_next_event` should be false if `event`
463
+ /// is the last event in the history.
464
+ ///
465
+ /// This function will attempt to apply the event to the workflow state machines. If there is
466
+ /// not a matching machine for the event, a nondeterminism error is returned. Otherwise, the
467
+ /// event is applied to the machine, which may also return a nondeterminism error if the machine
468
+ /// does not match the expected type. A fatal error may be returned if the machine is in an
469
+ /// invalid state.
470
+ #[instrument(skip(self, event), fields(event=%event))]
471
+ fn handle_event(&mut self, event: HistoryEvent, has_next_event: bool) -> Result<()> {
472
+ if event.is_final_wf_execution_event() {
473
+ self.have_seen_terminal_event = true;
474
+ }
475
+ if matches!(
476
+ event.event_type(),
477
+ EventType::WorkflowExecutionTerminated | EventType::WorkflowExecutionTimedOut
478
+ ) {
479
+ return if has_next_event {
480
+ Err(WFMachinesError::Fatal(
481
+ "Machines were fed a history which has an event after workflow execution was \
482
+ terminated!"
483
+ .to_string(),
484
+ ))
485
+ } else {
486
+ Ok(())
487
+ };
488
+ }
489
+ if self.replaying
490
+ && self.current_started_event_id
491
+ >= self.last_history_from_server.previous_started_event_id
492
+ && event.event_type() != EventType::WorkflowTaskCompleted
493
+ {
494
+ // Replay is finished
495
+ self.replaying = false;
496
+ }
497
+ if event.event_type() == EventType::Unspecified || event.attributes.is_none() {
498
+ return if !event.worker_may_ignore {
499
+ Err(WFMachinesError::Fatal(format!(
500
+ "Event type is unspecified! This history is invalid. Event detail: {:?}",
501
+ event
502
+ )))
503
+ } else {
504
+ debug!("Event is ignorable");
505
+ Ok(())
506
+ };
507
+ }
508
+
509
+ if event.is_command_event() {
510
+ self.handle_command_event(event)?;
511
+ return Ok(());
512
+ }
513
+
514
+ if let Some(initial_cmd_id) = event.get_initial_command_event_id() {
515
+ // We remove the machine while we it handles events, then return it, to avoid
516
+ // borrowing from ourself mutably.
517
+ let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
518
+ match maybe_machine {
519
+ Some(sm) => {
520
+ self.submachine_handle_event(sm, event, has_next_event)?;
521
+ // Restore machine if not in it's final state
522
+ if !self.machine(sm).is_final_state() {
523
+ self.machines_by_event_id.insert(initial_cmd_id, sm);
524
+ }
525
+ }
526
+ None => {
527
+ return Err(WFMachinesError::Nondeterminism(format!(
528
+ "During event handling, this event had an initial command ID but we \
529
+ could not find a matching command for it: {:?}",
530
+ event
531
+ )));
532
+ }
533
+ }
534
+ } else {
535
+ self.handle_non_stateful_event(event, has_next_event)?;
536
+ }
537
+
538
+ Ok(())
539
+ }
540
+
541
+ /// A command event is an event which is generated from a command emitted as a result of
542
+ /// performing a workflow task. Each command has a corresponding event. For example
543
+ /// ScheduleActivityTaskCommand is recorded to the history as ActivityTaskScheduledEvent.
544
+ ///
545
+ /// Command events always follow WorkflowTaskCompletedEvent.
546
+ ///
547
+ /// The handling consists of verifying that the next command in the commands queue is associated
548
+ /// with a state machine, which is then notified about the event and the command is removed from
549
+ /// the commands queue.
550
+ fn handle_command_event(&mut self, event: HistoryEvent) -> Result<()> {
551
+ if event.is_local_activity_marker() {
552
+ let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
553
+ WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
554
+ })?;
555
+ let cmdid = CommandID::LocalActivity(deets.seq);
556
+ let mkey = self.get_machine_key(cmdid)?;
557
+ if let Machines::LocalActivityMachine(lam) = self.machine(mkey) {
558
+ if lam.marker_should_get_special_handling()? {
559
+ self.submachine_handle_event(mkey, event, false)?;
560
+ return Ok(());
561
+ }
562
+ } else {
563
+ return Err(WFMachinesError::Fatal(format!(
564
+ "Encountered local activity marker but the associated machine was of the \
565
+ wrong type! {:?}",
566
+ event
567
+ )));
568
+ }
569
+ }
570
+
571
+ let event_id = event.event_id;
572
+
573
+ let consumed_cmd = loop {
574
+ if let Some(peek_machine) = self.commands.front() {
575
+ let mach = self.machine(peek_machine.machine);
576
+ match change_marker_handling(&event, mach)? {
577
+ ChangeMarkerOutcome::SkipEvent => return Ok(()),
578
+ ChangeMarkerOutcome::SkipCommand => {
579
+ self.commands.pop_front();
580
+ continue;
581
+ }
582
+ ChangeMarkerOutcome::Normal => {}
583
+ }
584
+ }
585
+
586
+ let maybe_command = self.commands.pop_front();
587
+ let command = if let Some(c) = maybe_command {
588
+ c
589
+ } else {
590
+ return Err(WFMachinesError::Nondeterminism(format!(
591
+ "No command scheduled for event {}",
592
+ event
593
+ )));
594
+ };
595
+
596
+ let canceled_before_sent = self
597
+ .machine(command.machine)
598
+ .was_cancelled_before_sent_to_server();
599
+
600
+ if !canceled_before_sent {
601
+ // Feed the machine the event
602
+ self.submachine_handle_event(command.machine, event, true)?;
603
+ break command;
604
+ }
605
+ };
606
+
607
+ if !self.machine(consumed_cmd.machine).is_final_state() {
608
+ self.machines_by_event_id
609
+ .insert(event_id, consumed_cmd.machine);
610
+ }
611
+
612
+ Ok(())
613
+ }
614
+
615
+ fn handle_non_stateful_event(
616
+ &mut self,
617
+ event: HistoryEvent,
618
+ has_next_event: bool,
619
+ ) -> Result<()> {
620
+ trace!(
621
+ event = %event,
622
+ "handling non-stateful event"
623
+ );
624
+ let event_id = event.event_id;
625
+ match EventType::from_i32(event.event_type) {
626
+ Some(EventType::WorkflowExecutionStarted) => {
627
+ if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
628
+ attrs,
629
+ )) = event.attributes
630
+ {
631
+ if let Some(st) = event.event_time.clone() {
632
+ let as_systime: SystemTime = st.try_into()?;
633
+ self.workflow_start_time = Some(as_systime);
634
+ // Set the workflow time to be the event time of the first event, so that
635
+ // if there is a query issued before first WFT started event, there is some
636
+ // workflow time set.
637
+ self.set_current_time(as_systime);
638
+ }
639
+ // Notify the lang sdk that it's time to kick off a workflow
640
+ self.drive_me.start(
641
+ self.workflow_id.clone(),
642
+ str_to_randomness_seed(&attrs.original_execution_run_id),
643
+ event.event_time.unwrap_or_default(),
644
+ attrs,
645
+ );
646
+ } else {
647
+ return Err(WFMachinesError::Fatal(format!(
648
+ "WorkflowExecutionStarted event did not have appropriate attributes: {}",
649
+ event
650
+ )));
651
+ }
652
+ }
653
+ Some(EventType::WorkflowTaskScheduled) => {
654
+ let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
655
+ let key = self.all_machines.insert(wf_task_sm.into());
656
+ self.submachine_handle_event(key, event, has_next_event)?;
657
+ self.machines_by_event_id.insert(event_id, key);
658
+ }
659
+ Some(EventType::WorkflowExecutionSignaled) => {
660
+ if let Some(history_event::Attributes::WorkflowExecutionSignaledEventAttributes(
661
+ attrs,
662
+ )) = event.attributes
663
+ {
664
+ self.drive_me
665
+ .send_job(workflow_activation::SignalWorkflow::from(attrs).into());
666
+ } else {
667
+ // err
668
+ }
669
+ }
670
+ Some(EventType::WorkflowExecutionCancelRequested) => {
671
+ if let Some(
672
+ history_event::Attributes::WorkflowExecutionCancelRequestedEventAttributes(
673
+ attrs,
674
+ ),
675
+ ) = event.attributes
676
+ {
677
+ self.drive_me
678
+ .send_job(workflow_activation::CancelWorkflow::from(attrs).into());
679
+ } else {
680
+ // err
681
+ }
682
+ }
683
+ _ => {
684
+ return Err(WFMachinesError::Fatal(format!(
685
+ "The event is not a non-stateful event, but we tried to handle it as one: {}",
686
+ event
687
+ )));
688
+ }
689
+ }
690
+ Ok(())
691
+ }
692
+
693
+ fn set_current_time(&mut self, time: SystemTime) -> SystemTime {
694
+ if self.current_wf_time.map_or(true, |t| t < time) {
695
+ self.current_wf_time = Some(time);
696
+ }
697
+ self.current_wf_time
698
+ .expect("We have just ensured this is populated")
699
+ }
700
+
701
+ /// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
702
+ /// on the returned machine responses
703
+ fn submachine_handle_event(
704
+ &mut self,
705
+ sm: MachineKey,
706
+ event: HistoryEvent,
707
+ has_next_event: bool,
708
+ ) -> Result<()> {
709
+ let machine_responses = self.machine_mut(sm).handle_event(event, has_next_event)?;
710
+ self.process_machine_responses(sm, machine_responses)?;
711
+ Ok(())
712
+ }
713
+
714
+ /// Transfer commands from `current_wf_task_commands` to `commands`, so they may be sent off
715
+ /// to the server. While doing so, [TemporalStateMachine::handle_command] is called on the
716
+ /// machine associated with the command.
717
+ fn prepare_commands(&mut self) -> Result<()> {
718
+ // It's possible we might prepare commands more than once before completing a WFT. (Because
719
+ // of local activities, of course). Some commands might have since been cancelled that we
720
+ // already prepared. Rip them out of the outgoing command list if so.
721
+ self.commands.retain(|c| {
722
+ !self
723
+ .all_machines
724
+ .get(c.machine)
725
+ .expect("Machine must exist")
726
+ .was_cancelled_before_sent_to_server()
727
+ });
728
+
729
+ while let Some(c) = self.current_wf_task_commands.pop_front() {
730
+ if !self
731
+ .machine(c.machine)
732
+ .was_cancelled_before_sent_to_server()
733
+ {
734
+ match &c.command {
735
+ MachineAssociatedCommand::Real(cmd) => {
736
+ let machine_responses = self
737
+ .machine_mut(c.machine)
738
+ .handle_command(cmd.command_type())?;
739
+ self.process_machine_responses(c.machine, machine_responses)?;
740
+ }
741
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => {}
742
+ }
743
+ self.commands.push_back(c);
744
+ }
745
+ }
746
+ debug!(commands = %self.commands.display(), "prepared commands");
747
+ Ok(())
748
+ }
749
+
750
+ /// After a machine handles either an event or a command, it produces [MachineResponses] which
751
+ /// this function uses to drive sending jobs to lang, triggering new workflow tasks, etc.
752
+ fn process_machine_responses(
753
+ &mut self,
754
+ smk: MachineKey,
755
+ machine_responses: Vec<MachineResponse>,
756
+ ) -> Result<()> {
757
+ let sm = self.machine(smk);
758
+ if !machine_responses.is_empty() {
759
+ debug!(responses = %machine_responses.display(), machine_name = %sm.name(),
760
+ "Machine produced responses");
761
+ }
762
+ self.process_machine_resps_impl(smk, machine_responses)
763
+ }
764
+
765
+ fn process_machine_resps_impl(
766
+ &mut self,
767
+ smk: MachineKey,
768
+ machine_responses: Vec<MachineResponse>,
769
+ ) -> Result<()> {
770
+ for response in machine_responses {
771
+ match response {
772
+ MachineResponse::PushWFJob(a) => {
773
+ // We don't need to notify lang about jobs created by core-internal machines
774
+ if !self.machine_is_core_created.contains_key(smk) {
775
+ self.drive_me.send_job(a);
776
+ }
777
+ }
778
+ MachineResponse::TriggerWFTaskStarted {
779
+ task_started_event_id,
780
+ time,
781
+ } => {
782
+ self.task_started(task_started_event_id, time)?;
783
+ }
784
+ MachineResponse::UpdateRunIdOnWorkflowReset { run_id: new_run_id } => {
785
+ self.drive_me.send_job(
786
+ workflow_activation_job::Variant::UpdateRandomSeed(UpdateRandomSeed {
787
+ randomness_seed: str_to_randomness_seed(&new_run_id),
788
+ })
789
+ .into(),
790
+ );
791
+ }
792
+ MachineResponse::IssueNewCommand(c) => {
793
+ self.current_wf_task_commands.push_back(CommandAndMachine {
794
+ command: MachineAssociatedCommand::Real(Box::new(c)),
795
+ machine: smk,
796
+ })
797
+ }
798
+ MachineResponse::NewCoreOriginatedCommand(attrs) => match attrs {
799
+ ProtoCmdAttrs::RequestCancelExternalWorkflowExecutionCommandAttributes(
800
+ attrs,
801
+ ) => {
802
+ let we = NamespacedWorkflowExecution {
803
+ namespace: attrs.namespace,
804
+ workflow_id: attrs.workflow_id,
805
+ run_id: attrs.run_id,
806
+ };
807
+ self.add_cmd_to_wf_task(
808
+ new_external_cancel(0, we, attrs.child_workflow_only, attrs.reason),
809
+ CommandIdKind::CoreInternal,
810
+ );
811
+ }
812
+ c => {
813
+ return Err(WFMachinesError::Fatal(format!(
814
+ "A machine requested to create a new command of an unsupported type: {:?}",
815
+ c
816
+ )))
817
+ }
818
+ },
819
+ MachineResponse::IssueFakeLocalActivityMarker(seq) => {
820
+ self.current_wf_task_commands.push_back(CommandAndMachine {
821
+ command: MachineAssociatedCommand::FakeLocalActivityMarker(seq),
822
+ machine: smk,
823
+ });
824
+ }
825
+ MachineResponse::QueueLocalActivity(act) => {
826
+ self.local_activity_data.enqueue(act);
827
+ }
828
+ MachineResponse::RequestCancelLocalActivity(seq) => {
829
+ // We might already know about the status from a pre-resolution. Apply it if so.
830
+ // We need to do this because otherwise we might need to perform additional
831
+ // activations during replay that didn't happen during execution, just like
832
+ // we sometimes pre-resolve activities when first requested.
833
+ if let Some(preres) = self.local_activity_data.take_preresolution(seq) {
834
+ if let Machines::LocalActivityMachine(lam) = self.machine_mut(smk) {
835
+ let more_responses = lam.try_resolve_with_dat(preres)?;
836
+ self.process_machine_responses(smk, more_responses)?;
837
+ } else {
838
+ panic!("A non local-activity machine returned a request cancel LA response");
839
+ }
840
+ }
841
+ // If it's in the request queue, just rip it out.
842
+ else if let Some(removed_act) =
843
+ self.local_activity_data.remove_from_queue(seq)
844
+ {
845
+ // We removed it. Notify the machine that the activity cancelled.
846
+ if let Machines::LocalActivityMachine(lam) = self.machine_mut(smk) {
847
+ let more_responses = lam.try_resolve(
848
+ LocalActivityExecutionResult::empty_cancel(),
849
+ Duration::from_secs(0),
850
+ removed_act.attempt,
851
+ None,
852
+ removed_act.original_schedule_time,
853
+ )?;
854
+ self.process_machine_responses(smk, more_responses)?;
855
+ } else {
856
+ panic!("A non local-activity machine returned a request cancel LA response");
857
+ }
858
+ } else {
859
+ // Finally, if we know about the LA at all, it's currently running, so
860
+ // queue the cancel request to be given to the LA manager.
861
+ self.local_activity_data.enqueue_cancel(ExecutingLAId {
862
+ run_id: self.run_id.clone(),
863
+ seq_num: seq,
864
+ });
865
+ }
866
+ }
867
+ MachineResponse::AbandonLocalActivity(seq) => {
868
+ self.local_activity_data.done_executing(seq);
869
+ }
870
+ MachineResponse::UpdateWFTime(t) => {
871
+ if let Some(t) = t {
872
+ self.set_current_time(t);
873
+ }
874
+ }
875
+ }
876
+ }
877
+ Ok(())
878
+ }
879
+
880
+ /// Called when a workflow task started event has triggered. Ensures we are tracking the ID
881
+ /// of the current started event as well as workflow time properly.
882
+ fn task_started(&mut self, task_started_event_id: i64, time: SystemTime) -> Result<()> {
883
+ self.current_started_event_id = task_started_event_id;
884
+ self.wft_start_time = Some(time);
885
+ self.set_current_time(time);
886
+
887
+ // Notify local activity machines that we started a non-replay WFT, which will allow any
888
+ // which were waiting for a marker to instead decide to execute the LA since it clearly
889
+ // will not be resolved via marker.
890
+ if !self.replaying {
891
+ let mut resps = vec![];
892
+ for (k, mach) in self.all_machines.iter_mut() {
893
+ if let Machines::LocalActivityMachine(lam) = mach {
894
+ resps.push((k, lam.encountered_non_replay_wft()?));
895
+ }
896
+ }
897
+ for (mkey, resp_set) in resps {
898
+ self.process_machine_responses(mkey, resp_set)?;
899
+ }
900
+ }
901
+ Ok(())
902
+ }
903
+
904
+ /// Handles results of the workflow activation, delegating work to the appropriate state
905
+ /// machine. Returns a list of workflow jobs that should be queued in the pending activation for
906
+ /// the next poll. This list will be populated only if state machine produced lang activations
907
+ /// as part of command processing. For example some types of activity cancellation need to
908
+ /// immediately unblock lang side without having it to poll for an actual workflow task from the
909
+ /// server.
910
+ fn handle_driven_results(&mut self, results: Vec<WFCommand>) -> Result<()> {
911
+ for cmd in results {
912
+ match cmd {
913
+ WFCommand::AddTimer(attrs) => {
914
+ let seq = attrs.seq;
915
+ self.add_cmd_to_wf_task(new_timer(attrs), CommandID::Timer(seq).into());
916
+ }
917
+ WFCommand::UpsertSearchAttributes(attrs) => {
918
+ self.add_cmd_to_wf_task(
919
+ upsert_search_attrs(attrs),
920
+ CommandIdKind::NeverResolves,
921
+ );
922
+ }
923
+ WFCommand::CancelTimer(attrs) => {
924
+ self.process_cancellation(CommandID::Timer(attrs.seq))?;
925
+ }
926
+ WFCommand::AddActivity(attrs) => {
927
+ let seq = attrs.seq;
928
+ self.add_cmd_to_wf_task(new_activity(attrs), CommandID::Activity(seq).into());
929
+ }
930
+ WFCommand::AddLocalActivity(attrs) => {
931
+ let seq = attrs.seq;
932
+ let attrs: ValidScheduleLA = ValidScheduleLA::from_schedule_la(
933
+ attrs,
934
+ self.get_started_info()
935
+ .as_ref()
936
+ .and_then(|x| x.workflow_execution_timeout),
937
+ )
938
+ .map_err(|e| {
939
+ WFMachinesError::Fatal(format!(
940
+ "Invalid schedule local activity request (seq {}): {}",
941
+ seq, e
942
+ ))
943
+ })?;
944
+ let (la, mach_resp) = new_local_activity(
945
+ attrs,
946
+ self.replaying,
947
+ self.local_activity_data.take_preresolution(seq),
948
+ self.current_wf_time,
949
+ )?;
950
+ let machkey = self.all_machines.insert(la.into());
951
+ self.id_to_machine
952
+ .insert(CommandID::LocalActivity(seq), machkey);
953
+ self.process_machine_responses(machkey, mach_resp)?;
954
+ }
955
+ WFCommand::RequestCancelActivity(attrs) => {
956
+ self.process_cancellation(CommandID::Activity(attrs.seq))?;
957
+ }
958
+ WFCommand::RequestCancelLocalActivity(attrs) => {
959
+ self.process_cancellation(CommandID::LocalActivity(attrs.seq))?;
960
+ }
961
+ WFCommand::CompleteWorkflow(attrs) => {
962
+ self.metrics.wf_completed();
963
+ self.add_terminal_command(complete_workflow(attrs));
964
+ }
965
+ WFCommand::FailWorkflow(attrs) => {
966
+ self.metrics.wf_failed();
967
+ self.add_terminal_command(fail_workflow(attrs));
968
+ }
969
+ WFCommand::ContinueAsNew(attrs) => {
970
+ self.metrics.wf_continued_as_new();
971
+ let attrs = self.augment_continue_as_new_with_current_values(attrs);
972
+ self.add_terminal_command(continue_as_new(attrs));
973
+ }
974
+ WFCommand::CancelWorkflow(attrs) => {
975
+ self.metrics.wf_canceled();
976
+ self.add_terminal_command(cancel_workflow(attrs));
977
+ }
978
+ WFCommand::SetPatchMarker(attrs) => {
979
+ // Do not create commands for change IDs that we have already created commands
980
+ // for.
981
+ if !matches!(self.encountered_change_markers.get(&attrs.patch_id),
982
+ Some(ChangeInfo {created_command}) if *created_command)
983
+ {
984
+ self.add_cmd_to_wf_task(
985
+ has_change(attrs.patch_id.clone(), self.replaying, attrs.deprecated),
986
+ CommandIdKind::NeverResolves,
987
+ );
988
+
989
+ if let Some(ci) = self.encountered_change_markers.get_mut(&attrs.patch_id) {
990
+ ci.created_command = true;
991
+ } else {
992
+ self.encountered_change_markers.insert(
993
+ attrs.patch_id,
994
+ ChangeInfo {
995
+ created_command: true,
996
+ },
997
+ );
998
+ }
999
+ }
1000
+ }
1001
+ WFCommand::AddChildWorkflow(attrs) => {
1002
+ let seq = attrs.seq;
1003
+ self.add_cmd_to_wf_task(
1004
+ new_child_workflow(attrs),
1005
+ CommandID::ChildWorkflowStart(seq).into(),
1006
+ );
1007
+ }
1008
+ WFCommand::CancelChild(attrs) => self.process_cancellation(
1009
+ CommandID::ChildWorkflowStart(attrs.child_workflow_seq),
1010
+ )?,
1011
+ WFCommand::RequestCancelExternalWorkflow(attrs) => {
1012
+ let (we, only_child) = match attrs.target {
1013
+ None => {
1014
+ return Err(WFMachinesError::Fatal(
1015
+ "Cancel external workflow command had empty target field"
1016
+ .to_string(),
1017
+ ))
1018
+ }
1019
+ Some(cancel_we::Target::ChildWorkflowId(wfid)) => (
1020
+ NamespacedWorkflowExecution {
1021
+ namespace: self.namespace.clone(),
1022
+ workflow_id: wfid,
1023
+ run_id: "".to_string(),
1024
+ },
1025
+ true,
1026
+ ),
1027
+ Some(cancel_we::Target::WorkflowExecution(we)) => (we, false),
1028
+ };
1029
+ self.add_cmd_to_wf_task(
1030
+ new_external_cancel(
1031
+ attrs.seq,
1032
+ we,
1033
+ only_child,
1034
+ format!("Cancel requested by workflow with run id {}", self.run_id),
1035
+ ),
1036
+ CommandID::CancelExternal(attrs.seq).into(),
1037
+ );
1038
+ }
1039
+ WFCommand::SignalExternalWorkflow(attrs) => {
1040
+ let seq = attrs.seq;
1041
+ self.add_cmd_to_wf_task(
1042
+ new_external_signal(attrs, &self.namespace)?,
1043
+ CommandID::SignalExternal(seq).into(),
1044
+ );
1045
+ }
1046
+ WFCommand::CancelSignalWorkflow(attrs) => {
1047
+ self.process_cancellation(CommandID::SignalExternal(attrs.seq))?;
1048
+ }
1049
+ WFCommand::QueryResponse(_) => {
1050
+ // Nothing to do here, queries are handled above the machine level
1051
+ unimplemented!("Query responses should not make it down into the machines")
1052
+ }
1053
+ WFCommand::ModifyWorkflowProperties(attrs) => {
1054
+ self.add_cmd_to_wf_task(
1055
+ modify_workflow_properties(attrs),
1056
+ CommandIdKind::NeverResolves,
1057
+ );
1058
+ }
1059
+ WFCommand::NoCommandsFromLang => (),
1060
+ }
1061
+ }
1062
+ Ok(())
1063
+ }
1064
+
1065
+ /// Given a command id to attempt to cancel, try to cancel it and return any jobs that should
1066
+ /// be included in the activation
1067
+ fn process_cancellation(&mut self, id: CommandID) -> Result<()> {
1068
+ let m_key = self.get_machine_key(id)?;
1069
+ let mach = self.machine_mut(m_key);
1070
+ let machine_resps = mach.cancel()?;
1071
+ debug!(machine_responses = %machine_resps.display(), cmd_id = ?id,
1072
+ "Cancel request responses");
1073
+ self.process_machine_resps_impl(m_key, machine_resps)
1074
+ }
1075
+
1076
+ fn get_machine_key(&self, id: CommandID) -> Result<MachineKey> {
1077
+ Ok(*self.id_to_machine.get(&id).ok_or_else(|| {
1078
+ WFMachinesError::Fatal(format!("Missing associated machine for {:?}", id))
1079
+ })?)
1080
+ }
1081
+
1082
+ fn add_terminal_command(&mut self, machine: NewMachineWithCommand) {
1083
+ let cwfm = self.add_new_command_machine(machine);
1084
+ self.workflow_end_time = Some(SystemTime::now());
1085
+ self.current_wf_task_commands.push_back(cwfm);
1086
+ }
1087
+
1088
+ /// Add a new command/machines for that command to the current workflow task
1089
+ fn add_cmd_to_wf_task(&mut self, machine: NewMachineWithCommand, id: CommandIdKind) {
1090
+ let mach = self.add_new_command_machine(machine);
1091
+ if let CommandIdKind::LangIssued(id) = id {
1092
+ self.id_to_machine.insert(id, mach.machine);
1093
+ }
1094
+ if matches!(id, CommandIdKind::CoreInternal) {
1095
+ self.machine_is_core_created.insert(mach.machine, ());
1096
+ }
1097
+ self.current_wf_task_commands.push_back(mach);
1098
+ }
1099
+
1100
+ fn add_new_command_machine(&mut self, machine: NewMachineWithCommand) -> CommandAndMachine {
1101
+ let k = self.all_machines.insert(machine.machine);
1102
+ CommandAndMachine {
1103
+ command: MachineAssociatedCommand::Real(Box::new(machine.command)),
1104
+ machine: k,
1105
+ }
1106
+ }
1107
+
1108
+ fn machine(&self, m: MachineKey) -> &Machines {
1109
+ self.all_machines
1110
+ .get(m)
1111
+ .expect("Machine must exist")
1112
+ .borrow()
1113
+ }
1114
+
1115
+ fn machine_mut(&mut self, m: MachineKey) -> &mut Machines {
1116
+ self.all_machines
1117
+ .get_mut(m)
1118
+ .expect("Machine must exist")
1119
+ .borrow_mut()
1120
+ }
1121
+
1122
+ fn augment_continue_as_new_with_current_values(
1123
+ &self,
1124
+ mut attrs: ContinueAsNewWorkflowExecution,
1125
+ ) -> ContinueAsNewWorkflowExecution {
1126
+ if let Some(started_info) = self.drive_me.get_started_info() {
1127
+ if attrs.memo.is_empty() {
1128
+ attrs.memo = started_info
1129
+ .memo
1130
+ .clone()
1131
+ .map(Into::into)
1132
+ .unwrap_or_default();
1133
+ }
1134
+ if attrs.search_attributes.is_empty() {
1135
+ attrs.search_attributes = started_info
1136
+ .search_attrs
1137
+ .clone()
1138
+ .map(Into::into)
1139
+ .unwrap_or_default();
1140
+ }
1141
+ if attrs.retry_policy.is_none() {
1142
+ attrs.retry_policy = started_info.retry_policy.clone();
1143
+ }
1144
+ }
1145
+ attrs
1146
+ }
1147
+ }
1148
+
1149
+ fn str_to_randomness_seed(run_id: &str) -> u64 {
1150
+ // This was originally `DefaultHasher` but that is potentially unstable across Rust releases.
1151
+ // This must forever be `SipHasher13` now or we risk breaking history compat.
1152
+ let mut s = SipHasher13::new();
1153
+ run_id.hash(&mut s);
1154
+ s.finish()
1155
+ }
1156
+
1157
+ enum ChangeMarkerOutcome {
1158
+ SkipEvent,
1159
+ SkipCommand,
1160
+ Normal,
1161
+ }
1162
+
1163
+ /// Special handling for patch markers, when handling command events as in
1164
+ /// [WorkflowMachines::handle_command_event]
1165
+ fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<ChangeMarkerOutcome> {
1166
+ if !mach.matches_event(event) {
1167
+ // Version markers can be skipped in the event they are deprecated
1168
+ if let Some((patch_name, deprecated)) = event.get_patch_marker_details() {
1169
+ // Is deprecated. We can simply ignore this event, as deprecated change
1170
+ // markers are allowed without matching changed calls.
1171
+ if deprecated {
1172
+ debug!("Deprecated patch marker tried against wrong machine, skipping.");
1173
+ return Ok(ChangeMarkerOutcome::SkipEvent);
1174
+ }
1175
+ return Err(WFMachinesError::Nondeterminism(format!(
1176
+ "Non-deprecated patch marker encountered for change {}, \
1177
+ but there is no corresponding change command!",
1178
+ patch_name
1179
+ )));
1180
+ }
1181
+ // Patch machines themselves may also not *have* matching markers, where non-deprecated
1182
+ // calls take the old path, and deprecated calls assume history is produced by a new-code
1183
+ // worker.
1184
+ if matches!(mach, Machines::PatchMachine(_)) {
1185
+ debug!("Skipping non-matching event against patch machine");
1186
+ return Ok(ChangeMarkerOutcome::SkipCommand);
1187
+ }
1188
+ }
1189
+ Ok(ChangeMarkerOutcome::Normal)
1190
+ }
1191
+
1192
+ #[derive(derive_more::From)]
1193
+ enum CommandIdKind {
1194
+ /// A normal command, requested by lang
1195
+ LangIssued(CommandID),
1196
+ /// A command created internally
1197
+ CoreInternal,
1198
+ /// A command which is fire-and-forget (ex: Upsert search attribs)
1199
+ NeverResolves,
1200
+ }