temporalio 0.0.0 → 0.0.2

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 (327) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +301 -0
  3. data/bridge/Cargo.lock +2888 -0
  4. data/bridge/Cargo.toml +27 -0
  5. data/bridge/sdk-core/ARCHITECTURE.md +76 -0
  6. data/bridge/sdk-core/Cargo.lock +2606 -0
  7. data/bridge/sdk-core/Cargo.toml +2 -0
  8. data/bridge/sdk-core/LICENSE.txt +23 -0
  9. data/bridge/sdk-core/README.md +104 -0
  10. data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
  11. data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
  12. data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  13. data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
  14. data/bridge/sdk-core/client/Cargo.toml +40 -0
  15. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  16. data/bridge/sdk-core/client/src/lib.rs +1286 -0
  17. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  18. data/bridge/sdk-core/client/src/raw.rs +932 -0
  19. data/bridge/sdk-core/client/src/retry.rs +751 -0
  20. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  21. data/bridge/sdk-core/core/Cargo.toml +116 -0
  22. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  23. data/bridge/sdk-core/core/benches/workflow_replay.rs +76 -0
  24. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  25. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +1014 -0
  26. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  27. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  28. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +925 -0
  29. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  30. data/bridge/sdk-core/core/src/core_tests/queries.rs +894 -0
  31. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  32. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  33. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  34. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2090 -0
  35. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  36. data/bridge/sdk-core/core/src/lib.rs +282 -0
  37. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  38. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  39. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  40. data/bridge/sdk-core/core/src/replay/mod.rs +215 -0
  41. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  42. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  43. data/bridge/sdk-core/core/src/telemetry/metrics.rs +428 -0
  44. data/bridge/sdk-core/core/src/telemetry/mod.rs +407 -0
  45. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +78 -0
  46. data/bridge/sdk-core/core/src/test_help/mod.rs +889 -0
  47. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  48. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1048 -0
  49. data/bridge/sdk-core/core/src/worker/activities.rs +481 -0
  50. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  51. data/bridge/sdk-core/core/src/worker/client.rs +373 -0
  52. data/bridge/sdk-core/core/src/worker/mod.rs +570 -0
  53. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  54. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +101 -0
  55. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +532 -0
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +907 -0
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +294 -0
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +167 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +858 -0
  60. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +136 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +157 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +129 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1450 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +316 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +708 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +439 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +435 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +175 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +242 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1200 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +272 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +655 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1200 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +985 -0
  80. data/bridge/sdk-core/core-api/Cargo.toml +32 -0
  81. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  82. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  83. data/bridge/sdk-core/core-api/src/lib.rs +109 -0
  84. data/bridge/sdk-core/core-api/src/telemetry.rs +147 -0
  85. data/bridge/sdk-core/core-api/src/worker.rs +148 -0
  86. data/bridge/sdk-core/etc/deps.svg +162 -0
  87. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  88. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  89. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  90. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  91. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  92. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  93. data/bridge/sdk-core/fsm/README.md +3 -0
  94. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  95. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  96. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  97. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  98. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  99. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  100. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  115. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  116. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  117. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  118. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  119. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  120. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  121. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  122. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  123. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  124. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  125. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  126. data/bridge/sdk-core/protos/api_upstream/buf.yaml +9 -0
  127. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  128. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  129. data/bridge/sdk-core/protos/api_upstream/build/tools.go +29 -0
  130. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  131. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  132. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +89 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +260 -0
  134. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
  135. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +47 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +56 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +170 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +118 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/interaction_type.proto +39 -0
  141. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
  142. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
  143. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
  144. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  145. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
  146. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +40 -0
  147. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
  148. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
  149. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
  150. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
  151. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +758 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +121 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +80 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +379 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  160. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
  161. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +146 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1168 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +415 -0
  164. data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  165. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  166. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
  167. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +263 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +304 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  174. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  175. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  176. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  179. data/bridge/sdk-core/rustfmt.toml +1 -0
  180. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  181. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  182. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  183. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  184. data/bridge/sdk-core/sdk/src/interceptors.rs +50 -0
  185. data/bridge/sdk-core/sdk/src/lib.rs +794 -0
  186. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  187. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  188. data/bridge/sdk-core/sdk/src/workflow_context.rs +694 -0
  189. data/bridge/sdk-core/sdk/src/workflow_future.rs +499 -0
  190. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  191. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  192. data/bridge/sdk-core/sdk-core-protos/build.rs +107 -0
  193. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  194. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +544 -0
  195. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  196. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1970 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  199. data/bridge/sdk-core/test-utils/Cargo.toml +36 -0
  200. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  201. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  202. data/bridge/sdk-core/test-utils/src/lib.rs +650 -0
  203. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  204. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  205. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +221 -0
  206. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  207. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +133 -0
  208. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  209. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  210. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  211. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  212. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +788 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  219. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
  220. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +223 -0
  221. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
  222. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
  223. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
  224. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
  225. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
  226. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +597 -0
  227. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  228. data/bridge/sdk-core/tests/main.rs +113 -0
  229. data/bridge/sdk-core/tests/runner.rs +93 -0
  230. data/bridge/src/connection.rs +186 -0
  231. data/bridge/src/lib.rs +239 -0
  232. data/bridge/src/runtime.rs +54 -0
  233. data/bridge/src/worker.rs +124 -0
  234. data/ext/Rakefile +9 -0
  235. data/lib/bridge.so +0 -0
  236. data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
  237. data/lib/gen/temporal/api/batch/v1/message_pb.rb +50 -0
  238. data/lib/gen/temporal/api/command/v1/message_pb.rb +174 -0
  239. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  240. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +33 -0
  241. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +39 -0
  242. data/lib/gen/temporal/api/enums/v1/common_pb.rb +42 -0
  243. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +68 -0
  244. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +77 -0
  245. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +25 -0
  246. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  247. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  248. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  249. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  250. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  251. data/lib/gen/temporal/api/enums/v1/update_pb.rb +23 -0
  252. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  253. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  254. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  255. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  256. data/lib/gen/temporal/api/history/v1/message_pb.rb +490 -0
  257. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +49 -0
  258. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
  259. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +85 -0
  260. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
  261. data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
  262. data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
  263. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +149 -0
  264. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  265. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  266. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +111 -0
  267. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +788 -0
  268. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  269. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  270. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  271. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  272. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  273. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  274. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  275. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  276. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +165 -0
  277. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +196 -0
  278. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  279. data/lib/temporalio/activity/context.rb +97 -0
  280. data/lib/temporalio/activity/info.rb +67 -0
  281. data/lib/temporalio/activity.rb +85 -0
  282. data/lib/temporalio/bridge/error.rb +8 -0
  283. data/lib/temporalio/bridge.rb +14 -0
  284. data/lib/temporalio/client/implementation.rb +340 -0
  285. data/lib/temporalio/client/workflow_handle.rb +243 -0
  286. data/lib/temporalio/client.rb +131 -0
  287. data/lib/temporalio/connection.rb +751 -0
  288. data/lib/temporalio/data_converter.rb +191 -0
  289. data/lib/temporalio/error/failure.rb +194 -0
  290. data/lib/temporalio/error/workflow_failure.rb +19 -0
  291. data/lib/temporalio/errors.rb +40 -0
  292. data/lib/temporalio/failure_converter/base.rb +26 -0
  293. data/lib/temporalio/failure_converter/basic.rb +319 -0
  294. data/lib/temporalio/failure_converter.rb +7 -0
  295. data/lib/temporalio/interceptor/chain.rb +28 -0
  296. data/lib/temporalio/interceptor/client.rb +123 -0
  297. data/lib/temporalio/payload_codec/base.rb +32 -0
  298. data/lib/temporalio/payload_converter/base.rb +24 -0
  299. data/lib/temporalio/payload_converter/bytes.rb +27 -0
  300. data/lib/temporalio/payload_converter/composite.rb +49 -0
  301. data/lib/temporalio/payload_converter/encoding_base.rb +35 -0
  302. data/lib/temporalio/payload_converter/json.rb +26 -0
  303. data/lib/temporalio/payload_converter/nil.rb +26 -0
  304. data/lib/temporalio/payload_converter.rb +14 -0
  305. data/lib/temporalio/retry_policy.rb +82 -0
  306. data/lib/temporalio/retry_state.rb +35 -0
  307. data/lib/temporalio/runtime.rb +25 -0
  308. data/lib/temporalio/timeout_type.rb +29 -0
  309. data/lib/temporalio/version.rb +3 -0
  310. data/lib/temporalio/worker/activity_runner.rb +92 -0
  311. data/lib/temporalio/worker/activity_worker.rb +138 -0
  312. data/lib/temporalio/worker/reactor.rb +46 -0
  313. data/lib/temporalio/worker/runner.rb +63 -0
  314. data/lib/temporalio/worker/sync_worker.rb +88 -0
  315. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  316. data/lib/temporalio/worker.rb +198 -0
  317. data/lib/temporalio/workflow/execution_info.rb +54 -0
  318. data/lib/temporalio/workflow/execution_status.rb +36 -0
  319. data/lib/temporalio/workflow/id_reuse_policy.rb +36 -0
  320. data/lib/temporalio/workflow/query_reject_condition.rb +33 -0
  321. data/lib/temporalio.rb +12 -1
  322. data/lib/thermite_patch.rb +23 -0
  323. data/temporalio.gemspec +45 -0
  324. metadata +566 -9
  325. data/lib/temporal/version.rb +0 -3
  326. data/lib/temporal.rb +0 -4
  327. data/temporal.gemspec +0 -20
