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
@@ -4,15 +4,17 @@ use crate::{
4
4
  build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
5
5
  mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
6
6
  single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
7
- ResponseType, WorkflowCachingPolicy, TEST_Q,
7
+ QueueResponse, ResponseType, WorkerExt, WorkflowCachingPolicy, TEST_Q,
8
8
  },
9
9
  worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
10
10
  ActivityHeartbeat, Worker, WorkerConfigBuilder,
11
11
  };
12
12
  use futures::FutureExt;
13
+ use itertools::Itertools;
13
14
  use std::{
14
15
  cell::RefCell,
15
16
  collections::{hash_map::Entry, HashMap, VecDeque},
17
+ future,
16
18
  rc::Rc,
17
19
  sync::{
18
20
  atomic::{AtomicUsize, Ordering},
@@ -22,8 +24,10 @@ use std::{
22
24
  };
23
25
  use temporal_client::WorkflowOptions;
24
26
  use temporal_sdk::{ActivityOptions, WfContext};
25
- use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
26
- use temporal_sdk_core_protos::temporal::api::command::v1::ScheduleActivityTaskCommandAttributes;
27
+ use temporal_sdk_core_api::{
28
+ errors::{CompleteActivityError, PollActivityError},
29
+ Worker as WorkerTrait,
30
+ };
27
31
  use temporal_sdk_core_protos::{
28
32
  coresdk::{
29
33
  activity_result::{
@@ -40,8 +44,11 @@ use temporal_sdk_core_protos::{
40
44
  ActivityTaskCompletion,
41
45
  },
42
46
  temporal::api::{
43
- command::v1::command::Attributes,
47
+ command::v1::{command::Attributes, ScheduleActivityTaskCommandAttributes},
44
48
  enums::v1::EventType,
49
+ history::v1::{
50
+ history_event::Attributes as EventAttributes, ActivityTaskScheduledEventAttributes,
51
+ },
45
52
  workflowservice::v1::{
46
53
  PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
47
54
  RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
@@ -52,6 +59,7 @@ use temporal_sdk_core_protos::{
52
59
  };
53
60
  use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
54
61
  use tokio::{sync::Barrier, time::sleep};
62
+ use tokio_util::sync::CancellationToken;
55
63
 
56
64
  #[tokio::test]
57
65
  async fn max_activities_respected() {
@@ -121,7 +129,7 @@ async fn activity_not_found_returns_ok() {
121
129
  })
122
130
  .await
123
131
  .unwrap();
124
- core.shutdown().await;
132
+ core.drain_activity_poller_and_shutdown().await;
125
133
  }
126
134
 
127
135
  #[tokio::test]
@@ -217,12 +225,14 @@ async fn heartbeats_report_cancels_only_once() {
217
225
  })
218
226
  .await
219
227
  .unwrap();
220
- core.shutdown().await;
228
+ core.drain_activity_poller_and_shutdown().await;
221
229
  }
222
230
 
223
231
  #[tokio::test]
224
232
  async fn activity_cancel_interrupts_poll() {
225
233
  let mut mock_poller = mock_manual_poller();
234
+ let shutdown_token = CancellationToken::new();
235
+ let shutdown_token_clone = shutdown_token.clone();
226
236
  let mut poll_resps = VecDeque::from(vec![
227
237
  async {
228
238
  Some(Ok(PollActivityTaskQueueResponse {
@@ -237,10 +247,15 @@ async fn activity_cancel_interrupts_poll() {
237
247
  Some(Ok(Default::default()))
238
248
  }
239
249
  .boxed(),
250
+ async move {
251
+ shutdown_token.cancelled().await;
252
+ None
253
+ }
254
+ .boxed(),
240
255
  ]);
241
256
  mock_poller
242
257
  .expect_poll()
243
- .times(2)
258
+ .times(3)
244
259
  .returning(move || poll_resps.pop_front().unwrap());
245
260
 
246
261
  let mut mock_client = mock_manual_workflow_client();
@@ -289,11 +304,12 @@ async fn activity_cancel_interrupts_poll() {
289
304
  }
290
305
  ).await.unwrap();
291
306
  last_finisher.store(2, Ordering::SeqCst);
307
+ shutdown_token_clone.cancel();
292
308
  }
293
309
  };
294
310
  // So that we know we blocked
295
311
  assert_eq!(last_finisher.load(Ordering::Acquire), 2);
296
- core.shutdown().await;
312
+ core.drain_activity_poller_and_shutdown().await;
297
313
  }
298
314
 
299
315
  #[tokio::test]
@@ -342,13 +358,10 @@ async fn many_concurrent_heartbeat_cancels() {
342
358
  })
343
359
  .collect::<Vec<_>>(),
344
360
  );
345
- // Because the mock is so fast, it's possible it can return before the cancel channel in
346
- // the activity task poll selector. So, the final poll when there are no more tasks must
347
- // take a while.
348
361
  poll_resps.push_back(
349
362
  async {
350
- sleep(Duration::from_secs(10)).await;
351
- unreachable!("Long poll")
363
+ future::pending::<()>().await;
364
+ unreachable!()
352
365
  }
353
366
  .boxed(),
354
367
  );
@@ -431,13 +444,13 @@ async fn many_concurrent_heartbeat_cancels() {
431
444
  })
432
445
  .await;
433
446
 
434
- worker.shutdown().await;
447
+ worker.drain_activity_poller_and_shutdown().await;
435
448
  }
436
449
 
437
450
  #[tokio::test]
438
451
  async fn activity_timeout_no_double_resolve() {
439
452
  let t = canned_histories::activity_double_resolve_repro();
440
- let core = build_fake_worker("fake_wf_id", t, &[3]);
453
+ let core = build_fake_worker("fake_wf_id", t, [3]);
441
454
  let activity_id = 1;
442
455
 
443
456
  poll_and_reply(
@@ -483,7 +496,7 @@ async fn activity_timeout_no_double_resolve() {
483
496
  )
484
497
  .await;
485
498
 
486
- core.shutdown().await;
499
+ core.drain_pollers_and_shutdown().await;
487
500
  }
488
501
 
489
502
  #[tokio::test]
@@ -529,7 +542,7 @@ async fn can_heartbeat_acts_during_shutdown() {
529
542
  })
530
543
  .await
531
544
  .unwrap();
532
- shutdown_fut.await;
545
+ core.drain_activity_poller_and_shutdown().await;
533
546
  }
