temporalio 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -7
  3. data/bridge/Cargo.lock +208 -76
  4. data/bridge/Cargo.toml +5 -2
  5. data/bridge/sdk-core/Cargo.toml +1 -1
  6. data/bridge/sdk-core/README.md +20 -10
  7. data/bridge/sdk-core/client/Cargo.toml +1 -1
  8. data/bridge/sdk-core/client/src/lib.rs +227 -59
  9. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  10. data/bridge/sdk-core/client/src/raw.rs +13 -12
  11. data/bridge/sdk-core/client/src/retry.rs +132 -43
  12. data/bridge/sdk-core/core/Cargo.toml +28 -15
  13. data/bridge/sdk-core/core/benches/workflow_replay.rs +13 -10
  14. data/bridge/sdk-core/core/src/abstractions.rs +225 -36
  15. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +217 -79
  16. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  17. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +565 -34
  18. data/bridge/sdk-core/core/src/core_tests/queries.rs +247 -90
  19. data/bridge/sdk-core/core/src/core_tests/workers.rs +3 -5
  20. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  21. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +430 -67
  22. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  23. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  24. data/bridge/sdk-core/core/src/lib.rs +148 -34
  25. data/bridge/sdk-core/core/src/protosext/mod.rs +1 -1
  26. data/bridge/sdk-core/core/src/replay/mod.rs +185 -41
  27. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  28. data/bridge/sdk-core/core/src/telemetry/metrics.rs +219 -140
  29. data/bridge/sdk-core/core/src/telemetry/mod.rs +326 -315
  30. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +20 -14
  31. data/bridge/sdk-core/core/src/test_help/mod.rs +85 -21
  32. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  33. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  34. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +364 -128
  35. data/bridge/sdk-core/core/src/worker/activities.rs +263 -170
  36. data/bridge/sdk-core/core/src/worker/client/mocks.rs +23 -3
  37. data/bridge/sdk-core/core/src/worker/client.rs +48 -6
  38. data/bridge/sdk-core/core/src/worker/mod.rs +186 -75
  39. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  40. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +13 -24
  41. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +879 -226
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +101 -48
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +8 -12
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -9
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +90 -32
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +6 -9
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -10
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +6 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +160 -83
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +36 -54
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +179 -0
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +104 -157
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +8 -12
  54. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -13
  55. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +10 -4
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -11
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +395 -299
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +12 -20
  60. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +33 -18
  61. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1032 -374
  62. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +525 -392
  63. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  64. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +3 -6
  66. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +456 -681
  69. data/bridge/sdk-core/core-api/Cargo.toml +6 -4
  70. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  71. data/bridge/sdk-core/core-api/src/lib.rs +7 -45
  72. data/bridge/sdk-core/core-api/src/telemetry.rs +141 -0
  73. data/bridge/sdk-core/core-api/src/worker.rs +27 -1
  74. data/bridge/sdk-core/etc/deps.svg +115 -140
  75. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  76. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  77. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  78. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  79. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  80. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  81. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  82. data/bridge/sdk-core/protos/api_upstream/buf.yaml +0 -3
  83. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  84. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  85. data/bridge/sdk-core/protos/api_upstream/{temporal/api/enums/v1/cluster.proto → build/tools.go} +7 -18
  86. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  87. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  88. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +15 -26
  89. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  90. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  91. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +4 -9
  92. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  93. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +10 -8
  94. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +28 -2
  95. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  96. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  97. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  98. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  99. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  100. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  101. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  102. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  103. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  104. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  105. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +62 -26
  106. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  107. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +24 -61
  108. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -21
  109. data/bridge/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  110. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  111. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  112. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +110 -31
  113. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  114. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +4 -4
  115. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  116. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  117. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +3 -2
  118. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +111 -36
  119. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -5
  120. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  121. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  122. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  123. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  124. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  125. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  126. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +9 -0
  127. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +9 -1
  128. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  129. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  130. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  131. data/bridge/sdk-core/sdk/Cargo.toml +4 -3
  132. data/bridge/sdk-core/sdk/src/interceptors.rs +36 -3
  133. data/bridge/sdk-core/sdk/src/lib.rs +94 -25
  134. data/bridge/sdk-core/sdk/src/workflow_context.rs +13 -2
  135. data/bridge/sdk-core/sdk/src/workflow_future.rs +10 -13
  136. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  137. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -2
  138. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +164 -104
  139. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +27 -23
  140. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +252 -74
  141. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  142. data/bridge/sdk-core/test-utils/Cargo.toml +4 -1
  143. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  144. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  145. data/bridge/sdk-core/test-utils/src/lib.rs +161 -50
  146. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  147. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  148. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  149. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  150. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  151. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  152. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +239 -0
  153. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -60
  154. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  155. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  156. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  157. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  158. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  159. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  160. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  161. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  162. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +151 -116
  163. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +54 -0
  164. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  165. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +115 -24
  166. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  167. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  168. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  169. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  170. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  171. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +27 -18
  172. data/bridge/sdk-core/tests/main.rs +8 -16
  173. data/bridge/sdk-core/tests/runner.rs +75 -36
  174. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  175. data/bridge/src/connection.rs +117 -82
  176. data/bridge/src/lib.rs +356 -42
  177. data/bridge/src/runtime.rs +10 -3
  178. data/bridge/src/test_server.rs +153 -0
  179. data/bridge/src/worker.rs +133 -9
  180. data/lib/gen/temporal/api/batch/v1/message_pb.rb +8 -6
  181. data/lib/gen/temporal/api/command/v1/message_pb.rb +10 -16
  182. data/lib/gen/temporal/api/common/v1/message_pb.rb +5 -1
  183. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +2 -1
  184. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +3 -3
  185. data/lib/gen/temporal/api/enums/v1/common_pb.rb +2 -1
  186. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +5 -4
  187. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +9 -1
  188. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +1 -1
  189. data/lib/gen/temporal/api/enums/v1/query_pb.rb +1 -1
  190. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +1 -1
  191. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +1 -1
  192. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +1 -1
  193. data/lib/gen/temporal/api/enums/v1/update_pb.rb +7 -10
  194. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +1 -1
  195. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +1 -1
  196. data/lib/gen/temporal/api/failure/v1/message_pb.rb +1 -1
  197. data/lib/gen/temporal/api/filter/v1/message_pb.rb +1 -1
  198. data/lib/gen/temporal/api/history/v1/message_pb.rb +34 -25
  199. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +2 -1
  200. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +14 -51
  201. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +1 -1
  202. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  203. data/lib/gen/temporal/api/query/v1/message_pb.rb +1 -1
  204. data/lib/gen/temporal/api/replication/v1/message_pb.rb +1 -1
  205. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +22 -1
  206. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  207. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +2 -2
  208. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  209. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  210. data/lib/gen/temporal/api/update/v1/message_pb.rb +49 -3
  211. data/lib/gen/temporal/api/version/v1/message_pb.rb +1 -1
  212. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +2 -1
  213. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +47 -20
  214. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +1 -1
  215. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  216. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  217. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  218. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  219. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  220. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  221. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +28 -21
  222. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +32 -24
  223. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  224. data/lib/temporalio/activity/context.rb +102 -0
  225. data/lib/temporalio/activity/info.rb +67 -0
  226. data/lib/temporalio/activity.rb +85 -0
  227. data/lib/temporalio/bridge/connect_options.rb +15 -0
  228. data/lib/temporalio/bridge/error.rb +8 -0
  229. data/lib/temporalio/bridge/retry_config.rb +24 -0
  230. data/lib/temporalio/bridge/tls_options.rb +19 -0
  231. data/lib/temporalio/bridge.rb +14 -0
  232. data/lib/{temporal → temporalio}/client/implementation.rb +57 -56
  233. data/lib/{temporal → temporalio}/client/workflow_handle.rb +35 -35
  234. data/lib/{temporal → temporalio}/client.rb +19 -32
  235. data/lib/temporalio/connection/retry_config.rb +44 -0
  236. data/lib/temporalio/connection/service.rb +20 -0
  237. data/lib/temporalio/connection/test_service.rb +92 -0
  238. data/lib/temporalio/connection/tls_options.rb +51 -0
  239. data/lib/temporalio/connection/workflow_service.rb +731 -0
  240. data/lib/temporalio/connection.rb +86 -0
  241. data/lib/{temporal → temporalio}/data_converter.rb +76 -35
  242. data/lib/{temporal → temporalio}/error/failure.rb +6 -6
  243. data/lib/{temporal → temporalio}/error/workflow_failure.rb +4 -2
  244. data/lib/{temporal → temporalio}/errors.rb +19 -1
  245. data/lib/{temporal → temporalio}/failure_converter/base.rb +5 -5
  246. data/lib/{temporal → temporalio}/failure_converter/basic.rb +58 -52
  247. data/lib/temporalio/failure_converter.rb +7 -0
  248. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  249. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  250. data/lib/{temporal → temporalio}/interceptor/chain.rb +7 -6
  251. data/lib/{temporal → temporalio}/interceptor/client.rb +27 -2
  252. data/lib/temporalio/interceptor.rb +22 -0
  253. data/lib/{temporal → temporalio}/payload_codec/base.rb +5 -5
  254. data/lib/{temporal → temporalio}/payload_converter/base.rb +3 -3
  255. data/lib/{temporal → temporalio}/payload_converter/bytes.rb +4 -3
  256. data/lib/{temporal → temporalio}/payload_converter/composite.rb +7 -5
  257. data/lib/{temporal → temporalio}/payload_converter/encoding_base.rb +4 -4
  258. data/lib/{temporal → temporalio}/payload_converter/json.rb +4 -3
  259. data/lib/{temporal → temporalio}/payload_converter/nil.rb +4 -3
  260. data/lib/temporalio/payload_converter.rb +14 -0
  261. data/lib/{temporal → temporalio}/retry_policy.rb +17 -7
  262. data/lib/{temporal → temporalio}/retry_state.rb +1 -1
  263. data/lib/temporalio/runtime.rb +25 -0
  264. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  265. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  266. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  267. data/lib/temporalio/testing.rb +175 -0
  268. data/lib/{temporal → temporalio}/timeout_type.rb +2 -2
  269. data/lib/temporalio/version.rb +3 -0
  270. data/lib/temporalio/worker/activity_runner.rb +114 -0
  271. data/lib/temporalio/worker/activity_worker.rb +164 -0
  272. data/lib/temporalio/worker/reactor.rb +46 -0
  273. data/lib/temporalio/worker/runner.rb +63 -0
  274. data/lib/temporalio/worker/sync_worker.rb +124 -0
  275. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  276. data/lib/temporalio/worker.rb +204 -0
  277. data/lib/temporalio/workflow/async.rb +46 -0
  278. data/lib/{temporal → temporalio}/workflow/execution_info.rb +4 -4
  279. data/lib/{temporal → temporalio}/workflow/execution_status.rb +1 -1
  280. data/lib/temporalio/workflow/future.rb +138 -0
  281. data/lib/{temporal → temporalio}/workflow/id_reuse_policy.rb +6 -6
  282. data/lib/temporalio/workflow/info.rb +76 -0
  283. data/lib/{temporal → temporalio}/workflow/query_reject_condition.rb +5 -5
  284. data/lib/temporalio.rb +12 -3
  285. data/temporalio.gemspec +11 -6
  286. metadata +137 -64
  287. data/bridge/sdk-core/Cargo.lock +0 -2606
  288. data/bridge/sdk-core/bridge-ffi/Cargo.toml +0 -24
  289. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  290. data/bridge/sdk-core/bridge-ffi/build.rs +0 -25
  291. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -249
  292. data/bridge/sdk-core/bridge-ffi/src/lib.rs +0 -825
  293. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +0 -211
  294. data/bridge/sdk-core/core/src/log_export.rs +0 -62
  295. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  296. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  297. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  298. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  299. data/bridge/sdk-core/sdk/src/conversions.rs +0 -8
  300. data/lib/bridge.so +0 -0
  301. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +0 -67
  302. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +0 -26
  303. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
  304. data/lib/temporal/bridge.rb +0 -14
  305. data/lib/temporal/connection.rb +0 -736
  306. data/lib/temporal/failure_converter.rb +0 -8
  307. data/lib/temporal/payload_converter.rb +0 -14
  308. data/lib/temporal/runtime.rb +0 -22
  309. data/lib/temporal/version.rb +0 -3
  310. data/lib/temporal.rb +0 -8
