temporalio 0.0.0 → 0.0.1

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