temporalio 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -7
  3. data/bridge/Cargo.lock +208 -76
  4. data/bridge/Cargo.toml +5 -2
  5. data/bridge/sdk-core/Cargo.toml +1 -1
  6. data/bridge/sdk-core/README.md +20 -10
  7. data/bridge/sdk-core/client/Cargo.toml +1 -1
  8. data/bridge/sdk-core/client/src/lib.rs +227 -59
  9. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  10. data/bridge/sdk-core/client/src/raw.rs +13 -12
  11. data/bridge/sdk-core/client/src/retry.rs +132 -43
  12. data/bridge/sdk-core/core/Cargo.toml +28 -15
  13. data/bridge/sdk-core/core/benches/workflow_replay.rs +13 -10
  14. data/bridge/sdk-core/core/src/abstractions.rs +225 -36
  15. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +217 -79
  16. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  17. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +565 -34
  18. data/bridge/sdk-core/core/src/core_tests/queries.rs +247 -90
  19. data/bridge/sdk-core/core/src/core_tests/workers.rs +3 -5
  20. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  21. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +430 -67
  22. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  23. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  24. data/bridge/sdk-core/core/src/lib.rs +148 -34
  25. data/bridge/sdk-core/core/src/protosext/mod.rs +1 -1
  26. data/bridge/sdk-core/core/src/replay/mod.rs +185 -41
  27. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  28. data/bridge/sdk-core/core/src/telemetry/metrics.rs +219 -140
  29. data/bridge/sdk-core/core/src/telemetry/mod.rs +326 -315
  30. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +20 -14
  31. data/bridge/sdk-core/core/src/test_help/mod.rs +85 -21
  32. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  33. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  34. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +364 -128
  35. data/bridge/sdk-core/core/src/worker/activities.rs +263 -170
  36. data/bridge/sdk-core/core/src/worker/client/mocks.rs +23 -3
  37. data/bridge/sdk-core/core/src/worker/client.rs +48 -6
  38. data/bridge/sdk-core/core/src/worker/mod.rs +186 -75
  39. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  40. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +13 -24
  41. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +879 -226
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +101 -48
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +8 -12
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -9
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +90 -32
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +6 -9
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -10
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +6 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +160 -83
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +36 -54
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +179 -0
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +104 -157
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +8 -12
  54. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -13
  55. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +10 -4
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -11
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +395 -299
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +12 -20
  60. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +33 -18
  61. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1032 -374
  62. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +525 -392
  63. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  64. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +3 -6
  66. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +456 -681
  69. data/bridge/sdk-core/core-api/Cargo.toml +6 -4
  70. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  71. data/bridge/sdk-core/core-api/src/lib.rs +7 -45
  72. data/bridge/sdk-core/core-api/src/telemetry.rs +141 -0
  73. data/bridge/sdk-core/core-api/src/worker.rs +27 -1
  74. data/bridge/sdk-core/etc/deps.svg +115 -140
  75. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  76. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  77. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  78. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  79. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  80. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  81. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  82. data/bridge/sdk-core/protos/api_upstream/buf.yaml +0 -3
  83. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  84. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  85. data/bridge/sdk-core/protos/api_upstream/{temporal/api/enums/v1/cluster.proto → build/tools.go} +7 -18
  86. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  87. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  88. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +15 -26
  89. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  90. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  91. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +4 -9
  92. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  93. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +10 -8
  94. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +28 -2
  95. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  96. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  97. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  98. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  99. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  100. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  101. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  102. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  103. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  104. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  105. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +62 -26
  106. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  107. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +24 -61
  108. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -21
  109. data/bridge/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  110. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  111. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  112. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +110 -31
  113. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  114. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +4 -4
  115. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  116. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  117. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +3 -2
  118. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +111 -36
  119. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -5
  120. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  121. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  122. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  123. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  124. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  125. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  126. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +9 -0
  127. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +9 -1
  128. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  129. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  130. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  131. data/bridge/sdk-core/sdk/Cargo.toml +4 -3
  132. data/bridge/sdk-core/sdk/src/interceptors.rs +36 -3
  133. data/bridge/sdk-core/sdk/src/lib.rs +94 -25
  134. data/bridge/sdk-core/sdk/src/workflow_context.rs +13 -2
  135. data/bridge/sdk-core/sdk/src/workflow_future.rs +10 -13
  136. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  137. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -2
  138. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +164 -104
  139. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +27 -23
  140. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +252 -74
  141. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  142. data/bridge/sdk-core/test-utils/Cargo.toml +4 -1
  143. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  144. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  145. data/bridge/sdk-core/test-utils/src/lib.rs +161 -50
  146. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  147. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  148. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  149. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  150. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  151. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  152. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +239 -0
  153. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -60
  154. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  155. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  156. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  157. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  158. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  159. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  160. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  161. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  162. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +151 -116
  163. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +54 -0
  164. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  165. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +115 -24
  166. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  167. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  168. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  169. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  170. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  171. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +27 -18
  172. data/bridge/sdk-core/tests/main.rs +8 -16
  173. data/bridge/sdk-core/tests/runner.rs +75 -36
  174. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  175. data/bridge/src/connection.rs +117 -82
  176. data/bridge/src/lib.rs +356 -42
  177. data/bridge/src/runtime.rs +10 -3
  178. data/bridge/src/test_server.rs +153 -0
  179. data/bridge/src/worker.rs +133 -9
  180. data/lib/gen/temporal/api/batch/v1/message_pb.rb +8 -6
  181. data/lib/gen/temporal/api/command/v1/message_pb.rb +10 -16
  182. data/lib/gen/temporal/api/common/v1/message_pb.rb +5 -1
  183. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +2 -1
  184. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +3 -3
  185. data/lib/gen/temporal/api/enums/v1/common_pb.rb +2 -1
  186. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +5 -4
  187. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +9 -1
  188. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +1 -1
  189. data/lib/gen/temporal/api/enums/v1/query_pb.rb +1 -1
  190. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +1 -1
  191. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +1 -1
  192. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +1 -1
  193. data/lib/gen/temporal/api/enums/v1/update_pb.rb +7 -10
  194. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +1 -1
  195. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +1 -1
  196. data/lib/gen/temporal/api/failure/v1/message_pb.rb +1 -1
  197. data/lib/gen/temporal/api/filter/v1/message_pb.rb +1 -1
  198. data/lib/gen/temporal/api/history/v1/message_pb.rb +34 -25
  199. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +2 -1
  200. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +14 -51
  201. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +1 -1
  202. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  203. data/lib/gen/temporal/api/query/v1/message_pb.rb +1 -1
  204. data/lib/gen/temporal/api/replication/v1/message_pb.rb +1 -1
  205. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +22 -1
  206. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  207. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +2 -2
  208. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  209. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  210. data/lib/gen/temporal/api/update/v1/message_pb.rb +49 -3
  211. data/lib/gen/temporal/api/version/v1/message_pb.rb +1 -1
  212. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +2 -1
  213. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +47 -20
  214. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +1 -1
  215. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  216. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  217. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  218. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  219. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  220. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  221. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +28 -21
  222. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +32 -24
  223. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  224. data/lib/temporalio/activity/context.rb +102 -0
  225. data/lib/temporalio/activity/info.rb +67 -0
  226. data/lib/temporalio/activity.rb +85 -0
  227. data/lib/temporalio/bridge/connect_options.rb +15 -0
  228. data/lib/temporalio/bridge/error.rb +8 -0
  229. data/lib/temporalio/bridge/retry_config.rb +24 -0
  230. data/lib/temporalio/bridge/tls_options.rb +19 -0
  231. data/lib/temporalio/bridge.rb +14 -0
  232. data/lib/{temporal → temporalio}/client/implementation.rb +57 -56
  233. data/lib/{temporal → temporalio}/client/workflow_handle.rb +35 -35
  234. data/lib/{temporal → temporalio}/client.rb +19 -32
  235. data/lib/temporalio/connection/retry_config.rb +44 -0
  236. data/lib/temporalio/connection/service.rb +20 -0
  237. data/lib/temporalio/connection/test_service.rb +92 -0
  238. data/lib/temporalio/connection/tls_options.rb +51 -0
  239. data/lib/temporalio/connection/workflow_service.rb +731 -0
  240. data/lib/temporalio/connection.rb +86 -0
  241. data/lib/{temporal → temporalio}/data_converter.rb +76 -35
  242. data/lib/{temporal → temporalio}/error/failure.rb +6 -6
  243. data/lib/{temporal → temporalio}/error/workflow_failure.rb +4 -2
  244. data/lib/{temporal → temporalio}/errors.rb +19 -1
  245. data/lib/{temporal → temporalio}/failure_converter/base.rb +5 -5
  246. data/lib/{temporal → temporalio}/failure_converter/basic.rb +58 -52
  247. data/lib/temporalio/failure_converter.rb +7 -0
  248. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  249. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  250. data/lib/{temporal → temporalio}/interceptor/chain.rb +7 -6
  251. data/lib/{temporal → temporalio}/interceptor/client.rb +27 -2
  252. data/lib/temporalio/interceptor.rb +22 -0
  253. data/lib/{temporal → temporalio}/payload_codec/base.rb +5 -5
  254. data/lib/{temporal → temporalio}/payload_converter/base.rb +3 -3
  255. data/lib/{temporal → temporalio}/payload_converter/bytes.rb +4 -3
  256. data/lib/{temporal → temporalio}/payload_converter/composite.rb +7 -5
  257. data/lib/{temporal → temporalio}/payload_converter/encoding_base.rb +4 -4
  258. data/lib/{temporal → temporalio}/payload_converter/json.rb +4 -3
  259. data/lib/{temporal → temporalio}/payload_converter/nil.rb +4 -3
  260. data/lib/temporalio/payload_converter.rb +14 -0
  261. data/lib/{temporal → temporalio}/retry_policy.rb +17 -7
  262. data/lib/{temporal → temporalio}/retry_state.rb +1 -1
  263. data/lib/temporalio/runtime.rb +25 -0
  264. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  265. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  266. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  267. data/lib/temporalio/testing.rb +175 -0
  268. data/lib/{temporal → temporalio}/timeout_type.rb +2 -2
  269. data/lib/temporalio/version.rb +3 -0
  270. data/lib/temporalio/worker/activity_runner.rb +114 -0
  271. data/lib/temporalio/worker/activity_worker.rb +164 -0
  272. data/lib/temporalio/worker/reactor.rb +46 -0
  273. data/lib/temporalio/worker/runner.rb +63 -0
  274. data/lib/temporalio/worker/sync_worker.rb +124 -0
  275. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  276. data/lib/temporalio/worker.rb +204 -0
  277. data/lib/temporalio/workflow/async.rb +46 -0
  278. data/lib/{temporal → temporalio}/workflow/execution_info.rb +4 -4
  279. data/lib/{temporal → temporalio}/workflow/execution_status.rb +1 -1
  280. data/lib/temporalio/workflow/future.rb +138 -0
  281. data/lib/{temporal → temporalio}/workflow/id_reuse_policy.rb +6 -6
  282. data/lib/temporalio/workflow/info.rb +76 -0
  283. data/lib/{temporal → temporalio}/workflow/query_reject_condition.rb +5 -5
  284. data/lib/temporalio.rb +12 -3
  285. data/temporalio.gemspec +11 -6
  286. metadata +137 -64
  287. data/bridge/sdk-core/Cargo.lock +0 -2606
  288. data/bridge/sdk-core/bridge-ffi/Cargo.toml +0 -24
  289. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  290. data/bridge/sdk-core/bridge-ffi/build.rs +0 -25
  291. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -249
  292. data/bridge/sdk-core/bridge-ffi/src/lib.rs +0 -825
  293. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +0 -211
  294. data/bridge/sdk-core/core/src/log_export.rs +0 -62
  295. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  296. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  297. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  298. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  299. data/bridge/sdk-core/sdk/src/conversions.rs +0 -8
  300. data/lib/bridge.so +0 -0
  301. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +0 -67
  302. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +0 -26
  303. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
  304. data/lib/temporal/bridge.rb +0 -14
  305. data/lib/temporal/connection.rb +0 -736
  306. data/lib/temporal/failure_converter.rb +0 -8
  307. data/lib/temporal/payload_converter.rb +0 -14
  308. data/lib/temporal/runtime.rb +0 -22
  309. data/lib/temporal/version.rb +0 -3
  310. data/lib/temporal.rb +0 -8
