temporalio 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +130 -0
  3. data/bridge/Cargo.lock +2865 -0
  4. data/bridge/Cargo.toml +26 -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 +107 -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/bridge-ffi/Cargo.toml +24 -0
  15. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  16. data/bridge/sdk-core/bridge-ffi/build.rs +25 -0
  17. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +249 -0
  18. data/bridge/sdk-core/bridge-ffi/src/lib.rs +825 -0
  19. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +211 -0
  20. data/bridge/sdk-core/client/Cargo.toml +40 -0
  21. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  22. data/bridge/sdk-core/client/src/lib.rs +1294 -0
  23. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  24. data/bridge/sdk-core/client/src/raw.rs +931 -0
  25. data/bridge/sdk-core/client/src/retry.rs +674 -0
  26. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  27. data/bridge/sdk-core/core/Cargo.toml +116 -0
  28. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  29. data/bridge/sdk-core/core/benches/workflow_replay.rs +73 -0
  30. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  31. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +911 -0
  32. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  33. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  34. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +515 -0
  35. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  36. data/bridge/sdk-core/core/src/core_tests/queries.rs +736 -0
  37. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  38. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  39. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  40. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2070 -0
  41. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  42. data/bridge/sdk-core/core/src/lib.rs +175 -0
  43. data/bridge/sdk-core/core/src/log_export.rs +62 -0
  44. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  45. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  46. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  47. data/bridge/sdk-core/core/src/replay/mod.rs +71 -0
  48. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  49. data/bridge/sdk-core/core/src/telemetry/metrics.rs +383 -0
  50. data/bridge/sdk-core/core/src/telemetry/mod.rs +412 -0
  51. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +77 -0
  52. data/bridge/sdk-core/core/src/test_help/mod.rs +875 -0
  53. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  54. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1042 -0
  55. data/bridge/sdk-core/core/src/worker/activities.rs +464 -0
  56. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  57. data/bridge/sdk-core/core/src/worker/client.rs +347 -0
  58. data/bridge/sdk-core/core/src/worker/mod.rs +566 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  60. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +110 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +458 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +911 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +298 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +171 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +860 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +140 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +161 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +133 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1448 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +342 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +127 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +712 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +71 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +443 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +439 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +169 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +246 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1184 -0
  80. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +277 -0
  81. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  82. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +647 -0
  83. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1143 -0
  84. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  85. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  86. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +940 -0
  87. data/bridge/sdk-core/core-api/Cargo.toml +31 -0
  88. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  89. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  90. data/bridge/sdk-core/core-api/src/lib.rs +151 -0
  91. data/bridge/sdk-core/core-api/src/worker.rs +135 -0
  92. data/bridge/sdk-core/etc/deps.svg +187 -0
  93. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  94. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  95. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  96. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  97. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  98. data/bridge/sdk-core/fsm/README.md +3 -0
  99. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  100. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  115. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  116. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  117. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  118. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  119. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  120. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  121. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  122. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  123. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  124. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  125. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  126. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  127. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  128. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  129. data/bridge/sdk-core/protos/api_upstream/buf.yaml +12 -0
  130. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  131. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  132. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +259 -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 +46 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +55 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +168 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +97 -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 +51 -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 +751 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +161 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +99 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -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 +145 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1124 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +401 -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/bridge/bridge.proto +210 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +261 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +297 -0
  174. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  175. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  176. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  179. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  180. data/bridge/sdk-core/rustfmt.toml +1 -0
  181. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  182. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  183. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  184. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  185. data/bridge/sdk-core/sdk/src/conversions.rs +8 -0
  186. data/bridge/sdk-core/sdk/src/interceptors.rs +17 -0
  187. data/bridge/sdk-core/sdk/src/lib.rs +792 -0
  188. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  189. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  190. data/bridge/sdk-core/sdk/src/workflow_context.rs +683 -0
  191. data/bridge/sdk-core/sdk/src/workflow_future.rs +503 -0
  192. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  193. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  194. data/bridge/sdk-core/sdk-core-protos/build.rs +108 -0
  195. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  196. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +497 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1910 -0
  199. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  200. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  201. data/bridge/sdk-core/test-utils/Cargo.toml +35 -0
  202. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  203. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  204. data/bridge/sdk-core/test-utils/src/lib.rs +598 -0
  205. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  206. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  207. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +218 -0
  208. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +146 -0
  209. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  210. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  211. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  212. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +634 -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 +137 -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 +587 -0
  227. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  228. data/bridge/sdk-core/tests/main.rs +111 -0
  229. data/bridge/sdk-core/tests/runner.rs +93 -0
  230. data/bridge/src/connection.rs +167 -0
  231. data/bridge/src/lib.rs +180 -0
  232. data/bridge/src/runtime.rs +47 -0
  233. data/bridge/src/worker.rs +73 -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 +48 -0
  238. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +67 -0
  239. data/lib/gen/temporal/api/command/v1/message_pb.rb +166 -0
  240. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  241. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +32 -0
  242. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +26 -0
  243. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +37 -0
  244. data/lib/gen/temporal/api/enums/v1/common_pb.rb +41 -0
  245. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +67 -0
  246. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +71 -0
  247. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  248. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  249. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  250. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  251. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  252. data/lib/gen/temporal/api/enums/v1/update_pb.rb +28 -0
  253. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  254. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  255. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  256. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  257. data/lib/gen/temporal/api/history/v1/message_pb.rb +489 -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 +125 -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 +128 -0
  264. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  265. data/lib/gen/temporal/api/update/v1/message_pb.rb +26 -0
  266. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  267. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +110 -0
  268. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +771 -0
  269. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  270. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  271. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  272. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  273. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  274. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  275. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  276. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  277. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +164 -0
  278. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +192 -0
  279. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  280. data/lib/temporal/bridge.rb +14 -0
  281. data/lib/temporal/client/implementation.rb +339 -0
  282. data/lib/temporal/client/workflow_handle.rb +243 -0
  283. data/lib/temporal/client.rb +144 -0
  284. data/lib/temporal/connection.rb +736 -0
  285. data/lib/temporal/data_converter.rb +150 -0
  286. data/lib/temporal/error/failure.rb +194 -0
  287. data/lib/temporal/error/workflow_failure.rb +17 -0
  288. data/lib/temporal/errors.rb +22 -0
  289. data/lib/temporal/failure_converter/base.rb +26 -0
  290. data/lib/temporal/failure_converter/basic.rb +313 -0
  291. data/lib/temporal/failure_converter.rb +8 -0
  292. data/lib/temporal/interceptor/chain.rb +27 -0
  293. data/lib/temporal/interceptor/client.rb +102 -0
  294. data/lib/temporal/payload_codec/base.rb +32 -0
  295. data/lib/temporal/payload_converter/base.rb +24 -0
  296. data/lib/temporal/payload_converter/bytes.rb +26 -0
  297. data/lib/temporal/payload_converter/composite.rb +47 -0
  298. data/lib/temporal/payload_converter/encoding_base.rb +35 -0
  299. data/lib/temporal/payload_converter/json.rb +25 -0
  300. data/lib/temporal/payload_converter/nil.rb +25 -0
  301. data/lib/temporal/payload_converter.rb +14 -0
  302. data/lib/temporal/retry_policy.rb +82 -0
  303. data/lib/temporal/retry_state.rb +35 -0
  304. data/lib/temporal/runtime.rb +22 -0
  305. data/lib/temporal/timeout_type.rb +29 -0
  306. data/lib/temporal/version.rb +1 -1
  307. data/lib/temporal/workflow/execution_info.rb +54 -0
  308. data/lib/temporal/workflow/execution_status.rb +36 -0
  309. data/lib/temporal/workflow/id_reuse_policy.rb +36 -0
  310. data/lib/temporal/workflow/query_reject_condition.rb +33 -0
  311. data/lib/temporal.rb +4 -0
  312. data/lib/temporalio.rb +3 -1
  313. data/lib/thermite_patch.rb +23 -0
  314. data/temporalio.gemspec +41 -0
  315. metadata +543 -9
  316. data/temporal.gemspec +0 -20
