temporalio 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }