temporalio 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -7
  3. data/bridge/Cargo.lock +208 -76
  4. data/bridge/Cargo.toml +5 -2
  5. data/bridge/sdk-core/Cargo.toml +1 -1
  6. data/bridge/sdk-core/README.md +20 -10
  7. data/bridge/sdk-core/client/Cargo.toml +1 -1
  8. data/bridge/sdk-core/client/src/lib.rs +227 -59
  9. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  10. data/bridge/sdk-core/client/src/raw.rs +13 -12
  11. data/bridge/sdk-core/client/src/retry.rs +132 -43
  12. data/bridge/sdk-core/core/Cargo.toml +28 -15
  13. data/bridge/sdk-core/core/benches/workflow_replay.rs +13 -10
  14. data/bridge/sdk-core/core/src/abstractions.rs +225 -36
  15. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +217 -79
  16. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  17. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +565 -34
  18. data/bridge/sdk-core/core/src/core_tests/queries.rs +247 -90
  19. data/bridge/sdk-core/core/src/core_tests/workers.rs +3 -5
  20. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  21. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +430 -67
  22. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  23. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  24. data/bridge/sdk-core/core/src/lib.rs +148 -34
  25. data/bridge/sdk-core/core/src/protosext/mod.rs +1 -1
  26. data/bridge/sdk-core/core/src/replay/mod.rs +185 -41
  27. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  28. data/bridge/sdk-core/core/src/telemetry/metrics.rs +219 -140
  29. data/bridge/sdk-core/core/src/telemetry/mod.rs +326 -315
  30. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +20 -14
  31. data/bridge/sdk-core/core/src/test_help/mod.rs +85 -21
  32. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  33. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  34. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +364 -128
  35. data/bridge/sdk-core/core/src/worker/activities.rs +263 -170
  36. data/bridge/sdk-core/core/src/worker/client/mocks.rs +23 -3
  37. data/bridge/sdk-core/core/src/worker/client.rs +48 -6
  38. data/bridge/sdk-core/core/src/worker/mod.rs +186 -75
  39. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  40. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +13 -24
  41. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +879 -226
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +101 -48
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +8 -12
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -9
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +90 -32
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +6 -9
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -10
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +6 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +160 -83
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +36 -54
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +179 -0
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +104 -157
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +8 -12
  54. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -13
  55. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +10 -4
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -11
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +395 -299
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +12 -20
  60. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +33 -18
  61. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1032 -374
  62. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +525 -392
  63. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  64. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +3 -6
  66. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +456 -681
  69. data/bridge/sdk-core/core-api/Cargo.toml +6 -4
  70. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  71. data/bridge/sdk-core/core-api/src/lib.rs +7 -45
  72. data/bridge/sdk-core/core-api/src/telemetry.rs +141 -0
  73. data/bridge/sdk-core/core-api/src/worker.rs +27 -1
  74. data/bridge/sdk-core/etc/deps.svg +115 -140
  75. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  76. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  77. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  78. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  79. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  80. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  81. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  82. data/bridge/sdk-core/protos/api_upstream/buf.yaml +0 -3
  83. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  84. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  85. data/bridge/sdk-core/protos/api_upstream/{temporal/api/enums/v1/cluster.proto → build/tools.go} +7 -18
  86. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  87. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  88. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +15 -26
  89. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  90. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  91. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +4 -9
  92. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  93. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +10 -8
  94. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +28 -2
  95. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  96. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  97. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  98. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  99. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  100. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  101. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  102. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  103. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  104. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  105. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +62 -26
  106. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  107. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +24 -61
  108. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -21
  109. data/bridge/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  110. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  111. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  112. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +110 -31
  113. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  114. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +4 -4
  115. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  116. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  117. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +3 -2
  118. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +111 -36
  119. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -5
  120. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  121. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  122. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  123. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  124. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  125. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  126. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +9 -0
  127. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +9 -1
  128. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  129. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  130. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  131. data/bridge/sdk-core/sdk/Cargo.toml +4 -3
  132. data/bridge/sdk-core/sdk/src/interceptors.rs +36 -3
  133. data/bridge/sdk-core/sdk/src/lib.rs +94 -25
  134. data/bridge/sdk-core/sdk/src/workflow_context.rs +13 -2
  135. data/bridge/sdk-core/sdk/src/workflow_future.rs +10 -13
  136. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  137. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -2
  138. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +164 -104
  139. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +27 -23
  140. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +252 -74
  141. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  142. data/bridge/sdk-core/test-utils/Cargo.toml +4 -1
  143. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  144. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  145. data/bridge/sdk-core/test-utils/src/lib.rs +161 -50
  146. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  147. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  148. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  149. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  150. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  151. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  152. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +239 -0
  153. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -60
  154. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  155. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  156. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  157. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  158. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  159. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  160. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  161. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  162. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +151 -116
  163. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +54 -0
  164. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  165. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +115 -24
  166. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  167. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  168. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  169. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  170. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  171. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +27 -18
  172. data/bridge/sdk-core/tests/main.rs +8 -16
  173. data/bridge/sdk-core/tests/runner.rs +75 -36
  174. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  175. data/bridge/src/connection.rs +117 -82
  176. data/bridge/src/lib.rs +356 -42
  177. data/bridge/src/runtime.rs +10 -3
  178. data/bridge/src/test_server.rs +153 -0
  179. data/bridge/src/worker.rs +133 -9
  180. data/lib/gen/temporal/api/batch/v1/message_pb.rb +8 -6
  181. data/lib/gen/temporal/api/command/v1/message_pb.rb +10 -16
  182. data/lib/gen/temporal/api/common/v1/message_pb.rb +5 -1
  183. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +2 -1
  184. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +3 -3
  185. data/lib/gen/temporal/api/enums/v1/common_pb.rb +2 -1
  186. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +5 -4
  187. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +9 -1
  188. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +1 -1
  189. data/lib/gen/temporal/api/enums/v1/query_pb.rb +1 -1
  190. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +1 -1
  191. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +1 -1
  192. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +1 -1
  193. data/lib/gen/temporal/api/enums/v1/update_pb.rb +7 -10
  194. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +1 -1
  195. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +1 -1
  196. data/lib/gen/temporal/api/failure/v1/message_pb.rb +1 -1
  197. data/lib/gen/temporal/api/filter/v1/message_pb.rb +1 -1
  198. data/lib/gen/temporal/api/history/v1/message_pb.rb +34 -25
  199. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +2 -1
  200. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +14 -51
  201. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +1 -1
  202. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  203. data/lib/gen/temporal/api/query/v1/message_pb.rb +1 -1
  204. data/lib/gen/temporal/api/replication/v1/message_pb.rb +1 -1
  205. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +22 -1
  206. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  207. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +2 -2
  208. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  209. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  210. data/lib/gen/temporal/api/update/v1/message_pb.rb +49 -3
  211. data/lib/gen/temporal/api/version/v1/message_pb.rb +1 -1
  212. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +2 -1
  213. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +47 -20
  214. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +1 -1
  215. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  216. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  217. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  218. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  219. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  220. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  221. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +28 -21
  222. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +32 -24
  223. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  224. data/lib/temporalio/activity/context.rb +102 -0
  225. data/lib/temporalio/activity/info.rb +67 -0
  226. data/lib/temporalio/activity.rb +85 -0
  227. data/lib/temporalio/bridge/connect_options.rb +15 -0
  228. data/lib/temporalio/bridge/error.rb +8 -0
  229. data/lib/temporalio/bridge/retry_config.rb +24 -0
  230. data/lib/temporalio/bridge/tls_options.rb +19 -0
  231. data/lib/temporalio/bridge.rb +14 -0
  232. data/lib/{temporal → temporalio}/client/implementation.rb +57 -56
  233. data/lib/{temporal → temporalio}/client/workflow_handle.rb +35 -35
  234. data/lib/{temporal → temporalio}/client.rb +19 -32
  235. data/lib/temporalio/connection/retry_config.rb +44 -0
  236. data/lib/temporalio/connection/service.rb +20 -0
  237. data/lib/temporalio/connection/test_service.rb +92 -0
  238. data/lib/temporalio/connection/tls_options.rb +51 -0
  239. data/lib/temporalio/connection/workflow_service.rb +731 -0
  240. data/lib/temporalio/connection.rb +86 -0
  241. data/lib/{temporal → temporalio}/data_converter.rb +76 -35
  242. data/lib/{temporal → temporalio}/error/failure.rb +6 -6
  243. data/lib/{temporal → temporalio}/error/workflow_failure.rb +4 -2
  244. data/lib/{temporal → temporalio}/errors.rb +19 -1
  245. data/lib/{temporal → temporalio}/failure_converter/base.rb +5 -5
  246. data/lib/{temporal → temporalio}/failure_converter/basic.rb +58 -52
  247. data/lib/temporalio/failure_converter.rb +7 -0
  248. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  249. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  250. data/lib/{temporal → temporalio}/interceptor/chain.rb +7 -6
  251. data/lib/{temporal → temporalio}/interceptor/client.rb +27 -2
  252. data/lib/temporalio/interceptor.rb +22 -0
  253. data/lib/{temporal → temporalio}/payload_codec/base.rb +5 -5
  254. data/lib/{temporal → temporalio}/payload_converter/base.rb +3 -3
  255. data/lib/{temporal → temporalio}/payload_converter/bytes.rb +4 -3
  256. data/lib/{temporal → temporalio}/payload_converter/composite.rb +7 -5
  257. data/lib/{temporal → temporalio}/payload_converter/encoding_base.rb +4 -4
  258. data/lib/{temporal → temporalio}/payload_converter/json.rb +4 -3
  259. data/lib/{temporal → temporalio}/payload_converter/nil.rb +4 -3
  260. data/lib/temporalio/payload_converter.rb +14 -0
  261. data/lib/{temporal → temporalio}/retry_policy.rb +17 -7
  262. data/lib/{temporal → temporalio}/retry_state.rb +1 -1
  263. data/lib/temporalio/runtime.rb +25 -0
  264. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  265. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  266. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  267. data/lib/temporalio/testing.rb +175 -0
  268. data/lib/{temporal → temporalio}/timeout_type.rb +2 -2
  269. data/lib/temporalio/version.rb +3 -0
  270. data/lib/temporalio/worker/activity_runner.rb +114 -0
  271. data/lib/temporalio/worker/activity_worker.rb +164 -0
  272. data/lib/temporalio/worker/reactor.rb +46 -0
  273. data/lib/temporalio/worker/runner.rb +63 -0
  274. data/lib/temporalio/worker/sync_worker.rb +124 -0
  275. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  276. data/lib/temporalio/worker.rb +204 -0
  277. data/lib/temporalio/workflow/async.rb +46 -0
  278. data/lib/{temporal → temporalio}/workflow/execution_info.rb +4 -4
  279. data/lib/{temporal → temporalio}/workflow/execution_status.rb +1 -1
  280. data/lib/temporalio/workflow/future.rb +138 -0
  281. data/lib/{temporal → temporalio}/workflow/id_reuse_policy.rb +6 -6
  282. data/lib/temporalio/workflow/info.rb +76 -0
  283. data/lib/{temporal → temporalio}/workflow/query_reject_condition.rb +5 -5
  284. data/lib/temporalio.rb +12 -3
  285. data/temporalio.gemspec +11 -6
  286. metadata +137 -64
  287. data/bridge/sdk-core/Cargo.lock +0 -2606
  288. data/bridge/sdk-core/bridge-ffi/Cargo.toml +0 -24
  289. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  290. data/bridge/sdk-core/bridge-ffi/build.rs +0 -25
  291. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -249
  292. data/bridge/sdk-core/bridge-ffi/src/lib.rs +0 -825
  293. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +0 -211
  294. data/bridge/sdk-core/core/src/log_export.rs +0 -62
  295. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  296. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  297. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  298. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  299. data/bridge/sdk-core/sdk/src/conversions.rs +0 -8
  300. data/lib/bridge.so +0 -0
  301. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +0 -67
  302. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +0 -26
  303. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
  304. data/lib/temporal/bridge.rb +0 -14
  305. data/lib/temporal/connection.rb +0 -736
  306. data/lib/temporal/failure_converter.rb +0 -8
  307. data/lib/temporal/payload_converter.rb +0 -14
  308. data/lib/temporal/runtime.rb +0 -22
  309. data/lib/temporal/version.rb +0 -3
  310. data/lib/temporal.rb +0 -8