@@ -0,0 +1,911 @@
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 std::{
14
+ cell::RefCell,
15
+ collections::{hash_map::Entry, HashMap, VecDeque},
16
+ rc::Rc,
17
+ sync::{
18
+ atomic::{AtomicUsize, Ordering},
19
+ Arc,
20
+ },
21
+ time::Duration,
22
+ };
23
+ use temporal_client::WorkflowOptions;
24
+ use temporal_sdk::{ActivityOptions, WfContext};
25
+ use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
26
+ use temporal_sdk_core_protos::temporal::api::command::v1::ScheduleActivityTaskCommandAttributes;
27
+ use temporal_sdk_core_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,
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 verifies that activity tasks which come as replies to completing a WFT are properly
617
+ /// delivered via polling.
618
+ #[tokio::test]
619
+ async fn activity_tasks_from_completion_are_delivered() {
620
+ let wfid = "fake_wf_id";
621
+ let mut t = TestHistoryBuilder::default();
622
+ t.add_by_type(EventType::WorkflowExecutionStarted);
623
+ t.add_full_wf_task();
624
+ let act_same_queue_sched_id = t.add_activity_task_scheduled("act_id_same_queue");
625
+ let act_different_queue_sched_id = t.add_activity_task_scheduled("act_id_different_queue");
626
+ let act_same_queue_start_id = t.add_activity_task_started(act_same_queue_sched_id);
627
+ t.add_activity_task_completed(
628
+ act_same_queue_sched_id,
629
+ act_same_queue_start_id,
630
+ b"hi".into(),
631
+ );
632
+ t.add_full_wf_task();
633
+ t.add_activity_task_cancel_requested(act_different_queue_sched_id);
634
+ t.add_workflow_execution_completed();
635
+
636
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
637
+ // Clone it to move into the callback below
638
+ let num_eager_requested_clone = num_eager_requested.clone();
639
+
640
+ let mut mock = mock_workflow_client();
641
+ mock.expect_complete_workflow_task()
642
+ .times(1)
643
+ .returning(move |req| {
644
+ // Store the number of eager activities requested to be checked below
645
+ let count = req
646
+ .commands
647
+ .into_iter()
648
+ .filter(|c| match c.attributes {
649
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
650
+ ScheduleActivityTaskCommandAttributes {
651
+ request_eager_execution,
652
+ ..
653
+ },
654
+ )) => request_eager_execution,
655
+ _ => false,
656
+ })
657
+ .count();
658
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
659
+ Ok(RespondWorkflowTaskCompletedResponse {
660
+ workflow_task: None,
661
+ activity_tasks: vec![PollActivityTaskQueueResponse {
662
+ task_token: vec![1],
663
+ activity_id: "act_id_same_queue".to_string(),
664
+ ..Default::default()
665
+ }],
666
+ })
667
+ });
668
+ mock.expect_complete_activity_task()
669
+ .times(1)
670
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
671
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
672
+ let mut mock_poller = mock_manual_poller();
673
+ mock_poller
674
+ .expect_poll()
675
+ .returning(|| futures::future::pending().boxed());
676
+ mock.set_act_poller(Box::new(mock_poller));
677
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
678
+ let core = mock_worker(mock);
679
+
680
+ let wf_task = core.poll_workflow_activation().await.unwrap();
681
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
682
+ wf_task.run_id,
683
+ vec![
684
+ ScheduleActivity {
685
+ seq: 1,
686
+ activity_id: "act_id_same_queue".to_string(),
687
+ task_queue: TEST_Q.to_string(),
688
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
689
+ ..Default::default()
690
+ }
691
+ .into(),
692
+ ScheduleActivity {
693
+ seq: 2,
694
+ activity_id: "act_id_different_queue".to_string(),
695
+ task_queue: "different_queue".to_string(),
696
+ cancellation_type: ActivityCancellationType::Abandon as i32,
697
+ ..Default::default()
698
+ }
699
+ .into(),
700
+ ],
701
+ ))
702
+ .await
703
+ .unwrap();
704
+
705
+ // We should see the activity when we poll now
706
+ let act_task = core.poll_activity_task().await.unwrap();
707
+ assert_eq!(act_task.task_token, vec![1]);
708
+
709
+ core.complete_activity_task(ActivityTaskCompletion {
710
+ task_token: act_task.task_token.clone(),
711
+ result: Some(ActivityExecutionResult::ok("hi".into())),
712
+ })
713
+ .await
714
+ .unwrap();
715
+
716
+ core.shutdown().await;
717
+
718
+ // Verify only a single eager activity was scheduled (the one on our worker's task queue)
719
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 1);
720
+ }
721
+
722
+ #[tokio::test]
723
+ async fn activity_tasks_from_completion_reserve_slots() {
724
+ let wf_id = "fake_wf_id";
725
+ let mut t = TestHistoryBuilder::default();
726
+ t.add_by_type(EventType::WorkflowExecutionStarted);
727
+ t.add_full_wf_task();
728
+ let schedid = t.add_activity_task_scheduled("1");
729
+ let startid = t.add_activity_task_started(schedid);
730
+ t.add_activity_task_completed(schedid, startid, b"hi".into());
731
+ t.add_full_wf_task();
732
+ let schedid = t.add_activity_task_scheduled("2");
733
+ let startid = t.add_activity_task_started(schedid);
734
+ t.add_activity_task_completed(schedid, startid, b"hi".into());
735
+ t.add_full_wf_task();
736
+ t.add_workflow_execution_completed();
737
+
738
+ let mut mock = mock_workflow_client();
739
+ // Set up two tasks to be returned via normal activity polling
740
+ let act_tasks = VecDeque::from(vec![
741
+ PollActivityTaskQueueResponse {
742
+ task_token: vec![1],
743
+ activity_id: "act1".to_string(),
744
+ ..Default::default()
745
+ }
746
+ .into(),
747
+ PollActivityTaskQueueResponse {
748
+ task_token: vec![2],
749
+ activity_id: "act2".to_string(),
750
+ ..Default::default()
751
+ }
752
+ .into(),
753
+ ]);
754
+ mock.expect_complete_activity_task()
755
+ .times(2)
756
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
757
+ let barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
758
+ let mut mh = MockPollCfg::from_resp_batches(
759
+ wf_id,
760
+ t,
761
+ [
762
+ ResponseType::ToTaskNum(1),
763
+ // We don't want the second task to be delivered until *after* the activity tasks
764
+ // have been completed, so that the second activity schedule will have slots available
765
+ ResponseType::UntilResolved(
766
+ async {
767
+ barr.wait().await;
768
+ barr.wait().await;
769
+ }
770
+ .boxed(),
771
+ 2,
772
+ ),
773
+ ResponseType::AllHistory,
774
+ ],
775
+ mock,
776
+ );
777
+ mh.completion_asserts = Some(Box::new(|wftc| {
778
+ // Make sure when we see the completion with the schedule act command that it does
779
+ // not have the eager execution flag set the first time, and does the second.
780
+ if let Some(Attributes::ScheduleActivityTaskCommandAttributes(attrs)) =
781
+ wftc.commands.get(0).and_then(|cmd| cmd.attributes.as_ref())
782
+ {
783
+ if attrs.activity_id == "1" {
784
+ assert!(!attrs.request_eager_execution);
785
+ } else {
786
+ assert!(attrs.request_eager_execution);
787
+ }
788
+ }
789
+ }));
790
+ let mut mock = build_mock_pollers(mh);
791
+ mock.worker_cfg(|cfg| {
792
+ cfg.max_cached_workflows = 2;
793
+ cfg.max_outstanding_activities = 2;
794
+ });
795
+ mock.set_act_poller(mock_poller_from_resps(act_tasks));
796
+ let core = Arc::new(mock_worker(mock));
797
+ let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
798
+
799
+ // First poll for activities twice, occupying both slots
800
+ let at1 = core.poll_activity_task().await.unwrap();
801
+ let at2 = core.poll_activity_task().await.unwrap();
802
+
803
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
804
+ ctx.activity(ActivityOptions {
805
+ activity_type: "act1".to_string(),
806
+ ..Default::default()
807
+ })
808
+ .await;
809
+ ctx.activity(ActivityOptions {
810
+ activity_type: "act2".to_string(),
811
+ ..Default::default()
812
+ })
813
+ .await;
814
+ Ok(().into())
815
+ });
816
+
817
+ worker
818
+ .submit_wf(
819
+ wf_id.to_owned(),
820
+ DEFAULT_WORKFLOW_TYPE,
821
+ vec![],
822
+ WorkflowOptions::default(),
823
+ )
824
+ .await
825
+ .unwrap();
826
+ let act_completer = async {
827
+ barr.wait().await;
828
+ core.complete_activity_task(ActivityTaskCompletion {
829
+ task_token: at1.task_token,
830
+ result: Some(ActivityExecutionResult::ok("hi".into())),
831
+ })
832
+ .await
833
+ .unwrap();
834
+ core.complete_activity_task(ActivityTaskCompletion {
835
+ task_token: at2.task_token,
836
+ result: Some(ActivityExecutionResult::ok("hi".into())),
837
+ })
838
+ .await
839
+ .unwrap();
840
+ barr.wait().await;
841
+ };
842
+ // This wf poll should *not* set the flag that it wants tasks back since both slots are
843
+ // occupied
844
+ let run_fut = async { worker.run_until_done().await.unwrap() };
845
+ tokio::join!(run_fut, act_completer);
846
+ }
847
+
848
+ #[tokio::test]
849
+ async fn retryable_net_error_exhaustion_is_nonfatal() {
850
+ let mut mock_client = mock_workflow_client();
851
+ mock_client
852
+ .expect_complete_activity_task()
853
+ .times(1)
854
+ .returning(|_, _| Err(tonic::Status::internal("retryable error")));
855
+
856
+ let core = mock_worker(MocksHolder::from_client_with_activities(
857
+ mock_client,
858
+ [PollActivityTaskQueueResponse {
859
+ task_token: vec![1],
860
+ activity_id: "act1".to_string(),
861
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
862
+ ..Default::default()
863
+ }
864
+ .into()],
865
+ ));
866
+
867
+ let act = core.poll_activity_task().await.unwrap();
868
+ core.complete_activity_task(ActivityTaskCompletion {
869
+ task_token: act.task_token,
870
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
871
+ })
872
+ .await
873
+ .unwrap();
874
+ core.shutdown().await;
875
+ }
876
+
877
+ #[tokio::test]
878
+ async fn cant_complete_activity_with_unset_result_payload() {
879
+ let mut mock_client = mock_workflow_client();
880
+ mock_client
881
+ .expect_poll_activity_task()
882
+ .returning(move |_, _| {
883
+ Ok(PollActivityTaskQueueResponse {
884
+ task_token: vec![1],
885
+ ..Default::default()
886
+ })
887
+ });
888
+
889
+ let cfg = WorkerConfigBuilder::default()
890
+ .namespace("enchi")
891
+ .task_queue("cat")
892
+ .worker_build_id("enchi_loves_salmon")
893
+ .build()
894
+ .unwrap();
895
+ let worker = Worker::new_test(cfg, mock_client);
896
+ let t = worker.poll_activity_task().await.unwrap();
897
+ let res = worker
898
+ .complete_activity_task(ActivityTaskCompletion {
899
+ task_token: t.task_token,
900
+ result: Some(ActivityExecutionResult {
901
+ status: Some(activity_execution_result::Status::Completed(Success {
902
+ result: None,
903
+ })),
904
+ }),
905
+ })
906
+ .await;
907
+ assert_matches!(
908
+ res,
909
+ Err(CompleteActivityError::MalformedActivityCompletion { .. })
910
+ )
911
+ }