@@ -1,43 +1,45 @@
1
- use crate::telemetry::{default_resource, metrics::SDKAggSelector};
1
+ use crate::telemetry::default_resource;
2
2
  use hyper::{
3
3
  header::CONTENT_TYPE,
4
+ server::conn::AddrIncoming,
4
5
  service::{make_service_fn, service_fn},
5
6
  Body, Method, Request, Response, Server,
6
7
  };
7
- use opentelemetry::{
8
- metrics::MetricsError,
9
- sdk::{
10
- export::metrics::aggregation::TemporalitySelector,
11
- metrics::{controllers, processors},
12
- },
8
+ use opentelemetry::sdk::{
9
+ export::metrics::{aggregation::TemporalitySelector, AggregatorSelector},
10
+ metrics::{controllers, processors},
13
11
  };
14
12
  use opentelemetry_prometheus::{ExporterBuilder, PrometheusExporter};
15
13
  use prometheus::{Encoder, TextEncoder};
16
- use std::{convert::Infallible, net::SocketAddr, sync::Arc};
14
+ use std::{convert::Infallible, net::SocketAddr, sync::Arc, time::Duration};
17
15
 
18
16
  /// Exposes prometheus metrics for scraping
19
17
  pub(super) struct PromServer {
20
- addr: SocketAddr,
18
+ bound_addr: AddrIncoming,
21
19
  pub exporter: Arc<PrometheusExporter>,
22
20
  }
23
21
 
24
22
  impl PromServer {
25
23
  pub fn new(
26
24
  addr: SocketAddr,
25
+ aggregation: impl AggregatorSelector + Send + Sync + 'static,
27
26
  temporality: impl TemporalitySelector + Send + Sync + 'static,
28
- ) -> Result<Self, MetricsError> {
27
+ ) -> Result<Self, anyhow::Error> {
29
28
  let controller =
30
- controllers::basic(processors::factory(SDKAggSelector, temporality).with_memory(true))
29
+ controllers::basic(processors::factory(aggregation, temporality).with_memory(true))
30
+ // Because Prom is pull-based, make this always refresh
31
+ .with_collect_period(Duration::from_secs(0))
31
32
  .with_resource(default_resource())
32
33
  .build();
33
34
  let exporter = ExporterBuilder::new(controller).try_init()?;
35
+ let bound_addr = AddrIncoming::bind(&addr)?;
34
36
  Ok(Self {
35
37
  exporter: Arc::new(exporter),
36
- addr,
38
+ bound_addr,
37
39
  })
38
40
  }
39
41
 
40
- pub async fn run(&self) -> hyper::Result<()> {
42
+ pub async fn run(self) -> hyper::Result<()> {
41
43
  // Spin up hyper server to serve metrics for scraping. We use hyper since we already depend
42
44
  // on it via Tonic.
43
45
  let expclone = self.exporter.clone();
@@ -45,9 +47,13 @@ impl PromServer {
45
47
  let expclone = expclone.clone();
46
48
  async move { Ok::<_, Infallible>(service_fn(move |req| metrics_req(req, expclone.clone()))) }
47
49
  });
48
- let server = Server::bind(&self.addr).serve(svc);
50
+ let server = Server::builder(self.bound_addr).serve(svc);
49
51
  server.await
50
52
  }
53
+
54
+ pub fn bound_addr(&self) -> SocketAddr {
55
+ self.bound_addr.local_addr()
56
+ }
51
57
  }
52
58
 
53
59
  /// Serves prometheus metrics in the expected format for scraping
@@ -5,12 +5,16 @@ use crate::{
5
5
  protosext::ValidPollWFTQResponse,
6
6
  replay::TestHistoryBuilder,
7
7
  sticky_q_name_for_worker,
8
+ telemetry::metrics::MetricsContext,
8
9
  worker::{
9
- client::{mocks::mock_workflow_client, MockWorkerClient, WorkerClient},
10
+ client::{
11
+ mocks::mock_workflow_client, MockWorkerClient, WorkerClient, WorkflowTaskCompletion,
12
+ },
10
13
  new_wft_poller,
11
14
  },
12
15
  TaskToken, Worker, WorkerConfig, WorkerConfigBuilder,
13
16
  };
17
+ use async_trait::async_trait;
14
18
  use bimap::BiMap;
15
19
  use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
16
20
  use mockall::TimesRange;
@@ -26,7 +30,7 @@ use std::{
26
30
  task::{Context, Poll},
27
31
  time::Duration,
28
32
  };
29
- use temporal_client::WorkflowTaskCompletion;
33
+ use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
30
34
  use temporal_sdk_core_api::Worker as WorkerTrait;
31
35
  use temporal_sdk_core_protos::{
32
36
  coresdk::{
@@ -50,13 +54,13 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
50
54
  use tokio_util::sync::CancellationToken;
51
55
 
52
56
  pub const TEST_Q: &str = "q";
53
- pub static NO_MORE_WORK_ERROR_MSG: &str = "No more work to do";
54
57
 
55
58
  pub fn test_worker_cfg() -> WorkerConfigBuilder {
56
59
  let mut wcb = WorkerConfigBuilder::default();
57
60
  wcb.namespace("default")
58
61
  .task_queue(TEST_Q)
59
62
  .worker_build_id("test_bin_id")
63
+ .ignore_evicts_on_shutdown(true)
60
64
  // Serial polling since it makes mocking much easier.
61
65
  .max_concurrent_wft_polls(1_usize);
62
66
  wcb
@@ -133,13 +137,19 @@ pub(crate) fn build_fake_worker(
133
137
 
134
138
  pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
135
139
  let sticky_q = sticky_q_name_for_worker("unit-test", &mocks.inputs.config);
140
+ let act_poller = if mocks.inputs.config.no_remote_activities {
141
+ None
142
+ } else {
143
+ mocks.inputs.act_poller
144
+ };
136
145
  Worker::new_with_pollers(
137
146
  mocks.inputs.config,
138
147
  sticky_q,
139
148
  mocks.client,
140
149
  mocks.inputs.wft_stream,
141
- mocks.inputs.act_poller,
142
- Default::default(),
150
+ act_poller,
151
+ MetricsContext::no_op(),
152
+ None,
143
153
  CancellationToken::new(),
144
154
  )
145
155
  }
@@ -209,7 +219,7 @@ impl MockWorkerInputs {
209
219
  }
210
220
  pub fn new_from_poller(wf_poller: BoxedWFPoller) -> Self {
211
221
  Self {
212
- wft_stream: new_wft_poller(wf_poller, Default::default()).boxed(),
222
+ wft_stream: new_wft_poller(wf_poller, MetricsContext::no_op()).boxed(),
213
223
  act_poller: None,
214
224
  config: test_worker_cfg().build().unwrap(),
215
225
  }
@@ -293,7 +303,7 @@ where
293
303
  }
294
304
  .boxed()
295
305
  } else {
296
- async { Some(Err(tonic::Status::cancelled(NO_MORE_WORK_ERROR_MSG))) }.boxed()
306
+ async { None }.boxed()
297
307
  }
298
308
  });
299
309
  Box::new(mock_poller) as BoxedPoller<T>
@@ -367,10 +377,12 @@ pub(crate) struct MockPollCfg {
367
377
  pub expect_fail_wft_matcher:
368
378
  Box<dyn Fn(&TaskToken, &WorkflowTaskFailedCause, &Option<Failure>) -> bool + Send>,
369
379
  pub completion_asserts: Option<Box<dyn Fn(&WorkflowTaskCompletion) + Send>>,
380
+ pub num_expected_completions: Option<TimesRange>,
370
381
  /// If being used with the Rust SDK, this is set true. It ensures pollers will not error out
371
382
  /// early with no work, since we cannot know the exact number of times polling will happen.
372
383
  /// Instead, they will just block forever.
373
384
  pub using_rust_sdk: bool,
385
+ pub make_poll_stream_interminable: bool,
374
386
  }
375
387
 
376
388
  impl MockPollCfg {
@@ -387,7 +399,9 @@ impl MockPollCfg {
387
399
  mock_client: mock_workflow_client(),
388
400
  expect_fail_wft_matcher: Box::new(|_, _, _| true),
389
401
  completion_asserts: None,
402
+ num_expected_completions: None,
390
403
  using_rust_sdk: false,
404
+ make_poll_stream_interminable: false,
391
405
  }
392
406
  }
393
407
  pub fn from_resp_batches(
@@ -408,7 +422,9 @@ impl MockPollCfg {
408
422
  mock_client,
409
423
  expect_fail_wft_matcher: Box::new(|_, _, _| true),
410
424
  completion_asserts: None,
425
+ num_expected_completions: None,
411
426
  using_rust_sdk: false,
427
+ make_poll_stream_interminable: false,
412
428
  }
413
429
  }
414
430
  }