@@ -3,22 +3,26 @@ use crate::{
3
3
  replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
4
4
  test_help::{
5
5
  hist_to_poll_resp, mock_sdk, mock_sdk_cfg, mock_worker, single_hist_mock_sg, MockPollCfg,
6
- ResponseType, TEST_Q,
6
+ ResponseType,
7
7
  },
8
- worker::client::mocks::mock_workflow_client,
8
+ worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
9
9
  };
10
10
  use anyhow::anyhow;
11
+ use crossbeam::queue::SegQueue;
11
12
  use futures::{future::join_all, FutureExt};
12
13
  use std::{
13
14
  collections::HashMap,
15
+ ops::Sub,
14
16
  sync::{
15
17
  atomic::{AtomicUsize, Ordering},
16
18
  Arc,
17
19
  },
18
- time::Duration,
20
+ time::{Duration, SystemTime},
19
21
  };
20
22
  use temporal_client::WorkflowOptions;
21
- use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
23
+ use temporal_sdk::{
24
+ ActContext, ActivityCancelledError, LocalActivityOptions, WfContext, WorkflowResult,
25
+ };
22
26
  use temporal_sdk_core_api::Worker;
23
27
  use temporal_sdk_core_protos::{
24
28
  coresdk::{
@@ -29,11 +33,16 @@ use temporal_sdk_core_protos::{
29
33
  ActivityTaskCompletion, AsJsonPayloadExt,
30
34
  },
31
35
  temporal::api::{
32
- common::v1::RetryPolicy, enums::v1::EventType, failure::v1::Failure,
36
+ common::v1::RetryPolicy,
37
+ enums::v1::{EventType, TimeoutType, WorkflowTaskFailedCause},
38
+ failure::v1::Failure,
33
39
  query::v1::WorkflowQuery,
34
40
  },
41
+ DEFAULT_ACTIVITY_TYPE,
42
+ };
43
+ use temporal_sdk_core_test_utils::{
44
+ schedule_local_activity_cmd, start_timer_cmd, WorkerTestHelpers,
35
45
  };
36
- use temporal_sdk_core_test_utils::{schedule_local_activity_cmd, WorkerTestHelpers};
37
46
  use tokio::sync::Barrier;
38
47
 
39
48
  async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
@@ -52,7 +61,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
52
61
  let mut t = TestHistoryBuilder::default();
53
62
  t.add_by_type(EventType::WorkflowExecutionStarted);
54
63
  t.add_full_wf_task();
55
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
64
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
56
65
  t.add_full_wf_task();
57
66
  t.add_local_activity_result_marker(1, "1", b"echo".into());
58
67
  t.add_timer_fired(timer_started_event_id, "1".to_string());
@@ -77,7 +86,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
77
86
  DEFAULT_WORKFLOW_TYPE.to_owned(),
78
87
  |ctx: WfContext| async move {
79
88
  let la = ctx.local_activity(LocalActivityOptions {
80
- activity_type: "echo".to_string(),
89
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
81
90
  input: "hi".as_json_payload().expect("serializes fine"),
82
91
  ..Default::default()
83
92
  });
@@ -86,7 +95,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
86
95
  Ok(().into())
87
96
  },
88
97
  );
