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,580 @@
1
+ use crate::{
2
+ worker::{activities::PendingActivityCancel, client::WorkerClient},
3
+ TaskToken,
4
+ };
5
+ use futures::StreamExt;
6
+ use std::{
7
+ collections::{hash_map::Entry, HashMap},
8
+ sync::Arc,
9
+ time::{self, Duration, Instant},
10
+ };
11
+ use temporal_sdk_core_protos::{
12
+ coresdk::{activity_task::ActivityCancelReason, ActivityHeartbeat, IntoPayloadsExt},
13
+ temporal::api::{
14
+ common::v1::Payload, workflowservice::v1::RecordActivityTaskHeartbeatResponse,
15
+ },
16
+ };
17
+ use tokio::{
18
+ sync::{
19
+ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
20
+ Mutex, Notify,
21
+ },
22
+ task::JoinHandle,
23
+ };
24
+ use tokio_util::sync::CancellationToken;
25
+
26
+ /// Used to supply new heartbeat events to the activity heartbeat manager, or to send a shutdown
27
+ /// request.
28
+ pub(crate) struct ActivityHeartbeatManager {
29
+ /// Cancellations that have been received when heartbeating are queued here and can be consumed
30
+ /// by [fetch_cancellations]
31
+ incoming_cancels: Mutex<UnboundedReceiver<PendingActivityCancel>>,
32
+ shutdown_token: CancellationToken,
33
+ /// Used during `shutdown` to await until all inflight requests are sent.
34
+ join_handle: Mutex<Option<JoinHandle<()>>>,
35
+ heartbeat_tx: UnboundedSender<HeartbeatAction>,
36
+ }
37
+
38
+ #[derive(Debug)]
39
+ enum HeartbeatAction {
40
+ SendHeartbeat(ValidActivityHeartbeat),
41
+ Evict {
42
+ token: TaskToken,
43
+ on_complete: Arc<Notify>,
44
+ },
45
+ CompleteReport(TaskToken),
46
+ CompleteThrottle(TaskToken),
47
+ }
48
+
49
+ #[derive(Debug)]
50
+ pub struct ValidActivityHeartbeat {
51
+ pub task_token: TaskToken,
52
+ pub details: Vec<Payload>,
53
+ pub throttle_interval: time::Duration,
54
+ }
55
+
56
+ #[derive(Debug)]
57
+ enum HeartbeatExecutorAction {
58
+ /// Heartbeats are throttled for this task token, sleep until duration or wait to be cancelled
59
+ Sleep(TaskToken, Duration, CancellationToken),
60
+ /// Report heartbeat to the server
61
+ Report {
62
+ task_token: TaskToken,
63
+ details: Vec<Payload>,
64
+ },
65
+ }
66
+
67
+ /// Errors thrown when heartbeating
68
+ #[derive(thiserror::Error, Debug)]
69
+ pub enum ActivityHeartbeatError {
70
+ /// Heartbeat referenced an activity that we don't think exists. It may have completed already.
71
+ #[error("Heartbeat has been sent for activity that either completed or never started on this worker.")]
72
+ UnknownActivity,
73
+ /// There was a set heartbeat timeout, but it was not parseable. A valid timeout is requried
74
+ /// to heartbeat.
75
+ #[error("Unable to parse activity heartbeat timeout.")]
76
+ InvalidHeartbeatTimeout,
77
+ /// Core is shutting down and thus new heartbeats are not accepted
78
+ #[error("New heartbeat requests are not accepted while shutting down")]
79
+ ShuttingDown,
80
+ }
81
+
82
+ /// Manages activity heartbeating for a worker. Allows sending new heartbeats or requesting and
83
+ /// awaiting for the shutdown. When shutdown is requested, signal gets sent to all processors, which
84
+ /// allows them to complete gracefully.
85
+ impl ActivityHeartbeatManager {
86
+ /// Records a new heartbeat, the first call will result in an immediate call to the server,
87
+ /// while rapid successive calls would accumulate for up to `delay` and then latest heartbeat
88
+ /// details will be sent to the server.
89
+ ///
90
+ /// It is important that this function is never called with a task token equal to one given
91
+ /// to [Self::evict] after it has been called, doing so will cause a memory leak, as there is
92
+ /// no longer an efficient way to forget about that task token.
93
+ pub(super) fn record(
94
+ &self,
95
+ hb: ActivityHeartbeat,
96
+ throttle_interval: Duration,
97
+ ) -> Result<(), ActivityHeartbeatError> {
98
+ if self.shutdown_token.is_cancelled() {
99
+ return Err(ActivityHeartbeatError::ShuttingDown);
100
+ }
101
+ self.heartbeat_tx
102
+ .send(HeartbeatAction::SendHeartbeat(ValidActivityHeartbeat {
103
+ task_token: TaskToken(hb.task_token),
104
+ details: hb.details,
105
+ throttle_interval,
106
+ }))
107
+ .expect("Receive half of the heartbeats event channel must not be dropped");
108
+
109
+ Ok(())
110
+ }
111
+
112
+ /// Tell the heartbeat manager we are done forever with a certain task, so it may be forgotten.
113
+ /// This will also force-flush the most recently provided details.
114
+ /// Record *should* not be called with the same TaskToken after calling this.
115
+ pub(super) async fn evict(&self, task_token: TaskToken) {
116
+ let completed = Arc::new(Notify::new());
117
+ let _ = self.heartbeat_tx.send(HeartbeatAction::Evict {
118
+ token: task_token,
119
+ on_complete: completed.clone(),
120
+ });
121
+ completed.notified().await;
122
+ }
123
+
124
+ /// Returns a future that resolves any time there is a new activity cancel that must be
125
+ /// dispatched to lang
126
+ pub(super) async fn next_pending_cancel(&self) -> Option<PendingActivityCancel> {
127
+ self.incoming_cancels.lock().await.recv().await
128
+ }
129
+
130
+ // TODO: Can own self now!
131
+ /// Initiates shutdown procedure by stopping lifecycle loop and awaiting for all in-flight
132
+ /// heartbeat requests to be flushed to the server.
133
+ pub(super) async fn shutdown(&self) {
134
+ self.shutdown_token.cancel();
135
+ let mut handle = self.join_handle.lock().await;
136
+ if let Some(h) = handle.take() {
137
+ let handle_r = h.await;
138
+ if let Err(e) = handle_r {
139
+ if !e.is_cancelled() {
140
+ error!(
141
+ "Unexpected error joining heartbeating tasks during shutdown: {:?}",
142
+ e
143
+ )
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ #[derive(Debug)]
151
+ struct ActivityHeartbeatState {
152
+ /// If None and throttle interval is over, untrack this task token
153
+ last_recorded_details: Option<Vec<Payload>>,
154
+ /// True if we've queued up a request to record against server, but it hasn't yet completed
155
+ is_record_in_flight: bool,
156
+ last_send_requested: Instant,
157
+ throttle_interval: Duration,
158
+ throttled_cancellation_token: Option<CancellationToken>,
159
+ }
160
+
161
+ impl ActivityHeartbeatState {
162
+ /// Get duration to sleep by subtracting `throttle_interval` by elapsed time since
163
+ /// `last_send_requested`
164
+ fn get_throttle_sleep_duration(&self) -> Duration {
165
+ let time_since_last_sent = self.last_send_requested.elapsed();
166
+
167
+ if time_since_last_sent > Duration::ZERO && self.throttle_interval > time_since_last_sent {
168
+ self.throttle_interval - time_since_last_sent
169
+ } else {
170
+ Duration::ZERO
171
+ }
172
+ }
173
+ }
174
+
175
+ #[derive(Debug)]
176
+ struct HeartbeatStreamState {
177
+ tt_to_state: HashMap<TaskToken, ActivityHeartbeatState>,
178
+ tt_needs_flush: HashMap<TaskToken, Arc<Notify>>,
179
+ incoming_hbs: UnboundedReceiver<HeartbeatAction>,
180
+ /// Token that can be used to cancel the entire stream.
181
+ /// Requests to the server are not cancelled with this token.
182
+ cancellation_token: CancellationToken,
183
+ }
184
+
185
+ impl HeartbeatStreamState {
186
+ fn new() -> (Self, UnboundedSender<HeartbeatAction>, CancellationToken) {
187
+ let (heartbeat_tx, incoming_hbs) = unbounded_channel();
188
+ let cancellation_token = CancellationToken::new();
189
+ (
190
+ Self {
191
+ cancellation_token: cancellation_token.clone(),
192
+ tt_to_state: Default::default(),
193
+ tt_needs_flush: Default::default(),
194
+ incoming_hbs,
195
+ },
196
+ heartbeat_tx,
197
+ cancellation_token,
198
+ )
199
+ }
200
+
201
+ /// Record a heartbeat received from lang
202
+ fn record(&mut self, hb: ValidActivityHeartbeat) -> Option<HeartbeatExecutorAction> {
203
+ match self.tt_to_state.entry(hb.task_token.clone()) {
204
+ Entry::Vacant(e) => {
205
+ let state = ActivityHeartbeatState {
206
+ throttle_interval: hb.throttle_interval,
207
+ last_send_requested: Instant::now(),
208
+ // Don't record here because we already flush out these details.
209
+ // None is used to mark that after throttling we can stop tracking this task
210
+ // token.
211
+ last_recorded_details: None,
212
+ is_record_in_flight: true,
213
+ throttled_cancellation_token: None,
214
+ };
215
+ e.insert(state);
216
+ Some(HeartbeatExecutorAction::Report {
217
+ task_token: hb.task_token,
218
+ details: hb.details,
219
+ })
220
+ }
221
+ Entry::Occupied(mut o) => {
222
+ let state = o.get_mut();
223
+ state.last_recorded_details = Some(hb.details);
224
+ None
225
+ }
226
+ }
227
+ }
228
+
229
+ /// Heartbeat report to server completed
230
+ fn handle_report_completed(&mut self, tt: TaskToken) -> Option<HeartbeatExecutorAction> {
231
+ if let Some(not) = self.tt_needs_flush.remove(&tt) {
232
+ not.notify_one();
233
+ }
234
+ if let Some(st) = self.tt_to_state.get_mut(&tt) {
235
+ st.is_record_in_flight = false;
236
+ let cancellation_token = self.cancellation_token.child_token();
237
+ st.throttled_cancellation_token = Some(cancellation_token.clone());
238
+ // Always sleep for simplicity even if the duration is 0
239
+ Some(HeartbeatExecutorAction::Sleep(
240
+ tt.clone(),
241
+ st.get_throttle_sleep_duration(),
242
+ cancellation_token,
243
+ ))
244
+ } else {
245
+ None
246
+ }
247
+ }
248
+
249
+ /// Throttling completed, report or stop tracking task token
250
+ fn handle_throttle_completed(&mut self, tt: TaskToken) -> Option<HeartbeatExecutorAction> {
251
+ match self.tt_to_state.entry(tt.clone()) {
252
+ Entry::Occupied(mut e) => {
253
+ let state = e.get_mut();
254
+ if let Some(details) = state.last_recorded_details.take() {
255
+ // Delete the recorded details before reporting
256
+ // Reset the cancellation token and schedule another report
257
+ state.throttled_cancellation_token = None;
258
+ state.last_send_requested = Instant::now();
259
+ state.is_record_in_flight = true;
260
+ Some(HeartbeatExecutorAction::Report {
261
+ task_token: tt,
262
+ details,
263
+ })
264
+ } else {
265
+ // Nothing to report, forget this task token
266
+ e.remove();
267
+ None
268
+ }
269
+ }
270
+ Entry::Vacant(_) => None,
271
+ }
272
+ }
273
+
274
+ /// Activity should not be tracked anymore, cancel throttle timer if running.
275
+ ///
276
+ /// Will return a report action if there are recorded details present, to ensure we flush the
277
+ /// latest details before we cease tracking this activity.
278
+ fn evict(
279
+ &mut self,
280
+ tt: TaskToken,
281
+ on_complete: Arc<Notify>,
282
+ ) -> Option<HeartbeatExecutorAction> {
283
+ if let Some(state) = self.tt_to_state.remove(&tt) {
284
+ if let Some(cancel_tok) = state.throttled_cancellation_token {
285
+ cancel_tok.cancel();
286
+ }
287
+ if let Some(last_deets) = state.last_recorded_details {
288
+ self.tt_needs_flush.insert(tt.clone(), on_complete);
289
+ return Some(HeartbeatExecutorAction::Report {
290
+ task_token: tt,
291
+ details: last_deets,
292
+ });
293
+ } else if state.is_record_in_flight {
294
+ self.tt_needs_flush.insert(tt, on_complete);
295
+ return None;
296
+ }
297
+ }
298
+ // Since there's nothing to flush immediately report back that eviction is finished
299
+ on_complete.notify_one();
300
+ None
301
+ }
302
+ }
303
+
304
+ impl ActivityHeartbeatManager {
305
+ /// Creates a new instance of an activity heartbeat manager and returns a handle to the user,
306
+ /// which allows to send new heartbeats and initiate the shutdown.
307
+ pub fn new(client: Arc<dyn WorkerClient>) -> Self {
308
+ let (heartbeat_stream_state, heartbeat_tx_source, shutdown_token) =
309
+ HeartbeatStreamState::new();
310
+ let (cancels_tx, cancels_rx) = unbounded_channel();
311
+ let heartbeat_tx = heartbeat_tx_source.clone();
312
+
313
+ let join_handle = tokio::spawn(
314
+ // The stream of incoming heartbeats uses unfold to carry state across each item in the
315
+ // stream. The closure checks if, for any given activity, we should heartbeat or not
316
+ // depending on its delay and when we last issued a heartbeat for it.
317
+ futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
318
+ async move {
319
+ let hb = tokio::select! {
320
+ biased;
321
+
322
+ _ = hb_states.cancellation_token.cancelled() => {
323
+ return None
324
+ }
325
+ hb = hb_states.incoming_hbs.recv() => match hb {
326
+ None => return None,
327
+ Some(hb) => hb,
328
+ }
329
+ };
330
+
331
+ Some((
332
+ match hb {
333
+ HeartbeatAction::SendHeartbeat(hb) => hb_states.record(hb),
334
+ HeartbeatAction::CompleteReport(tt) => hb_states.handle_report_completed(tt),
335
+ HeartbeatAction::CompleteThrottle(tt) => hb_states.handle_throttle_completed(tt),
336
+ HeartbeatAction::Evict{ token, on_complete } => hb_states.evict(token, on_complete),
337
+ },
338
+ hb_states,
339
+ ))
340
+ }
341
+ })
342
+ // Filters out `None`s
343
+ .filter_map(|opt| async { opt })
344
+ .for_each_concurrent(None, move |action| {
345
+ let heartbeat_tx = heartbeat_tx_source.clone();
346
+ let sg = client.clone();
347
+ let cancels_tx = cancels_tx.clone();
348
+ async move {
349
+ match action {
350
+ HeartbeatExecutorAction::Sleep(tt, duration, cancellation_token) => {
351
+ tokio::select! {
352
+ _ = cancellation_token.cancelled() => (),
353
+ _ = tokio::time::sleep(duration) => {
354
+ let _ = heartbeat_tx.send(HeartbeatAction::CompleteThrottle(tt));
355
+ },
356
+ };
357
+ }
358
+ HeartbeatExecutorAction::Report { task_token: tt, details } => {
359
+ match sg
360
+ .record_activity_heartbeat(tt.clone(), details.into_payloads())
361
+ .await
362
+ {
363
+ Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
364
+ if cancel_requested {
365
+ cancels_tx
366
+ .send(PendingActivityCancel::new(
367
+ tt.clone(),
368
+ ActivityCancelReason::Cancelled,
369
+ ))
370
+ .expect(
371
+ "Receive half of heartbeat cancels not blocked",
372
+ );
373
+ }
374
+ }
375
+ // Send cancels for any activity that learns its workflow already
376
+ // finished (which is one thing not found implies - other reasons
377
+ // would seem equally valid).
378
+ Err(s) if s.code() == tonic::Code::NotFound => {
379
+ debug!(task_token = %tt,
380
+ "Activity not found when recording heartbeat");
381
+ cancels_tx
382
+ .send(PendingActivityCancel::new(
383
+ tt.clone(),
384
+ ActivityCancelReason::NotFound,
385
+ ))
386
+ .expect("Receive half of heartbeat cancels not blocked");
387
+ }
388
+ Err(e) => {
389
+ warn!("Error when recording heartbeat: {:?}", e);
390
+ }
391
+ };
392
+ let _ = heartbeat_tx.send(HeartbeatAction::CompleteReport(tt));
393
+ }
394
+ }
395
+ }
396
+ }),
397
+ );
398
+
399
+ Self {
400
+ incoming_cancels: Mutex::new(cancels_rx),
401
+ join_handle: Mutex::new(Some(join_handle)),
402
+ shutdown_token,
403
+ heartbeat_tx,
404
+ }
405
+ }
406
+ }
407
+
408
+ #[cfg(test)]
409
+ mod test {
410
+ use super::*;
411
+
412
+ use crate::worker::client::mocks::mock_workflow_client;
413
+ use std::time::Duration;
414
+ use temporal_sdk_core_protos::temporal::api::{
415
+ common::v1::Payload, workflowservice::v1::RecordActivityTaskHeartbeatResponse,
416
+ };
417
+ use tokio::time::sleep;
418
+
419
+ /// Ensure that heartbeats that are sent with a small `throttle_interval` are aggregated and sent roughly once
420
+ /// every 1/2 of the heartbeat timeout.
421
+ #[tokio::test]
422
+ async fn process_heartbeats_and_shutdown() {
423
+ let mut mock_client = mock_workflow_client();
424
+ mock_client
425
+ .expect_record_activity_heartbeat()
426
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
427
+ .times(2);
428
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
429
+ let fake_task_token = vec![1, 2, 3];
430
+ // Send 2 heartbeat requests for 20ms apart.
431
+ // The first heartbeat should be sent right away, and
432
+ // the second should be throttled until 50ms have passed.
433
+ for i in 0_u8..2 {
434
+ record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(50));
435
+ sleep(Duration::from_millis(20)).await;
436
+ }
437
+ // sleep again to let heartbeats be flushed
438
+ sleep(Duration::from_millis(20)).await;
439
+ hm.shutdown().await;
440
+ }
441
+
442
+ #[tokio::test]
443
+ async fn send_heartbeats_less_frequently_than_throttle_interval() {
444
+ let mut mock_client = mock_workflow_client();
445
+ mock_client
446
+ .expect_record_activity_heartbeat()
447
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
448
+ .times(3);
449
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
450
+ let fake_task_token = vec![1, 2, 3];
451
+ // Heartbeats always get sent if recorded less frequently than the throttle interval
452
+ for i in 0_u8..3 {
453
+ record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(10));
454
+ sleep(Duration::from_millis(20)).await;
455
+ }
456
+ // sleep again to let heartbeats be flushed
457
+ hm.shutdown().await;
458
+ }
459
+
460
+ /// Ensure that heartbeat can be called from a tight loop without any throttle_interval, resulting in two
461
+ /// interactions with the server - one immediately and one after 500ms after the throttle_interval.
462
+ #[tokio::test]
463
+ async fn process_tight_loop_and_shutdown() {
464
+ let mut mock_client = mock_workflow_client();
465
+ mock_client
466
+ .expect_record_activity_heartbeat()
467
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
468
+ .times(1);
469
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
470
+ let fake_task_token = vec![1, 2, 3];
471
+ // Send a whole bunch of heartbeats very fast. We should still only send one total.
472
+ for i in 0_u8..50 {
473
+ record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(2000));
474
+ // Let it propagate
475
+ sleep(Duration::from_millis(10)).await;
476
+ }
477
+ hm.shutdown().await;
478
+ }
479
+
480
+ /// This test reports one heartbeat and waits for the throttle_interval to elapse before sending another
481
+ #[tokio::test]
482
+ async fn report_heartbeat_after_timeout() {
483
+ let mut mock_client = mock_workflow_client();
484
+ mock_client
485
+ .expect_record_activity_heartbeat()
486
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
487
+ .times(2);
488
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
489
+ let fake_task_token = vec![1, 2, 3];
490
+ record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
491
+ sleep(Duration::from_millis(500)).await;
492
+ record_heartbeat(&hm, fake_task_token, 1, Duration::from_millis(100));
493
+ // Let it propagate
494
+ sleep(Duration::from_millis(50)).await;
495
+ hm.shutdown().await;
496
+ }
497
+
498
+ #[tokio::test]
499
+ async fn evict_works() {
500
+ let mut mock_client = mock_workflow_client();
501
+ mock_client
502
+ .expect_record_activity_heartbeat()
503
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
504
+ .times(2);
505
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
506
+ let fake_task_token = vec![1, 2, 3];
507
+ record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
508
+ // Let it propagate
509
+ sleep(Duration::from_millis(10)).await;
510
+ hm.evict(fake_task_token.clone().into()).await;
511
+ record_heartbeat(&hm, fake_task_token, 0, Duration::from_millis(100));
512
+ // Let it propagate
513
+ sleep(Duration::from_millis(10)).await;
514
+ // We know it works b/c otherwise we would have only called record 1 time w/o sleep
515
+ hm.shutdown().await;
516
+ }
517
+
518
+ #[tokio::test]
519
+ async fn evict_immediate_after_record() {
520
+ let mut mock_client = mock_workflow_client();
521
+ mock_client
522
+ .expect_record_activity_heartbeat()
523
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
524
+ .times(1);
525
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
526
+ let fake_task_token = vec![1, 2, 3];
527
+ record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
528
+ hm.evict(fake_task_token.clone().into()).await;
529
+ hm.shutdown().await;
530
+ }
531
+
532
+ /// Recording new heartbeats after shutdown is not allowed, and will result in error.
533
+ #[tokio::test]
534
+ async fn record_after_shutdown() {
535
+ let mut mock_client = mock_workflow_client();
536
+ mock_client
537
+ .expect_record_activity_heartbeat()
538
+ .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
539
+ .times(0);
540
+ let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
541
+ hm.shutdown().await;
542
+ match hm.record(
543
+ ActivityHeartbeat {
544
+ task_token: vec![1, 2, 3],
545
+ details: vec![Payload {
546
+ // payload doesn't matter in this case, as it shouldn't get sent anyways.
547
+ ..Default::default()
548
+ }],
549
+ },
550
+ Duration::from_millis(1000),
551
+ ) {
552
+ Ok(_) => {
553
+ unreachable!("heartbeat should not be recorded after the shutdown");
554
+ }
555
+ Err(e) => {
556
+ matches!(e, ActivityHeartbeatError::ShuttingDown);
557
+ }
558
+ }
559
+ }
560
+
561
+ fn record_heartbeat(
562
+ hm: &ActivityHeartbeatManager,
563
+ task_token: Vec<u8>,
564
+ payload_data: u8,
565
+ throttle_interval: Duration,
566
+ ) {
567
+ hm.record(
568
+ ActivityHeartbeat {
569
+ task_token,
570
+ details: vec![Payload {
571
+ metadata: Default::default(),
572
+ data: vec![payload_data],
573
+ }],
574
+ },
575
+ // Mimic the same delay we would apply in activity task manager
576
+ throttle_interval,
577
+ )
578
+ .expect("hearbeat recording should not fail");
579
+ }
580
+ }