@@ -498,7 +514,7 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
498
514
  .into_iter()
499
515
  .map(|response| {
500
516
  let cur_attempt = attempts_at_task_num.entry(response.hashable()).or_insert(1);
501
- let mut r = hist_to_poll_resp(&hist.hist, hist.wf_id.clone(), response, TEST_Q);
517
+ let mut r = hist_to_poll_resp(&hist.hist, hist.wf_id.clone(), response);
502
518
  r.attempt = *cur_attempt;
503
519
  *cur_attempt += 1;
504
520
  r
@@ -576,16 +592,20 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
576
592
  );
577
593
 
578
594
  let outstanding = outstanding_wf_task_tokens.clone();
579
- cfg.mock_client
580
- .expect_complete_workflow_task()
581
- .returning(move |comp| {
582
- if let Some(ass) = cfg.completion_asserts.as_ref() {
583
- // tee hee
584
- ass(&comp)
585
- }
586
- outstanding.release_token(&comp.task_token);
587
- Ok(RespondWorkflowTaskCompletedResponse::default())
588
- });
595
+ let expect_completes = cfg.mock_client.expect_complete_workflow_task();
596
+ if let Some(range) = cfg.num_expected_completions {
597
+ expect_completes.times(range);
598
+ } else if cfg.completion_asserts.is_some() {
599
+ expect_completes.times(1..);
600
+ }
601
+ expect_completes.returning(move |comp| {
602
+ if let Some(ass) = cfg.completion_asserts.as_ref() {
603
+ // tee hee
604
+ ass(&comp)
605
+ }
606
+ outstanding.release_token(&comp.task_token);
607
+ Ok(RespondWorkflowTaskCompletedResponse::default())
608
+ });
589
609
  let outstanding = outstanding_wf_task_tokens.clone();
590
610
  cfg.mock_client
591
611
  .expect_fail_workflow_task()
@@ -604,11 +624,15 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
604
624
  Ok(Default::default())
605
625
  });
