temporalio 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +20 -0
  4. data/README.md +130 -0
  5. data/bridge/Cargo.lock +2865 -0
  6. data/bridge/Cargo.toml +26 -0
  7. data/bridge/sdk-core/ARCHITECTURE.md +76 -0
  8. data/bridge/sdk-core/Cargo.lock +2606 -0
  9. data/bridge/sdk-core/Cargo.toml +2 -0
  10. data/bridge/sdk-core/LICENSE.txt +23 -0
  11. data/bridge/sdk-core/README.md +107 -0
  12. data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
  13. data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
  14. data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  15. data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
  16. data/bridge/sdk-core/bridge-ffi/Cargo.toml +24 -0
  17. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  18. data/bridge/sdk-core/bridge-ffi/build.rs +25 -0
  19. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +249 -0
  20. data/bridge/sdk-core/bridge-ffi/src/lib.rs +825 -0
  21. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +211 -0
  22. data/bridge/sdk-core/client/Cargo.toml +40 -0
  23. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  24. data/bridge/sdk-core/client/src/lib.rs +1294 -0
  25. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  26. data/bridge/sdk-core/client/src/raw.rs +931 -0
  27. data/bridge/sdk-core/client/src/retry.rs +674 -0
  28. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  29. data/bridge/sdk-core/core/Cargo.toml +116 -0
  30. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  31. data/bridge/sdk-core/core/benches/workflow_replay.rs +73 -0
  32. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  33. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +911 -0
  34. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  35. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  36. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +515 -0
  37. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  38. data/bridge/sdk-core/core/src/core_tests/queries.rs +736 -0
  39. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  40. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  41. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  42. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2070 -0
  43. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  44. data/bridge/sdk-core/core/src/lib.rs +175 -0
  45. data/bridge/sdk-core/core/src/log_export.rs +62 -0
  46. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  47. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  48. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  49. data/bridge/sdk-core/core/src/replay/mod.rs +71 -0
  50. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  51. data/bridge/sdk-core/core/src/telemetry/metrics.rs +383 -0
  52. data/bridge/sdk-core/core/src/telemetry/mod.rs +412 -0
  53. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +77 -0
  54. data/bridge/sdk-core/core/src/test_help/mod.rs +875 -0
  55. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  56. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1042 -0
  57. data/bridge/sdk-core/core/src/worker/activities.rs +464 -0
  58. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  59. data/bridge/sdk-core/core/src/worker/client.rs +347 -0
  60. data/bridge/sdk-core/core/src/worker/mod.rs +566 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +110 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +458 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +911 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +298 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +171 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +860 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +140 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +161 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +133 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1448 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +342 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +127 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +712 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +71 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +443 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +439 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +169 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +246 -0
  80. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  81. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1184 -0
  82. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +277 -0
  83. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  84. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +647 -0
  85. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1143 -0
  86. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  87. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  88. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +940 -0
  89. data/bridge/sdk-core/core-api/Cargo.toml +31 -0
  90. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  91. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  92. data/bridge/sdk-core/core-api/src/lib.rs +151 -0
  93. data/bridge/sdk-core/core-api/src/worker.rs +135 -0
  94. data/bridge/sdk-core/etc/deps.svg +187 -0
  95. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  96. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  97. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  98. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  99. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  100. data/bridge/sdk-core/fsm/README.md +3 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  115. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  116. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  117. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  118. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  119. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  120. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  121. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  122. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  123. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  124. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  125. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  126. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  127. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  128. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  129. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  130. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  131. data/bridge/sdk-core/protos/api_upstream/buf.yaml +12 -0
  132. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  134. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  135. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +259 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +55 -0
  141. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +168 -0
  142. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +97 -0
  143. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
  144. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
  145. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
  146. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  147. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
  148. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  149. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
  150. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
  151. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +751 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +161 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +99 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  160. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  161. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +145 -0
  164. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1124 -0
  165. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +401 -0
  166. data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  167. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +210 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  174. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +261 -0
  175. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +297 -0
  176. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  179. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  180. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  181. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  182. data/bridge/sdk-core/rustfmt.toml +1 -0
  183. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  184. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  185. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  186. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  187. data/bridge/sdk-core/sdk/src/conversions.rs +8 -0
  188. data/bridge/sdk-core/sdk/src/interceptors.rs +17 -0
  189. data/bridge/sdk-core/sdk/src/lib.rs +792 -0
  190. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  191. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  192. data/bridge/sdk-core/sdk/src/workflow_context.rs +683 -0
  193. data/bridge/sdk-core/sdk/src/workflow_future.rs +503 -0
  194. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  195. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  196. data/bridge/sdk-core/sdk-core-protos/build.rs +108 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +497 -0
  199. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  200. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1910 -0
  201. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  202. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  203. data/bridge/sdk-core/test-utils/Cargo.toml +35 -0
  204. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  205. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  206. data/bridge/sdk-core/test-utils/src/lib.rs +598 -0
  207. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  208. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  209. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +218 -0
  210. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +146 -0
  211. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  212. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  219. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  220. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +634 -0
  221. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
  222. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +137 -0
  223. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
  224. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
  225. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
  226. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
  227. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
  228. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +587 -0
  229. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  230. data/bridge/sdk-core/tests/main.rs +111 -0
  231. data/bridge/sdk-core/tests/runner.rs +93 -0
  232. data/bridge/src/connection.rs +167 -0
  233. data/bridge/src/lib.rs +180 -0
  234. data/bridge/src/runtime.rs +47 -0
  235. data/bridge/src/worker.rs +73 -0
  236. data/ext/Rakefile +9 -0
  237. data/lib/bridge.so +0 -0
  238. data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
  239. data/lib/gen/temporal/api/batch/v1/message_pb.rb +48 -0
  240. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +67 -0
  241. data/lib/gen/temporal/api/command/v1/message_pb.rb +166 -0
  242. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  243. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +32 -0
  244. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +26 -0
  245. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +37 -0
  246. data/lib/gen/temporal/api/enums/v1/common_pb.rb +41 -0
  247. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +67 -0
  248. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +71 -0
  249. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  250. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  251. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  252. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  253. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  254. data/lib/gen/temporal/api/enums/v1/update_pb.rb +28 -0
  255. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  256. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  257. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  258. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  259. data/lib/gen/temporal/api/history/v1/message_pb.rb +489 -0
  260. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
  261. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +125 -0
  262. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
  263. data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
  264. data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
  265. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +128 -0
  266. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  267. data/lib/gen/temporal/api/update/v1/message_pb.rb +26 -0
  268. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  269. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +110 -0
  270. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +771 -0
  271. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  272. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  273. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  274. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  275. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  276. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  277. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  278. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  279. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +164 -0
  280. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +192 -0
  281. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  282. data/lib/temporal/bridge.rb +14 -0
  283. data/lib/temporal/client/implementation.rb +339 -0
  284. data/lib/temporal/client/workflow_handle.rb +243 -0
  285. data/lib/temporal/client.rb +144 -0
  286. data/lib/temporal/connection.rb +736 -0
  287. data/lib/temporal/data_converter.rb +150 -0
  288. data/lib/temporal/error/failure.rb +194 -0
  289. data/lib/temporal/error/workflow_failure.rb +17 -0
  290. data/lib/temporal/errors.rb +22 -0
  291. data/lib/temporal/failure_converter/base.rb +26 -0
  292. data/lib/temporal/failure_converter/basic.rb +313 -0
  293. data/lib/temporal/failure_converter.rb +8 -0
  294. data/lib/temporal/interceptor/chain.rb +27 -0
  295. data/lib/temporal/interceptor/client.rb +102 -0
  296. data/lib/temporal/payload_codec/base.rb +32 -0
  297. data/lib/temporal/payload_converter/base.rb +24 -0
  298. data/lib/temporal/payload_converter/bytes.rb +26 -0
  299. data/lib/temporal/payload_converter/composite.rb +47 -0
  300. data/lib/temporal/payload_converter/encoding_base.rb +35 -0
  301. data/lib/temporal/payload_converter/json.rb +25 -0
  302. data/lib/temporal/payload_converter/nil.rb +25 -0
  303. data/lib/temporal/payload_converter.rb +14 -0
  304. data/lib/temporal/retry_policy.rb +82 -0
  305. data/lib/temporal/retry_state.rb +35 -0
  306. data/lib/temporal/runtime.rb +22 -0
  307. data/lib/temporal/timeout_type.rb +29 -0
  308. data/lib/temporal/version.rb +3 -0
  309. data/lib/temporal/workflow/execution_info.rb +54 -0
  310. data/lib/temporal/workflow/execution_status.rb +36 -0
  311. data/lib/temporal/workflow/id_reuse_policy.rb +36 -0
  312. data/lib/temporal/workflow/query_reject_condition.rb +33 -0
  313. data/lib/temporal.rb +8 -0
  314. data/lib/temporalio.rb +3 -0
  315. data/lib/thermite_patch.rb +23 -0
  316. data/temporalio.gemspec +41 -0
  317. metadata +583 -0
@@ -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
+ }