534
547
 
535
548
  /// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
@@ -580,7 +593,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
580
593
  })
581
594
  .await
582
595
  .unwrap();
583
- core.shutdown().await;
596
+ core.drain_activity_poller_and_shutdown().await;
584
597
 
585
598
  // Verify the last seen call to record a heartbeat had the last detail payload
586
599
  let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
@@ -613,26 +626,20 @@ async fn max_tq_acts_set_passed_to_poll_properly() {
613
626
  worker.poll_activity_task().await.unwrap();
614
627
  }
615
628
 
616
- /// This test verifies that activity tasks which come as replies to completing a WFT are properly
617
- /// delivered via polling.
629
+ /// This test doesn't test the real worker config since [mock_worker] bypasses the worker
630
+ /// constructor, [mock_worker] will not pass an activity poller to the worker when
631
+ /// `no_remote_activities` is set to `true`.
618
632
  #[tokio::test]
619
- async fn activity_tasks_from_completion_are_delivered() {
633
+ async fn no_eager_activities_requested_when_worker_options_disable_remote_activities() {
620
634
  let wfid = "fake_wf_id";
621
635
  let mut t = TestHistoryBuilder::default();
622
636
  t.add_by_type(EventType::WorkflowExecutionStarted);
623
637
  t.add_full_wf_task();
624
- let act_same_queue_sched_id = t.add_activity_task_scheduled("act_id_same_queue");
625
- let act_different_queue_sched_id = t.add_activity_task_scheduled("act_id_different_queue");
626
- let act_same_queue_start_id = t.add_activity_task_started(act_same_queue_sched_id);
627
- t.add_activity_task_completed(
628
- act_same_queue_sched_id,
629
- act_same_queue_start_id,
630
- b"hi".into(),
631
- );
638
+ let scheduled_event_id = t.add_activity_task_scheduled("act_id");
639
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
640
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, b"hi".into());
632
641
  t.add_full_wf_task();
633
- t.add_activity_task_cancel_requested(act_different_queue_sched_id);
634
642
  t.add_workflow_execution_completed();
635
-
636
643
  let num_eager_requested = Arc::new(AtomicUsize::new(0));
637
644
  // Clone it to move into the callback below
638
645
  let num_eager_requested_clone = num_eager_requested.clone();
@@ -658,65 +665,171 @@ async fn activity_tasks_from_completion_are_delivered() {
658
665
  num_eager_requested_clone.store(count, Ordering::Relaxed);
659
666
  Ok(RespondWorkflowTaskCompletedResponse {
660
667
  workflow_task: None,
661
- activity_tasks: vec![PollActivityTaskQueueResponse {
662
- task_token: vec![1],
663
- activity_id: "act_id_same_queue".to_string(),
664
- ..Default::default()
665
- }],
668
+ activity_tasks: vec![],
669
+ reset_history_event_id: 0,
666
670
  })
667
671
  });