606
626
 
607
- MocksHolder {
627
+ let mut mh = MocksHolder {
608
628
  client: Arc::new(cfg.mock_client),
609
629
  inputs: mock_worker,
610
630
  outstanding_task_map: Some(outstanding_wf_task_tokens),
631
+ };
632
+ if cfg.make_poll_stream_interminable {
633
+ mh.make_wft_stream_interminable();
611
634
  }
635
+ mh
612
636
  }
613
637
 
614
638
  pub struct QueueResponse<T> {
@@ -649,7 +673,6 @@ pub fn hist_to_poll_resp(
649
673
  t: &TestHistoryBuilder,
650
674
  wf_id: impl Into<String>,
651
675
  response_type: ResponseType,
652
- task_queue: impl Into<String>,
653
676
  ) -> QueueResponse<PollWorkflowTaskQueueResponse> {
654
677
  let run_id = t.get_orig_run_id();
655
678
  let wf = WorkflowExecution {
@@ -678,7 +701,7 @@ pub fn hist_to_poll_resp(
678
701
  }
679
702
  }
680
703
  };
681
- let mut resp = hist_info.as_poll_wft_response(task_queue);
704
+ let mut resp = hist_info.as_poll_wft_response();
682
705
  resp.workflow_execution = Some(wf);
683
706
  QueueResponse { resp, delay_until }
684
707
  }