89
- worker.register_activity("echo", echo);
98
+ worker.register_activity(DEFAULT_ACTIVITY_TYPE, echo);
90
99
  worker
91
100
  .submit_wf(
92
101
  wf_id.to_owned(),
@@ -104,7 +113,7 @@ pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
104
113
  .map(|i| {
105
114
  ctx.local_activity(LocalActivityOptions {
106
115
  activity_type: "echo".to_string(),
107
- input: format!("Hi {}", i)
116
+ input: format!("Hi {i}")
108
117
  .as_json_payload()
109
118
  .expect("serializes fine"),
110
119
  ..Default::default()
@@ -121,7 +130,7 @@ async fn local_act_many_concurrent() {
121
130
  let mut t = TestHistoryBuilder::default();
122
131
  t.add_by_type(EventType::WorkflowExecutionStarted);
123
132
  t.add_full_wf_task();
124
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
133
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
125
134
  t.add_full_wf_task();
126
135
  for i in 1..=50 {
127
136
  t.add_local_activity_result_marker(i, &i.to_string(), b"echo".into());
@@ -162,12 +171,7 @@ async fn local_act_many_concurrent() {
162
171
  async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
163
172
  let mut t = TestHistoryBuilder::default();
164
173
  let wft_timeout = Duration::from_millis(200);
165
- let mut wes_short_wft_timeout = default_wes_attribs();
166
- wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.try_into().unwrap());
167
- t.add(
168
- EventType::WorkflowExecutionStarted,
169
- wes_short_wft_timeout.into(),
170
- );
174
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
171
175
  t.add_full_wf_task();
172
176
  // Task created by WFT heartbeat
173
177
  t.add_full_wf_task();
@@ -298,7 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
298
302
  "1",
299
303
  Failure::application_failure("la failed".to_string(), false),
300
304
  );
301
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
305
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
302
306
  t.add_timer_fired(timer_started_event_id, "1".to_string());
303
307
  t.add_full_wf_task();
304
308
  t.add_local_activity_fail_marker(
@@ -306,7 +310,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
306
310
  "2",
307
311
  Failure::application_failure("la failed".to_string(), false),
308
312
  );
309
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
313
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
310
314
  t.add_timer_fired(timer_started_event_id, "2".to_string());
311
315
  t.add_full_wf_task();
312
316
  t.add_workflow_execution_completed();
@@ -326,7 +330,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
326
330
  |ctx: WfContext| async move {
327
331
  let la_res = ctx
328
332
  .local_activity(LocalActivityOptions {
329
- activity_type: "echo".to_string(),
333
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
330
334
  input: "hi".as_json_payload().expect("serializes fine"),
331
335
  retry_policy: RetryPolicy {
332
336
  initial_interval: Some(prost_dur!(from_millis(65))),
@@ -345,9 +349,12 @@ async fn local_act_retry_long_backoff_uses_timer() {
345
349
  Ok(().into())
346
350
  },
347
351
  );
348
- worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
349
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
350
- });
352
+ worker.register_activity(
353
+ DEFAULT_ACTIVITY_TYPE,
354
+ move |_ctx: ActContext, _: String| async move {
355
+ Result::<(), _>::Err(anyhow!("Oh no I failed!"))
356
+ },
357
+ );
351
358
  worker
352
359
  .submit_wf(
353
360
  wf_id.to_owned(),
@@ -365,7 +372,7 @@ async fn local_act_null_result() {
365
372
  let mut t = TestHistoryBuilder::default();
366
373
  t.add_by_type(EventType::WorkflowExecutionStarted);
367
374
  t.add_full_wf_task();
368
- t.add_local_activity_marker(1, "1", None, None, None);
375
+ t.add_local_activity_marker(1, "1", None, None, |_| {});
369
376
  t.add_workflow_execution_completed();
370
377
 
371
378
  let wf_id = "fakeid";
@@ -398,24 +405,63 @@ async fn local_act_null_result() {
398
405
  worker.run_until_done().await.unwrap();
399
406
  }
400
407
 
408
+ #[tokio::test]
409
+ async fn local_act_command_immediately_follows_la_marker() {
410
+ // This repro only works both when cache is off, and there is at least one heartbeat wft
411
+ // before the marker & next command are recorded.
412
+ let mut t = TestHistoryBuilder::default();
413
+ t.add_by_type(EventType::WorkflowExecutionStarted);
414
+ t.add_full_wf_task();
415
+ t.add_full_wf_task();
416
+ t.add_local_activity_result_marker(1, "1", "done".into());
417
+ t.add_by_type(EventType::TimerStarted);
418
+ t.add_full_wf_task();
419
+
420
+ let wf_id = "fakeid";
421
+ let mock = mock_workflow_client();
422
+ // Bug only repros when seeing history up to third wft
423
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [3], mock);
424
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 0);
425
+
426
+ worker.register_wf(
427
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
428
+ |ctx: WfContext| async move {
429
+ ctx.local_activity(LocalActivityOptions {
430
+ activity_type: "nullres".to_string(),
431
+ input: "hi".as_json_payload().expect("serializes fine"),
432
+ ..Default::default()
433
+ })
434
+ .await;
435
+ ctx.timer(Duration::from_secs(1)).await;
436
+ Ok(().into())
437
+ },
438
+ );
439
+ worker.register_activity("nullres", |_ctx: ActContext, _: String| async { Ok(()) });
440
+ worker
441
+ .submit_wf(
442
+ wf_id.to_owned(),
443
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
444
+ vec![],
445
+ WorkflowOptions::default(),
446
+ )
447
+ .await
448
+ .unwrap();
449
+ worker.run_until_done().await.unwrap();
450
+ }
451
+
401
452
  #[tokio::test]
402
453
  async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbeat() {
403
454
  let wfid = "fake_wf_id";
404
455
  let mut t = TestHistoryBuilder::default();
405
- let mut wes_short_wft_timeout = default_wes_attribs();
406
- wes_short_wft_timeout.workflow_task_timeout = Some(prost_dur!(from_millis(200)));
407
- t.add(
408
- EventType::WorkflowExecutionStarted,
409
- wes_short_wft_timeout.into(),
410
- );
456
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
411
457
  t.add_full_wf_task();
412
458
  // get query here
413
459
  t.add_full_wf_task();
414
- t.add_local_activity_marker(1, "1", None, None, None);
460
+ t.add_local_activity_result_marker(1, "1", "done".into());
415
461
  t.add_workflow_execution_completed();
416
462
 
417
463
  let query_with_hist_task = {
418
- let mut pr = hist_to_poll_resp(&t, wfid, ResponseType::ToTaskNum(1), TEST_Q);
464
+ let mut pr = hist_to_poll_resp(&t, wfid, ResponseType::ToTaskNum(1));
419
465
  pr.queries = HashMap::new();
420
466
  pr.queries.insert(
421
467
  "the-query".to_string(),
@@ -441,7 +487,6 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
441
487
  .boxed(),
442
488
  3,
443
489
  ),
444
- TEST_Q,
445
490
  ),
446
491
  ];
447
492
  let mock = mock_workflow_client();
@@ -457,7 +502,7 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
457
502
  task.run_id,
458
503
  schedule_local_activity_cmd(
459
504
  1,
460
- "act-id",
505
+ "1",
461
506
  ActivityCancellationType::TryCancel,
462
507
  Duration::from_secs(60),
463
508
  ),
@@ -513,3 +558,489 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
513
558
 
514
559
  tokio::join!(wf_fut, act_fut);
515
560
  }
561
+
562
+ #[rstest::rstest]
563
+ #[case::impossible_query_in_task(true)]
564
+ #[case::real_history(false)]
565
+ #[tokio::test]
566
+ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_query_in_task: bool) {
567
+ // Ensures we do not send an activation with a legacy query and any other work, which should
568
+ // never happen, but there was an issue where an LA resolving could trigger that.
569
+ let wfid = "fake_wf_id";
570
+ let mut t = TestHistoryBuilder::default();
571
+ t.add(default_wes_attribs());
572
+ // Since we don't send queries with start workflow, need one workflow task of something else
573
+ // b/c we want to get an activation with a job and a nonlegacy query
574
+ t.add_full_wf_task();
575
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
576
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
577
+
578
+ // nonlegacy query got here & LA started here
579
+ t.add_full_wf_task();
580
+ // legacy query got here, at the same time that the LA is resolved
581
+ t.add_local_activity_result_marker(1, "1", "whatever".into());
582
+ t.add_workflow_execution_completed();
583
+
584
+ let barr = Arc::new(Barrier::new(2));
585
+ let barr_c = barr.clone();
586
+
587
+ let tasks = [
588
+ hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::ToTaskNum(1)),
589
+ {
590
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), ResponseType::OneTask(2));
591
+ pr.queries = HashMap::new();
592
+ pr.queries.insert(
593
+ "q1".to_string(),
594
+ WorkflowQuery {
595
+ query_type: "query-type".to_string(),
596
+ query_args: Some(b"hi".into()),
597
+ header: None,
598
+ },
599
+ );
600
+ pr
601
+ },
602
+ {
603
+ let mut pr = hist_to_poll_resp(
604
+ &t,
605
+ wfid.to_owned(),
606
+ ResponseType::UntilResolved(
607
+ async move {
608
+ barr_c.wait().await;
609
+ // This sleep is the only not-incredibly-invasive way to ensure the LA
610
+ // resolves & updates machines before we process this task
611
+ tokio::time::sleep(Duration::from_secs(1)).await;
612
+ }
613
+ .boxed(),
614
+ 2,
615
+ ),
616
+ );
617
+ // Strip beginning of history so the only events are WFT sched/started, we need to look
618
+ // like we hit the cache
619
+ {
620
+ let h = pr.history.as_mut().unwrap();
621
+ h.events = h.events.split_off(6);
622
+ }
623
+ // In the nonsense server response case, we attach a legacy query, otherwise this
624
+ // response looks like a normal response to a forced WFT heartbeat.
625
+ if impossible_query_in_task {
626
+ pr.query = Some(WorkflowQuery {
627
+ query_type: "query-type".to_string(),
628
+ query_args: Some(b"hi".into()),
629
+ header: None,
630
+ });
631
+ }
632
+ pr
633
+ },
634
+ ];
635
+ let mut mock = mock_workflow_client();
636
+ if impossible_query_in_task {
637
+ mock.expect_respond_legacy_query()
638
+ .times(1)
639
+ .returning(move |_, _| Ok(Default::default()));
640
+ }
641
+ let mut mock = single_hist_mock_sg(wfid, t, tasks, mock, true);
642
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
643
+ let core = mock_worker(mock);
644
+
645
+ let wf_fut = async {
646
+ let task = core.poll_workflow_activation().await.unwrap();
647
+ assert_matches!(
648
+ task.jobs.as_slice(),
649
+ &[WorkflowActivationJob {
650
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
651
+ },]
652
+ );
653
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
654
+ task.run_id,
655
+ start_timer_cmd(1, Duration::from_secs(1)),
656
+ ))
657
+ .await
658
+ .unwrap();
659
+ let task = core.poll_workflow_activation().await.unwrap();
660
+ assert_matches!(
661
+ task.jobs.as_slice(),
662
+ &[
663
+ WorkflowActivationJob {
664
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
665
+ },
666
+ WorkflowActivationJob {
667
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(_)),
668
+ }
669
+ ]
670
+ );
671
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
672
+ task.run_id,
673
+ vec![
674
+ schedule_local_activity_cmd(
675
+ 1,
676
+ "act-id",
677
+ ActivityCancellationType::TryCancel,
678
+ Duration::from_secs(60),
679
+ ),
680
+ QueryResult {
681
+ query_id: "q1".to_string(),
682
+ variant: Some(
683
+ QuerySuccess {
684
+ response: Some("whatev".into()),
685
+ }
686
+ .into(),
687
+ ),
688
+ }
689
+ .into(),
690
+ ],
691
+ ))
692
+ .await
693
+ .unwrap();
694
+ barr.wait().await;
695
+ let task = core.poll_workflow_activation().await.unwrap();
696
+ // The next task needs to be resolve, since the LA is completed immediately
697
+ assert_matches!(
698
+ task.jobs.as_slice(),
699
+ [WorkflowActivationJob {
700
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
701
+ }]
702
+ );
703
+ // Complete workflow
704
+ core.complete_execution(&task.run_id).await;
705
+ if impossible_query_in_task {
706
+ // finish last query
707
+ let task = core.poll_workflow_activation().await.unwrap();
708
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
709
+ task.run_id,
710
+ vec![QueryResult {
711
+ query_id: LEGACY_QUERY_ID.to_string(),
712
+ variant: Some(
713
+ QuerySuccess {
714
+ response: Some("whatev".into()),
715
+ }
716
+ .into(),
717
+ ),
718
+ }
719
+ .into()],
720
+ ))
721
+ .await
722
+ .unwrap();
723
+ }
724
+ };
725
+ let act_fut = async {
726
+ let act_task = core.poll_activity_task().await.unwrap();
727
+ core.complete_activity_task(ActivityTaskCompletion {
728
+ task_token: act_task.task_token,
729
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
730
+ })
731
+ .await
732
+ .unwrap();
733
+ };
734
+
735
+ tokio::join!(wf_fut, act_fut);
736
+ core.shutdown().await;
737
+ }
738
+
739
+ #[tokio::test]
740
+ async fn test_schedule_to_start_timeout() {
741
+ let mut t = TestHistoryBuilder::default();
742
+ t.add_by_type(EventType::WorkflowExecutionStarted);
743
+ t.add_full_wf_task();
744
+
745
+ let wf_id = "fakeid";
746
+ let mock = mock_workflow_client();
747
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::ToTaskNum(1)], mock);
748
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
749
+
750
+ worker.register_wf(
751
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
752
+ |ctx: WfContext| async move {
753
+ let la_res = ctx
754
+ .local_activity(LocalActivityOptions {
755
+ activity_type: "echo".to_string(),
756
+ input: "hi".as_json_payload().expect("serializes fine"),
757
+ // Impossibly small timeout so we timeout in the queue
758
+ schedule_to_start_timeout: prost_dur!(from_nanos(1)),
759
+ ..Default::default()
760
+ })
761
+ .await;
762
+ assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToStart));
763
+ Ok(().into())
764
+ },
765
+ );
766
+ worker.register_activity(
767
+ "echo",
768
+ move |_ctx: ActContext, _: String| async move { Ok(()) },
769
+ );
770
+ worker
771
+ .submit_wf(
772
+ wf_id.to_owned(),
773
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
774
+ vec![],
775
+ WorkflowOptions::default(),
776
+ )
777
+ .await
778
+ .unwrap();
779
+ worker.run_until_done().await.unwrap();
780
+ }
781
+
782
+ #[rstest::rstest]
783
+ #[case::sched_to_start(true)]
784
+ #[case::sched_to_close(false)]
785
+ #[tokio::test]
786
+ async fn test_schedule_to_start_timeout_not_based_on_original_time(
787
+ #[case] is_sched_to_start: bool,
788
+ ) {
789
+ // We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
790
+ // created newly after backing off across a timer. That was a mistake, since schedule-to-start
791
+ // timeouts should apply to when the new attempt was scheduled. This test verifies:
792
+ // * we don't time out on s-t-s timeouts because of that, when the param is true.
793
+ // * we do properly time out on s-t-c timeouts when the param is false
794
+
795
+ let mut t = TestHistoryBuilder::default();
796
+ t.add_by_type(EventType::WorkflowExecutionStarted);
797
+ t.add_full_wf_task();
798
+ let orig_sched = SystemTime::now().sub(Duration::from_secs(60 * 20));
799
+ t.add_local_activity_marker(
800
+ 1,
801
+ "1",
802
+ None,
803
+ Some(Failure::application_failure("la failed".to_string(), false)),
804
+ |deets| {
805
+ // Really old schedule time, which should _not_ count against schedule_to_start
806
+ deets.original_schedule_time = Some(orig_sched.into());
807
+ // Backoff value must be present since we're simulating timer backoff
808
+ deets.backoff = Some(prost_dur!(from_secs(100)));
809
+ },
810
+ );
811
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
812
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
813
+ t.add_workflow_task_scheduled_and_started();
814
+
815
+ let wf_id = "fakeid";
816
+ let mock = mock_workflow_client();
817
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
818
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
819
+
820
+ let schedule_to_close_timeout = Some(if is_sched_to_start {
821
+ // This 60 minute timeout will not have elapsed according to the original
822
+ // schedule time in the history.
823
+ Duration::from_secs(60 * 60)
824
+ } else {
825
+ // This 10 minute timeout will have already elapsed
826
+ Duration::from_secs(10 * 60)
827
+ });
828
+
829
+ worker.register_wf(
830
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
831
+ move |ctx: WfContext| async move {
832
+ let la_res = ctx
833
+ .local_activity(LocalActivityOptions {
834
+ activity_type: "echo".to_string(),
835
+ input: "hi".as_json_payload().expect("serializes fine"),
836
+ retry_policy: RetryPolicy {
837
+ initial_interval: Some(prost_dur!(from_millis(50))),
838
+ backoff_coefficient: 1.2,
839
+ maximum_interval: None,
840
+ maximum_attempts: 5,
841
+ non_retryable_error_types: vec![],
842
+ },
843
+ schedule_to_start_timeout: Some(Duration::from_secs(60)),
844
+ schedule_to_close_timeout,
845
+ ..Default::default()
846
+ })
847
+ .await;
848
+ if is_sched_to_start {
849
+ assert!(la_res.completed_ok());
850
+ } else {
851
+ assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
852
+ }
853
+ Ok(().into())
854
+ },
855
+ );
856
+ worker.register_activity(
857
+ "echo",
858
+ move |_ctx: ActContext, _: String| async move { Ok(()) },
859
+ );
860
+ worker
861
+ .submit_wf(
862
+ wf_id.to_owned(),
863
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
864
+ vec![],
865
+ WorkflowOptions::default(),
866
+ )
867
+ .await
868
+ .unwrap();
869
+ worker.run_until_done().await.unwrap();
870
+ }
871
+
872
+ #[tokio::test]
873
+ async fn wft_failure_cancels_running_las() {
874
+ let mut t = TestHistoryBuilder::default();
875
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
876
+ t.add_full_wf_task();
877
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
878
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
879
+ t.add_workflow_task_scheduled_and_started();
880
+
881
+ let wf_id = "fakeid";
882
+ let mock = mock_workflow_client();
883
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2], mock);
884
+ mh.num_expected_fails = 1;
885
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
886
+
887
+ worker.register_wf(
888
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
889
+ |ctx: WfContext| async move {
890
+ let la_handle = ctx.local_activity(LocalActivityOptions {
891
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
892
+ input: "hi".as_json_payload().expect("serializes fine"),
893
+ ..Default::default()
894
+ });
895
+ tokio::join!(
896
+ async {
897
+ ctx.timer(Duration::from_secs(1)).await;
898
+ panic!("ahhh I'm failing wft")
899
+ },
900
+ la_handle
901
+ );
902
+ Ok(().into())
903
+ },
904
+ );
905
+ worker.register_activity(
906
+ DEFAULT_ACTIVITY_TYPE,
907
+ move |ctx: ActContext, _: String| async move {
908
+ let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
909
+ if res.is_err() {
910
+ panic!("Activity must be cancelled!!!!");
911
+ }
912
+ Result::<(), _>::Err(ActivityCancelledError::default().into())
913
+ },
914
+ );
915
+ worker
916
+ .submit_wf(
917
+ wf_id.to_owned(),
918
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
919
+ vec![],
920
+ WorkflowOptions::default(),
921
+ )
922
+ .await
923
+ .unwrap();
924
+ worker.run_until_done().await.unwrap();
925
+ }
926
+
927
+ #[tokio::test]
928
+ async fn resolved_las_not_recorded_if_wft_fails_many_times() {
929
+ // We shouldn't record any LA results if the workflow activation is repeatedly failing. There
930
+ // was an issue that, because we stop reporting WFT failures after 2 tries, this meant the WFT
931
+ // was not marked as "completed" and the WFT could accidentally be replied to with LA results.
932
+ let mut t = TestHistoryBuilder::default();
933
+ t.add_by_type(EventType::WorkflowExecutionStarted);
934
+ t.add_workflow_task_scheduled_and_started();
935
+ t.add_workflow_task_failed_with_failure(
936
+ WorkflowTaskFailedCause::Unspecified,
937
+ Default::default(),
938
+ );
939
+ t.add_workflow_task_scheduled_and_started();
940
+
941
+ let wf_id = "fakeid";
942
+ let mock = mock_workflow_client();
943
+ let mut mh = MockPollCfg::from_resp_batches(
944
+ wf_id,
945
+ t,
946
+ [1.into(), ResponseType::AllHistory, ResponseType::AllHistory],
947
+ mock,
948
+ );
949
+ mh.num_expected_fails = 2;
950
+ mh.num_expected_completions = Some(0.into());
951
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
952
+
953
+ worker.register_wf(
954
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
955
+ |ctx: WfContext| async move {
956
+ ctx.local_activity(LocalActivityOptions {
957
+ activity_type: "echo".to_string(),
958
+ input: "hi".as_json_payload().expect("serializes fine"),
959
+ ..Default::default()
960
+ })
961
+ .await;
962
+ panic!("Oh nooooo")
963
+ },
964
+ );
965
+ worker.register_activity(
966
+ "echo",
967
+ move |_: ActContext, _: String| async move { Ok(()) },
968
+ );
969
+ worker
970
+ .submit_wf(
971
+ wf_id.to_owned(),
972
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
973
+ vec![],
974
+ WorkflowOptions::default(),
975
+ )
976
+ .await
977
+ .unwrap();
978
+ worker.run_until_done().await.unwrap();
979
+ }
980
+
981
+ #[tokio::test]
982
+ async fn local_act_records_nonfirst_attempts_ok() {
983
+ let mut t = TestHistoryBuilder::default();
984
+ let wft_timeout = Duration::from_millis(200);
985
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
986
+ t.add_full_wf_task();
987
+ t.add_full_wf_task();
988
+ t.add_full_wf_task();
989
+ t.add_workflow_task_scheduled_and_started();
990
+
991
+ let wf_id = "fakeid";
992
+ let mock = mock_workflow_client();
993
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
994
+ let nonfirst_counts = Arc::new(SegQueue::new());
995
+ let nfc_c = nonfirst_counts.clone();
996
+ mh.completion_asserts = Some(Box::new(move |c| {
997
+ nfc_c.push(
998
+ c.metering_metadata
999
+ .nonfirst_local_activity_execution_attempts,
1000
+ );
1001
+ }));
1002
+ let mut worker = mock_sdk_cfg(mh, |wc| {
1003
+ wc.max_cached_workflows = 1;
1004
+ wc.max_outstanding_workflow_tasks = 1;
1005
+ });
1006
+
1007
+ worker.register_wf(
1008
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
1009
+ |ctx: WfContext| async move {
1010
+ ctx.local_activity(LocalActivityOptions {
1011
+ activity_type: "echo".to_string(),
1012
+ input: "hi".as_json_payload().expect("serializes fine"),
1013
+ retry_policy: RetryPolicy {
1014
+ initial_interval: Some(prost_dur!(from_millis(10))),
1015
+ backoff_coefficient: 1.0,
1016
+ maximum_interval: None,
1017
+ maximum_attempts: 0,
1018
+ non_retryable_error_types: vec![],
1019
+ },
1020
+ ..Default::default()
1021
+ })
1022
+ .await;
1023
+ Ok(().into())
1024
+ },
1025
+ );
1026
+ worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
1027
+ Result::<(), _>::Err(anyhow!("I fail"))
1028
+ });
1029
+ worker
1030
+ .submit_wf(
1031
+ wf_id.to_owned(),
1032
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
1033
+ vec![],
1034
+ WorkflowOptions::default(),
1035
+ )
1036
+ .await
1037
+ .unwrap();
1038
+ worker.run_until_done().await.unwrap();
1039
+ // 3 workflow tasks
1040
+ assert_eq!(nonfirst_counts.len(), 3);
1041
+ // First task's non-first count should, of course, be 0
1042
+ assert_eq!(nonfirst_counts.pop().unwrap(), 0);
1043
+ // Next two, some nonzero amount which could vary based on test load
1044
+ assert!(nonfirst_counts.pop().unwrap() > 0);
1045
+ assert!(nonfirst_counts.pop().unwrap() > 0);
1046
+ }