668
- mock.expect_complete_activity_task()
669
- .times(1)
670
- .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
671
672
  let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
672
673
  let mut mock_poller = mock_manual_poller();
673
674
  mock_poller
674
675
  .expect_poll()
675
676
  .returning(|| futures::future::pending().boxed());
676
677
  mock.set_act_poller(Box::new(mock_poller));
677
- mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
678
+ mock.worker_cfg(|wc| {
679
+ wc.max_cached_workflows = 2;
680
+ wc.no_remote_activities = true;
681
+ });
678
682
  let core = mock_worker(mock);
679
683
 
684
+ // Test start
680
685
  let wf_task = core.poll_workflow_activation().await.unwrap();
686
+ let cmds = vec![ScheduleActivity {
687
+ seq: 1,
688
+ activity_id: "act_id".to_string(),
689
+ task_queue: TEST_Q.to_string(),
690
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
691
+ ..Default::default()
692
+ }
693
+ .into()];
694
+
681
695
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
682
696
  wf_task.run_id,
683
- vec![
697
+ cmds,
698
+ ))
699
+ .await
700
+ .unwrap();
701
+
702
+ core.drain_pollers_and_shutdown().await;
703
+
704
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 0);
705
+ }
706
+
707
+ /// This test verifies that activity tasks which come as replies to completing a WFT are properly
708
+ /// delivered via polling.
709
+ #[tokio::test]
710
+ async fn activity_tasks_from_completion_are_delivered() {
711
+ // Construct the history - one task with 5 activities, 4 on the same task queue, and 1 on a
712
+ // different queue, 3 activities will be executed eagerly as specified by the
713
+ // MAX_EAGER_ACTIVITY_RESERVATIONS_PER_WORKFLOW_TASK constant.
714
+ let wfid = "fake_wf_id";
715
+ let mut t = TestHistoryBuilder::default();
716
+ t.add_by_type(EventType::WorkflowExecutionStarted);
717
+ t.add_full_wf_task();
718
+ let act_same_queue_scheduled_ids = (1..4)
719
+ .map(|i| t.add_activity_task_scheduled(format!("act_id_{i}_same_queue")))
720
+ .collect_vec();
721
+ t.add_activity_task_scheduled("act_id_same_queue_not_eager");
722
+ t.add_activity_task_scheduled("act_id_different_queue");
723
+ for scheduled_event_id in act_same_queue_scheduled_ids {
724
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
725
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, b"hi".into());
726
+ }
727
+ t.add_full_wf_task();
728
+ t.add_workflow_execution_completed();
729
+
730
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
731
+ // Clone it to move into the callback below
732
+ let num_eager_requested_clone = num_eager_requested.clone();
733
+
734
+ let mut mock = mock_workflow_client();
735
+ mock.expect_complete_workflow_task()
736
+ .times(1)
737
+ .returning(move |req| {
738
+ // Store the number of eager activities requested to be checked below
739
+ let count = req
740
+ .commands
741
+ .into_iter()
742
+ .filter(|c| match c.attributes {
743
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
744
+ ScheduleActivityTaskCommandAttributes {
745
+ request_eager_execution,
746
+ ..
747
+ },
748
+ )) => request_eager_execution,
749
+ _ => false,
750
+ })
751
+ .count();
752
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
753
+ Ok(RespondWorkflowTaskCompletedResponse {
754
+ workflow_task: None,
755
+ activity_tasks: (1..4)
756
+ .map(|i| PollActivityTaskQueueResponse {
757
+ task_token: vec![i],
758
+ activity_id: format!("act_id_{i}_same_queue"),
759
+ ..Default::default()
760
+ })
761
+ .collect_vec(),
762
+ reset_history_event_id: 0,
763
+ })
764
+ });
765
+ mock.expect_complete_activity_task()
766
+ .times(3)
767
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
768
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
769
+ let act_tasks: Vec<QueueResponse<PollActivityTaskQueueResponse>> = vec![];
770
+ mock.set_act_poller(mock_poller_from_resps(act_tasks));
771
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
772
+ let core = mock_worker(mock);
773
+
774
+ // Test start
775
+ let wf_task = core.poll_workflow_activation().await.unwrap();
776
+ let mut cmds = (1..4)
777
+ .map(|seq| {
684
778
  ScheduleActivity {
685
- seq: 1,
686
- activity_id: "act_id_same_queue".to_string(),
779
+ seq,
780
+ activity_id: format!("act_id_{seq}_same_queue"),
687
781
  task_queue: TEST_Q.to_string(),
688
782
  cancellation_type: ActivityCancellationType::TryCancel as i32,
689
783
  ..Default::default()
690
784
  }
691
- .into(),
692
- ScheduleActivity {
693
- seq: 2,
694
- activity_id: "act_id_different_queue".to_string(),
695
- task_queue: "different_queue".to_string(),
696
- cancellation_type: ActivityCancellationType::Abandon as i32,
697
- ..Default::default()
698
- }
699
- .into(),
700
- ],
785
+ .into()
786
+ })
787
+ .collect_vec();
788
+ cmds.push(
789
+ ScheduleActivity {
790
+ seq: 4,
791
+ activity_id: "act_id_same_queue_not_eager".to_string(),
792
+ task_queue: TEST_Q.to_string(),
793
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
794
+ ..Default::default()
795
+ }
796
+ .into(),
797
+ );
798
+ cmds.push(
799
+ ScheduleActivity {
800
+ seq: 5,
801
+ activity_id: "act_id_different_queue".to_string(),
802
+ task_queue: "different_queue".to_string(),
803
+ cancellation_type: ActivityCancellationType::Abandon as i32,
804
+ ..Default::default()
805
+ }
806
+ .into(),
807
+ );
808
+
809
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
810
+ wf_task.run_id,
811
+ cmds,
701
812
  ))