@@ -828,6 +851,7 @@ pub(crate) fn gen_assert_and_fail(asserter: &dyn Fn(&WorkflowActivation)) -> Ass
828
851
  message: "Intentional test failure".to_string(),
829
852
  ..Default::default()
830
853
  }),
854
+ ..Default::default()
831
855
  }
832
856
  .into(),
833
857
  )
@@ -873,3 +897,43 @@ macro_rules! prost_dur {
873
897
  .expect("test duration fits")
874
898
  };
875
899
  }
900
+
901
+ #[async_trait]
902
+ pub(crate) trait WorkerExt {
903
+ /// Initiate shutdown, drain the pollers, and wait for shutdown to complete.
904
+ async fn drain_pollers_and_shutdown(self);
905
+ /// Initiate shutdown, drain the *activity* poller, and wait for shutdown to complete.
906
+ /// Takes a ref because of that one test that needs it.
907
+ async fn drain_activity_poller_and_shutdown(&self);
908
+ }
909
+
910
+ #[async_trait]
911
+ impl WorkerExt for Worker {
912
+ async fn drain_pollers_and_shutdown(self) {
913
+ self.initiate_shutdown();
914
+ tokio::join!(
915
+ async {
916
+ assert_matches!(
917
+ self.poll_activity_task().await.unwrap_err(),
918
+ PollActivityError::ShutDown
919
+ );
920
+ },
921
+ async {
922
+ assert_matches!(
923
+ self.poll_workflow_activation().await.unwrap_err(),
924
+ PollWfError::ShutDown
925
+ );
926
+ }
927
+ );
928
+ self.finalize_shutdown().await;
929
+ }
930
+
931
+ async fn drain_activity_poller_and_shutdown(&self) {
932
+ self.initiate_shutdown();
933
+ assert_matches!(
934
+ self.poll_activity_task().await.unwrap_err(),
935
+ PollActivityError::ShutDown
936
+ );
937
+ self.shutdown().await;
938
+ }
939
+ }
@@ -26,9 +26,6 @@ use tokio_util::sync::CancellationToken;
26
26
  /// Used to supply new heartbeat events to the activity heartbeat manager, or to send a shutdown
