temporalio 0.0.1 → 0.1.0

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 (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,4 +1,5 @@
1
1
  mod activity_heartbeat_manager;
2
+ mod activity_task_poller_stream;
2
3
  mod local_activities;
3
4
 
4
5
  pub(crate) use local_activities::{
@@ -7,10 +8,14 @@ pub(crate) use local_activities::{
7
8
  LocalInFlightActInfo, NewLocalAct,
8
9
  };
9
10
 
11
+ use crate::abstractions::{ClosableMeteredSemaphore, TrackedOwnedMeteredSemPermit};
12
+ use crate::worker::activities::activity_task_poller_stream::new_activity_task_poller;
10
13
  use crate::{
11
- abstractions::{MeteredSemaphore, OwnedMeteredSemPermit},
14
+ abstractions::{MeteredSemaphore, OwnedMeteredSemPermit, UsedMeteredSemPermit},
12
15
  pollers::BoxedActPoller,
13
- telemetry::metrics::{activity_type, activity_worker_type, workflow_type, MetricsContext},
16
+ telemetry::metrics::{
17
+ activity_type, activity_worker_type, eager, workflow_type, MetricsContext,
18
+ },
14
19
  worker::{
15
20
  activities::activity_heartbeat_manager::ActivityHeartbeatError, client::WorkerClient,
16
21
  },
@@ -18,14 +23,11 @@ use crate::{
18
23
  };
19
24
  use activity_heartbeat_manager::ActivityHeartbeatManager;
20
25
  use dashmap::DashMap;
21
- use governor::{
22
- clock::DefaultClock,
23
- middleware::NoOpMiddleware,
24
- state::{InMemoryState, NotKeyed},
25
- Quota, RateLimiter,
26
- };
26
+ use futures::{stream, stream::BoxStream, stream::PollNext, Stream, StreamExt};
27
+ use governor::{Quota, RateLimiter};
27
28
  use std::{
28
29
  convert::TryInto,
30
+ future,
29
31
  sync::Arc,
30
32
  time::{Duration, Instant},
31
33
  };
@@ -40,7 +42,11 @@ use temporal_sdk_core_protos::{
40
42
  workflowservice::v1::PollActivityTaskQueueResponse,
41
43
  },
42
44
  };
43
- use tokio::sync::Notify;
45
+ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
46
+ use tokio::sync::{Mutex, Notify};
47
+ use tokio_stream::wrappers::UnboundedReceiverStream;
48
+ use tokio_util::sync::CancellationToken;
49
+ use tracing::Span;
44
50
 
45
51
  #[derive(Debug, derive_more::Constructor)]
46
52
  struct PendingActivityCancel {
@@ -48,11 +54,15 @@ struct PendingActivityCancel {
48
54
  reason: ActivityCancelReason,
49
55
  }
50
56
 
51
- /// Contains minimal set of details that core needs to store while an activity is running.
57
+ /// Contains details that core wants to store while an activity is running.
52
58
  #[derive(Debug)]
53
59
  struct InFlightActInfo {
54
60
  pub activity_type: String,
55
61
  pub workflow_type: String,
62
+ /// Only kept for logging reasons
63
+ pub workflow_id: String,
64
+ /// Only kept for logging reasons
65
+ pub workflow_run_id: String,
56
66
  start_time: Instant,
57
67
  }
58
68
 
@@ -68,22 +78,20 @@ struct RemoteInFlightActInfo {
68
78
  /// discard the reply.
69
79
  pub known_not_found: bool,
70
80
  /// The permit from the max concurrent semaphore
71
- _permit: OwnedMeteredSemPermit,
81
+ _permit: UsedMeteredSemPermit,
72
82
  }
73
83
  impl RemoteInFlightActInfo {
74
- fn new(
75
- activity_type: String,
76
- workflow_type: String,
77
- heartbeat_timeout: Option<prost_types::Duration>,
78
- permit: OwnedMeteredSemPermit,
79
- ) -> Self {
84
+ fn new(poll_resp: &PollActivityTaskQueueResponse, permit: UsedMeteredSemPermit) -> Self {
85
+ let wec = poll_resp.workflow_execution.clone().unwrap_or_default();
80
86
  Self {
81
87
  base: InFlightActInfo {
82
- activity_type,
83
- workflow_type,
88
+ activity_type: poll_resp.activity_type.clone().unwrap_or_default().name,
89
+ workflow_type: poll_resp.workflow_type.clone().unwrap_or_default().name,
90
+ workflow_id: wec.workflow_id,
91
+ workflow_run_id: wec.run_id,
84
92
  start_time: Instant::now(),
85
93
  },
86
- heartbeat_timeout,
94
+ heartbeat_timeout: poll_resp.heartbeat_timeout.clone(),
87
95
  issued_cancel_to_lang: false,
88
96
  known_not_found: false,
89
97
  _permit: permit,
@@ -91,43 +99,38 @@ impl RemoteInFlightActInfo {
91
99
  }
92
100
  }
93
101
 
94
- struct NonPollActBuffer {
95
- tx: async_channel::Sender<PermittedTqResp>,
96
- rx: async_channel::Receiver<PermittedTqResp>,
97
- }
98
- impl NonPollActBuffer {
99
- pub fn new() -> Self {
100
- let (tx, rx) = async_channel::unbounded();
101
- Self { tx, rx }
102
- }
103
-
104
- pub async fn next(&self) -> PermittedTqResp {
105
- self.rx.recv().await.expect("Send half cannot be dropped")
106
- }
107
- }
108
-
109
102
  pub(crate) struct WorkerActivityTasks {
103
+ /// Token used to signal the server task poller that shutdown is beginning
104
+ poller_shutdown_token: CancellationToken,
110
105
  /// Centralizes management of heartbeat issuing / throttling
111
106
  heartbeat_manager: ActivityHeartbeatManager,
107
+ /// Combined stream for any ActivityTask producing source (polls, eager activities, cancellations)
108
+ activity_task_stream: Mutex<BoxStream<'static, Result<ActivityTask, PollActivityError>>>,
112
109
  /// Activities that have been issued to lang but not yet completed
113
- outstanding_activity_tasks: DashMap<TaskToken, RemoteInFlightActInfo>,
114
- /// Buffers activity task polling in the event we need to return a cancellation while a poll is
115
- /// ongoing.
116
- poller: BoxedActPoller,
117
- /// Holds activity tasks we have received by non-polling means. EX: In direct response to
118
- /// workflow task completion.
119
- non_poll_tasks: NonPollActBuffer,
120
- /// Ensures we stay at or below this worker's maximum concurrent activity limit
121
- activities_semaphore: Arc<MeteredSemaphore>,
122
- /// Enables per-worker rate-limiting of activity tasks
123
- ratelimiter: Option<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
124
- /// Wakes every time an activity is removed from the outstanding map
125
- complete_notify: Notify,
110
+ outstanding_activity_tasks: Arc<DashMap<TaskToken, RemoteInFlightActInfo>>,
111
+ /// Ensures we don't exceed this worker's maximum concurrent activity limit for activities.
112
+ /// This semaphore is used to limit eager activities but shares the same underlying [MeteredSemaphore] that is used
113
+ /// to limit the concurrency for non-eager activities.
114
+ eager_activities_semaphore: Arc<ClosableMeteredSemaphore>,
115
+ /// Holds activity tasks we have received in direct response to workflow task completion (a.k.a eager activities).
116
+ /// Tasks received in this stream hold a "tracked" permit that is issued by the `eager_activities_semaphore`.
117
+ eager_activities_tx: UnboundedSender<TrackedPermittedTqResp>,
126
118
 
127
119
  metrics: MetricsContext,
128
120
 
129
121
  max_heartbeat_throttle_interval: Duration,
130
122
  default_heartbeat_throttle_interval: Duration,
123
+
124
+ /// Wakes every time an activity is removed from the outstanding map
125
+ complete_notify: Arc<Notify>,
126
+ /// Token to notify when poll returned a shutdown error
127
+ poll_returned_shutdown_token: CancellationToken,
128
+ }
129
+
130
+ #[derive(derive_more::From)]
131
+ enum ActivityTaskSource {
132
+ PendingCancel(PendingActivityCancel),
133
+ PendingStart(Result<(PermittedTqResp, bool), PollActivityError>),
131
134
  }
132
135
 
133
136
  impl WorkerActivityTasks {
@@ -140,90 +143,186 @@ impl WorkerActivityTasks {
140
143
  max_heartbeat_throttle_interval: Duration,
141
144
  default_heartbeat_throttle_interval: Duration,
142
145
  ) -> Self {
143
- Self {
144
- heartbeat_manager: ActivityHeartbeatManager::new(client),
145
- outstanding_activity_tasks: Default::default(),
146
+ let semaphore = Arc::new(MeteredSemaphore::new(
147
+ max_activity_tasks,
148
+ metrics.with_new_attrs([activity_worker_type()]),
149
+ MetricsContext::available_task_slots,
150
+ ));
151
+ let poller_shutdown_token = CancellationToken::new();
152
+ let rate_limiter = max_worker_act_per_sec.and_then(|ps| {
153
+ Quota::with_period(Duration::from_secs_f64(ps.recip())).map(RateLimiter::direct)
154
+ });
155
+ let outstanding_activity_tasks = Arc::new(DashMap::new());
156
+ let server_poller_stream = new_activity_task_poller(
146
157
  poller,
147
- non_poll_tasks: NonPollActBuffer::new(),
148
- activities_semaphore: Arc::new(MeteredSemaphore::new(
149
- max_activity_tasks,
150
- metrics.with_new_attrs([activity_worker_type()]),
151
- MetricsContext::available_task_slots,
152
- )),
153
- ratelimiter: max_worker_act_per_sec.and_then(|ps| {
154
- Quota::with_period(Duration::from_secs_f64(ps.recip())).map(RateLimiter::direct)
155
- }),
156
- complete_notify: Notify::new(),
158
+ semaphore.clone(),
159
+ rate_limiter,
160
+ metrics.clone(),
161
+ poller_shutdown_token.clone(),
162
+ );
163
+ let (eager_activities_tx, eager_activities_rx) = unbounded_channel();
164
+ let eager_activities_semaphore = ClosableMeteredSemaphore::new_arc(semaphore);
165
+
166
+ let start_tasks_stream_complete = CancellationToken::new();
167
+ let starts_stream = Self::merge_start_task_sources(
168
+ eager_activities_rx,
169
+ server_poller_stream,
170
+ eager_activities_semaphore.clone(),
171
+ start_tasks_stream_complete.clone(),
172
+ );
173
+ let (heartbeat_manager, cancels_rx) = ActivityHeartbeatManager::new(client);
174
+ let complete_notify = Arc::new(Notify::new());
175
+ let source_stream = stream::select_with_strategy(
176
+ UnboundedReceiverStream::new(cancels_rx).map(ActivityTaskSource::from),
177
+ starts_stream.map(ActivityTaskSource::from),
178
+ |_: &mut ()| PollNext::Left,
179
+ );
180
+ // Create a task stream composed of (in poll preference order):
181
+ // cancels_stream ------------------------------+--- activity_task_stream
182
+ // eager_activities_rx ---+--- starts_stream ---|
183
+ // server_poll_stream ---|
184
+ let activity_task_stream = Self::merge_source_streams(
185
+ source_stream,
186
+ outstanding_activity_tasks.clone(),
187
+ start_tasks_stream_complete,
188
+ complete_notify.clone(),
189
+ metrics.clone(),
190
+ );
191
+
192
+ Self {
193
+ poller_shutdown_token,
194
+ eager_activities_tx,
195
+ heartbeat_manager,
196
+ activity_task_stream: Mutex::new(activity_task_stream.boxed()),
197
+ outstanding_activity_tasks,
198
+ eager_activities_semaphore,
199
+ complete_notify,
157
200
  metrics,
158
201
  max_heartbeat_throttle_interval,
159
202
  default_heartbeat_throttle_interval,
203
+ poll_returned_shutdown_token: CancellationToken::new(),
160
204
  }
161
205
  }
162
206
 
163
- pub(crate) fn notify_shutdown(&self) {
164
- self.poller.notify_shutdown();
207
+ /// Merges the server poll and eager [ActivityTask] sources
208
+ fn merge_start_task_sources(
209
+ non_poll_tasks_rx: UnboundedReceiver<TrackedPermittedTqResp>,
210
+ poller_stream: impl Stream<Item = Result<PermittedTqResp, tonic::Status>>,
211
+ eager_activities_semaphore: Arc<ClosableMeteredSemaphore>,
212
+ on_complete_token: CancellationToken,
213
+ ) -> impl Stream<Item = Result<(PermittedTqResp, bool), PollActivityError>> {
214
+ let non_poll_stream = stream::unfold(
215
+ (non_poll_tasks_rx, eager_activities_semaphore),
216
+ |(mut non_poll_tasks_rx, eager_activities_semaphore)| async move {
217
+ loop {
218
+ tokio::select! {
219
+ biased;
220
+
221
+ task_opt = non_poll_tasks_rx.recv() => {
222
+ // Add is_eager true and wrap in Result
223
+ return task_opt.map(|task| (Ok((PermittedTqResp{ permit: task.permit.into(), resp: task.resp }, true)), (non_poll_tasks_rx, eager_activities_semaphore)));
224
+ }
225
+ _ = eager_activities_semaphore.close_complete() => {
226
+ // Once shutting down, we stop accepting eager activities
227
+ non_poll_tasks_rx.close();
228
+ continue;
229
+ }
230
+ }
231
+ }
232
+ },
233
+ );
234
+ // Add is_eager false
235
+ let poller_stream = poller_stream.map(|res| res.map(|task| (task, false)));
236
+
237
+ // Prefer eager activities over polling the server
238
+ stream::select_with_strategy(non_poll_stream, poller_stream, |_: &mut ()| PollNext::Left)
239
+ .map(|res| res.map_err(|err| err.into()))
240
+ // This map, chain, filter_map sequence is here to cancel the token when this stream ends.
241
+ .map(Some)
242
+ .chain(futures::stream::once(async move {
243
+ on_complete_token.cancel();
244
+ None
245
+ }))
246
+ .filter_map(future::ready)
165
247
  }
166
248
 
167
- /// Wait for all outstanding activity tasks to finish
168
- pub(crate) async fn wait_all_finished(&self) {
169
- while !self.outstanding_activity_tasks.is_empty() {
170
- self.complete_notify.notified().await
171
- }
249
+ /// Builds an [ActivityTask] stream for cancellation tasks from cancels delivered from heartbeats
250
+ fn merge_source_streams(
251
+ source_stream: impl Stream<Item = ActivityTaskSource>,
252
+ outstanding_tasks: Arc<DashMap<TaskToken, RemoteInFlightActInfo>>,
253
+ start_tasks_stream_complete: CancellationToken,
254
+ complete_notify: Arc<Notify>,
255
+ metrics: MetricsContext,
256
+ ) -> impl Stream<Item = Result<ActivityTask, PollActivityError>> {
257
+ let outstanding_tasks_clone = outstanding_tasks.clone();
258
+ source_stream.filter_map(move |source| {
259
+ let outstanding_tasks = outstanding_tasks.clone();
260
+ let metrics = metrics.clone();
261
+ async move {
262
+ match source {
263
+ ActivityTaskSource::PendingCancel(next_pc) => {
264
+ // It's possible that activity has been completed and we no longer have an
265
+ // outstanding activity task. This is fine because it means that we no
266
+ // longer need to cancel this activity, so we'll just ignore such orphaned
267
+ // cancellations.
268
+ if let Some(mut details) = outstanding_tasks.get_mut(&next_pc.task_token) {
269
+ if details.issued_cancel_to_lang {
270
+ // Don't double-issue cancellations
271
+ return None
272
+ }
273
+
274
+ details.issued_cancel_to_lang = true;
275
+ if next_pc.reason == ActivityCancelReason::NotFound {
276
+ details.known_not_found = true;
277
+ }
278
+ Some(Ok(ActivityTask::cancel_from_ids(next_pc.task_token.0, next_pc.reason)))
279
+ } else {
280
+ debug!(task_token = ?next_pc.task_token, "Unknown activity task when issuing cancel");
281
+ // If we can't find the activity here, it's already been completed,
282
+ // in which case issuing a cancel again is pointless.
283
+ None
284
+ }
285
+ },
286
+ ActivityTaskSource::PendingStart(res) => {
287
+ Some(res.map(|(task, is_eager)| {
288
+ Self::about_to_issue_task(outstanding_tasks, task, is_eager, metrics)
289
+ }))
290
+ }
291
+ }
292
+ }
293
+ }).take_until(async move {
294
+ start_tasks_stream_complete.cancelled().await;
295
+ while !outstanding_tasks_clone.is_empty() {
296
+ complete_notify.notified().await
297
+ }
298
+ })
172
299
  }
173
300
 
174
- pub(crate) async fn shutdown(self) {
175
- self.poller.shutdown_box().await;
176
- self.heartbeat_manager.shutdown().await;
301
+ pub(crate) fn notify_shutdown(&self) {
302
+ self.poller_shutdown_token.cancel();
303
+ self.eager_activities_semaphore.close();
177
304
  }
178
305
 
179
- /// Wait until not at the outstanding activity limit, and then poll for an activity task.
180
- ///
181
- /// Returns `Ok(None)` if no activity is ready and the overall polling loop should be retried.
182
- pub(crate) async fn poll(&self) -> Result<Option<ActivityTask>, PollActivityError> {
183
- let poll_with_semaphore = async {
184
- // Acquire and subsequently forget a permit for an outstanding activity. When they are
185
- // completed, we must add a new permit to the semaphore, since holding the permit the
186
- // entire time lang does work would be a challenge.
187
- let perm = self
188
- .activities_semaphore
189
- .acquire_owned()
190
- .await
191
- .expect("outstanding activity semaphore not closed");
192
- if let Some(ref rl) = self.ratelimiter {
193
- rl.until_ready().await;
194
- }
195
- (self.poller.poll().await, perm)
196
- };
306
+ async fn shutdown_complete(&self) {
307
+ self.poll_returned_shutdown_token.cancelled().await;
308
+ self.heartbeat_manager.shutdown().await;
309
+ }
197
310
 
198
- tokio::select! {
199
- biased;
311
+ pub(crate) async fn shutdown(&self) {
312
+ self.notify_shutdown();
313
+ self.shutdown_complete().await;
314
+ }
200
315
 
201
- cancel_task = self.next_pending_cancel_task() => {
202
- cancel_task
203
- }
204
- task = self.non_poll_tasks.next() => {
205
- Ok(Some(self.about_to_issue_task(task)))
206
- }
207
- (work, permit) = poll_with_semaphore => {
208
- match work {
209
- Some(Ok(work)) => {
210
- if work == PollActivityTaskQueueResponse::default() {
211
- // Timeout
212
- self.metrics.act_poll_timeout();
213
- return Ok(None)
214
- }
215
- let work = self.about_to_issue_task(PermittedTqResp {
216
- resp: work, permit
217
- });
218
- Ok(Some(work))
219
- }
220
- None => {
221
- Err(PollActivityError::ShutDown)
222
- }
223
- Some(Err(e)) => Err(e.into())
224
- }
225
- }
226
- }
316
+ /// Exclusive poll for activity tasks
317
+ ///
318
+ /// Polls the various task sources (server polls, eager activities, cancellations) while respecting the provided rate limits and allowed concurrency.
319
+ /// Returns Err(PollActivityError::ShutDown) after shutdown is completed and all tasks sources are depleted.
320
+ pub(crate) async fn poll(&self) -> Result<ActivityTask, PollActivityError> {
321
+ let mut poller_stream = self.activity_task_stream.lock().await;
322
+ poller_stream.next().await.unwrap_or_else(|| {
323
+ self.poll_returned_shutdown_token.cancel();
324
+ Err(PollActivityError::ShutDown)
325
+ })
227
326
  }
228
327
 
229
328
  pub(crate) async fn complete(
@@ -234,12 +333,14 @@ impl WorkerActivityTasks {
234
333
  ) {
235
334
  if let Some((_, act_info)) = self.outstanding_activity_tasks.remove(&task_token) {
236
335
  let act_metrics = self.metrics.with_new_attrs([
237
- activity_type(act_info.base.activity_type.clone()),
238
- workflow_type(act_info.base.workflow_type.clone()),
336
+ activity_type(act_info.base.activity_type),
337
+ workflow_type(act_info.base.workflow_type),
239
338
  ]);
339
+ Span::current().record("workflow_id", act_info.base.workflow_id);
340
+ Span::current().record("run_id", act_info.base.workflow_run_id);
240
341
  act_metrics.act_execution_latency(act_info.base.start_time.elapsed());
241
342
  let known_not_found = act_info.known_not_found;
242
- drop(act_info); // TODO: Get rid of dashmap. If we hold ref across await, bad stuff.
343
+
243
344
  self.heartbeat_manager.evict(task_token.clone()).await;
244
345
  self.complete_notify.notify_waiters();
245
346
 
@@ -331,57 +432,39 @@ impl WorkerActivityTasks {
331
432
  /// Returns a handle that the workflows management side can use to interact with this manager
332
433
  pub(crate) fn get_handle_for_workflows(&self) -> ActivitiesFromWFTsHandle {
333
434
  ActivitiesFromWFTsHandle {
334
- sem: self.activities_semaphore.clone(),
335
- tx: self.non_poll_tasks.tx.clone(),
435
+ sem: self.eager_activities_semaphore.clone(),
436
+ tx: self.eager_activities_tx.clone(),
336
437
  }
337
438
  }
338
439
 
339
- async fn next_pending_cancel_task(&self) -> Result<Option<ActivityTask>, PollActivityError> {
340
- let next_pc = self.heartbeat_manager.next_pending_cancel().await;
341
- // Issue cancellations for anything we noticed was cancelled during heartbeating
342
- if let Some(PendingActivityCancel { task_token, reason }) = next_pc {
343
- // It's possible that activity has been completed and we no longer have an
344
- // outstanding activity task. This is fine because it means that we no
345
- // longer need to cancel this activity, so we'll just ignore such orphaned
346
- // cancellations.
347
- if let Some(mut details) = self.outstanding_activity_tasks.get_mut(&task_token) {
348
- if details.issued_cancel_to_lang {
349
- // Don't double-issue cancellations
350
- return Ok(None);
351
- }
352
-
353
- details.issued_cancel_to_lang = true;
354
- if reason == ActivityCancelReason::NotFound {
355
- details.known_not_found = true;
356
- }
357
- Ok(Some(ActivityTask::cancel_from_ids(task_token.0, reason)))
358
- } else {
359
- debug!(task_token = ?task_token, "Unknown activity task when issuing cancel");
360
- // If we can't find the activity here, it's already been completed,
361
- // in which case issuing a cancel again is pointless.
362
- Ok(None)
440
+ /// Called when there is a new [ActivityTask] about to be bubbled up out of the poller
441
+ fn about_to_issue_task(
442
+ outstanding_tasks: Arc<DashMap<TaskToken, RemoteInFlightActInfo>>,
443
+ task: PermittedTqResp,
444
+ is_eager: bool,
445
+ metrics: MetricsContext,
446
+ ) -> ActivityTask {
447
+ if let Some(ref act_type) = task.resp.activity_type {
448
+ if let Some(ref wf_type) = task.resp.workflow_type {
449
+ metrics
450
+ .with_new_attrs([
451
+ activity_type(act_type.name.clone()),
452
+ workflow_type(wf_type.name.clone()),
453
+ eager(is_eager),
454
+ ])
455
+ .act_task_received();
363
456
  }
364
- } else {
365
- // The only situation where the next cancel would return none is if the manager
366
- // was dropped, which can only happen on shutdown.
367
- Err(PollActivityError::ShutDown)
368
457
  }
369
- }
458
+ // There could be an else statement here but since the response should always contain both
459
+ // activity_type and workflow_type, we won't bother.
370
460
 
371
- /// Called when there is a new act task about to be bubbled up out of the manager
372
- fn about_to_issue_task(&self, task: PermittedTqResp) -> ActivityTask {
373
461
  if let Some(dur) = task.resp.sched_to_start() {
374
- self.metrics.act_sched_to_start_latency(dur);
462
+ metrics.act_sched_to_start_latency(dur);
375
463
  };
376
464
 
377
- self.outstanding_activity_tasks.insert(
465
+ outstanding_tasks.insert(
378
466
  task.resp.task_token.clone().into(),
379
- RemoteInFlightActInfo::new(
380
- task.resp.activity_type.clone().unwrap_or_default().name,
381
- task.resp.workflow_type.clone().unwrap_or_default().name,
382
- task.resp.heartbeat_timeout.clone(),
383
- task.permit,
384
- ),
467
+ RemoteInFlightActInfo::new(&task.resp, task.permit.into_used()),
385
468
  );
386
469
 
387
470
  ActivityTask::start_from_poll_resp(task.resp)
@@ -389,38 +472,48 @@ impl WorkerActivityTasks {
389
472
 
390
473
  #[cfg(test)]
391
474
  pub(crate) fn remaining_activity_capacity(&self) -> usize {
392
- self.activities_semaphore.available_permits()
475
+ self.eager_activities_semaphore.available_permits()
393
476
  }
394
477
  }
395
478
 
396
479
  /// Provides facilities for the workflow side of things to interact with the activity manager.
397
480
  /// Allows for the handling of activities returned by WFT completions.
398
481
  pub(crate) struct ActivitiesFromWFTsHandle {
399
- sem: Arc<MeteredSemaphore>,
400
- tx: async_channel::Sender<PermittedTqResp>,
482
+ sem: Arc<ClosableMeteredSemaphore>,
483
+ tx: UnboundedSender<TrackedPermittedTqResp>,
401
484
  }
402
485
 
403
486
  impl ActivitiesFromWFTsHandle {
404
487
  /// Returns a handle that can be used to reserve an activity slot. EX: When requesting eager
405
488
  /// dispatch of an activity to this worker upon workflow task completion
406
- pub(crate) fn reserve_slot(&self) -> Option<OwnedMeteredSemPermit> {
489
+ pub(crate) fn reserve_slot(&self) -> Option<TrackedOwnedMeteredSemPermit> {
490
+ // TODO: check if rate limit is not exceeded and count this reservation towards the rate limit
407
491
  self.sem.try_acquire_owned().ok()
408
492
  }
409
493
 
410
494
  /// Queue new activity tasks for dispatch received from non-polling sources (ex: eager returns
411
495
  /// from WFT completion)
412
- pub(crate) fn add_tasks(&self, tasks: impl IntoIterator<Item = PermittedTqResp>) {
496
+ pub(crate) fn add_tasks(&self, tasks: impl IntoIterator<Item = TrackedPermittedTqResp>) {
413
497
  for t in tasks.into_iter() {
414
- self.tx.try_send(t).expect("Receive half cannot be dropped");
498
+ // Technically we should be reporting `activity_task_received` here, but for simplicity
499
+ // and time insensitivity, that metric is tracked in `about_to_issue_task`.
500
+ self.tx.send(t).expect("Receive half cannot be dropped");
415
501
  }
416
502
  }
417
503
  }
418
504
 
505
+ #[derive(Debug)]
419
506
  pub(crate) struct PermittedTqResp {
420
507
  pub permit: OwnedMeteredSemPermit,
421
508
  pub resp: PollActivityTaskQueueResponse,
422
509
  }
423
510
 
511
+ #[derive(Debug)]
512
+ pub(crate) struct TrackedPermittedTqResp {
513
+ pub permit: TrackedOwnedMeteredSemPermit,
514
+ pub resp: PollActivityTaskQueueResponse,
515
+ }
516
+
424
517
  #[cfg(test)]
425
518
  mod tests {
426
519
  use super::*;
@@ -449,13 +542,13 @@ mod tests {
449
542
  Some(2.0),
450
543
  poller,
451
544
  Arc::new(mock_manual_workflow_client()),
452
- MetricsContext::default(),
545
+ MetricsContext::no_op(),
453
546
  Duration::from_secs(1),
454
547
  Duration::from_secs(1),
455
548
  );
456
549
  let start = Instant::now();
457
- atm.poll().await.unwrap().unwrap();
458
- atm.poll().await.unwrap().unwrap();
550
+ atm.poll().await.unwrap();
551
+ atm.poll().await.unwrap();
459
552
  // At least half a second will have elapsed since we only allow 2 tasks per second.
460
553
  // With no ratelimit, even on a slow CI server with lots of load, this would typically take
461
554
  // low single digit ms or less.
@@ -1,22 +1,40 @@
1
1
  use super::*;
2
2
  use futures::Future;
3
3
 
4
+ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
5
+ signal_and_query_header: true,
6
+ internal_error_differentiation: true,
7
+ activity_failure_include_heartbeat: true,
8
+ supports_schedules: true,
9
+ encoded_failure_attributes: true,
10
+ build_id_based_versioning: true,
11
+ upsert_memo: true,
12
+ eager_workflow_start: true,
13
+ sdk_metadata: true,
14
+ };
15
+
4
16
  #[cfg(test)]
5
17
  /// Create a mock client primed with basic necessary expectations
6
18
  pub(crate) fn mock_workflow_client() -> MockWorkerClient {
7
- MockWorkerClient::new()
19
+ let mut r = MockWorkerClient::new();
20
+ r.expect_capabilities()
21
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
22
+ r
8
23
  }
9
24
 
10
25
  /// Create a mock manual client primed with basic necessary expectations
11
26
  pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
12
- MockManualWorkerClient::new()
27
+ let mut r = MockManualWorkerClient::new();
28
+ r.expect_capabilities()
29
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
30
+ r
13
31
  }
14
32
 
15
33
  // Need a version of the mock that can return futures so we can return potentially pending
16
34
  // results. This is really annoying b/c of the async trait stuff. Need
17
35
  // https://github.com/asomers/mockall/issues/189 to be fixed for it to go away.
18
36
  mockall::mock! {
19
- pub ManualWorkerClient {}
37
+ pub(crate) ManualWorkerClient {}
20
38
  #[allow(unused)]
21
39
  impl WorkerClient for ManualWorkerClient {
22
40
  fn poll_workflow_task<'a, 'b>(&'a self, task_queue: String, is_sticky: bool)
@@ -83,5 +101,7 @@ mockall::mock! {
83
101
  query_result: QueryResult,
84
102
  ) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
85
103
  where 'a: 'b, Self: 'b;
104
+
105
+ fn capabilities(&self) -> Option<&'static get_system_info_response::Capabilities>;
86
106
  }
87
107
  }