702
813
  .await
703
814
  .unwrap();
704
815
 
705
- // We should see the activity when we poll now
706
- let act_task = core.poll_activity_task().await.unwrap();
707
- assert_eq!(act_task.task_token, vec![1]);
816
+ // We should see the 3 eager activities when we poll now
817
+ for i in 1..4 {
818
+ let act_task = core.poll_activity_task().await.unwrap();
819
+ assert_eq!(act_task.task_token, vec![i]);
708
820
 
709
- core.complete_activity_task(ActivityTaskCompletion {
710
- task_token: act_task.task_token.clone(),
711
- result: Some(ActivityExecutionResult::ok("hi".into())),
712
- })
713
- .await
714
- .unwrap();
821
+ core.complete_activity_task(ActivityTaskCompletion {
822
+ task_token: act_task.task_token.clone(),
823
+ result: Some(ActivityExecutionResult::ok("hi".into())),
824
+ })
825
+ .await
826
+ .unwrap();
827
+ }
715
828
 
716
- core.shutdown().await;
829
+ core.drain_activity_poller_and_shutdown().await;
717
830
 
718
831
  // Verify only a single eager activity was scheduled (the one on our worker's task queue)
719
- assert_eq!(num_eager_requested.load(Ordering::Relaxed), 1);
832
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
720
833
  }
721
834
 
722
835
  #[tokio::test]
@@ -725,11 +838,23 @@ async fn activity_tasks_from_completion_reserve_slots() {
725
838
  let mut t = TestHistoryBuilder::default();
726
839
  t.add_by_type(EventType::WorkflowExecutionStarted);
727
840
  t.add_full_wf_task();
728
- let schedid = t.add_activity_task_scheduled("1");
841
+ let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
842
+ ActivityTaskScheduledEventAttributes {
843
+ activity_id: "1".to_string(),
844
+ activity_type: Some("act1".into()),
845
+ ..Default::default()
846
+ },
847
+ ));
729
848
  let startid = t.add_activity_task_started(schedid);
730
849
  t.add_activity_task_completed(schedid, startid, b"hi".into());
731
850
  t.add_full_wf_task();