27
27
  /// request.
28
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
29
  shutdown_token: CancellationToken,
33
30
  /// Used during `shutdown` to await until all inflight requests are sent.
34
31
  join_handle: Mutex<Option<JoinHandle<()>>>,
@@ -74,15 +71,118 @@ pub enum ActivityHeartbeatError {
74
71
  /// to heartbeat.
75
72
  #[error("Unable to parse activity heartbeat timeout.")]
76
73
  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
74
  }
81
75
 
82
76
  /// Manages activity heartbeating for a worker. Allows sending new heartbeats or requesting and
83
77
  /// awaiting for the shutdown. When shutdown is requested, signal gets sent to all processors, which
84
78
  /// allows them to complete gracefully.
85
79
  impl ActivityHeartbeatManager {
80
+ /// Creates a new instance of an activity heartbeat manager and returns a handle to the user,
81
+ /// which allows to send new heartbeats and initiate the shutdown.
82
+ /// Returns the manager and a channel that buffers cancellation notifications to be sent to Lang.
83
+ pub(super) fn new(
84
+ client: Arc<dyn WorkerClient>,
85
+ ) -> (Self, UnboundedReceiver<PendingActivityCancel>) {
86
+ let (heartbeat_stream_state, heartbeat_tx_source, shutdown_token) =
87
+ HeartbeatStreamState::new();
88
+ let (cancels_tx, cancels_rx) = unbounded_channel();
89
+ let heartbeat_tx = heartbeat_tx_source.clone();
90
+
91
+ let join_handle = tokio::spawn(
92
+ // The stream of incoming heartbeats uses unfold to carry state across each item in the
93
+ // stream. The closure checks if, for any given activity, we should heartbeat or not
94
+ // depending on its delay and when we last issued a heartbeat for it.
95
+ futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
96
+ async move {
97
+ let hb = tokio::select! {
98
+ biased;
99
+
100
+ _ = hb_states.cancellation_token.cancelled() => {
101
+ return None
102
+ }
103
+ hb = hb_states.incoming_hbs.recv() => match hb {
104
+ None => return None,
105
+ Some(hb) => hb,
106
+ }
107
+ };
108
+
109
+ Some((
110
+ match hb {
111
+ HeartbeatAction::SendHeartbeat(hb) => hb_states.record(hb),
112
+ HeartbeatAction::CompleteReport(tt) => hb_states.handle_report_completed(tt),
113
+ HeartbeatAction::CompleteThrottle(tt) => hb_states.handle_throttle_completed(tt),
114
+ HeartbeatAction::Evict{ token, on_complete } => hb_states.evict(token, on_complete),
115
+ },
116
+ hb_states,
117
+ ))
118
+ }
119
+ })
120
+ // Filters out `None`s
121
+ .filter_map(|opt| async { opt })
122
+ .for_each_concurrent(None, move |action| {
123
+ let heartbeat_tx = heartbeat_tx_source.clone();
124
+ let sg = client.clone();
125
+ let cancels_tx = cancels_tx.clone();
126
+ async move {
127
+ match action {
128
+ HeartbeatExecutorAction::Sleep(tt, duration, cancellation_token) => {
129
+ tokio::select! {
130
+ _ = cancellation_token.cancelled() => (),
131
+ _ = tokio::time::sleep(duration) => {
132
+ let _ = heartbeat_tx.send(HeartbeatAction::CompleteThrottle(tt));
133
+ },
134
+ };
135
+ }
136
+ HeartbeatExecutorAction::Report { task_token: tt, details } => {
137
+ match sg
138
+ .record_activity_heartbeat(tt.clone(), details.into_payloads())
139
+ .await
140
+ {
141
+ Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
142
+ if cancel_requested {
143
+ cancels_tx
144
+ .send(PendingActivityCancel::new(
145
+ tt.clone(),
146
+ ActivityCancelReason::Cancelled,
147
+ ))
148
+ .expect(
149
+ "Receive half of heartbeat cancels not blocked",
150
+ );
151
+ }
152
+ }
153
+ // Send cancels for any activity that learns its workflow already
154
+ // finished (which is one thing not found implies - other reasons
155
+ // would seem equally valid).
156
+ Err(s) if s.code() == tonic::Code::NotFound => {
157
+ debug!(task_token = %tt,
158
+ "Activity not found when recording heartbeat");
159
+ cancels_tx
160
+ .send(PendingActivityCancel::new(
161
+ tt.clone(),
162
+ ActivityCancelReason::NotFound,
163
+ ))
164
+ .expect("Receive half of heartbeat cancels not blocked");
165
+ }
166
+ Err(e) => {
167
+ warn!("Error when recording heartbeat: {:?}", e);
168
+ }
169
+ };
170
+ let _ = heartbeat_tx.send(HeartbeatAction::CompleteReport(tt));
171
+ }
172
+ }
173
+ }
174
+ }),
175
+ );
176
+
177
+ (
178
+ Self {
179
+ join_handle: Mutex::new(Some(join_handle)),
180
+ shutdown_token,
181
+ heartbeat_tx,
182
+ },
183
+ cancels_rx,
184
+ )
185
+ }
86
186
  /// Records a new heartbeat, the first call will result in an immediate call to the server,
