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
@@ -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
+ }