732
- let schedid = t.add_activity_task_scheduled("2");
851
+ let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
852
+ ActivityTaskScheduledEventAttributes {
853
+ activity_id: "2".to_string(),
854
+ activity_type: Some("act2".into()),
855
+ ..Default::default()
856
+ },
857
+ ));
733
858
  let startid = t.add_activity_task_started(schedid);
734
859
  t.add_activity_task_completed(schedid, startid, b"hi".into());
735
860
  t.add_full_wf_task();
@@ -799,19 +924,25 @@ async fn activity_tasks_from_completion_reserve_slots() {
799
924
  // First poll for activities twice, occupying both slots
800
925
  let at1 = core.poll_activity_task().await.unwrap();
801
926
  let at2 = core.poll_activity_task().await.unwrap();
927
+ let workflow_complete_token = CancellationToken::new();
928
+ let workflow_complete_token_clone = workflow_complete_token.clone();
802
929
 
803
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
804
- ctx.activity(ActivityOptions {
805
- activity_type: "act1".to_string(),
806
- ..Default::default()
807
- })
808
- .await;
809
- ctx.activity(ActivityOptions {
810
- activity_type: "act2".to_string(),
811
- ..Default::default()
812
- })
813
- .await;
814
- Ok(().into())
930
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| {
931
+ let complete_token = workflow_complete_token.clone();
932
+ async move {
933
+ ctx.activity(ActivityOptions {
934
+ activity_type: "act1".to_string(),
935
+ ..Default::default()
936
+ })
937
+ .await;
938
+ ctx.activity(ActivityOptions {
939
+ activity_type: "act2".to_string(),
940
+ ..Default::default()
941
+ })
942
+ .await;
943
+ complete_token.cancel();
944
+ Ok(().into())
945
+ }
815
946
  });
816
947
 
817
948
  worker
@@ -838,6 +969,13 @@ async fn activity_tasks_from_completion_reserve_slots() {
838
969
  .await
839
970
  .unwrap();
840
971
  barr.wait().await;
972
+ // Wait for workflow to complete in order for all eager activities to be requested before shutting down.
973
+ // After shutdown, no eager activities slots can be allocated.
974
+ workflow_complete_token_clone.cancelled().await;
975
+ core.initiate_shutdown();
976
+ // Even though this test requests eager activity tasks, none are returned in poll responses.
977
+ let err = core.poll_activity_task().await.unwrap_err();
978
+ assert_matches!(err, PollActivityError::ShutDown);
841
979
  };
842
980
  // This wf poll should *not* set the flag that it wants tasks back since both slots are
843
981
  // occupied
@@ -871,7 +1009,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
871
1009
  })
872
1010
  .await
873
1011
  .unwrap();
874
- core.shutdown().await;
1012
+ core.drain_activity_poller_and_shutdown().await;
875
1013
  }
876
1014
 
877
1015
  #[tokio::test]
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ internal_flags::CoreInternalFlags,
2
3
  replay::DEFAULT_WORKFLOW_TYPE,
3
4
  test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
4
5
  worker::client::mocks::mock_workflow_client,
@@ -8,8 +9,13 @@ use std::{
8
9
  time::Duration,
9
10
  };
10
11
  use temporal_client::WorkflowOptions;
11
- use temporal_sdk::{WfContext, WorkflowResult};
12
- use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
12
+ use temporal_sdk::{
13
+ ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
14
+ };
15
+ use temporal_sdk_core_protos::{
16
+ temporal::api::{enums::v1::WorkflowTaskFailedCause, failure::v1::Failure},
17
+ DEFAULT_ACTIVITY_TYPE,
18
+ };
13
19
 
14
20
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
15
21
  pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
@@ -105,3 +111,160 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
105
111
  // timer and proceed without restarting
106
112
  assert_eq!(2, started_count.load(Ordering::Relaxed));
107
113
  }
