temporalio 0.0.0 → 0.0.2

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