87
187
  /// while rapid successive calls would accumulate for up to `delay` and then latest heartbeat
88
188
  /// details will be sent to the server.
@@ -95,9 +195,6 @@ impl ActivityHeartbeatManager {
95
195
  hb: ActivityHeartbeat,
96
196
  throttle_interval: Duration,
97
197
  ) -> Result<(), ActivityHeartbeatError> {
98
- if self.shutdown_token.is_cancelled() {
99
- return Err(ActivityHeartbeatError::ShuttingDown);
100
- }
101
198
  self.heartbeat_tx
102
199
  .send(HeartbeatAction::SendHeartbeat(ValidActivityHeartbeat {
103
200
  task_token: TaskToken(hb.task_token),
@@ -121,13 +218,6 @@ impl ActivityHeartbeatManager {
121
218
  completed.notified().await;
122
219
  }
123
220
 
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
221
  /// Initiates shutdown procedure by stopping lifecycle loop and awaiting for all in-flight
132
222
  /// heartbeat requests to be flushed to the server.
133
223
  pub(super) async fn shutdown(&self) {
@@ -301,110 +391,6 @@ impl HeartbeatStreamState {
301
391
  }
302
392
  }
303
393
 
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
394
  #[cfg(test)]
409
395
  mod test {
410
396
  use super::*;
@@ -425,7 +411,7 @@ mod test {
425
411
  .expect_record_activity_heartbeat()
426
412
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
427
413
  .times(2);
428
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
414
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
429
415
  let fake_task_token = vec![1, 2, 3];
430
416
  // Send 2 heartbeat requests for 20ms apart.
431
417
  // The first heartbeat should be sent right away, and
@@ -446,14 +432,13 @@ mod test {
446
432
  .expect_record_activity_heartbeat()
447
433
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
448
434
  .times(3);
449
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
435
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
450
436
  let fake_task_token = vec![1, 2, 3];
451
437
  // Heartbeats always get sent if recorded less frequently than the throttle interval
452
438
  for i in 0_u8..3 {
453
439
  record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(10));
454
440
  sleep(Duration::from_millis(20)).await;
455
441
  }
456
- // sleep again to let heartbeats be flushed
457
442
  hm.shutdown().await;
458
443
  }
459
444
 
@@ -466,7 +451,7 @@ mod test {
466
451
  .expect_record_activity_heartbeat()
467
452
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
468
453
  .times(1);
469
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
454
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
470
455
  let fake_task_token = vec![1, 2, 3];
471
456
  // Send a whole bunch of heartbeats very fast. We should still only send one total.
472
457
  for i in 0_u8..50 {
@@ -485,7 +470,7 @@ mod test {
485
470
  .expect_record_activity_heartbeat()
486
471
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
487
472
  .times(2);
488
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
473
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
489
474
  let fake_task_token = vec![1, 2, 3];
490
475
  record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
491
476
  sleep(Duration::from_millis(500)).await;
@@ -502,7 +487,7 @@ mod test {
502
487
  .expect_record_activity_heartbeat()
503
488
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
504
489
  .times(2);
505
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
490
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
506
491
  let fake_task_token = vec![1, 2, 3];
507
492
  record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
508
493
  // Let it propagate
@@ -522,42 +507,13 @@ mod test {
522
507
  .expect_record_activity_heartbeat()
523
508
  .returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
524
509
  .times(1);
525
- let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
510
+ let (hm, _) = ActivityHeartbeatManager::new(Arc::new(mock_client));
526
511
  let fake_task_token = vec![1, 2, 3];
527
512
  record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
528
513
  hm.evict(fake_task_token.clone().into()).await;
529
514
  hm.shutdown().await;
530
515
  }
531
516
 
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
517
  fn record_heartbeat(
562
518
  hm: &ActivityHeartbeatManager,
563
519
  task_token: Vec<u8>,