114
+
115
+ #[rstest::rstest]
116
+ #[tokio::test]
117
+ async fn activity_id_or_type_change_is_nondeterministic(
118
+ #[values(true, false)] use_cache: bool,
119
+ #[values(true, false)] id_change: bool,
120
+ #[values(true, false)] local_act: bool,
121
+ ) {
122
+ let wf_id = "fakeid";
123
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
124
+ let mut t = if local_act {
125
+ canned_histories::single_local_activity("1")
126
+ } else {
127
+ canned_histories::single_activity("1")
128
+ };
129
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
130
+ let mock = mock_workflow_client();
131
+ let mut mh = MockPollCfg::from_resp_batches(
132
+ wf_id,
133
+ t,
134
+ // Two polls are needed, since the first will fail
135
+ [ResponseType::AllHistory, ResponseType::AllHistory],
136
+ mock,
137
+ );
138
+ // We should see one wft failure which has nondeterminism cause
139
+ mh.num_expected_fails = 1;
140
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
141
+ let should_contain = if id_change {
142
+ "does not match activity id"
143
+ } else {
144
+ "does not match activity type"
145
+ };
146
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
147
+ && matches!(f, Some(Failure {
148
+ message,
149
+ ..
150
+ }) if message.contains(should_contain))
151
+ });
152
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
153
+ if use_cache {
154
+ cfg.max_cached_workflows = 2;
155
+ }
156
+ });
157
+
158
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
159
+ if local_act {
160
+ ctx.local_activity(if id_change {
161
+ LocalActivityOptions {
162
+ activity_id: Some("I'm bad and wrong!".to_string()),
163
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
164
+ ..Default::default()
165
+ }
166
+ } else {
167
+ LocalActivityOptions {
168
+ activity_type: "not the default act type".to_string(),
169
+ ..Default::default()
170
+ }
171
+ })
172
+ .await;
173
+ } else {
174
+ ctx.activity(if id_change {
175
+ ActivityOptions {
176
+ activity_id: Some("I'm bad and wrong!".to_string()),
177
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
178
+ ..Default::default()
179
+ }
180
+ } else {
181
+ ActivityOptions {
182
+ activity_type: "not the default act type".to_string(),
183
+ ..Default::default()
184
+ }
185
+ })
186
+ .await;
187
+ }
188
+ Ok(().into())
189
+ });
190
+
191
+ worker
192
+ .submit_wf(
193
+ wf_id.to_owned(),
194
+ wf_type.to_owned(),
195
+ vec![],
196
+ WorkflowOptions::default(),
197
+ )
198
+ .await
199
+ .unwrap();
200
+ worker.run_until_done().await.unwrap();
201
+ }
202
+
203
+ #[rstest::rstest]
204
+ #[tokio::test]
205
+ async fn child_wf_id_or_type_change_is_nondeterministic(
206
+ #[values(true, false)] use_cache: bool,
207
+ #[values(true, false)] id_change: bool,
208
+ ) {
209
+ let wf_id = "fakeid";
210
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
211
+ let mut t = canned_histories::single_child_workflow("1");
212
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
213
+ let mock = mock_workflow_client();
214
+ let mut mh = MockPollCfg::from_resp_batches(
215
+ wf_id,
216
+ t,
217
+ // Two polls are needed, since the first will fail
218
+ [ResponseType::AllHistory, ResponseType::AllHistory],
219
+ mock,
220
+ );
221
+ // We should see one wft failure which has nondeterminism cause
222
+ mh.num_expected_fails = 1;
223
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
224
+ let should_contain = if id_change {
225
+ "does not match child workflow id"
226
+ } else {
227
+ "does not match child workflow type"
228
+ };
229
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
230
+ && matches!(f, Some(Failure {
231
+ message,
232
+ ..
233
+ }) if message.contains(should_contain))
234
+ });
235
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
236
+ if use_cache {
237
+ cfg.max_cached_workflows = 2;
238
+ }
239
+ });
240
+
241
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
242
+ ctx.child_workflow(if id_change {
243
+ ChildWorkflowOptions {
244
+ workflow_id: "I'm bad and wrong!".to_string(),
245
+ workflow_type: DEFAULT_ACTIVITY_TYPE.to_string(),
246
+ ..Default::default()
247
+ }
248
+ } else {
249
+ ChildWorkflowOptions {
250
+ workflow_id: "1".to_string(),
251
+ workflow_type: "not the child wf type".to_string(),
252
+ ..Default::default()
253
+ }
254
+ })
255
+ .start(&ctx)
256
+ .await;
257
+ Ok(().into())
258
+ });
259
+
260
+ worker
261
+ .submit_wf(
262
+ wf_id.to_owned(),
263
+ wf_type.to_owned(),
264
+ vec![],
265
+ WorkflowOptions::default(),
266
+ )
267
+ .await
268
+ .unwrap();
269
+ worker.run_until_done().await.unwrap();
270
+ }