@@ -0,0 +1,1014 @@
1
+ use crate::{
2
+ advance_fut, job_assert, prost_dur,
3
+ test_help::{
4
+ build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
5
+ mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
6
+ single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
7
+ ResponseType, WorkflowCachingPolicy, TEST_Q,
8
+ },
9
+ worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
10
+ ActivityHeartbeat, Worker, WorkerConfigBuilder,
11
+ };
12
+ use futures::FutureExt;
13
+ use itertools::Itertools;
14
+ use std::{
15
+ cell::RefCell,
16
+ collections::{hash_map::Entry, HashMap, VecDeque},
17
+ rc::Rc,
18
+ sync::{
19
+ atomic::{AtomicUsize, Ordering},
20
+ Arc,
21
+ },
22
+ time::Duration,
23
+ };
24
+ use temporal_client::WorkflowOptions;
25
+ use temporal_sdk::{ActivityOptions, WfContext};
26
+ use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
27
+ use temporal_sdk_core_protos::{
28
+ coresdk::{
29
+ activity_result::{
30
+ activity_execution_result, activity_resolution, ActivityExecutionResult,
31
+ ActivityResolution, Success,
32
+ },
33
+ activity_task::{activity_task, ActivityTask},
34
+ workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
35
+ workflow_commands::{
36
+ ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
37
+ ScheduleActivity,
38
+ },
39
+ workflow_completion::WorkflowActivationCompletion,
40
+ ActivityTaskCompletion,
41
+ },
42
+ temporal::api::{
43
+ command::v1::{command::Attributes, ScheduleActivityTaskCommandAttributes},
44
+ enums::v1::EventType,
45
+ workflowservice::v1::{
46
+ PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
47
+ RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
48
+ RespondActivityTaskFailedResponse, RespondWorkflowTaskCompletedResponse,
49
+ },
50
+ },
51
+ TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
52
+ };
53
+ use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
54
+ use tokio::{sync::Barrier, time::sleep};
55
+
56
+ #[tokio::test]
57
+ async fn max_activities_respected() {
58
+ let _task_q = "q";
59
+ let mut tasks = VecDeque::from(vec![
60
+ PollActivityTaskQueueResponse {
61
+ task_token: vec![1],
62
+ activity_id: "act1".to_string(),
63
+ ..Default::default()
64
+ },
65
+ PollActivityTaskQueueResponse {
66
+ task_token: vec![2],
67
+ activity_id: "act2".to_string(),
68
+ ..Default::default()
69
+ },
70
+ PollActivityTaskQueueResponse {
71
+ task_token: vec![3],
72
+ activity_id: "act3".to_string(),
73
+ ..Default::default()
74
+ },
75
+ ]);
76
+ let mut mock_client = mock_workflow_client();
77
+ mock_client
78
+ .expect_poll_activity_task()
79
+ .times(3)
80
+ .returning(move |_, _| Ok(tasks.pop_front().unwrap()));
81
+ mock_client
82
+ .expect_complete_activity_task()
83
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
84
+
85
+ let worker = Worker::new_test(
86
+ test_worker_cfg()
87
+ .max_outstanding_activities(2_usize)
88
+ .build()
89
+ .unwrap(),
90
+ mock_client,
91
+ );
92
+
93
+ // We allow two outstanding activities, therefore first two polls should return right away
94
+ let r1 = worker.poll_activity_task().await.unwrap();
95
+ let _r2 = worker.poll_activity_task().await.unwrap();
96
+ // Third poll should block until we complete one of the first two. To ensure this, manually
97
+ // poll it a bunch to see it's not resolving.
98
+ let poll_fut = worker.poll_activity_task();
99
+ advance_fut!(poll_fut);
100
+ worker
101
+ .complete_activity_task(ActivityTaskCompletion {
102
+ task_token: r1.task_token,
103
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
104
+ })
105
+ .await
106
+ .unwrap();
107
+ poll_fut.await.unwrap();
108
+ }
109
+
110
+ #[tokio::test]
111
+ async fn activity_not_found_returns_ok() {
112
+ let mut mock_client = mock_workflow_client();
113
+ // Mock won't even be called, since we weren't tracking activity
114
+ mock_client.expect_complete_activity_task().times(0);
115
+
116
+ let core = mock_worker(MocksHolder::from_client_with_activities(mock_client, []));
117
+
118
+ core.complete_activity_task(ActivityTaskCompletion {
119
+ task_token: vec![1],
120
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
121
+ })
122
+ .await
123
+ .unwrap();
124
+ core.shutdown().await;
125
+ }
126
+
127
+ #[tokio::test]
128
+ async fn heartbeats_report_cancels_only_once() {
129
+ let mut mock_client = mock_workflow_client();
130
+ mock_client
131
+ .expect_record_activity_heartbeat()
132
+ .times(2)
133
+ .returning(|_, _| {
134
+ Ok(RecordActivityTaskHeartbeatResponse {
135
+ cancel_requested: true,
136
+ })
137
+ });
138
+ mock_client
139
+ .expect_complete_activity_task()
140
+ .times(1)
141
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
142
+ mock_client
143
+ .expect_cancel_activity_task()
144
+ .times(1)
145
+ .returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
146
+
147
+ let core = mock_worker(MocksHolder::from_client_with_activities(
148
+ mock_client,
149
+ [
150
+ PollActivityTaskQueueResponse {
151
+ task_token: vec![1],
152
+ activity_id: "act1".to_string(),
153
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
154
+ ..Default::default()
155
+ }
156
+ .into(),
157
+ PollActivityTaskQueueResponse {
158
+ task_token: vec![2],
159
+ activity_id: "act2".to_string(),
160
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
161
+ ..Default::default()
162
+ }
163
+ .into(),
164
+ ],
165
+ ));
166
+
167
+ let act = core.poll_activity_task().await.unwrap();
168
+ core.record_activity_heartbeat(ActivityHeartbeat {
169
+ task_token: act.task_token.clone(),
170
+ details: vec![vec![1_u8, 2, 3].into()],
171
+ });
172
+ // We have to wait a beat for the heartbeat to be processed
173
+ sleep(Duration::from_millis(10)).await;
174
+ let act = core.poll_activity_task().await.unwrap();
175
+ assert_matches!(
176
+ &act,
177
+ ActivityTask {
178
+ task_token,
179
+ variant: Some(activity_task::Variant::Cancel(_)),
180
+ ..
181
+ } => { task_token == &vec![1] }
182
+ );
183
+
184
+ // Verify if we try to record another heartbeat for this task we do not issue a double cancel
185
+ // Allow heartbeat delay to elapse
186
+ sleep(Duration::from_millis(10)).await;
187
+ core.record_activity_heartbeat(ActivityHeartbeat {
188
+ task_token: act.task_token.clone(),
189
+ details: vec![vec![1_u8, 2, 3].into()],
190
+ });
191
+ // Wait delay again to flush heartbeat
192
+ sleep(Duration::from_millis(10)).await;
193
+ // Now complete it as cancelled
194
+ core.complete_activity_task(ActivityTaskCompletion {
195
+ task_token: act.task_token,
196
+
197
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
198
+ })
199
+ .await
200
+ .unwrap();
201
+ // Since cancels always come before new tasks, if we get a new non-cancel task, we did not
202
+ // double-issue cancels.
203
+ let act = core.poll_activity_task().await.unwrap();
204
+ assert_matches!(
205
+ &act,
206
+ ActivityTask {
207
+ task_token,
208
+ variant: Some(activity_task::Variant::Start(_)),
209
+ ..
210
+ } => { task_token == &[2] }
211
+ );
212
+ // Complete it so shutdown goes through
213
+ core.complete_activity_task(ActivityTaskCompletion {
214
+ task_token: act.task_token,
215
+
216
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
217
+ })
218
+ .await
219
+ .unwrap();
220
+ core.shutdown().await;
221
+ }
222
+
223
+ #[tokio::test]
224
+ async fn activity_cancel_interrupts_poll() {
225
+ let mut mock_poller = mock_manual_poller();
226
+ let mut poll_resps = VecDeque::from(vec![
227
+ async {
228
+ Some(Ok(PollActivityTaskQueueResponse {
229
+ task_token: vec![1],
230
+ heartbeat_timeout: Some(prost_dur!(from_secs(1))),
231
+ ..Default::default()
232
+ }))
233
+ }
234
+ .boxed(),
235
+ async {
236
+ tokio::time::sleep(Duration::from_millis(500)).await;
237
+ Some(Ok(Default::default()))
238
+ }
239
+ .boxed(),
240
+ ]);
241
+ mock_poller
242
+ .expect_poll()
243
+ .times(2)
244
+ .returning(move || poll_resps.pop_front().unwrap());
245
+
246
+ let mut mock_client = mock_manual_workflow_client();
247
+ mock_client
248
+ .expect_record_activity_heartbeat()
249
+ .times(1)
250
+ .returning(|_, _| {
251
+ async {
252
+ Ok(RecordActivityTaskHeartbeatResponse {
253
+ cancel_requested: true,
254
+ })
255
+ }
256
+ .boxed()
257
+ });
258
+ mock_client
259
+ .expect_complete_activity_task()
260
+ .times(1)
261
+ .returning(|_, _| async { Ok(RespondActivityTaskCompletedResponse::default()) }.boxed());
262
+
263
+ let mw = MockWorkerInputs {
264
+ act_poller: Some(Box::from(mock_poller)),
265
+ ..Default::default()
266
+ };
267
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
268
+ let last_finisher = AtomicUsize::new(0);
269
+ // Perform first poll to get the activity registered
270
+ let act = core.poll_activity_task().await.unwrap();
271
+ // Poll should block until heartbeat is sent, issuing the cancel, and interrupting the poll
272
+ tokio::join! {
273
+ async {
274
+ core.record_activity_heartbeat(ActivityHeartbeat {
275
+ task_token: act.task_token,
276
+
277
+ details: vec![vec![1_u8, 2, 3].into()],
278
+ });
279
+ last_finisher.store(1, Ordering::SeqCst);
280
+ },
281
+ async {
282
+ let act = core.poll_activity_task().await.unwrap();
283
+ // Must complete this activity for shutdown to finish
284
+ core.complete_activity_task(
285
+ ActivityTaskCompletion {
286
+ task_token: act.task_token,
287
+
288
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
289
+ }
290
+ ).await.unwrap();
291
+ last_finisher.store(2, Ordering::SeqCst);
292
+ }
293
+ };
294
+ // So that we know we blocked
295
+ assert_eq!(last_finisher.load(Ordering::Acquire), 2);
296
+ core.shutdown().await;
297
+ }
298
+
299
+ #[tokio::test]
300
+ async fn activity_poll_timeout_retries() {
301
+ let mock_client = mock_workflow_client();
302
+ let mut calls = 0;
303
+ let mut mock_act_poller = mock_poller();
304
+ mock_act_poller.expect_poll().times(3).returning(move || {
305
+ calls += 1;
306
+ if calls <= 2 {
307
+ Some(Ok(PollActivityTaskQueueResponse::default()))
308
+ } else {
309
+ Some(Ok(PollActivityTaskQueueResponse {
310
+ task_token: b"hello!".to_vec(),
311
+ ..Default::default()
312
+ }))
313
+ }
314
+ });
315
+ let mw = MockWorkerInputs {
316
+ act_poller: Some(Box::from(mock_act_poller)),
317
+ ..Default::default()
318
+ };
319
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
320
+ let r = core.poll_activity_task().await.unwrap();
321
+ assert_matches!(r.task_token.as_slice(), b"hello!");
322
+ }
323
+
324
+ #[tokio::test]
325
+ async fn many_concurrent_heartbeat_cancels() {
326
+ // Run a whole bunch of activities in parallel, having the server return cancellations for
327
+ // them after a few successful heartbeats
328
+ const CONCURRENCY_NUM: usize = 5;
329
+
330
+ let mut mock_client = mock_manual_workflow_client();
331
+ let mut poll_resps = VecDeque::from(
332
+ (0..CONCURRENCY_NUM)
333
+ .map(|i| {
334
+ async move {
335
+ Ok(PollActivityTaskQueueResponse {
336
+ task_token: i.to_be_bytes().to_vec(),
337
+ heartbeat_timeout: Some(prost_dur!(from_millis(200))),
338
+ ..Default::default()
339
+ })
340
+ }
341
+ .boxed()
342
+ })
343
+ .collect::<Vec<_>>(),
344
+ );
345
+ // Because the mock is so fast, it's possible it can return before the cancel channel in
346
+ // the activity task poll selector. So, the final poll when there are no more tasks must
347
+ // take a while.
348
+ poll_resps.push_back(
349
+ async {
350
+ sleep(Duration::from_secs(10)).await;
351
+ unreachable!("Long poll")
352
+ }
353
+ .boxed(),
354
+ );
355
+ let mut calls_map = HashMap::<_, i32>::new();
356
+ mock_client
357
+ .expect_poll_activity_task()
358
+ .returning(move |_, _| poll_resps.pop_front().unwrap());
359
+ mock_client
360
+ .expect_cancel_activity_task()
361
+ .returning(move |_, _| async move { Ok(Default::default()) }.boxed());
362
+ mock_client
363
+ .expect_record_activity_heartbeat()
364
+ .returning(move |tt, _| {
365
+ let calls = match calls_map.entry(tt) {
366
+ Entry::Occupied(mut e) => {
367
+ *e.get_mut() += 1;
368
+ *e.get()
369
+ }
370
+ Entry::Vacant(v) => *v.insert(1),
371
+ };
372
+ async move {
373
+ if calls < 5 {
374
+ Ok(RecordActivityTaskHeartbeatResponse {
375
+ cancel_requested: false,
376
+ })
377
+ } else {
378
+ Ok(RecordActivityTaskHeartbeatResponse {
379
+ cancel_requested: true,
380
+ })
381
+ }
382
+ }
383
+ .boxed()
384
+ });
385
+
386
+ let worker = &Worker::new_test(
387
+ test_worker_cfg()
388
+ .max_outstanding_activities(CONCURRENCY_NUM)
389
+ // Only 1 poll at a time to avoid over-polling and running out of responses
390
+ .max_concurrent_at_polls(1_usize)
391
+ .build()
392
+ .unwrap(),
393
+ mock_client,
394
+ );
395
+
396
+ // Poll all activities first so they are registered
397
+ for _ in 0..CONCURRENCY_NUM {
398
+ worker.poll_activity_task().await.unwrap();
399
+ }
400
+
401
+ // Spawn "activities"
402
+ fanout_tasks(CONCURRENCY_NUM, |i| async move {
403
+ let task_token = i.to_be_bytes().to_vec();
404
+ for _ in 0..12 {
405
+ worker.record_activity_heartbeat(ActivityHeartbeat {
406
+ task_token: task_token.clone(),
407
+ details: vec![],
408
+ });
409
+ sleep(Duration::from_millis(50)).await;
410
+ }
411
+ })
412
+ .await;
413
+
414
+ // Read all the cancellations and reply to them concurrently
415
+ fanout_tasks(CONCURRENCY_NUM, |_| async move {
416
+ let r = worker.poll_activity_task().await.unwrap();
417
+ assert_matches!(
418
+ r,
419
+ ActivityTask {
420
+ variant: Some(activity_task::Variant::Cancel(_)),
421
+ ..
422
+ }
423
+ );
424
+ worker
425
+ .complete_activity_task(ActivityTaskCompletion {
426
+ task_token: r.task_token.clone(),
427
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
428
+ })
429
+ .await
430
+ .unwrap();
431
+ })
432
+ .await;
433
+
434
+ worker.shutdown().await;
435
+ }
436
+
437
+ #[tokio::test]
438
+ async fn activity_timeout_no_double_resolve() {
439
+ let t = canned_histories::activity_double_resolve_repro();
440
+ let core = build_fake_worker("fake_wf_id", t, [3]);
441
+ let activity_id = 1;
442
+
443
+ poll_and_reply(
444
+ &core,
445
+ WorkflowCachingPolicy::NonSticky,
446
+ &[
447
+ gen_assert_and_reply(
448
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
449
+ vec![ScheduleActivity {
450
+ seq: activity_id,
451
+ activity_id: activity_id.to_string(),
452
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
453
+ ..Default::default()
454
+ }
455
+ .into()],
456
+ ),
457
+ gen_assert_and_reply(
458
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
459
+ vec![
460
+ RequestCancelActivity { seq: activity_id }.into(),
461
+ start_timer_cmd(2, Duration::from_secs(1)),
462
+ ],
463
+ ),
464
+ gen_assert_and_reply(
465
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
466
+ ResolveActivity {
467
+ result: Some(ActivityResolution {
468
+ status: Some(activity_resolution::Status::Cancelled(..)),
469
+ }),
470
+ ..
471
+ }
472
+ )),
473
+ vec![],
474
+ ),
475
+ gen_assert_and_reply(
476
+ &job_assert!(
477
+ workflow_activation_job::Variant::SignalWorkflow(_),
478
+ workflow_activation_job::Variant::FireTimer(_)
479
+ ),
480
+ vec![CompleteWorkflowExecution { result: None }.into()],
481
+ ),
482
+ ],
483
+ )
484
+ .await;
485
+
486
+ core.shutdown().await;
487
+ }
488
+
489
+ #[tokio::test]
490
+ async fn can_heartbeat_acts_during_shutdown() {
491
+ let mut mock_client = mock_workflow_client();
492
+ mock_client
493
+ .expect_record_activity_heartbeat()
494
+ .times(1)
495
+ .returning(|_, _| {
496
+ Ok(RecordActivityTaskHeartbeatResponse {
497
+ cancel_requested: false,
498
+ })
499
+ });
500
+ mock_client
501
+ .expect_complete_activity_task()
502
+ .times(1)
503
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
504
+
505
+ let core = mock_worker(MocksHolder::from_client_with_activities(
506
+ mock_client,
507
+ [PollActivityTaskQueueResponse {
508
+ task_token: vec![1],
509
+ activity_id: "act1".to_string(),
510
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
511
+ ..Default::default()
512
+ }
513
+ .into()],
514
+ ));
515
+
516
+ let act = core.poll_activity_task().await.unwrap();
517
+ // Make sure shutdown has progressed before trying to record heartbeat / complete
518
+ let shutdown_fut = core.shutdown();
519
+ advance_fut!(shutdown_fut);
520
+ core.record_activity_heartbeat(ActivityHeartbeat {
521
+ task_token: act.task_token.clone(),
522
+
523
+ details: vec![vec![1_u8, 2, 3].into()],
524
+ });
525
+ core.complete_activity_task(ActivityTaskCompletion {
526
+ task_token: act.task_token,
527
+
528
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
529
+ })
530
+ .await
531
+ .unwrap();
532
+ shutdown_fut.await;
533
+ }
534
+
535
+ /// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
536
+ /// activity, that we flush those details before reporting the failure completion.
537
+ #[tokio::test]
538
+ async fn complete_act_with_fail_flushes_heartbeat() {
539
+ let last_hb = 50;
540
+ let mut mock_client = mock_workflow_client();
541
+ let last_seen_payload = Rc::new(RefCell::new(None));
542
+ let lsp = last_seen_payload.clone();
543
+ mock_client
544
+ .expect_record_activity_heartbeat()
545
+ // Two times b/c we always record the first heartbeat, and we'll flush the last
546
+ .times(2)
547
+ .returning_st(move |_, payload| {
548
+ *lsp.borrow_mut() = payload;
549
+ Ok(RecordActivityTaskHeartbeatResponse {
550
+ cancel_requested: false,
551
+ })
552
+ });
553
+ mock_client
554
+ .expect_fail_activity_task()
555
+ .times(1)
556
+ .returning(|_, _| Ok(RespondActivityTaskFailedResponse::default()));
557
+
558
+ let core = mock_worker(MocksHolder::from_client_with_activities(
559
+ mock_client,
560
+ [PollActivityTaskQueueResponse {
561
+ task_token: vec![1],
562
+ activity_id: "act1".to_string(),
563
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
564
+ ..Default::default()
565
+ }
566
+ .into()],
567
+ ));
568
+
569
+ let act = core.poll_activity_task().await.unwrap();
570
+ // Record a bunch of heartbeats
571
+ for i in 1..=last_hb {
572
+ core.record_activity_heartbeat(ActivityHeartbeat {
573
+ task_token: act.task_token.clone(),
574
+ details: vec![vec![i].into()],
575
+ });
576
+ }
577
+ core.complete_activity_task(ActivityTaskCompletion {
578
+ task_token: act.task_token.clone(),
579
+ result: Some(ActivityExecutionResult::fail("Ahh".into())),
580
+ })
581
+ .await
582
+ .unwrap();
583
+ core.shutdown().await;
584
+
585
+ // Verify the last seen call to record a heartbeat had the last detail payload
586
+ let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
587
+ assert_eq!(last_seen_payload.data, &[last_hb]);
588
+ }
589
+
590
+ #[tokio::test]
591
+ async fn max_tq_acts_set_passed_to_poll_properly() {
592
+ let rate = 9.28;
593
+ let mut mock_client = mock_workflow_client();
594
+ mock_client
595
+ .expect_poll_activity_task()
596
+ .returning(move |_, tps| {
597
+ assert_eq!(tps, Some(rate));
598
+ Ok(PollActivityTaskQueueResponse {
599
+ task_token: vec![1],
600
+ ..Default::default()
601
+ })
602
+ });
603
+
604
+ let cfg = WorkerConfigBuilder::default()
605
+ .namespace("enchi")
606
+ .task_queue("cat")
607
+ .max_concurrent_at_polls(1_usize)
608
+ .worker_build_id("test_bin_id")
609
+ .max_task_queue_activities_per_second(rate)
610
+ .build()
611
+ .unwrap();
612
+ let worker = Worker::new_test(cfg, mock_client);
613
+ worker.poll_activity_task().await.unwrap();
614
+ }
615
+
616
+ /// This test doesn't test the real worker config since [mock_worker] bypasses the worker
617
+ /// constructor, [mock_worker] will not pass an activity poller to the worker when
618
+ /// `no_remote_activities` is set to `true`.
619
+ #[tokio::test]
620
+ async fn no_eager_activities_requested_when_worker_options_disable_remote_activities() {
621
+ let wfid = "fake_wf_id";
622
+ let mut t = TestHistoryBuilder::default();
623
+ t.add_by_type(EventType::WorkflowExecutionStarted);
624
+ t.add_full_wf_task();
625
+ let scheduled_event_id = t.add_activity_task_scheduled("act_id");
626
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
627
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, b"hi".into());
628
+ t.add_full_wf_task();
629
+ t.add_workflow_execution_completed();
630
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
631
+ // Clone it to move into the callback below
632
+ let num_eager_requested_clone = num_eager_requested.clone();
633
+
634
+ let mut mock = mock_workflow_client();
635
+ mock.expect_complete_workflow_task()
636
+ .times(1)
637
+ .returning(move |req| {
638
+ // Store the number of eager activities requested to be checked below
639
+ let count = req
640
+ .commands
641
+ .into_iter()
642
+ .filter(|c| match c.attributes {
643
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
644
+ ScheduleActivityTaskCommandAttributes {
645
+ request_eager_execution,
646
+ ..
647
+ },
648
+ )) => request_eager_execution,
649
+ _ => false,
650
+ })
651
+ .count();
652
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
653
+ Ok(RespondWorkflowTaskCompletedResponse {
654
+ workflow_task: None,
655
+ activity_tasks: vec![],
656
+ reset_history_event_id: 0,
657
+ })
658
+ });
659
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
660
+ let mut mock_poller = mock_manual_poller();
661
+ mock_poller
662
+ .expect_poll()
663
+ .returning(|| futures::future::pending().boxed());
664
+ mock.set_act_poller(Box::new(mock_poller));
665
+ mock.worker_cfg(|wc| {
666
+ wc.max_cached_workflows = 2;
667
+ wc.no_remote_activities = true;
668
+ });
669
+ let core = mock_worker(mock);
670
+
671
+ // Test start
672
+ let wf_task = core.poll_workflow_activation().await.unwrap();
673
+ let cmds = vec![ScheduleActivity {
674
+ seq: 1,
675
+ activity_id: "act_id".to_string(),
676
+ task_queue: TEST_Q.to_string(),
677
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
678
+ ..Default::default()
679
+ }
680
+ .into()];
681
+
682
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
683
+ wf_task.run_id,
684
+ cmds,
685
+ ))
686
+ .await
687
+ .unwrap();
688
+
689
+ core.shutdown().await;
690
+
691
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 0);
692
+ }
693
+
694
+ /// This test verifies that activity tasks which come as replies to completing a WFT are properly
695
+ /// delivered via polling.
696
+ #[tokio::test]
697
+ async fn activity_tasks_from_completion_are_delivered() {
698
+ // Construct the history - one task with 5 activities, 4 on the same task queue, and 1 on a
699
+ // different queue, 3 activities will be executed eagerly as specified by the
700
+ // MAX_EAGER_ACTIVITY_RESERVATIONS_PER_WORKFLOW_TASK constant.
701
+ let wfid = "fake_wf_id";
702
+ let mut t = TestHistoryBuilder::default();
703
+ t.add_by_type(EventType::WorkflowExecutionStarted);
704
+ t.add_full_wf_task();
705
+ let act_same_queue_scheduled_ids = (1..4)
706
+ .map(|i| t.add_activity_task_scheduled(format!("act_id_{}_same_queue", i)))
707
+ .collect_vec();
708
+ t.add_activity_task_scheduled("act_id_same_queue_not_eager");
709
+ t.add_activity_task_scheduled("act_id_different_queue");
710
+ for scheduled_event_id in act_same_queue_scheduled_ids {
711
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
712
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, b"hi".into());
713
+ }
714
+ t.add_full_wf_task();
715
+ t.add_workflow_execution_completed();
716
+
717
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
718
+ // Clone it to move into the callback below
719
+ let num_eager_requested_clone = num_eager_requested.clone();
720
+
721
+ let mut mock = mock_workflow_client();
722
+ mock.expect_complete_workflow_task()
723
+ .times(1)
724
+ .returning(move |req| {
725
+ // Store the number of eager activities requested to be checked below
726
+ let count = req
727
+ .commands
728
+ .into_iter()
729
+ .filter(|c| match c.attributes {
730
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
731
+ ScheduleActivityTaskCommandAttributes {
732
+ request_eager_execution,
733
+ ..
734
+ },
735
+ )) => request_eager_execution,
736
+ _ => false,
737
+ })
738
+ .count();
739
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
740
+ Ok(RespondWorkflowTaskCompletedResponse {
741
+ workflow_task: None,
742
+ activity_tasks: (1..4)
743
+ .map(|i| PollActivityTaskQueueResponse {
744
+ task_token: vec![i],
745
+ activity_id: format!("act_id_{}_same_queue", i),
746
+ ..Default::default()
747
+ })
748
+ .collect_vec(),
749
+ reset_history_event_id: 0,
750
+ })
751
+ });
752
+ mock.expect_complete_activity_task()
753
+ .times(3)
754
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
755
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
756
+ let mut mock_poller = mock_manual_poller();
757
+ mock_poller
758
+ .expect_poll()
759
+ .returning(|| futures::future::pending().boxed());
760
+ mock.set_act_poller(Box::new(mock_poller));
761
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
762
+ let core = mock_worker(mock);
763
+
764
+ // Test start
765
+ let wf_task = core.poll_workflow_activation().await.unwrap();
766
+ let mut cmds = (1..4)
767
+ .map(|seq| {
768
+ ScheduleActivity {
769
+ seq,
770
+ activity_id: format!("act_id_{}_same_queue", seq),
771
+ task_queue: TEST_Q.to_string(),
772
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
773
+ ..Default::default()
774
+ }
775
+ .into()
776
+ })
777
+ .collect_vec();
778
+ cmds.push(
779
+ ScheduleActivity {
780
+ seq: 4,
781
+ activity_id: "act_id_same_queue_not_eager".to_string(),
782
+ task_queue: TEST_Q.to_string(),
783
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
784
+ ..Default::default()
785
+ }
786
+ .into(),
787
+ );
788
+ cmds.push(
789
+ ScheduleActivity {
790
+ seq: 5,
791
+ activity_id: "act_id_different_queue".to_string(),
792
+ task_queue: "different_queue".to_string(),
793
+ cancellation_type: ActivityCancellationType::Abandon as i32,
794
+ ..Default::default()
795
+ }
796
+ .into(),
797
+ );
798
+
799
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
800
+ wf_task.run_id,
801
+ cmds,
802
+ ))
803
+ .await
804
+ .unwrap();
805
+
806
+ // We should see the 3 eager activities when we poll now
807
+ for i in 1..4 {
808
+ let act_task = core.poll_activity_task().await.unwrap();
809
+ assert_eq!(act_task.task_token, vec![i]);
810
+
811
+ core.complete_activity_task(ActivityTaskCompletion {
812
+ task_token: act_task.task_token.clone(),
813
+ result: Some(ActivityExecutionResult::ok("hi".into())),
814
+ })
815
+ .await
816
+ .unwrap();
817
+ }
818
+
819
+ core.shutdown().await;
820
+
821
+ // Verify only a single eager activity was scheduled (the one on our worker's task queue)
822
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
823
+ }
824
+
825
+ #[tokio::test]
826
+ async fn activity_tasks_from_completion_reserve_slots() {
827
+ let wf_id = "fake_wf_id";
828
+ let mut t = TestHistoryBuilder::default();
829
+ t.add_by_type(EventType::WorkflowExecutionStarted);
830
+ t.add_full_wf_task();
831
+ let schedid = t.add_activity_task_scheduled("1");
832
+ let startid = t.add_activity_task_started(schedid);
833
+ t.add_activity_task_completed(schedid, startid, b"hi".into());
834
+ t.add_full_wf_task();
835
+ let schedid = t.add_activity_task_scheduled("2");
836
+ let startid = t.add_activity_task_started(schedid);
837
+ t.add_activity_task_completed(schedid, startid, b"hi".into());
838
+ t.add_full_wf_task();
839
+ t.add_workflow_execution_completed();
840
+
841
+ let mut mock = mock_workflow_client();
842
+ // Set up two tasks to be returned via normal activity polling
843
+ let act_tasks = VecDeque::from(vec![
844
+ PollActivityTaskQueueResponse {
845
+ task_token: vec![1],
846
+ activity_id: "act1".to_string(),
847
+ ..Default::default()
848
+ }
849
+ .into(),
850
+ PollActivityTaskQueueResponse {
851
+ task_token: vec![2],
852
+ activity_id: "act2".to_string(),
853
+ ..Default::default()
854
+ }
855
+ .into(),
856
+ ]);
857
+ mock.expect_complete_activity_task()
858
+ .times(2)
859
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
860
+ let barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
861
+ let mut mh = MockPollCfg::from_resp_batches(
862
+ wf_id,
863
+ t,
864
+ [
865
+ ResponseType::ToTaskNum(1),
866
+ // We don't want the second task to be delivered until *after* the activity tasks
867
+ // have been completed, so that the second activity schedule will have slots available
868
+ ResponseType::UntilResolved(
869
+ async {
870
+ barr.wait().await;
871
+ barr.wait().await;
872
+ }
873
+ .boxed(),
874
+ 2,
875
+ ),
876
+ ResponseType::AllHistory,
877
+ ],
878
+ mock,
879
+ );
880
+ mh.completion_asserts = Some(Box::new(|wftc| {
881
+ // Make sure when we see the completion with the schedule act command that it does
882
+ // not have the eager execution flag set the first time, and does the second.
883
+ if let Some(Attributes::ScheduleActivityTaskCommandAttributes(attrs)) =
884
+ wftc.commands.get(0).and_then(|cmd| cmd.attributes.as_ref())
885
+ {
886
+ if attrs.activity_id == "1" {
887
+ assert!(!attrs.request_eager_execution);
888
+ } else {
889
+ assert!(attrs.request_eager_execution);
890
+ }
891
+ }
892
+ }));
893
+ let mut mock = build_mock_pollers(mh);
894
+ mock.worker_cfg(|cfg| {
895
+ cfg.max_cached_workflows = 2;
896
+ cfg.max_outstanding_activities = 2;
897
+ });
898
+ mock.set_act_poller(mock_poller_from_resps(act_tasks));
899
+ let core = Arc::new(mock_worker(mock));
900
+ let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
901
+
902
+ // First poll for activities twice, occupying both slots
903
+ let at1 = core.poll_activity_task().await.unwrap();
904
+ let at2 = core.poll_activity_task().await.unwrap();
905
+
906
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
907
+ ctx.activity(ActivityOptions {
908
+ activity_type: "act1".to_string(),
909
+ ..Default::default()
910
+ })
911
+ .await;
912
+ ctx.activity(ActivityOptions {
913
+ activity_type: "act2".to_string(),
914
+ ..Default::default()
915
+ })
916
+ .await;
917
+ Ok(().into())
918
+ });
919
+
920
+ worker
921
+ .submit_wf(
922
+ wf_id.to_owned(),
923
+ DEFAULT_WORKFLOW_TYPE,
924
+ vec![],
925
+ WorkflowOptions::default(),
926
+ )
927
+ .await
928
+ .unwrap();
929
+ let act_completer = async {
930
+ barr.wait().await;
931
+ core.complete_activity_task(ActivityTaskCompletion {
932
+ task_token: at1.task_token,
933
+ result: Some(ActivityExecutionResult::ok("hi".into())),
934
+ })
935
+ .await
936
+ .unwrap();
937
+ core.complete_activity_task(ActivityTaskCompletion {
938
+ task_token: at2.task_token,
939
+ result: Some(ActivityExecutionResult::ok("hi".into())),
940
+ })
941
+ .await
942
+ .unwrap();
943
+ barr.wait().await;
944
+ };
945
+ // This wf poll should *not* set the flag that it wants tasks back since both slots are
946
+ // occupied
947
+ let run_fut = async { worker.run_until_done().await.unwrap() };
948
+ tokio::join!(run_fut, act_completer);
949
+ }
950
+
951
+ #[tokio::test]
952
+ async fn retryable_net_error_exhaustion_is_nonfatal() {
953
+ let mut mock_client = mock_workflow_client();
954
+ mock_client
955
+ .expect_complete_activity_task()
956
+ .times(1)
957
+ .returning(|_, _| Err(tonic::Status::internal("retryable error")));
958
+
959
+ let core = mock_worker(MocksHolder::from_client_with_activities(
960
+ mock_client,
961
+ [PollActivityTaskQueueResponse {
962
+ task_token: vec![1],
963
+ activity_id: "act1".to_string(),
964
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
965
+ ..Default::default()
966
+ }
967
+ .into()],
968
+ ));
969
+
970
+ let act = core.poll_activity_task().await.unwrap();
971
+ core.complete_activity_task(ActivityTaskCompletion {
972
+ task_token: act.task_token,
973
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
974
+ })
975
+ .await
976
+ .unwrap();
977
+ core.shutdown().await;
978
+ }
979
+
980
+ #[tokio::test]
981
+ async fn cant_complete_activity_with_unset_result_payload() {
982
+ let mut mock_client = mock_workflow_client();
983
+ mock_client
984
+ .expect_poll_activity_task()
985
+ .returning(move |_, _| {
986
+ Ok(PollActivityTaskQueueResponse {
987
+ task_token: vec![1],
988
+ ..Default::default()
989
+ })
990
+ });
991
+
992
+ let cfg = WorkerConfigBuilder::default()
993
+ .namespace("enchi")
994
+ .task_queue("cat")
995
+ .worker_build_id("enchi_loves_salmon")
996
+ .build()
997
+ .unwrap();
998
+ let worker = Worker::new_test(cfg, mock_client);
999
+ let t = worker.poll_activity_task().await.unwrap();
1000
+ let res = worker
1001
+ .complete_activity_task(ActivityTaskCompletion {
1002
+ task_token: t.task_token,
1003
+ result: Some(ActivityExecutionResult {
1004
+ status: Some(activity_execution_result::Status::Completed(Success {
1005
+ result: None,
1006
+ })),
1007
+ }),
1008
+ })
1009
+ .await;
1010
+ assert_matches!(
1011
+ res,
1012
+ Err(CompleteActivityError::MalformedActivityCompletion { .. })
1013
+ )
1014
+ }