@@ -1,26 +1,32 @@
1
1
  mod local_acts;
2
2
 
3
- pub(crate) use temporal_sdk_core_api::errors::WFMachinesError;
4
-
5
3
  use super::{
6
- activity_state_machine::new_activity, cancel_external_state_machine::new_external_cancel,
4
+ cancel_external_state_machine::new_external_cancel,
7
5
  cancel_workflow_state_machine::cancel_workflow,
8
- child_workflow_state_machine::new_child_workflow,
9
6
  complete_workflow_state_machine::complete_workflow,
10
7
  continue_as_new_workflow_state_machine::continue_as_new,
11
8
  fail_workflow_state_machine::fail_workflow, local_activity_state_machine::new_local_activity,
12
9
  patch_state_machine::has_change, signal_external_state_machine::new_external_signal,
13
10
  timer_state_machine::new_timer, upsert_search_attributes_state_machine::upsert_search_attrs,
14
11
  workflow_machines::local_acts::LocalActivityData,
15
- workflow_task_state_machine::WorkflowTaskMachine, MachineKind, Machines, NewMachineWithCommand,
12
+ workflow_task_state_machine::WorkflowTaskMachine, Machines, NewMachineWithCommand,
16
13
  TemporalStateMachine,
17
14
  };
18
15
  use crate::{
16
+ internal_flags::InternalFlags,
19
17
  protosext::{HistoryEventExt, ValidScheduleLA},
20
18
  telemetry::{metrics::MetricsContext, VecDisplayer},
21
19
  worker::{
22
20
  workflow::{
23
- CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, WFCommand, WorkflowFetcher,
21
+ history_update::NextWFT,
22
+ machines::{
23
+ activity_state_machine::ActivityMachine,
24
+ child_workflow_state_machine::ChildWorkflowMachine,
25
+ modify_workflow_properties_state_machine::modify_workflow_properties,
26
+ HistEventData,
27
+ },
28
+ CommandID, DrivenWorkflow, HistoryUpdate, InternalFlagsRef, LocalResolution,
29
+ OutgoingJob, RunBasics, WFCommand, WFMachinesError, WorkflowFetcher,
24
30
  WorkflowStartedInfo,
25
31
  },
26
32
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
@@ -30,14 +36,17 @@ use siphasher::sip::SipHasher13;
30
36
  use slotmap::{SlotMap, SparseSecondaryMap};
31
37
  use std::{
32
38
  borrow::{Borrow, BorrowMut},
39
+ cell::RefCell,
33
40
  collections::{HashMap, VecDeque},
34
41
  convert::TryInto,
35
42
  hash::{Hash, Hasher},
43
+ rc::Rc,
36
44
  time::{Duration, Instant, SystemTime},
37
45
  };
38
46
  use temporal_sdk_core_protos::{
39
47
  coresdk::{
40
48
  common::NamespacedWorkflowExecution,
49
+ workflow_activation,
41
50
  workflow_activation::{
42
51
  workflow_activation_job, NotifyHasPatch, UpdateRandomSeed, WorkflowActivation,
43
52
  },
@@ -49,6 +58,7 @@ use temporal_sdk_core_protos::{
49
58
  command::v1::{command::Attributes as ProtoCmdAttrs, Command as ProtoCommand},
50
59
  enums::v1::EventType,
51
60
  history::v1::{history_event, HistoryEvent},
61
+ sdk::v1::WorkflowTaskCompletedMetadata,
52
62
  },
53
63
  };
54
64
 
@@ -93,6 +103,9 @@ pub(crate) struct WorkflowMachines {
93
103
  /// The current workflow time if it has been established. This may differ from the WFT start
94
104
  /// time since local activities may advance the clock
95
105
  current_wf_time: Option<SystemTime>,
106
+ /// The internal flags which have been seen so far during this run's execution and thus are
107
+ /// usable during replay.
108
+ observed_internal_flags: InternalFlagsRef,
96
109
 
97
110
  all_machines: SlotMap<MachineKey, Machines>,
98
111
  /// If a machine key is in this map, that machine was created internally by core, not as a
@@ -131,7 +144,7 @@ pub(crate) struct WorkflowMachines {
131
144
  }
132
145
 
133
146
  #[derive(Debug, derive_more::Display)]
134
- #[display(fmt = "Cmd&Machine({})", "command")]
147
+ #[display(fmt = "Cmd&Machine({command})")]
135
148
  struct CommandAndMachine {
136
149
  command: MachineAssociatedCommand,
137
150
  machine: MachineKey,
@@ -140,7 +153,7 @@ struct CommandAndMachine {
140
153
  #[derive(Debug, derive_more::Display)]
141
154
  enum MachineAssociatedCommand {
142
155
  Real(Box<ProtoCommand>),
143
- #[display(fmt = "FakeLocalActivityMarker({})", "_0")]
156
+ #[display(fmt = "FakeLocalActivityMarker({_0})")]
144
157
  FakeLocalActivityMarker(u32),
145
158
  }
146
159
 
@@ -153,9 +166,9 @@ struct ChangeInfo {
153
166
  #[derive(Debug, derive_more::Display)]
154
167
  #[must_use]
155
168
  #[allow(clippy::large_enum_variant)]
156
- pub enum MachineResponse {
157
- #[display(fmt = "PushWFJob({})", "_0")]
158
- PushWFJob(workflow_activation_job::Variant),
169
+ pub(super) enum MachineResponse {
170
+ #[display(fmt = "PushWFJob({_0})")]
171
+ PushWFJob(OutgoingJob),
159
172
 
160
173
  /// Pushes a new command into the list that will be sent to server once we respond with the
161
174
  /// workflow task completion
@@ -163,31 +176,31 @@ pub enum MachineResponse {
163
176
  /// The machine requests the creation of another *different* machine. This acts as if lang
164
177
  /// had replied to the activation with a command, but we use a special set of IDs to avoid
165
178
  /// collisions.
166
- #[display(fmt = "NewCoreOriginatedCommand({:?})", "_0")]
179
+ #[display(fmt = "NewCoreOriginatedCommand({_0:?})")]
167
180
  NewCoreOriginatedCommand(ProtoCmdAttrs),
168
- #[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
181
+ #[display(fmt = "IssueFakeLocalActivityMarker({_0})")]
169
182
  IssueFakeLocalActivityMarker(u32),
170
183
  #[display(fmt = "TriggerWFTaskStarted")]
171
184
  TriggerWFTaskStarted {
172
185
  task_started_event_id: i64,
173
186
  time: SystemTime,
174
187
  },
175
- #[display(fmt = "UpdateRunIdOnWorkflowReset({})", run_id)]
188
+ #[display(fmt = "UpdateRunIdOnWorkflowReset({run_id})")]
176
189
  UpdateRunIdOnWorkflowReset { run_id: String },
177
190
 
178
191
  /// Queue a local activity to be processed by the worker
179
192
  #[display(fmt = "QueueLocalActivity")]
180
193
  QueueLocalActivity(ValidScheduleLA),
181
194
  /// Request cancellation of an executing local activity
182
- #[display(fmt = "RequestCancelLocalActivity({})", "_0")]
195
+ #[display(fmt = "RequestCancelLocalActivity({_0})")]
183
196
  RequestCancelLocalActivity(u32),
184
197
  /// Indicates we are abandoning the indicated LA, so we can remove it from "outstanding" LAs
185
198
  /// and we will not try to WFT heartbeat because of it.
186
- #[display(fmt = "AbandonLocalActivity({:?})", "_0")]
199
+ #[display(fmt = "AbandonLocalActivity({_0:?})")]
187
200
  AbandonLocalActivity(u32),
188
201
 
189
202
  /// Set the workflow time to the provided time
190
- #[display(fmt = "UpdateWFTime({:?})", "_0")]
203
+ #[display(fmt = "UpdateWFTime({_0:?})")]
191
204
  UpdateWFTime(Option<SystemTime>),
192
205
  }
193
206
 
@@ -196,30 +209,27 @@ where
196
209
  T: Into<workflow_activation_job::Variant>,
197
210
  {
198
211
  fn from(v: T) -> Self {
199
- Self::PushWFJob(v.into())
212
+ Self::PushWFJob(v.into().into())
200
213
  }
201
214
  }
202
215
 
203
216
  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;
217
+ pub(crate) fn new(basics: RunBasics, driven_wf: DrivenWorkflow) -> Self {
218
+ let replaying = basics.history.previous_wft_started_id > 0;
219
+ let mut observed_internal_flags = InternalFlags::new(basics.capabilities);
220
+ // Peek ahead to determine used patches in the first WFT.
221
+ if let Some(attrs) = basics.history.peek_next_wft_completed(0) {
222
+ observed_internal_flags.add_from_complete(attrs);
223
+ };
214
224
  Self {
215
- last_history_from_server: history,
216
- namespace,
217
- workflow_id,
218
- workflow_type,
219
- run_id,
225
+ last_history_from_server: basics.history,
226
+ namespace: basics.namespace,
227
+ workflow_id: basics.workflow_id,
228
+ workflow_type: basics.workflow_type,
229
+ run_id: basics.run_id,
220
230
  drive_me: driven_wf,
221
231
  replaying,
222
- metrics,
232
+ metrics: basics.metrics,
223
233
  // In an ideal world one could say ..Default::default() here and it'd still work.
224
234
  current_started_event_id: 0,
225
235
  next_started_event_id: 0,
@@ -228,6 +238,7 @@ impl WorkflowMachines {
228
238
  workflow_end_time: None,
229
239
  wft_start_time: None,
230
240
  current_wf_time: None,
241
+ observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
231
242
  all_machines: Default::default(),
232
243
  machine_is_core_created: Default::default(),
233
244
  machines_by_event_id: Default::default(),
@@ -253,10 +264,10 @@ impl WorkflowMachines {
253
264
  .and_then(|(st, et)| et.duration_since(st).ok())
254
265
  }
255
266
 
256
- pub(crate) async fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
267
+ pub(crate) fn new_history_from_server(&mut self, update: HistoryUpdate) -> Result<()> {
257
268
  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?;
269
+ self.replaying = self.last_history_from_server.previous_wft_started_id > 0;
270
+ self.apply_next_wft_from_history()?;
260
271
  Ok(())
261
272
  }
262
273
 
@@ -288,9 +299,8 @@ impl WorkflowMachines {
288
299
  self.process_machine_responses(mk, resps)?;
289
300
  } else {
290
301
  return Err(WFMachinesError::Nondeterminism(format!(
291
- "Command matching activity with seq num {} existed but was not a \
292
- local activity!",
293
- seq
302
+ "Command matching activity with seq num {seq} existed but was not a \
303
+ local activity!"
294
304
  )));
295
305
  }
296
306
  self.local_activity_data.done_executing(seq);
@@ -315,6 +325,237 @@ impl WorkflowMachines {
315
325
  self.drive_me.get_started_info()
316
326
  }
317
327
 
328
+ /// Fetches commands which are ready for processing from the state machines, generally to be
329
+ /// sent off to the server. They are not removed from the internal queue, that happens when
330
+ /// corresponding history events from the server are being handled.
331
+ pub(crate) fn get_commands(&self) -> Vec<ProtoCommand> {
332
+ self.commands
333
+ .iter()
334
+ .filter_map(|c| {
335
+ if !self.machine(c.machine).is_final_state() {
336
+ match &c.command {
337
+ MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
338
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
339
+ }
340
+ } else {
341
+ None
342
+ }
343
+ })
344
+ .collect()
345
+ }
346
+
347
+ /// Returns the next activation that needs to be performed by the lang sdk. Things like unblock
348
+ /// timer, etc. This does *not* cause any advancement of the state machines, it merely drains
349
+ /// from the outgoing queue of activation jobs.
350
+ ///
351
+ /// The job list may be empty, in which case it is expected the caller handles what to do in a
352
+ /// "no work" situation. Possibly, it may know about some work the machines don't, like queries.
353
+ pub(crate) fn get_wf_activation(&mut self) -> WorkflowActivation {
354
+ let jobs = self.drive_me.drain_jobs();
355
+ WorkflowActivation {
356
+ timestamp: self.current_wf_time.map(Into::into),
357
+ is_replaying: self.replaying,
358
+ run_id: self.run_id.clone(),
359
+ history_length: self.last_processed_event as u32,
360
+ jobs,
361
+ available_internal_flags: (*self.observed_internal_flags)
362
+ .borrow()
363
+ .all_lang()
364
+ .iter()
365
+ .copied()
366
+ .collect(),
367
+ }
368
+ }
369
+
370
+ pub(crate) fn has_pending_jobs(&self) -> bool {
371
+ !self.drive_me.peek_pending_jobs().is_empty()
372
+ }
373
+
374
+ pub(crate) fn has_pending_la_resolutions(&self) -> bool {
375
+ self.drive_me
376
+ .peek_pending_jobs()
377
+ .iter()
378
+ .any(|v| v.is_la_resolution)
379
+ }
380
+
381
+ pub(crate) fn get_metadata_for_wft_complete(&self) -> WorkflowTaskCompletedMetadata {
382
+ (*self.observed_internal_flags)
383
+ .borrow_mut()
384
+ .gather_for_wft_complete()
385
+ }
386
+
387
+ pub(crate) fn add_lang_used_flags(&self, flags: Vec<u32>) {
388
+ (*self.observed_internal_flags)
389
+ .borrow_mut()
390
+ .add_lang_used(flags);
391
+ }
392
+
393
+ /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
394
+ /// the workflow code, handling them, and preparing them to be sent off to the server.
395
+ pub(crate) fn iterate_machines(&mut self) -> Result<()> {
396
+ let results = self.drive_me.fetch_workflow_iteration_output();
397
+ self.handle_driven_results(results)?;
398
+ self.prepare_commands()?;
399
+ if self.workflow_is_finished() {
400
+ if let Some(rt) = self.total_runtime() {
401
+ self.metrics.wf_e2e_latency(rt);
402
+ }
403
+ }
404
+ Ok(())
405
+ }
406
+
407
+ /// Returns true if machines are ready to apply the next WFT sequence, false if events will need
408
+ /// to be fetched in order to create a complete update with the entire next WFT sequence.
409
+ pub(crate) fn ready_to_apply_next_wft(&self) -> bool {
410
+ self.last_history_from_server
411
+ .can_take_next_wft_sequence(self.current_started_event_id)
412
+ }
413
+
414
+ /// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
415
+ /// any events that need to be replayed until caught up to the newest WFT.
416
+ pub(crate) fn apply_next_wft_from_history(&mut self) -> Result<usize> {
417
+ // If we have already seen the terminal event for the entire workflow in a previous WFT,
418
+ // then we don't need to do anything here, and in fact we need to avoid re-applying the
419
+ // final WFT.
420
+ if self.have_seen_terminal_event {
421
+ return Ok(0);
422
+ }
423
+
424
+ // Update observed patches with any that were used in the task
425
+ if let Some(next_complete) = self
426
+ .last_history_from_server
427
+ .peek_next_wft_completed(self.last_processed_event)
428
+ {
429
+ (*self.observed_internal_flags)
430
+ .borrow_mut()
431
+ .add_from_complete(next_complete);
432
+ }
433
+
434
+ let last_handled_wft_started_id = self.current_started_event_id;
435
+ let (events, has_final_event) = match self
436
+ .last_history_from_server
437
+ .take_next_wft_sequence(last_handled_wft_started_id)
438
+ {
439
+ NextWFT::ReplayOver => (vec![], true),
440
+ NextWFT::WFT(mut evts, has_final_event) => {
441
+ // Do not re-process events we have already processed
442
+ evts.retain(|e| e.event_id > self.last_processed_event);
443
+ (evts, has_final_event)
444
+ }
445
+ NextWFT::NeedFetch => {
446
+ return Err(WFMachinesError::Fatal(
447
+ "Need to fetch history events to continue applying workflow task, but this \
448
+ should be prevented ahead of time! This is a Core SDK bug."
449
+ .to_string(),
450
+ ));
451
+ }
452
+ };
453
+ let num_events_to_process = events.len();
454
+
455
+ // We're caught up on reply if there are no new events to process
456
+ if events.is_empty() {
457
+ self.replaying = false;
458
+ }
459
+ let replay_start = Instant::now();
460
+
461
+ if let Some(last_event) = events.last() {
462
+ if last_event.event_type == EventType::WorkflowTaskStarted as i32 {
463
+ self.next_started_event_id = last_event.event_id;
464
+ }
465
+ }
466
+
467
+ let mut saw_completed = false;
468
+ let mut history = events.into_iter().peekable();
469
+ while let Some(event) = history.next() {
470
+ if event.event_id != self.last_processed_event + 1 {
471
+ return Err(WFMachinesError::Fatal(format!(
472
+ "History is out of order. Last processed event: {}, event id: {}",
473
+ self.last_processed_event, event.event_id
474
+ )));
475
+ }
476
+ let next_event = history.peek();
477
+ let eid = event.event_id;
478
+
479
+ // This definition of replaying here is that we are no longer replaying as soon as we
480
+ // see new events that have never been seen or produced by the SDK.
481
+ //
482
+ // Specifically, replay ends once we have seen the last command-event which was produced
483
+ // as a result of the last completed WFT. Thus, replay would be false for things like
484
+ // signals which were received and after the last completion, and thus generated the
485
+ // current WFT being handled.
486
+ if self.replaying && has_final_event && saw_completed && !event.is_command_event() {
487
+ // Replay is finished
488
+ self.replaying = false;
489
+ }
490
+ if event.event_type() == EventType::WorkflowTaskCompleted {
491
+ saw_completed = true;
492
+ }
493
+
494
+ self.handle_event(
495
+ HistEventData {
496
+ event,
497
+ replaying: self.replaying,
498
+ current_task_is_last_in_history: has_final_event,
499
+ },
500
+ next_event.is_some() || !has_final_event,
501
+ )?;
502
+ self.last_processed_event = eid;
503
+ }
504
+
505
+ // Scan through to the next WFT, searching for any patch / la markers, so that we can
506
+ // pre-resolve them.
507
+ let mut wake_las = vec![];
508
+ for e in self
509
+ .last_history_from_server
510
+ .peek_next_wft_sequence(last_handled_wft_started_id)
511
+ {
512
+ if let Some((patch_id, _)) = e.get_patch_marker_details() {
513
+ self.encountered_change_markers.insert(
514
+ patch_id.clone(),
515
+ ChangeInfo {
516
+ created_command: false,
517
+ },
518
+ );
519
+ // Found a patch marker
520
+ self.drive_me.send_job(
521
+ workflow_activation_job::Variant::NotifyHasPatch(NotifyHasPatch { patch_id })
522
+ .into(),
523
+ );
524
+ } else if e.is_local_activity_marker() {
525
+ if let Some(la_dat) = e.clone().into_local_activity_marker_details() {
526
+ if let Ok(mk) =
527
+ self.get_machine_key(CommandID::LocalActivity(la_dat.marker_dat.seq))
528
+ {
529
+ wake_las.push((mk, la_dat));
530
+ } else {
531
+ self.local_activity_data.insert_peeked_marker(la_dat);
532
+ }
533
+ } else {
534
+ return Err(WFMachinesError::Fatal(format!(
535
+ "Local activity marker was unparsable: {e:?}"
536
+ )));
537
+ }
538
+ }
539
+ }
540
+ for (mk, la_dat) in wake_las {
541
+ let mach = self.machine_mut(mk);
542
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
543
+ if lam.will_accept_resolve_marker() {
544
+ let resps = lam.try_resolve_with_dat(la_dat.into())?;
545
+ self.process_machine_responses(mk, resps)?;
546
+ } else {
547
+ self.local_activity_data.insert_peeked_marker(la_dat);
548
+ }
549
+ }
550
+ }
551
+
552
+ if !self.replaying {
553
+ self.metrics.wf_task_replay_latency(replay_start.elapsed());
554
+ }
555
+
556
+ Ok(num_events_to_process)
557
+ }
558
+
318
559
  /// Handle a single event from the workflow history. `has_next_event` should be false if `event`
319
560
  /// is the last event in the history.
320
561
  ///
@@ -323,14 +564,9 @@ impl WorkflowMachines {
323
564
  /// event is applied to the machine, which may also return a nondeterminism error if the machine
324
565
  /// does not match the expected type. A fatal error may be returned if the machine is in an
325
566
  /// 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
- }
567
+ #[instrument(skip(self, event_dat), fields(event=%event_dat))]
568
+ fn handle_event(&mut self, event_dat: HistEventData, has_next_event: bool) -> Result<()> {
569
+ let event = &event_dat.event;
334
570
  if event.is_final_wf_execution_event() {
335
571
  self.have_seen_terminal_event = true;
336
572
  }
@@ -348,69 +584,45 @@ impl WorkflowMachines {
348
584
  Ok(())
349
585
  };
350
586
  }
587
+ if event.event_type() == EventType::Unspecified || event.attributes.is_none() {
588
+ return if !event.worker_may_ignore {
589
+ Err(WFMachinesError::Fatal(format!(
590
+ "Event type is unspecified! This history is invalid. Event detail: {event:?}"
591
+ )))
592
+ } else {
593
+ debug!("Event is ignorable");
594
+ Ok(())
595
+ };
596
+ }
351
597
 
352
598
  if event.is_command_event() {
353
- self.handle_command_event(event)?;
599
+ self.handle_command_event(event_dat)?;
354
600
  return Ok(());
355
601
  }
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
602
 
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
- )));
603
+ if let Some(initial_cmd_id) = event.get_initial_command_event_id() {
604
+ // We remove the machine while we it handles events, then return it, to avoid
605
+ // borrowing from ourself mutably.
606
+ let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
607
+ match maybe_machine {
608
+ Some(sm) => {
609
+ self.submachine_handle_event(sm, event_dat)?;
610
+ // Restore machine if not in it's final state
611
+ if !self.machine(sm).is_final_state() {
612
+ self.machines_by_event_id.insert(initial_cmd_id, sm);
384
613
  }
385
614
  }
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()?));
615
+ None => {
616
+ return Err(WFMachinesError::Nondeterminism(format!(
617
+ "During event handling, this event had an initial command ID but we \
618
+ could not find a matching command for it: {event:?}"
619
+ )));
408
620
  }
409
621
  }
410
- for (mkey, resp_set) in resps {
411
- self.process_machine_responses(mkey, resp_set)?;
412
- }
622
+ } else {
623
+ self.handle_non_stateful_event(event_dat)?;
413
624
  }
625
+
414
626
  Ok(())
415
627
  }
416
628
 
@@ -423,23 +635,23 @@ impl WorkflowMachines {
423
635
  /// The handling consists of verifying that the next command in the commands queue is associated
424
636
  /// with a state machine, which is then notified about the event and the command is removed from
425
637
  /// the commands queue.
426
- fn handle_command_event(&mut self, event: HistoryEvent) -> Result<()> {
638
+ fn handle_command_event(&mut self, event_dat: HistEventData) -> Result<()> {
639
+ let event = &event_dat.event;
427
640
  if event.is_local_activity_marker() {
428
641
  let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
429
- WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
642
+ WFMachinesError::Fatal(format!("Local activity marker was unparsable: {event:?}"))
430
643
  })?;
431
644
  let cmdid = CommandID::LocalActivity(deets.seq);
432
645
  let mkey = self.get_machine_key(cmdid)?;
433
646
  if let Machines::LocalActivityMachine(lam) = self.machine(mkey) {
434
647
  if lam.marker_should_get_special_handling()? {
435
- self.submachine_handle_event(mkey, event, false)?;
648
+ self.submachine_handle_event(mkey, event_dat)?;
436
649
  return Ok(());
437
650
  }
438
651
  } else {
439
652
  return Err(WFMachinesError::Fatal(format!(
440
653
  "Encountered local activity marker but the associated machine was of the \
441
- wrong type! {:?}",
442
- event
654
+ wrong type! {event:?}"
443
655
  )));
444
656
  }
445
657
  }
@@ -449,7 +661,7 @@ impl WorkflowMachines {
449
661
  let consumed_cmd = loop {
450
662
  if let Some(peek_machine) = self.commands.front() {
451
663
  let mach = self.machine(peek_machine.machine);
452
- match change_marker_handling(&event, mach)? {
664
+ match change_marker_handling(event, mach)? {
453
665
  ChangeMarkerOutcome::SkipEvent => return Ok(()),
454
666
  ChangeMarkerOutcome::SkipCommand => {
455
667
  self.commands.pop_front();
@@ -464,8 +676,7 @@ impl WorkflowMachines {
464
676
  c
465
677
  } else {
466
678
  return Err(WFMachinesError::Nondeterminism(format!(
467
- "No command scheduled for event {}",
468
- event
679
+ "No command scheduled for event {event}"
469
680
  )));
470
681
  };
471
682
 
@@ -475,7 +686,7 @@ impl WorkflowMachines {
475
686
 
476
687
  if !canceled_before_sent {
477
688
  // Feed the machine the event
478
- self.submachine_handle_event(command.machine, event, true)?;
689
+ self.submachine_handle_event(command.machine, event_dat)?;
479
690
  break command;
480
691
  }
481
692
  };
@@ -488,23 +699,19 @@ impl WorkflowMachines {
488
699
  Ok(())
489
700
  }
490
701
 
491
- fn handle_non_stateful_event(
492
- &mut self,
493
- event: HistoryEvent,
494
- has_next_event: bool,
495
- ) -> Result<()> {
702
+ fn handle_non_stateful_event(&mut self, event_dat: HistEventData) -> Result<()> {
496
703
  trace!(
497
- event = %event,
704
+ event = %event_dat.event,
498
705
  "handling non-stateful event"
499
706
  );
500
- let event_id = event.event_id;
501
- match EventType::from_i32(event.event_type) {
707
+ let event_id = event_dat.event.event_id;
708
+ match EventType::from_i32(event_dat.event.event_type) {
502
709
  Some(EventType::WorkflowExecutionStarted) => {
503
710
  if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
504
711
  attrs,
505
- )) = event.attributes
712
+ )) = event_dat.event.attributes
506
713
  {
507
- if let Some(st) = event.event_time {
714
+ if let Some(st) = event_dat.event.event_time.clone() {
508
715
  let as_systime: SystemTime = st.try_into()?;
509
716
  self.workflow_start_time = Some(as_systime);
510
717
  // Set the workflow time to be the event time of the first event, so that
@@ -516,27 +723,28 @@ impl WorkflowMachines {
516
723
  self.drive_me.start(
517
724
  self.workflow_id.clone(),
518
725
  str_to_randomness_seed(&attrs.original_execution_run_id),
726
+ event_dat.event.event_time.unwrap_or_default(),
519
727
  attrs,
520
728
  );
521
729
  } else {
522
730
  return Err(WFMachinesError::Fatal(format!(
523
- "WorkflowExecutionStarted event did not have appropriate attributes: {}",
524
- event
731
+ "WorkflowExecutionStarted event did not have appropriate attributes: {event_dat}"
525
732
  )));
526
733
  }
527
734
  }
528
735
  Some(EventType::WorkflowTaskScheduled) => {
529
736
  let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
530
737
  let key = self.all_machines.insert(wf_task_sm.into());
531
- self.submachine_handle_event(key, event, has_next_event)?;
738
+ self.submachine_handle_event(key, event_dat)?;
532
739
  self.machines_by_event_id.insert(event_id, key);
533
740
  }
534
741
  Some(EventType::WorkflowExecutionSignaled) => {
535
742
  if let Some(history_event::Attributes::WorkflowExecutionSignaledEventAttributes(
536
743
  attrs,
537
- )) = event.attributes
744
+ )) = event_dat.event.attributes
538
745
  {
539
- self.drive_me.signal(attrs.into());
746
+ self.drive_me
747
+ .send_job(workflow_activation::SignalWorkflow::from(attrs).into());
540
748
  } else {
541
749
  // err
542
750
  }
@@ -546,63 +754,23 @@ impl WorkflowMachines {
546
754
  history_event::Attributes::WorkflowExecutionCancelRequestedEventAttributes(
547
755
  attrs,
548
756
  ),
549
- ) = event.attributes
757
+ ) = event_dat.event.attributes
550
758
  {
551
- self.drive_me.cancel(attrs.into());
759
+ self.drive_me
760
+ .send_job(workflow_activation::CancelWorkflow::from(attrs).into());
552
761
  } else {
553
762
  // err
554
763
  }
555
764
  }
556
765
  _ => {
557
766
  return Err(WFMachinesError::Fatal(format!(
558
- "The event is not a non-stateful event, but we tried to handle it as one: {}",
559
- event
767
+ "The event is not a non-stateful event, but we tried to handle it as one: {event_dat}"
560
768
  )));
561
769
  }
562
770
  }
563
771
  Ok(())
564
772
  }
565
773
 
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
774
  fn set_current_time(&mut self, time: SystemTime) -> SystemTime {
607
775
  if self.current_wf_time.map_or(true, |t| t < time) {
608
776
  self.current_wf_time = Some(time);
@@ -611,111 +779,10 @@ impl WorkflowMachines {
611
779
  .expect("We have just ensured this is populated")
612
780
  }
613
781
 
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
782
  /// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
711
783
  /// 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)?;
784
+ fn submachine_handle_event(&mut self, sm: MachineKey, event: HistEventData) -> Result<()> {
785
+ let machine_responses = self.machine_mut(sm).handle_event(event)?;
719
786
  self.process_machine_responses(sm, machine_responses)?;
720
787
  Ok(())
721
788
  }
@@ -765,7 +832,7 @@ impl WorkflowMachines {
765
832
  ) -> Result<()> {
766
833
  let sm = self.machine(smk);
767
834
  if !machine_responses.is_empty() {
768
- debug!(responses = %machine_responses.display(), machine_name = %sm.kind(),
835
+ trace!(responses = %machine_responses.display(), machine_name = %sm.name(),
769
836
  "Machine produced responses");
770
837
  }
771
838
  self.process_machine_resps_impl(smk, machine_responses)
@@ -791,14 +858,12 @@ impl WorkflowMachines {
791
858
  self.task_started(task_started_event_id, time)?;
792
859
  }
793
860
  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
- ));
861
+ self.drive_me.send_job(
862
+ workflow_activation_job::Variant::UpdateRandomSeed(UpdateRandomSeed {
863
+ randomness_seed: str_to_randomness_seed(&new_run_id),
864
+ })
865
+ .into(),
866
+ );
802
867
  }
803
868
  MachineResponse::IssueNewCommand(c) => {
804
869
  self.current_wf_task_commands.push_back(CommandAndMachine {
@@ -822,8 +887,7 @@ impl WorkflowMachines {
822
887
  }
823
888
  c => {
824
889
  return Err(WFMachinesError::Fatal(format!(
825
- "A machine requested to create a new command of an unsupported type: {:?}",
826
- c
890
+ "A machine requested to create a new command of an unsupported type: {c:?}"
827
891
  )))
828
892
  }
829
893
  },
@@ -860,7 +924,7 @@ impl WorkflowMachines {
860
924
  Duration::from_secs(0),
861
925
  removed_act.attempt,
862
926
  None,
863
- None,
927
+ removed_act.original_schedule_time,
864
928
  )?;
865
929
  self.process_machine_responses(smk, more_responses)?;
866
930
  } else {
@@ -888,6 +952,30 @@ impl WorkflowMachines {
888
952
  Ok(())
889
953
  }
890
954
 
955
+ /// Called when a workflow task started event has triggered. Ensures we are tracking the ID
956
+ /// of the current started event as well as workflow time properly.
957
+ fn task_started(&mut self, task_started_event_id: i64, time: SystemTime) -> Result<()> {
958
+ self.current_started_event_id = task_started_event_id;
959
+ self.wft_start_time = Some(time);
960
+ self.set_current_time(time);
961
+
962
+ // Notify local activity machines that we started a non-replay WFT, which will allow any
963
+ // which were waiting for a marker to instead decide to execute the LA since it clearly
964
+ // will not be resolved via marker.
965
+ if !self.replaying {
966
+ let mut resps = vec![];
967
+ for (k, mach) in self.all_machines.iter_mut() {
968
+ if let Machines::LocalActivityMachine(lam) = mach {
969
+ resps.push((k, lam.encountered_non_replay_wft()?));
970
+ }
971
+ }
972
+ for (mkey, resp_set) in resps {
973
+ self.process_machine_responses(mkey, resp_set)?;
974
+ }
975
+ }
976
+ Ok(())
977
+ }
978
+
891
979
  /// Handles results of the workflow activation, delegating work to the appropriate state
892
980
  /// machine. Returns a list of workflow jobs that should be queued in the pending activation for
893
981
  /// the next poll. This list will be populated only if state machine produced lang activations
@@ -912,7 +1000,10 @@ impl WorkflowMachines {
912
1000
  }
913
1001
  WFCommand::AddActivity(attrs) => {
914
1002
  let seq = attrs.seq;
915
- self.add_cmd_to_wf_task(new_activity(attrs), CommandID::Activity(seq).into());
1003
+ self.add_cmd_to_wf_task(
1004
+ ActivityMachine::new_scheduled(attrs, self.observed_internal_flags.clone()),
1005
+ CommandID::Activity(seq).into(),
1006
+ );
916
1007
  }
917
1008
  WFCommand::AddLocalActivity(attrs) => {
918
1009
  let seq = attrs.seq;
@@ -924,8 +1015,7 @@ impl WorkflowMachines {
924
1015
  )
925
1016
  .map_err(|e| {
926
1017
  WFMachinesError::Fatal(format!(
927
- "Invalid schedule local activity request (seq {}): {}",
928
- seq, e
1018
+ "Invalid schedule local activity request (seq {seq}): {e}"
929
1019
  ))
930
1020
  })?;
931
1021
  let (la, mach_resp) = new_local_activity(
@@ -933,6 +1023,7 @@ impl WorkflowMachines {
933
1023
  self.replaying,
934
1024
  self.local_activity_data.take_preresolution(seq),
935
1025
  self.current_wf_time,
1026
+ self.observed_internal_flags.clone(),
936
1027
  )?;
937
1028
  let machkey = self.all_machines.insert(la.into());
938
1029
  self.id_to_machine
@@ -988,7 +1079,10 @@ impl WorkflowMachines {
988
1079
  WFCommand::AddChildWorkflow(attrs) => {
989
1080
  let seq = attrs.seq;
990
1081
  self.add_cmd_to_wf_task(
991
- new_child_workflow(attrs),
1082
+ ChildWorkflowMachine::new_scheduled(
1083
+ attrs,
1084
+ self.observed_internal_flags.clone(),
1085
+ ),
992
1086
  CommandID::ChildWorkflowStart(seq).into(),
993
1087
  );
994
1088
  }
@@ -1037,6 +1131,12 @@ impl WorkflowMachines {
1037
1131
  // Nothing to do here, queries are handled above the machine level
1038
1132
  unimplemented!("Query responses should not make it down into the machines")
1039
1133
  }
1134
+ WFCommand::ModifyWorkflowProperties(attrs) => {
1135
+ self.add_cmd_to_wf_task(
1136
+ modify_workflow_properties(attrs),
1137
+ CommandIdKind::NeverResolves,
1138
+ );
1139
+ }
1040
1140
  WFCommand::NoCommandsFromLang => (),
1041
1141
  }
1042
1142
  }
@@ -1056,7 +1156,7 @@ impl WorkflowMachines {
1056
1156
 
1057
1157
  fn get_machine_key(&self, id: CommandID) -> Result<MachineKey> {
1058
1158
  Ok(*self.id_to_machine.get(&id).ok_or_else(|| {
1059
- WFMachinesError::Fatal(format!("Missing associated machine for {:?}", id))
1159
+ WFMachinesError::Fatal(format!("Missing associated machine for {id:?}"))
1060
1160
  })?)
1061
1161
  }
1062
1162
 
@@ -1143,10 +1243,7 @@ enum ChangeMarkerOutcome {
1143
1243
 
1144
1244
  /// Special handling for patch markers, when handling command events as in
1145
1245
  /// [WorkflowMachines::handle_command_event]
1146
- fn change_marker_handling(
1147
- event: &HistoryEvent,
1148
- mach: &dyn TemporalStateMachine,
1149
- ) -> Result<ChangeMarkerOutcome> {
1246
+ fn change_marker_handling(event: &HistoryEvent, mach: &Machines) -> Result<ChangeMarkerOutcome> {
1150
1247
  if !mach.matches_event(event) {
1151
1248
  // Version markers can be skipped in the event they are deprecated
1152
1249
  if let Some((patch_name, deprecated)) = event.get_patch_marker_details() {
@@ -1157,16 +1254,15 @@ fn change_marker_handling(
1157
1254
  return Ok(ChangeMarkerOutcome::SkipEvent);
1158
1255
  }
1159
1256
  return Err(WFMachinesError::Nondeterminism(format!(
1160
- "Non-deprecated patch marker encountered for change {}, \
1161
- but there is no corresponding change command!",
1162
- patch_name
1257
+ "Non-deprecated patch marker encountered for change {patch_name}, \
1258
+ but there is no corresponding change command!"
1163
1259
  )));
1164
1260
  }
1165
- // Version machines themselves may also not *have* matching markers, where non-deprecated
1261
+ // Patch machines themselves may also not *have* matching markers, where non-deprecated
1166
1262
  // calls take the old path, and deprecated calls assume history is produced by a new-code
1167
1263
  // worker.
1168
- if mach.kind() == MachineKind::Patch {
1169
- debug!("Skipping non-matching event against version machine");
1264
+ if matches!(mach, Machines::PatchMachine(_)) {
1265
+ debug!("Skipping non-matching event against patch machine");
1170
1266
  return Ok(ChangeMarkerOutcome::SkipCommand);
1171
1267
  }
1172
1268
  }