@temporalio/core-bridge 1.13.2 → 1.14.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 (388) hide show
  1. package/Cargo.lock +135 -100
  2. package/Cargo.toml +3 -2
  3. package/{sdk-core/fsm/rustfsm_trait/LICENSE.txt → LICENSE} +5 -5
  4. package/README.md +0 -1
  5. package/bridge-macros/Cargo.toml +16 -0
  6. package/bridge-macros/src/derive_js_function.rs +126 -0
  7. package/bridge-macros/src/derive_tryfromjs.rs +138 -0
  8. package/bridge-macros/src/derive_tryintojs.rs +151 -0
  9. package/bridge-macros/src/lib.rs +42 -0
  10. package/lib/native.d.ts +13 -6
  11. package/package.json +6 -5
  12. package/releases/aarch64-apple-darwin/index.node +0 -0
  13. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  14. package/releases/x86_64-apple-darwin/index.node +0 -0
  15. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  16. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  17. package/sdk-core/.cargo/config.toml +9 -3
  18. package/sdk-core/.github/workflows/per-pr.yml +42 -5
  19. package/sdk-core/AGENTS.md +17 -19
  20. package/sdk-core/Cargo.toml +6 -7
  21. package/sdk-core/README.md +12 -15
  22. package/sdk-core/arch_docs/diagrams/deps.svg +102 -0
  23. package/sdk-core/{client → crates/client}/Cargo.toml +7 -9
  24. package/sdk-core/{client → crates/client}/src/lib.rs +110 -159
  25. package/sdk-core/{client → crates/client}/src/metrics.rs +1 -1
  26. package/sdk-core/{client → crates/client}/src/raw.rs +73 -44
  27. package/sdk-core/crates/client/src/request_extensions.rs +40 -0
  28. package/sdk-core/{client → crates/client}/src/retry.rs +24 -17
  29. package/sdk-core/crates/client/src/worker/mod.rs +1468 -0
  30. package/sdk-core/{client → crates/client}/src/workflow_handle/mod.rs +4 -4
  31. package/sdk-core/{core-api → crates/common}/Cargo.toml +17 -8
  32. package/sdk-core/crates/common/protos/api_cloud_upstream/VERSION +1 -0
  33. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/account/v1/message.proto +18 -0
  34. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +38 -11
  35. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +21 -4
  36. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +6 -6
  37. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/sink/v1/message.proto +22 -0
  38. package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/create-release.yml +5 -0
  39. package/sdk-core/{core-api → crates/common}/src/envconfig.rs +2 -2
  40. package/sdk-core/{core-api → crates/common}/src/errors.rs +8 -1
  41. package/sdk-core/{fsm/rustfsm_trait/src/lib.rs → crates/common/src/fsm_trait.rs} +1 -27
  42. package/sdk-core/{core-api → crates/common}/src/lib.rs +24 -9
  43. package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/canned_histories.rs +1 -1
  44. package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/history_builder.rs +1 -1
  45. package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/history_info.rs +2 -2
  46. package/sdk-core/{sdk-core-protos/src/lib.rs → crates/common/src/protos/mod.rs} +18 -17
  47. package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/test_utils.rs +1 -1
  48. package/sdk-core/{core-api → crates/common}/src/telemetry/metrics.rs +447 -50
  49. package/sdk-core/{core-api → crates/common}/src/telemetry.rs +3 -1
  50. package/sdk-core/{core-api → crates/common}/src/worker.rs +112 -18
  51. package/sdk-core/crates/common/tests/worker_task_types_test.rs +129 -0
  52. package/sdk-core/crates/macros/Cargo.toml +23 -0
  53. package/sdk-core/{fsm/rustfsm_procmacro → crates/macros}/src/lib.rs +6 -11
  54. package/sdk-core/{sdk → crates/sdk}/Cargo.toml +10 -10
  55. package/sdk-core/{sdk → crates/sdk}/src/activity_context.rs +8 -6
  56. package/sdk-core/{sdk → crates/sdk}/src/interceptors.rs +1 -1
  57. package/sdk-core/{sdk → crates/sdk}/src/lib.rs +42 -37
  58. package/sdk-core/{sdk → crates/sdk}/src/workflow_context/options.rs +2 -2
  59. package/sdk-core/{sdk → crates/sdk}/src/workflow_context.rs +21 -19
  60. package/sdk-core/{sdk → crates/sdk}/src/workflow_future.rs +1 -1
  61. package/sdk-core/{core → crates/sdk-core}/Cargo.toml +32 -25
  62. package/sdk-core/{tests → crates/sdk-core/benches}/workflow_replay_bench.rs +10 -10
  63. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +32 -0
  64. package/sdk-core/crates/sdk-core/machine_coverage/CancelExternalMachine_Coverage.puml +9 -0
  65. package/sdk-core/crates/sdk-core/machine_coverage/CancelWorkflowMachine_Coverage.puml +6 -0
  66. package/sdk-core/crates/sdk-core/machine_coverage/ChildWorkflowMachine_Coverage.puml +27 -0
  67. package/sdk-core/crates/sdk-core/machine_coverage/CompleteWorkflowMachine_Coverage.puml +6 -0
  68. package/sdk-core/crates/sdk-core/machine_coverage/ContinueAsNewWorkflowMachine_Coverage.puml +6 -0
  69. package/sdk-core/crates/sdk-core/machine_coverage/FailWorkflowMachine_Coverage.puml +6 -0
  70. package/sdk-core/crates/sdk-core/machine_coverage/LocalActivityMachine_Coverage.puml +23 -0
  71. package/sdk-core/crates/sdk-core/machine_coverage/ModifyWorkflowPropertiesMachine_Coverage.puml +5 -0
  72. package/sdk-core/crates/sdk-core/machine_coverage/PatchMachine_Coverage.puml +8 -0
  73. package/sdk-core/crates/sdk-core/machine_coverage/SignalExternalMachine_Coverage.puml +12 -0
  74. package/sdk-core/crates/sdk-core/machine_coverage/TimerMachine_Coverage.puml +13 -0
  75. package/sdk-core/crates/sdk-core/machine_coverage/UpdateMachine_Coverage.puml +19 -0
  76. package/sdk-core/crates/sdk-core/machine_coverage/UpsertSearchAttributesMachine_Coverage.puml +5 -0
  77. package/sdk-core/crates/sdk-core/machine_coverage/WorkflowTaskMachine_Coverage.puml +11 -0
  78. package/sdk-core/{core → crates/sdk-core}/src/abstractions.rs +62 -6
  79. package/sdk-core/crates/sdk-core/src/antithesis.rs +60 -0
  80. package/sdk-core/{core → crates/sdk-core}/src/core_tests/activity_tasks.rs +36 -31
  81. package/sdk-core/{core → crates/sdk-core}/src/core_tests/mod.rs +12 -9
  82. package/sdk-core/{core → crates/sdk-core}/src/core_tests/queries.rs +24 -21
  83. package/sdk-core/{core → crates/sdk-core}/src/core_tests/replay_flag.rs +10 -8
  84. package/sdk-core/{core → crates/sdk-core}/src/core_tests/updates.rs +17 -15
  85. package/sdk-core/{core → crates/sdk-core}/src/core_tests/workers.rs +242 -17
  86. package/sdk-core/{core → crates/sdk-core}/src/core_tests/workflow_cancels.rs +1 -1
  87. package/sdk-core/{core → crates/sdk-core}/src/core_tests/workflow_tasks.rs +126 -39
  88. package/sdk-core/{core → crates/sdk-core}/src/debug_client.rs +1 -1
  89. package/sdk-core/{core → crates/sdk-core}/src/ephemeral_server/mod.rs +3 -3
  90. package/sdk-core/crates/sdk-core/src/histfetch.rs +33 -0
  91. package/sdk-core/{core → crates/sdk-core}/src/internal_flags.rs +4 -3
  92. package/sdk-core/{core → crates/sdk-core}/src/lib.rs +85 -43
  93. package/sdk-core/{core → crates/sdk-core}/src/pollers/mod.rs +8 -6
  94. package/sdk-core/{core → crates/sdk-core}/src/pollers/poll_buffer.rs +51 -16
  95. package/sdk-core/{core → crates/sdk-core}/src/protosext/mod.rs +1 -1
  96. package/sdk-core/{core → crates/sdk-core}/src/protosext/protocol_messages.rs +1 -1
  97. package/sdk-core/{core → crates/sdk-core}/src/replay/mod.rs +14 -11
  98. package/sdk-core/{core → crates/sdk-core}/src/retry_logic.rs +19 -1
  99. package/sdk-core/{core → crates/sdk-core}/src/telemetry/log_export.rs +2 -2
  100. package/sdk-core/{core → crates/sdk-core}/src/telemetry/metrics.rs +80 -34
  101. package/sdk-core/{core → crates/sdk-core}/src/telemetry/mod.rs +4 -4
  102. package/sdk-core/{core → crates/sdk-core}/src/telemetry/otel.rs +1 -1
  103. package/sdk-core/{core → crates/sdk-core}/src/telemetry/prometheus_meter.rs +13 -13
  104. package/sdk-core/{core → crates/sdk-core}/src/telemetry/prometheus_server.rs +2 -2
  105. package/sdk-core/{core → crates/sdk-core}/src/test_help/integ_helpers.rs +127 -40
  106. package/sdk-core/{core → crates/sdk-core}/src/test_help/unit_helpers.rs +13 -11
  107. package/sdk-core/{core → crates/sdk-core}/src/worker/activities/activity_heartbeat_manager.rs +2 -2
  108. package/sdk-core/{core → crates/sdk-core}/src/worker/activities/local_activities.rs +14 -12
  109. package/sdk-core/{core → crates/sdk-core}/src/worker/activities.rs +21 -12
  110. package/sdk-core/{core → crates/sdk-core}/src/worker/client/mocks.rs +25 -12
  111. package/sdk-core/{core → crates/sdk-core}/src/worker/client.rs +164 -71
  112. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +246 -0
  113. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1462 -0
  114. package/sdk-core/{core → crates/sdk-core}/src/worker/nexus.rs +15 -14
  115. package/sdk-core/{core → crates/sdk-core}/src/worker/slot_provider.rs +12 -13
  116. package/sdk-core/{core → crates/sdk-core}/src/worker/tuner/fixed_size.rs +5 -1
  117. package/sdk-core/{core → crates/sdk-core}/src/worker/tuner/resource_based.rs +453 -57
  118. package/sdk-core/{core → crates/sdk-core}/src/worker/tuner.rs +21 -4
  119. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/driven_workflow.rs +1 -1
  120. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/history_update.rs +2 -2
  121. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/activity_state_machine.rs +147 -37
  122. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/cancel_external_state_machine.rs +8 -9
  123. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/cancel_nexus_op_state_machine.rs +14 -12
  124. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
  125. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/child_workflow_state_machine.rs +31 -37
  126. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -8
  127. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
  128. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/fail_workflow_state_machine.rs +6 -7
  129. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/local_activity_state_machine.rs +33 -30
  130. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/mod.rs +22 -17
  131. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +4 -3
  132. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/nexus_operation_state_machine.rs +20 -22
  133. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/patch_state_machine.rs +12 -11
  134. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/signal_external_state_machine.rs +9 -12
  135. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/timer_state_machine.rs +26 -13
  136. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/transition_coverage.rs +1 -2
  137. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/update_state_machine.rs +19 -13
  138. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +20 -18
  139. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/workflow_machines/local_acts.rs +1 -1
  140. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/workflow_machines.rs +61 -70
  141. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -15
  142. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/managed_run.rs +55 -37
  143. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/mod.rs +166 -60
  144. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/run_cache.rs +10 -7
  145. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/wft_extraction.rs +4 -2
  146. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/wft_poller.rs +15 -4
  147. package/sdk-core/{core → crates/sdk-core}/src/worker/workflow/workflow_stream.rs +30 -16
  148. package/sdk-core/{tests → crates/sdk-core/tests}/c_bridge_smoke_test.c +1 -1
  149. package/sdk-core/{tests → crates/sdk-core/tests}/cloud_tests.rs +1 -1
  150. package/sdk-core/crates/sdk-core/tests/common/fake_grpc_server.rs +106 -0
  151. package/sdk-core/{tests → crates/sdk-core/tests}/common/http_proxy.rs +1 -1
  152. package/sdk-core/{tests → crates/sdk-core/tests}/common/mod.rs +93 -74
  153. package/sdk-core/{tests → crates/sdk-core/tests}/common/workflows.rs +4 -3
  154. package/sdk-core/crates/sdk-core/tests/fsm_procmacro.rs +6 -0
  155. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/dupe_transitions_fail.rs +1 -3
  156. package/sdk-core/crates/sdk-core/tests/fsm_trybuild/dupe_transitions_fail.stderr +12 -0
  157. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/dynamic_dest_pass.rs +2 -4
  158. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/forgot_name_fail.rs +1 -3
  159. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/forgot_name_fail.stderr +4 -4
  160. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/handler_arg_pass.rs +2 -4
  161. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/handler_pass.rs +2 -4
  162. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/medium_complex_pass.rs +2 -4
  163. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/no_handle_conversions_require_into_fail.rs +2 -4
  164. package/sdk-core/crates/sdk-core/tests/fsm_trybuild/no_handle_conversions_require_into_fail.stderr +15 -0
  165. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/simple_pass.rs +2 -4
  166. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/struct_event_variant_fail.rs +1 -3
  167. package/sdk-core/crates/sdk-core/tests/fsm_trybuild/struct_event_variant_fail.stderr +5 -0
  168. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/tuple_more_item_event_variant_fail.rs +1 -3
  169. package/sdk-core/crates/sdk-core/tests/fsm_trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  170. package/sdk-core/{fsm/rustfsm_procmacro/tests/trybuild → crates/sdk-core/tests/fsm_trybuild}/tuple_zero_item_event_variant_fail.rs +1 -3
  171. package/sdk-core/crates/sdk-core/tests/fsm_trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  172. package/sdk-core/{tests → crates/sdk-core/tests}/global_metric_tests.rs +14 -15
  173. package/sdk-core/{tests → crates/sdk-core/tests/heavy_tests}/fuzzy_workflow.rs +3 -3
  174. package/sdk-core/{tests → crates/sdk-core/tests}/heavy_tests.rs +19 -12
  175. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/activity_functions.rs +1 -1
  176. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/client_tests.rs +16 -111
  177. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/ephemeral_server_tests.rs +5 -6
  178. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/heartbeat_tests.rs +23 -19
  179. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/metrics_tests.rs +134 -60
  180. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/pagination_tests.rs +4 -4
  181. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/polling_tests.rs +37 -36
  182. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/queries_tests.rs +12 -10
  183. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/update_tests.rs +41 -29
  184. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/visibility_tests.rs +24 -19
  185. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +1061 -0
  186. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/worker_tests.rs +113 -51
  187. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/worker_versioning_tests.rs +19 -17
  188. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/activities.rs +35 -30
  189. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/appdata_propagation.rs +3 -3
  190. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/cancel_external.rs +14 -9
  191. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/cancel_wf.rs +13 -8
  192. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/child_workflows.rs +48 -35
  193. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/continue_as_new.rs +14 -9
  194. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/determinism.rs +24 -15
  195. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/eager.rs +9 -4
  196. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/local_activities.rs +47 -47
  197. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/modify_wf_properties.rs +16 -11
  198. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/nexus.rs +51 -23
  199. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/patches.rs +22 -10
  200. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/replay.rs +19 -17
  201. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/resets.rs +14 -5
  202. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/signals.rs +24 -15
  203. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/stickyness.rs +8 -6
  204. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/timers.rs +28 -18
  205. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/upsert_search_attrs.rs +18 -13
  206. package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests.rs +46 -41
  207. package/sdk-core/{tests → crates/sdk-core/tests}/main.rs +15 -9
  208. package/sdk-core/{tests → crates/sdk-core/tests}/manual_tests.rs +20 -14
  209. package/sdk-core/{tests → crates/sdk-core/tests}/runner.rs +2 -2
  210. package/sdk-core/{tests → crates/sdk-core/tests}/shared_tests/mod.rs +10 -5
  211. package/sdk-core/{tests → crates/sdk-core/tests}/shared_tests/priority.rs +5 -5
  212. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/Cargo.toml +13 -10
  213. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/include/temporal-sdk-core-c-bridge.h +32 -23
  214. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/client.rs +55 -32
  215. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/envconfig.rs +1 -1
  216. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/lib.rs +1 -1
  217. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/metric.rs +1 -1
  218. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/runtime.rs +24 -9
  219. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/testing.rs +1 -1
  220. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/tests/context.rs +11 -10
  221. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/tests/mod.rs +7 -7
  222. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/tests/utils.rs +3 -4
  223. package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/worker.rs +111 -58
  224. package/sdk-core/docker-cgroup-tests.sh +24 -0
  225. package/sdk-core/{docker → etc/docker}/docker-compose-ci.yaml +9 -9
  226. package/sdk-core/{docker → etc/docker}/docker-compose-telem.yaml +11 -11
  227. package/sdk-core/{docker → etc/docker}/docker-compose.yaml +8 -8
  228. package/sdk-core/{integ-with-otel.sh → etc/integ-with-otel.sh} +1 -1
  229. package/sdk-core/etc/regen-depgraph.sh +2 -2
  230. package/src/client.rs +24 -33
  231. package/src/helpers/try_from_js.rs +1 -1
  232. package/src/logs.rs +1 -1
  233. package/src/metrics.rs +3 -3
  234. package/src/runtime.rs +42 -28
  235. package/src/testing.rs +3 -3
  236. package/src/worker.rs +70 -36
  237. package/ts/native.ts +13 -6
  238. package/LICENSE.md +0 -23
  239. package/sdk-core/client/src/worker_registry/mod.rs +0 -282
  240. package/sdk-core/core/src/worker/heartbeat.rs +0 -230
  241. package/sdk-core/core/src/worker/mod.rs +0 -990
  242. package/sdk-core/etc/deps.svg +0 -162
  243. package/sdk-core/fsm/Cargo.toml +0 -21
  244. package/sdk-core/fsm/README.md +0 -3
  245. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +0 -27
  246. package/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +0 -21
  247. package/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +0 -8
  248. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +0 -12
  249. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +0 -15
  250. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +0 -5
  251. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +0 -5
  252. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +0 -5
  253. package/sdk-core/fsm/rustfsm_trait/Cargo.toml +0 -14
  254. package/sdk-core/fsm/src/lib.rs +0 -2
  255. package/sdk-core/sdk-core-protos/Cargo.toml +0 -37
  256. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +0 -1
  257. /package/sdk-core/{client → crates/client}/src/callback_based.rs +0 -0
  258. /package/sdk-core/{client → crates/client}/src/proxy.rs +0 -0
  259. /package/sdk-core/{client → crates/client}/src/replaceable.rs +0 -0
  260. /package/sdk-core/{sdk-core-protos → crates/common}/build.rs +0 -0
  261. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/.github/workflows/build.yaml +0 -0
  262. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/.github/workflows/push-to-buf.yml +0 -0
  263. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/CODEOWNERS +0 -0
  264. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/LICENSE +0 -0
  265. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/Makefile +0 -0
  266. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/README.md +0 -0
  267. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/buf.gen.yaml +0 -0
  268. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/buf.lock +0 -0
  269. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/buf.yaml +0 -0
  270. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/connectivityrule/v1/message.proto +0 -0
  271. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +0 -0
  272. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +0 -0
  273. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/nexus/v1/message.proto +0 -0
  274. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +0 -0
  275. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/resource/v1/message.proto +0 -0
  276. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_cloud_upstream/temporal/api/cloud/usage/v1/message.proto +0 -0
  277. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/CODEOWNERS +0 -0
  278. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  279. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/ci.yml +0 -0
  280. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/publish-docs.yml +0 -0
  281. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/push-to-buf.yml +0 -0
  282. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/trigger-api-go-delete-release.yml +0 -0
  283. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/trigger-api-go-publish-release.yml +0 -0
  284. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  285. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/LICENSE +0 -0
  286. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/Makefile +0 -0
  287. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/README.md +0 -0
  288. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/api-linter.yaml +0 -0
  289. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/buf.gen.yaml +0 -0
  290. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/buf.lock +0 -0
  291. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/buf.yaml +0 -0
  292. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/api/annotations.proto +0 -0
  293. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/api/http.proto +0 -0
  294. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/any.proto +0 -0
  295. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/descriptor.proto +0 -0
  296. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/duration.proto +0 -0
  297. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/empty.proto +0 -0
  298. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/struct.proto +0 -0
  299. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/timestamp.proto +0 -0
  300. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/google/protobuf/wrappers.proto +0 -0
  301. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/openapi/openapiv2.json +0 -0
  302. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/openapi/openapiv3.yaml +0 -0
  303. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/openapi/payload_description.txt +0 -0
  304. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/activity/v1/message.proto +0 -0
  305. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/batch/v1/message.proto +0 -0
  306. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/command/v1/message.proto +0 -0
  307. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/common/v1/message.proto +0 -0
  308. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/deployment/v1/message.proto +0 -0
  309. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  310. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  311. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  312. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/deployment.proto +0 -0
  313. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  314. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +0 -0
  315. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  316. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/nexus.proto +0 -0
  317. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  318. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/reset.proto +0 -0
  319. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  320. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  321. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  322. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  323. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  324. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/export/v1/message.proto +0 -0
  325. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  326. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/filter/v1/message.proto +0 -0
  327. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/history/v1/message.proto +0 -0
  328. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/namespace/v1/message.proto +0 -0
  329. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/nexus/v1/message.proto +0 -0
  330. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +0 -0
  331. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +0 -0
  332. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  333. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/query/v1/message.proto +0 -0
  334. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/replication/v1/message.proto +0 -0
  335. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/rules/v1/message.proto +0 -0
  336. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/schedule/v1/message.proto +0 -0
  337. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +0 -0
  338. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +0 -0
  339. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +0 -0
  340. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/sdk/v1/worker_config.proto +0 -0
  341. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +0 -0
  342. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +0 -0
  343. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/update/v1/message.proto +0 -0
  344. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/version/v1/message.proto +0 -0
  345. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/worker/v1/message.proto +0 -0
  346. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/workflow/v1/message.proto +0 -0
  347. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +0 -0
  348. /package/sdk-core/{sdk-core-protos → crates/common}/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +0 -0
  349. /package/sdk-core/{sdk-core-protos → crates/common}/protos/google/rpc/status.proto +0 -0
  350. /package/sdk-core/{sdk-core-protos → crates/common}/protos/grpc/health/v1/health.proto +0 -0
  351. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  352. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  353. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  354. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/common/common.proto +0 -0
  355. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/core_interface.proto +0 -0
  356. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  357. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/nexus/nexus.proto +0 -0
  358. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +0 -0
  359. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +0 -0
  360. /package/sdk-core/{sdk-core-protos → crates/common}/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  361. /package/sdk-core/{sdk-core-protos → crates/common}/protos/testsrv_upstream/Makefile +0 -0
  362. /package/sdk-core/{sdk-core-protos → crates/common}/protos/testsrv_upstream/api-linter.yaml +0 -0
  363. /package/sdk-core/{sdk-core-protos → crates/common}/protos/testsrv_upstream/buf.yaml +0 -0
  364. /package/sdk-core/{sdk-core-protos → crates/common}/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  365. /package/sdk-core/{sdk-core-protos → crates/common}/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +0 -0
  366. /package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/constants.rs +0 -0
  367. /package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/task_token.rs +0 -0
  368. /package/sdk-core/{sdk-core-protos/src → crates/common/src/protos}/utilities.rs +0 -0
  369. /package/sdk-core/{fsm → crates/macros}/LICENSE.txt +0 -0
  370. /package/sdk-core/{sdk → crates/sdk}/src/app_data.rs +0 -0
  371. /package/sdk-core/{core → crates/sdk-core}/src/abstractions/take_cell.rs +0 -0
  372. /package/sdk-core/{core → crates/sdk-core}/src/test_help/mod.rs +0 -0
  373. /package/sdk-core/{core → crates/sdk-core}/src/worker/slot_supplier.rs +0 -0
  374. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/ends_empty_wft_complete.bin +0 -0
  375. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/evict_while_la_running_no_interference-16_history.bin +0 -0
  376. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/evict_while_la_running_no_interference-23_history.bin +0 -0
  377. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/evict_while_la_running_no_interference-85_history.bin +0 -0
  378. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/fail_wf_task.bin +0 -0
  379. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/long_local_activity_with_update-0_history.bin +0 -0
  380. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/long_local_activity_with_update-1_history.bin +0 -0
  381. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/long_local_activity_with_update-2_history.bin +0 -0
  382. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/long_local_activity_with_update-3_history.bin +0 -0
  383. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/old_change_marker_format.bin +0 -0
  384. /package/sdk-core/{histories → crates/sdk-core/tests/histories}/timer_workflow_history.bin +0 -0
  385. /package/sdk-core/{tests → crates/sdk-core/tests}/integ_tests/workflow_tests/priority.rs +0 -0
  386. /package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/build.rs +0 -0
  387. /package/sdk-core/{core-c-bridge → crates/sdk-core-c-bridge}/src/random.rs +0 -0
  388. /package/sdk-core/{cargo-tokio-console.sh → etc/cargo-tokio-console.sh} +0 -0
@@ -0,0 +1,1462 @@
1
+ mod activities;
2
+ pub(crate) mod client;
3
+ pub(crate) mod heartbeat;
4
+ mod nexus;
5
+ mod slot_provider;
6
+ pub(crate) mod tuner;
7
+ mod workflow;
8
+
9
+ pub use temporalio_common::worker::{WorkerConfig, WorkerConfigBuilder};
10
+ pub use tuner::{
11
+ FixedSizeSlotSupplier, ResourceBasedSlotsOptions, ResourceBasedSlotsOptionsBuilder,
12
+ ResourceBasedTuner, ResourceSlotOptions, SlotSupplierOptions, TunerBuilder, TunerHolder,
13
+ TunerHolderOptions, TunerHolderOptionsBuilder,
14
+ };
15
+ pub(crate) use tuner::{RealSysInfo, SystemResourceInfo};
16
+
17
+ pub(crate) use activities::{
18
+ ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
19
+ NewLocalAct,
20
+ };
21
+ pub(crate) use wft_poller::WFTPollerShared;
22
+
23
+ #[allow(unreachable_pub)] // re-exported in test_help::integ_helpers
24
+ pub use workflow::LEGACY_QUERY_ID;
25
+
26
+ use crate::{
27
+ ActivityHeartbeat, CompleteActivityError, PollError, WorkerTrait,
28
+ abstractions::{MeteredPermitDealer, PermitDealerContextData, dbg_panic},
29
+ errors::CompleteWfError,
30
+ pollers::{ActivityTaskOptions, BoxedActPoller, BoxedNexusPoller, LongPollBuffer},
31
+ protosext::validate_activity_completion,
32
+ sealed::AnyClient,
33
+ telemetry::{
34
+ TelemetryInstance,
35
+ metrics::{
36
+ MetricsContext, activity_poller, activity_worker_type, local_activity_worker_type,
37
+ nexus_poller, nexus_worker_type, workflow_worker_type,
38
+ },
39
+ },
40
+ worker::{
41
+ activities::{LACompleteAction, LocalActivityManager, NextPendingLAAction},
42
+ client::WorkerClient,
43
+ heartbeat::{HeartbeatFn, SharedNamespaceWorker},
44
+ nexus::NexusManager,
45
+ workflow::{
46
+ LAReqSink, LocalResolution, WorkflowBasics, Workflows, wft_poller,
47
+ wft_poller::make_wft_poller,
48
+ },
49
+ },
50
+ };
51
+ use activities::WorkerActivityTasks;
52
+ use anyhow::bail;
53
+ use crossbeam_utils::atomic::AtomicCell;
54
+ use futures_util::{StreamExt, stream};
55
+ use gethostname::gethostname;
56
+ use parking_lot::RwLock;
57
+ use slot_provider::SlotProvider;
58
+ use std::{
59
+ convert::TryInto,
60
+ future,
61
+ sync::{
62
+ Arc,
63
+ atomic::{AtomicBool, AtomicU64, Ordering},
64
+ },
65
+ time::{Duration, SystemTime},
66
+ };
67
+ use temporalio_client::worker::{
68
+ ClientWorker, HeartbeatCallback, SharedNamespaceWorkerTrait, Slot as SlotTrait,
69
+ };
70
+ use temporalio_common::{
71
+ errors::{CompleteNexusError, WorkerValidationError},
72
+ protos::{
73
+ TaskToken,
74
+ coresdk::{
75
+ ActivityTaskCompletion,
76
+ activity_result::activity_execution_result,
77
+ activity_task::ActivityTask,
78
+ nexus::{NexusTask, NexusTaskCompletion, nexus_task_completion},
79
+ workflow_activation::{WorkflowActivation, remove_from_cache::EvictionReason},
80
+ workflow_completion::WorkflowActivationCompletion,
81
+ },
82
+ temporal::api::{
83
+ deployment,
84
+ enums::v1::{TaskQueueKind, WorkerStatus},
85
+ taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
86
+ worker::v1::{WorkerHeartbeat, WorkerHostInfo, WorkerPollerInfo, WorkerSlotsInfo},
87
+ },
88
+ },
89
+ telemetry::metrics::{TemporalMeter, WorkerHeartbeatMetrics},
90
+ worker::{
91
+ ActivitySlotKind, LocalActivitySlotKind, NexusSlotKind, PollerBehavior, SlotKind,
92
+ WorkerTaskTypes, WorkflowSlotKind,
93
+ },
94
+ };
95
+ use tokio::sync::{mpsc::unbounded_channel, watch};
96
+ use tokio_stream::wrappers::UnboundedReceiverStream;
97
+ use tokio_util::sync::CancellationToken;
98
+ use tracing::Subscriber;
99
+ use uuid::Uuid;
100
+ #[cfg(any(feature = "test-utilities", test))]
101
+ use {
102
+ crate::{
103
+ pollers::{BoxedPoller, MockPermittedPollBuffer},
104
+ protosext::ValidPollWFTQResponse,
105
+ },
106
+ futures_util::stream::BoxStream,
107
+ temporalio_common::protos::temporal::api::workflowservice::v1::{
108
+ PollActivityTaskQueueResponse, PollNexusTaskQueueResponse,
109
+ },
110
+ };
111
+
112
+ /// A worker polls on a certain task queue
113
+ pub struct Worker {
114
+ config: WorkerConfig,
115
+ client: Arc<dyn WorkerClient>,
116
+ /// Worker instance key, unique identifier for this worker
117
+ worker_instance_key: Uuid,
118
+ /// Manages all workflows and WFT processing. None if workflow polling is disabled
119
+ workflows: Option<Workflows>,
120
+ /// Manages activity tasks for this worker/task queue
121
+ at_task_mgr: Option<WorkerActivityTasks>,
122
+ /// Manages local activities. None if workflow polling is disabled (local activities require workflows)
123
+ local_act_mgr: Option<Arc<LocalActivityManager>>,
124
+ /// Manages Nexus tasks
125
+ nexus_mgr: Option<NexusManager>,
126
+ /// Has shutdown been called?
127
+ shutdown_token: CancellationToken,
128
+ /// Will be called at the end of each activation completion
129
+ #[allow(clippy::type_complexity)] // Sorry clippy, there's no simple way to re-use here.
130
+ post_activate_hook: Option<Box<dyn Fn(&Self, PostActivateHookData<'_>) + Send + Sync>>,
131
+ /// Set when non-local activities are complete and should stop being polled
132
+ non_local_activities_complete: Arc<AtomicBool>,
133
+ /// Set when local activities are complete and should stop being polled
134
+ local_activities_complete: Arc<AtomicBool>,
135
+ /// Used to track all permits have been released
136
+ all_permits_tracker: tokio::sync::Mutex<AllPermitsTracker>,
137
+ /// Used to track worker client
138
+ client_worker_registrator: Arc<ClientWorkerRegistrator>,
139
+ /// Status of the worker
140
+ status: Arc<RwLock<WorkerStatus>>,
141
+ }
142
+
143
+ struct AllPermitsTracker {
144
+ wft_permits: watch::Receiver<usize>,
145
+ act_permits: watch::Receiver<usize>,
146
+ la_permits: watch::Receiver<usize>,
147
+ }
148
+
149
+ impl AllPermitsTracker {
150
+ async fn all_done(&mut self) {
151
+ let _ = self.wft_permits.wait_for(|x| *x == 0).await;
152
+ let _ = self.act_permits.wait_for(|x| *x == 0).await;
153
+ let _ = self.la_permits.wait_for(|x| *x == 0).await;
154
+ }
155
+ }
156
+
157
+ #[derive(Clone)]
158
+ pub(crate) struct WorkerTelemetry {
159
+ temporal_metric_meter: Option<TemporalMeter>,
160
+ trace_subscriber: Option<Arc<dyn Subscriber + Send + Sync>>,
161
+ }
162
+
163
+ #[async_trait::async_trait]
164
+ impl WorkerTrait for Worker {
165
+ async fn validate(&self) -> Result<(), WorkerValidationError> {
166
+ self.verify_namespace_exists().await?;
167
+ Ok(())
168
+ }
169
+
170
+ async fn poll_workflow_activation(&self) -> Result<WorkflowActivation, PollError> {
171
+ self.next_workflow_activation().await
172
+ }
173
+
174
+ #[instrument(skip(self))]
175
+ async fn poll_activity_task(&self) -> Result<ActivityTask, PollError> {
176
+ loop {
177
+ match self.activity_poll().await.transpose() {
178
+ Some(r) => break r,
179
+ None => {
180
+ tokio::task::yield_now().await;
181
+ continue;
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ #[instrument(skip(self))]
188
+ async fn poll_nexus_task(&self) -> Result<NexusTask, PollError> {
189
+ match &self.nexus_mgr {
190
+ Some(mgr) => mgr.next_nexus_task().await,
191
+ None => Err(PollError::ShutDown),
192
+ }
193
+ }
194
+
195
+ async fn complete_workflow_activation(
196
+ &self,
197
+ completion: WorkflowActivationCompletion,
198
+ ) -> Result<(), CompleteWfError> {
199
+ self.complete_workflow_activation(completion).await
200
+ }
201
+
202
+ async fn complete_activity_task(
203
+ &self,
204
+ completion: ActivityTaskCompletion,
205
+ ) -> Result<(), CompleteActivityError> {
206
+ let task_token = TaskToken(completion.task_token);
207
+ let status = if let Some(s) = completion.result.and_then(|r| r.status) {
208
+ s
209
+ } else {
210
+ return Err(CompleteActivityError::MalformedActivityCompletion {
211
+ reason: "Activity completion had empty result/status field".to_owned(),
212
+ completion: None,
213
+ });
214
+ };
215
+
216
+ self.complete_activity(task_token, status).await
217
+ }
218
+
219
+ async fn complete_nexus_task(
220
+ &self,
221
+ completion: NexusTaskCompletion,
222
+ ) -> Result<(), CompleteNexusError> {
223
+ let status = if let Some(s) = completion.status {
224
+ s
225
+ } else {
226
+ return Err(CompleteNexusError::MalformedNexusCompletion {
227
+ reason: "Nexus completion had empty status field".to_owned(),
228
+ });
229
+ };
230
+
231
+ self.complete_nexus_task(TaskToken(completion.task_token), status)
232
+ .await
233
+ }
234
+
235
+ fn record_activity_heartbeat(&self, details: ActivityHeartbeat) {
236
+ self.record_heartbeat(details);
237
+ }
238
+
239
+ fn request_workflow_eviction(&self, run_id: &str) {
240
+ self.request_wf_eviction(
241
+ run_id,
242
+ "Eviction explicitly requested by lang",
243
+ EvictionReason::LangRequested,
244
+ );
245
+ }
246
+
247
+ fn get_config(&self) -> &WorkerConfig {
248
+ &self.config
249
+ }
250
+
251
+ /// Begins the shutdown process, tells pollers they should stop. Is idempotent.
252
+ fn initiate_shutdown(&self) {
253
+ if !self.shutdown_token.is_cancelled() {
254
+ info!(
255
+ task_queue=%self.config.task_queue,
256
+ namespace=%self.config.namespace,
257
+ "Initiated shutdown",
258
+ );
259
+ }
260
+ self.shutdown_token.cancel();
261
+ {
262
+ *self.status.write() = WorkerStatus::ShuttingDown;
263
+ }
264
+ // First, unregister worker from the client
265
+ if !self.client_worker_registrator.shared_namespace_worker {
266
+ let _res = self
267
+ .client
268
+ .workers()
269
+ .unregister_worker(self.worker_instance_key);
270
+ }
271
+
272
+ // Push a BumpStream message to the workflow activation queue. This ensures that
273
+ // any pending workflow activation polls will resolve, even if there are no other inputs.
274
+ if let Some(workflows) = &self.workflows {
275
+ workflows.bump_stream();
276
+ }
277
+
278
+ // Second, we want to stop polling of both activity and workflow tasks
279
+ if let Some(atm) = self.at_task_mgr.as_ref() {
280
+ atm.initiate_shutdown();
281
+ }
282
+ // Let the manager know that shutdown has been initiated to try to unblock the local
283
+ // activity poll in case this worker is an activity-only worker.
284
+ if let Some(la_mgr) = &self.local_act_mgr {
285
+ la_mgr.shutdown_initiated();
286
+
287
+ // If workflows have never been polled, immediately tell the local activity manager
288
+ // that workflows have shut down, so it can proceed with shutdown without waiting.
289
+ // This is particularly important for activity-only workers.
290
+ if self.workflows.as_ref().is_none_or(|w| !w.ever_polled()) {
291
+ la_mgr.workflows_have_shutdown();
292
+ }
293
+ }
294
+ }
295
+
296
+ async fn shutdown(&self) {
297
+ self.shutdown().await
298
+ }
299
+
300
+ async fn finalize_shutdown(self) {
301
+ self.finalize_shutdown().await
302
+ }
303
+
304
+ fn worker_instance_key(&self) -> Uuid {
305
+ self.worker_instance_key
306
+ }
307
+ }
308
+
309
+ impl Worker {
310
+ /// Creates a new [Worker] from a [WorkerClient] instance with real task pollers and optional
311
+ /// telemetry.
312
+ pub fn new(
313
+ config: WorkerConfig,
314
+ sticky_queue_name: Option<String>,
315
+ client: Arc<dyn WorkerClient>,
316
+ telem_instance: Option<&TelemetryInstance>,
317
+ worker_heartbeat_interval: Option<Duration>,
318
+ ) -> Result<Worker, anyhow::Error> {
319
+ info!(task_queue=%config.task_queue, namespace=%config.namespace, "Initializing worker");
320
+
321
+ let worker_telemetry = telem_instance.map(|telem| WorkerTelemetry {
322
+ temporal_metric_meter: telem.get_temporal_metric_meter(),
323
+ trace_subscriber: telem.trace_subscriber(),
324
+ });
325
+
326
+ Self::new_with_pollers(
327
+ config,
328
+ sticky_queue_name,
329
+ client,
330
+ TaskPollers::Real,
331
+ worker_telemetry,
332
+ worker_heartbeat_interval,
333
+ false,
334
+ )
335
+ }
336
+
337
+ /// Replace client.
338
+ ///
339
+ /// For eager workflow purposes, this new client will now apply to future eager start requests
340
+ /// and the older client will not. Note, if this registration fails, the worker heartbeat will
341
+ /// also not be registered.
342
+ ///
343
+ /// For worker heartbeat, this will remove an existing shared worker if it is the last worker of
344
+ /// the old client and create a new nexus worker if it's the first client of the namespace on
345
+ /// the new client.
346
+ pub fn replace_client<CT>(&self, new_client: CT) -> Result<(), anyhow::Error>
347
+ where
348
+ CT: Into<AnyClient>,
349
+ {
350
+ // Unregister worker from current client, register in new client at the end
351
+ let client_worker = self
352
+ .client
353
+ .workers()
354
+ .unregister_worker(self.worker_instance_key)?;
355
+
356
+ let new_worker_client = super::init_worker_client(
357
+ self.config.namespace.clone(),
358
+ self.config.client_identity_override.clone(),
359
+ new_client,
360
+ );
361
+
362
+ self.client.replace_client(new_worker_client);
363
+ *self.client_worker_registrator.client.write() = self.client.clone();
364
+ self.client
365
+ .workers()
366
+ .register_worker(client_worker, self.config.skip_client_worker_set_check)
367
+ }
368
+
369
+ #[cfg(test)]
370
+ pub(crate) fn new_test(config: WorkerConfig, client: impl WorkerClient + 'static) -> Self {
371
+ let sticky_queue_name = if config.max_cached_workflows > 0 {
372
+ Some(format!("sticky-{}", config.task_queue))
373
+ } else {
374
+ None
375
+ };
376
+ Self::new(config, sticky_queue_name, Arc::new(client), None, None).unwrap()
377
+ }
378
+
379
+ pub(crate) fn new_with_pollers(
380
+ config: WorkerConfig,
381
+ sticky_queue_name: Option<String>,
382
+ client: Arc<dyn WorkerClient>,
383
+ task_pollers: TaskPollers,
384
+ worker_telemetry: Option<WorkerTelemetry>,
385
+ worker_heartbeat_interval: Option<Duration>,
386
+ shared_namespace_worker: bool,
387
+ ) -> Result<Worker, anyhow::Error> {
388
+ let (metrics, meter) = if let Some(wt) = worker_telemetry.as_ref() {
389
+ (
390
+ MetricsContext::top_level_with_meter(
391
+ config.namespace.clone(),
392
+ config.task_queue.clone(),
393
+ wt.temporal_metric_meter.clone(),
394
+ ),
395
+ wt.temporal_metric_meter.clone(),
396
+ )
397
+ } else {
398
+ (MetricsContext::no_op(), None)
399
+ };
400
+
401
+ let mut sys_info = None;
402
+ let tuner = config.tuner.as_ref().cloned().unwrap_or_else(|| {
403
+ let mut tuner_builder = TunerBuilder::from_config(&config);
404
+ sys_info = tuner_builder.get_sys_info();
405
+ Arc::new(tuner_builder.build())
406
+ });
407
+ let sys_info = sys_info.unwrap_or_else(|| Arc::new(RealSysInfo::new()));
408
+
409
+ metrics.worker_registered();
410
+ let shutdown_token = CancellationToken::new();
411
+ let slot_context_data = Arc::new(PermitDealerContextData {
412
+ task_queue: config.task_queue.clone(),
413
+ worker_identity: client.identity(),
414
+ worker_deployment_version: config.computed_deployment_version(),
415
+ });
416
+ let wft_slots = MeteredPermitDealer::new(
417
+ tuner.workflow_task_slot_supplier(),
418
+ metrics.with_new_attrs([workflow_worker_type()]),
419
+ if config.max_cached_workflows > 0 {
420
+ // Since we always need to be able to poll the normal task queue as well as the
421
+ // sticky queue, we need a value of at least 2 here.
422
+ Some(std::cmp::max(2, config.max_cached_workflows))
423
+ } else {
424
+ None
425
+ },
426
+ slot_context_data.clone(),
427
+ meter.clone(),
428
+ );
429
+ let wft_permits = wft_slots.get_extant_count_rcv();
430
+ let act_slots = MeteredPermitDealer::new(
431
+ tuner.activity_task_slot_supplier(),
432
+ metrics.with_new_attrs([activity_worker_type()]),
433
+ None,
434
+ slot_context_data.clone(),
435
+ meter.clone(),
436
+ );
437
+ let act_permits = act_slots.get_extant_count_rcv();
438
+ let (external_wft_tx, external_wft_rx) = unbounded_channel();
439
+
440
+ let wf_last_suc_poll_time = Arc::new(AtomicCell::new(None));
441
+ let wf_sticky_last_suc_poll_time = Arc::new(AtomicCell::new(None));
442
+ let act_last_suc_poll_time = Arc::new(AtomicCell::new(None));
443
+ let nexus_last_suc_poll_time = Arc::new(AtomicCell::new(None));
444
+
445
+ let nexus_slots = MeteredPermitDealer::new(
446
+ tuner.nexus_task_slot_supplier(),
447
+ metrics.with_new_attrs([nexus_worker_type()]),
448
+ None,
449
+ slot_context_data.clone(),
450
+ meter.clone(),
451
+ );
452
+ let (wft_stream, act_poller, nexus_poller) = match task_pollers {
453
+ TaskPollers::Real => {
454
+ let wft_stream = if config.task_types.enable_workflows {
455
+ let stream = make_wft_poller(
456
+ &config,
457
+ &sticky_queue_name,
458
+ &client,
459
+ &metrics,
460
+ &shutdown_token,
461
+ &wft_slots,
462
+ wf_last_suc_poll_time.clone(),
463
+ wf_sticky_last_suc_poll_time.clone(),
464
+ )
465
+ .boxed();
466
+ let stream = if !client.is_mock() {
467
+ // Some replay tests combine a mock client with real pollers,
468
+ // and they don't need to use the external stream
469
+ stream::select(stream, UnboundedReceiverStream::new(external_wft_rx))
470
+ .left_stream()
471
+ } else {
472
+ stream.right_stream()
473
+ };
474
+ Some(stream)
475
+ } else {
476
+ None
477
+ };
478
+
479
+ let act_poll_buffer = if config.task_types.enable_remote_activities {
480
+ let act_metrics = metrics.with_new_attrs([activity_poller()]);
481
+ let ap = LongPollBuffer::new_activity_task(
482
+ client.clone(),
483
+ config.task_queue.clone(),
484
+ config.activity_task_poller_behavior,
485
+ act_slots.clone(),
486
+ shutdown_token.child_token(),
487
+ Some(move |np| act_metrics.record_num_pollers(np)),
488
+ ActivityTaskOptions {
489
+ max_worker_acts_per_second: config.max_worker_activities_per_second,
490
+ max_tps: config.max_task_queue_activities_per_second,
491
+ },
492
+ act_last_suc_poll_time.clone(),
493
+ );
494
+ Some(Box::from(ap) as BoxedActPoller)
495
+ } else {
496
+ None
497
+ };
498
+
499
+ let nexus_poll_buffer = if config.task_types.enable_nexus {
500
+ let np_metrics = metrics.with_new_attrs([nexus_poller()]);
501
+ Some(Box::new(LongPollBuffer::new_nexus_task(
502
+ client.clone(),
503
+ config.task_queue.clone(),
504
+ config.nexus_task_poller_behavior,
505
+ nexus_slots.clone(),
506
+ shutdown_token.child_token(),
507
+ Some(move |np| np_metrics.record_num_pollers(np)),
508
+ nexus_last_suc_poll_time.clone(),
509
+ shared_namespace_worker,
510
+ )) as BoxedNexusPoller)
511
+ } else {
512
+ None
513
+ };
514
+
515
+ #[cfg(any(feature = "test-utilities", test))]
516
+ let wft_stream = wft_stream.map(|s| s.left_stream());
517
+ (wft_stream, act_poll_buffer, nexus_poll_buffer)
518
+ }
519
+ #[cfg(any(feature = "test-utilities", test))]
520
+ TaskPollers::Mocked {
521
+ wft_stream,
522
+ act_poller,
523
+ nexus_poller,
524
+ } => {
525
+ let wft_stream = config
526
+ .task_types
527
+ .enable_workflows
528
+ .then_some(wft_stream)
529
+ .flatten();
530
+ let act_poller = config
531
+ .task_types
532
+ .enable_remote_activities
533
+ .then_some(act_poller)
534
+ .flatten();
535
+ let nexus_poller = config
536
+ .task_types
537
+ .enable_nexus
538
+ .then_some(nexus_poller)
539
+ .flatten();
540
+
541
+ let ap = act_poller
542
+ .map(|ap| MockPermittedPollBuffer::new(Arc::new(act_slots.clone()), ap));
543
+ let np = nexus_poller
544
+ .map(|np| MockPermittedPollBuffer::new(Arc::new(nexus_slots.clone()), np));
545
+ let wfs = wft_stream.map(|stream| {
546
+ let wft_semaphore = wft_slots.clone();
547
+ let wfs = stream.then(move |s| {
548
+ let wft_semaphore = wft_semaphore.clone();
549
+ async move {
550
+ let permit = wft_semaphore.acquire_owned().await;
551
+ s.map(|s| (s, permit))
552
+ }
553
+ });
554
+ wfs.right_stream()
555
+ });
556
+ (
557
+ wfs,
558
+ ap.map(|ap| Box::new(ap) as BoxedActPoller),
559
+ np.map(|np| Box::new(np) as BoxedNexusPoller),
560
+ )
561
+ }
562
+ };
563
+
564
+ let la_permit_dealer = MeteredPermitDealer::new(
565
+ tuner.local_activity_slot_supplier(),
566
+ metrics.with_new_attrs([local_activity_worker_type()]),
567
+ None,
568
+ slot_context_data.clone(),
569
+ meter.clone(),
570
+ );
571
+ let la_permits = la_permit_dealer.get_extant_count_rcv();
572
+
573
+ let (local_act_mgr, la_sink, hb_rx) = if config.task_types.enable_local_activities {
574
+ let (hb_tx, hb_rx) = unbounded_channel();
575
+ let local_act_mgr = Arc::new(LocalActivityManager::new(
576
+ config.namespace.clone(),
577
+ la_permit_dealer.clone(),
578
+ hb_tx,
579
+ metrics.clone(),
580
+ ));
581
+ let la_sink = LAReqSink::new(local_act_mgr.clone());
582
+ (Some(local_act_mgr), Some(la_sink), Some(hb_rx))
583
+ } else {
584
+ (None, None, None)
585
+ };
586
+
587
+ let at_task_mgr = act_poller.map(|ap| {
588
+ WorkerActivityTasks::new(
589
+ act_slots.clone(),
590
+ ap,
591
+ client.clone(),
592
+ metrics.clone(),
593
+ config.max_heartbeat_throttle_interval,
594
+ config.default_heartbeat_throttle_interval,
595
+ config.graceful_shutdown_period,
596
+ config.local_timeout_buffer_for_activities,
597
+ )
598
+ });
599
+ let poll_on_non_local_activities = at_task_mgr.is_some();
600
+ if !poll_on_non_local_activities && !shared_namespace_worker {
601
+ info!("Activity polling is disabled for this worker");
602
+ };
603
+
604
+ let nexus_mgr = nexus_poller.map(|poller| {
605
+ NexusManager::new(
606
+ poller,
607
+ metrics.clone(),
608
+ config.graceful_shutdown_period,
609
+ shutdown_token.child_token(),
610
+ )
611
+ });
612
+
613
+ let deployment_options = match &config.versioning_strategy {
614
+ temporalio_common::worker::WorkerVersioningStrategy::WorkerDeploymentBased(opts) => {
615
+ Some(opts.clone())
616
+ }
617
+ _ => None,
618
+ };
619
+ let provider = SlotProvider::new(
620
+ config.namespace.clone(),
621
+ config.task_queue.clone(),
622
+ wft_slots.clone(),
623
+ external_wft_tx,
624
+ deployment_options,
625
+ );
626
+ let worker_instance_key = Uuid::new_v4();
627
+ let worker_status = Arc::new(RwLock::new(WorkerStatus::Running));
628
+
629
+ let sdk_name_and_ver = client.sdk_name_and_version();
630
+ let worker_heartbeat = worker_heartbeat_interval.map(|hb_interval| {
631
+ let hb_metrics = HeartbeatMetrics {
632
+ in_mem_metrics: metrics.in_memory_meter(),
633
+ wft_slots: wft_slots.clone(),
634
+ act_slots,
635
+ nexus_slots,
636
+ la_slots: la_permit_dealer,
637
+ wf_last_suc_poll_time,
638
+ wf_sticky_last_suc_poll_time,
639
+ act_last_suc_poll_time,
640
+ nexus_last_suc_poll_time,
641
+ status: worker_status.clone(),
642
+ sys_info,
643
+ };
644
+ WorkerHeartbeatManager::new(
645
+ config.clone(),
646
+ worker_instance_key,
647
+ hb_interval,
648
+ worker_telemetry.clone(),
649
+ hb_metrics,
650
+ )
651
+ });
652
+
653
+ let client_worker_registrator = Arc::new(ClientWorkerRegistrator {
654
+ worker_instance_key,
655
+ slot_provider: provider,
656
+ heartbeat_manager: worker_heartbeat,
657
+ client: RwLock::new(client.clone()),
658
+ shared_namespace_worker,
659
+ task_types: config.task_types,
660
+ });
661
+
662
+ if !shared_namespace_worker {
663
+ client.workers().register_worker(
664
+ client_worker_registrator.clone(),
665
+ config.skip_client_worker_set_check,
666
+ )?;
667
+ }
668
+
669
+ Ok(Self {
670
+ worker_instance_key,
671
+ client: client.clone(),
672
+ workflows: wft_stream.map(|stream| {
673
+ Workflows::new(
674
+ WorkflowBasics {
675
+ worker_config: Arc::new(config.clone()),
676
+ shutdown_token: shutdown_token.child_token(),
677
+ metrics,
678
+ server_capabilities: client.capabilities().unwrap_or_default(),
679
+ sdk_name: sdk_name_and_ver.0,
680
+ sdk_version: sdk_name_and_ver.1,
681
+ default_versioning_behavior: config
682
+ .versioning_strategy
683
+ .default_versioning_behavior(),
684
+ },
685
+ sticky_queue_name.map(|sq| StickyExecutionAttributes {
686
+ worker_task_queue: Some(TaskQueue {
687
+ name: sq,
688
+ kind: TaskQueueKind::Sticky as i32,
689
+ normal_name: config.task_queue.clone(),
690
+ }),
691
+ schedule_to_start_timeout: Some(
692
+ config
693
+ .sticky_queue_schedule_to_start_timeout
694
+ .try_into()
695
+ .expect("timeout fits into proto"),
696
+ ),
697
+ }),
698
+ client,
699
+ wft_slots,
700
+ stream,
701
+ la_sink,
702
+ local_act_mgr.clone(),
703
+ hb_rx,
704
+ at_task_mgr.as_ref().and_then(|mgr| {
705
+ match config.max_task_queue_activities_per_second {
706
+ Some(persec) if persec > 0.0 => None,
707
+ _ => Some(mgr.get_handle_for_workflows()),
708
+ }
709
+ }),
710
+ worker_telemetry
711
+ .as_ref()
712
+ .and_then(|telem| telem.trace_subscriber.clone()),
713
+ )
714
+ }),
715
+ at_task_mgr,
716
+ local_act_mgr,
717
+ config,
718
+ shutdown_token,
719
+ post_activate_hook: None,
720
+ // Non-local activities are already complete if configured not to poll for them.
721
+ non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
722
+ local_activities_complete: Default::default(),
723
+ all_permits_tracker: tokio::sync::Mutex::new(AllPermitsTracker {
724
+ wft_permits,
725
+ act_permits,
726
+ la_permits,
727
+ }),
728
+ nexus_mgr,
729
+ client_worker_registrator,
730
+ status: worker_status,
731
+ })
732
+ }
733
+
734
+ /// Will shutdown the worker. Does not resolve until all outstanding workflow tasks have been
735
+ /// completed
736
+ async fn shutdown(&self) {
737
+ self.initiate_shutdown();
738
+ if let Some(workflows) = &self.workflows
739
+ && let Some(name) = workflows.get_sticky_queue_name()
740
+ {
741
+ let heartbeat = self
742
+ .client_worker_registrator
743
+ .heartbeat_manager
744
+ .as_ref()
745
+ .map(|hm| hm.heartbeat_callback.clone()());
746
+
747
+ // This is a best effort call and we can still shutdown the worker if it fails
748
+ match self.client.shutdown_worker(name, heartbeat).await {
749
+ Err(err)
750
+ if !matches!(
751
+ err.code(),
752
+ tonic::Code::Unimplemented | tonic::Code::Unavailable
753
+ ) =>
754
+ {
755
+ warn!(
756
+ "shutdown_worker rpc errored during worker shutdown: {:?}",
757
+ err
758
+ );
759
+ }
760
+ _ => {}
761
+ }
762
+ }
763
+ // We need to wait for all local activities to finish so no more workflow task heartbeats
764
+ // will be generated
765
+ if let Some(la_mgr) = &self.local_act_mgr {
766
+ la_mgr.wait_all_outstanding_tasks_finished().await;
767
+ }
768
+ // Wait for workflows to finish
769
+ if let Some(workflows) = &self.workflows {
770
+ workflows
771
+ .shutdown()
772
+ .await
773
+ .expect("Workflow processing terminates cleanly");
774
+ }
775
+ // Wait for activities to finish
776
+ if let Some(acts) = self.at_task_mgr.as_ref() {
777
+ acts.shutdown().await;
778
+ }
779
+ // Wait for nexus tasks to finish
780
+ if let Some(nexus) = &self.nexus_mgr {
781
+ nexus.shutdown().await;
782
+ }
783
+ // Wait for all permits to be released, but don't totally hang real-world shutdown.
784
+ tokio::select! {
785
+ _ = async { self.all_permits_tracker.lock().await.all_done().await } => {},
786
+ _ = tokio::time::sleep(Duration::from_secs(1)) => {
787
+ dbg_panic!("Waiting for all slot permits to release took too long!");
788
+ }
789
+ }
790
+ }
791
+
792
+ /// Finish shutting down by consuming the background pollers and freeing all resources
793
+ async fn finalize_shutdown(self) {
794
+ self.shutdown().await;
795
+ if let Some(b) = self.at_task_mgr {
796
+ b.shutdown().await;
797
+ }
798
+ }
799
+
800
+ pub(crate) fn shutdown_token(&self) -> CancellationToken {
801
+ self.shutdown_token.clone()
802
+ }
803
+
804
+ /// Returns number of currently cached workflows
805
+ pub async fn cached_workflows(&self) -> usize {
806
+ match &self.workflows {
807
+ Some(workflows) => workflows
808
+ .get_state_info()
809
+ .await
810
+ .map(|r| r.cached_workflows)
811
+ .unwrap_or_default(),
812
+ None => 0,
813
+ }
814
+ }
815
+
816
+ /// Returns number of currently outstanding workflow tasks
817
+ #[cfg(test)]
818
+ pub(crate) async fn outstanding_workflow_tasks(&self) -> usize {
819
+ match &self.workflows {
820
+ Some(workflows) => workflows
821
+ .get_state_info()
822
+ .await
823
+ .map(|r| r.outstanding_wft)
824
+ .unwrap_or_default(),
825
+ None => 0,
826
+ }
827
+ }
828
+
829
+ #[allow(unused)]
830
+ pub(crate) fn available_wft_permits(&self) -> Option<usize> {
831
+ self.workflows
832
+ .as_ref()
833
+ .and_then(|w| w.available_wft_permits())
834
+ }
835
+ #[cfg(test)]
836
+ pub(crate) fn unused_wft_permits(&self) -> Option<usize> {
837
+ self.workflows.as_ref().and_then(|w| w.unused_wft_permits())
838
+ }
839
+
840
+ /// Get new activity tasks (may be local or nonlocal). Local activities are returned first
841
+ /// before polling the server if there are any.
842
+ ///
843
+ /// Returns `Ok(None)` in the event of a poll timeout or if the polling loop should otherwise
844
+ /// be restarted
845
+ async fn activity_poll(&self) -> Result<Option<ActivityTask>, PollError> {
846
+ let local_activities_complete = self.local_activities_complete.load(Ordering::Relaxed);
847
+ let non_local_activities_complete =
848
+ self.non_local_activities_complete.load(Ordering::Relaxed);
849
+ if local_activities_complete && non_local_activities_complete {
850
+ return Err(PollError::ShutDown);
851
+ }
852
+ let act_mgr_poll = async {
853
+ if non_local_activities_complete {
854
+ future::pending::<()>().await;
855
+ unreachable!()
856
+ }
857
+ if self.config.task_types.enable_remote_activities {
858
+ if let Some(ref act_mgr) = self.at_task_mgr {
859
+ let res = act_mgr.poll().await;
860
+ if let Err(err) = res.as_ref()
861
+ && matches!(err, PollError::ShutDown)
862
+ {
863
+ self.non_local_activities_complete
864
+ .store(true, Ordering::Relaxed);
865
+ return Ok(None);
866
+ };
867
+ res.map(Some)
868
+ } else {
869
+ // We expect the local activity branch below to produce shutdown when appropriate if
870
+ // there are no activity pollers.
871
+ future::pending::<()>().await;
872
+ unreachable!()
873
+ }
874
+ } else {
875
+ self.non_local_activities_complete
876
+ .store(true, Ordering::Relaxed);
877
+ Ok(None)
878
+ }
879
+ };
880
+ let local_activities_poll = async {
881
+ if local_activities_complete {
882
+ future::pending::<()>().await;
883
+ unreachable!()
884
+ }
885
+ if self.config.task_types.enable_local_activities {
886
+ match &self.local_act_mgr {
887
+ Some(la_mgr) => match la_mgr.next_pending().await {
888
+ Some(NextPendingLAAction::Dispatch(r)) => Ok(Some(r)),
889
+ Some(NextPendingLAAction::Autocomplete(action)) => {
890
+ Ok(self.handle_la_complete_action(action))
891
+ }
892
+ None => {
893
+ if self.shutdown_token.is_cancelled() {
894
+ self.local_activities_complete
895
+ .store(true, Ordering::Relaxed);
896
+ }
897
+ Ok(None)
898
+ }
899
+ },
900
+ None => {
901
+ self.local_activities_complete
902
+ .store(true, Ordering::Relaxed);
903
+ Ok(None)
904
+ }
905
+ }
906
+ } else {
907
+ self.local_activities_complete
908
+ .store(true, Ordering::Relaxed);
909
+ Ok(None)
910
+ }
911
+ };
912
+
913
+ let r = tokio::select! {
914
+ biased;
915
+
916
+ r = local_activities_poll => r,
917
+ r = act_mgr_poll => r,
918
+ };
919
+ // Since we consider network errors (at this level) fatal, we want to start shutdown if one
920
+ // is encountered
921
+ if matches!(r, Err(PollError::TonicError(_))) {
922
+ self.initiate_shutdown();
923
+ }
924
+ r
925
+ }
926
+
927
+ /// Attempt to record an activity heartbeat
928
+ pub(crate) fn record_heartbeat(&self, details: ActivityHeartbeat) {
929
+ if let Some(at_mgr) = self.at_task_mgr.as_ref() {
930
+ let tt = TaskToken(details.task_token.clone());
931
+ if let Err(e) = at_mgr.record_heartbeat(details) {
932
+ warn!(task_token = %tt, details = ?e, "Activity heartbeat failed.");
933
+ }
934
+ }
935
+ }
936
+
937
+ #[instrument(skip(self, task_token, status),
938
+ fields(task_token=%&task_token, status=%&status,
939
+ task_queue=%self.config.task_queue, workflow_id, run_id))]
940
+ pub(crate) async fn complete_activity(
941
+ &self,
942
+ task_token: TaskToken,
943
+ status: activity_execution_result::Status,
944
+ ) -> Result<(), CompleteActivityError> {
945
+ validate_activity_completion(&status)?;
946
+ if task_token.is_local_activity_task() {
947
+ let as_la_res: LocalActivityExecutionResult = status.try_into()?;
948
+ self.complete_local_act(task_token, as_la_res);
949
+ return Ok(());
950
+ }
951
+
952
+ if let Some(atm) = &self.at_task_mgr {
953
+ atm.complete(task_token, status, &*self.client).await;
954
+ Ok(())
955
+ } else {
956
+ Err(CompleteActivityError::ActivityNotEnabled)
957
+ }
958
+ }
959
+
960
+ #[instrument(skip(self), fields(run_id, workflow_id, task_queue=%self.config.task_queue))]
961
+ pub(crate) async fn next_workflow_activation(&self) -> Result<WorkflowActivation, PollError> {
962
+ match &self.workflows {
963
+ Some(workflows) => {
964
+ let r = workflows.next_workflow_activation().await;
965
+ // In the event workflows are shutdown or erroring, begin shutdown of everything else. Once
966
+ // they are shut down, tell the local activity manager that, so that it can know to cancel
967
+ // any remaining outstanding LAs and shutdown.
968
+ if let Err(ref e) = r {
969
+ // This is covering the situation where WFT pollers dying is the reason for shutdown
970
+ self.initiate_shutdown();
971
+ if matches!(e, PollError::ShutDown)
972
+ && let Some(la_mgr) = &self.local_act_mgr
973
+ {
974
+ la_mgr.workflows_have_shutdown();
975
+ }
976
+ }
977
+ r
978
+ }
979
+ None => Err(PollError::ShutDown),
980
+ }
981
+ }
982
+
983
+ #[instrument(skip(self, completion),
984
+ fields(completion=%&completion, run_id=%completion.run_id, workflow_id,
985
+ task_queue=%self.config.task_queue))]
986
+ pub(crate) async fn complete_workflow_activation(
987
+ &self,
988
+ completion: WorkflowActivationCompletion,
989
+ ) -> Result<(), CompleteWfError> {
990
+ match &self.workflows {
991
+ Some(workflows) => {
992
+ workflows
993
+ .activation_completed(
994
+ completion,
995
+ false,
996
+ self.post_activate_hook
997
+ .as_ref()
998
+ .map(|h| |data: PostActivateHookData| h(self, data)),
999
+ )
1000
+ .await?;
1001
+ Ok(())
1002
+ }
1003
+ None => Err(CompleteWfError::WorkflowNotEnabled),
1004
+ }
1005
+ }
1006
+
1007
+ #[instrument(
1008
+ skip(self, tt, status),
1009
+ fields(task_token=%&tt, status=%&status, task_queue=%self.config.task_queue)
1010
+ )]
1011
+ async fn complete_nexus_task(
1012
+ &self,
1013
+ tt: TaskToken,
1014
+ status: nexus_task_completion::Status,
1015
+ ) -> Result<(), CompleteNexusError> {
1016
+ match &self.nexus_mgr {
1017
+ Some(mgr) => mgr.complete_task(tt, status, &*self.client).await,
1018
+ None => Err(CompleteNexusError::NexusNotEnabled),
1019
+ }
1020
+ }
1021
+
1022
+ /// Request a workflow eviction
1023
+ pub(crate) fn request_wf_eviction(
1024
+ &self,
1025
+ run_id: &str,
1026
+ message: impl Into<String>,
1027
+ reason: EvictionReason,
1028
+ ) {
1029
+ if let Some(workflows) = &self.workflows {
1030
+ workflows.request_eviction(run_id, message, reason);
1031
+ } else {
1032
+ dbg_panic!("trying to request wf eviction when workflows not enabled for this worker");
1033
+ }
1034
+ }
1035
+
1036
+ /// Sets a function to be called at the end of each activation completion
1037
+ pub(crate) fn set_post_activate_hook(
1038
+ &mut self,
1039
+ callback: impl Fn(&Self, PostActivateHookData<'_>) + Send + Sync + 'static,
1040
+ ) {
1041
+ self.post_activate_hook = Some(Box::new(callback))
1042
+ }
1043
+
1044
+ fn complete_local_act(&self, task_token: TaskToken, la_res: LocalActivityExecutionResult) {
1045
+ if let Some(la_mgr) = &self.local_act_mgr
1046
+ && self
1047
+ .handle_la_complete_action(la_mgr.complete(&task_token, la_res))
1048
+ .is_some()
1049
+ {
1050
+ dbg_panic!("Should never be a task from direct completion");
1051
+ }
1052
+ }
1053
+
1054
+ fn handle_la_complete_action(&self, action: LACompleteAction) -> Option<ActivityTask> {
1055
+ match action {
1056
+ LACompleteAction::Report {
1057
+ run_id,
1058
+ resolution,
1059
+ task,
1060
+ } => {
1061
+ self.notify_local_result(&run_id, LocalResolution::LocalActivity(resolution));
1062
+ task
1063
+ }
1064
+ LACompleteAction::WillBeRetried(task) => task,
1065
+ LACompleteAction::Untracked => None,
1066
+ }
1067
+ }
1068
+
1069
+ fn notify_local_result(&self, run_id: &str, res: LocalResolution) {
1070
+ if let Some(workflows) = &self.workflows {
1071
+ workflows.notify_of_local_result(run_id, res);
1072
+ } else {
1073
+ dbg_panic!("trying to notify local result when workflows not enabled for this worker");
1074
+ }
1075
+ }
1076
+
1077
+ async fn verify_namespace_exists(&self) -> Result<(), WorkerValidationError> {
1078
+ if let Err(e) = self.client.describe_namespace().await {
1079
+ // Ignore if unimplemented since we wouldn't want to fail against an old server, for
1080
+ // example.
1081
+ if e.code() != tonic::Code::Unimplemented {
1082
+ return Err(WorkerValidationError::NamespaceDescribeError {
1083
+ source: e,
1084
+ namespace: self.config.namespace.clone(),
1085
+ });
1086
+ }
1087
+ }
1088
+ Ok(())
1089
+ }
1090
+ }
1091
+
1092
+ struct ClientWorkerRegistrator {
1093
+ worker_instance_key: Uuid,
1094
+ slot_provider: SlotProvider,
1095
+ heartbeat_manager: Option<WorkerHeartbeatManager>,
1096
+ client: RwLock<Arc<dyn WorkerClient>>,
1097
+ shared_namespace_worker: bool,
1098
+ task_types: WorkerTaskTypes,
1099
+ }
1100
+
1101
+ impl ClientWorker for ClientWorkerRegistrator {
1102
+ fn namespace(&self) -> &str {
1103
+ self.slot_provider.namespace()
1104
+ }
1105
+ fn task_queue(&self) -> &str {
1106
+ self.slot_provider.task_queue()
1107
+ }
1108
+
1109
+ fn try_reserve_wft_slot(&self) -> Option<Box<dyn SlotTrait + Send>> {
1110
+ self.slot_provider.try_reserve_wft_slot()
1111
+ }
1112
+
1113
+ fn deployment_options(&self) -> Option<temporalio_common::worker::WorkerDeploymentOptions> {
1114
+ self.slot_provider.deployment_options()
1115
+ }
1116
+
1117
+ fn worker_instance_key(&self) -> Uuid {
1118
+ self.worker_instance_key
1119
+ }
1120
+
1121
+ fn heartbeat_enabled(&self) -> bool {
1122
+ self.heartbeat_manager.is_some()
1123
+ }
1124
+
1125
+ fn heartbeat_callback(&self) -> Option<HeartbeatCallback> {
1126
+ if let Some(hb_mgr) = self.heartbeat_manager.as_ref() {
1127
+ Some(hb_mgr.heartbeat_callback.clone())
1128
+ } else {
1129
+ None
1130
+ }
1131
+ }
1132
+
1133
+ fn new_shared_namespace_worker(
1134
+ &self,
1135
+ ) -> Result<Box<dyn SharedNamespaceWorkerTrait + Send + Sync>, anyhow::Error> {
1136
+ if let Some(ref hb_mgr) = self.heartbeat_manager {
1137
+ Ok(Box::new(SharedNamespaceWorker::new(
1138
+ self.client.read().clone(),
1139
+ self.namespace().to_string(),
1140
+ hb_mgr.heartbeat_interval,
1141
+ hb_mgr.telemetry.clone(),
1142
+ )?))
1143
+ } else {
1144
+ bail!("Shared namespace worker creation never be called without a heartbeat manager");
1145
+ }
1146
+ }
1147
+
1148
+ fn worker_task_types(&self) -> WorkerTaskTypes {
1149
+ self.task_types
1150
+ }
1151
+ }
1152
+
1153
+ struct HeartbeatMetrics {
1154
+ in_mem_metrics: Option<Arc<WorkerHeartbeatMetrics>>,
1155
+ wft_slots: MeteredPermitDealer<WorkflowSlotKind>,
1156
+ act_slots: MeteredPermitDealer<ActivitySlotKind>,
1157
+ nexus_slots: MeteredPermitDealer<NexusSlotKind>,
1158
+ la_slots: MeteredPermitDealer<LocalActivitySlotKind>,
1159
+ wf_last_suc_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
1160
+ wf_sticky_last_suc_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
1161
+ act_last_suc_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
1162
+ nexus_last_suc_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
1163
+ status: Arc<RwLock<WorkerStatus>>,
1164
+ sys_info: Arc<dyn SystemResourceInfo + Send + Sync>,
1165
+ }
1166
+
1167
+ struct WorkerHeartbeatManager {
1168
+ /// Heartbeat interval, defaults to 60s
1169
+ heartbeat_interval: Duration,
1170
+ /// Telemetry instance, needed to initialize [SharedNamespaceWorker] when replacing client
1171
+ telemetry: Option<WorkerTelemetry>,
1172
+ /// Heartbeat callback
1173
+ heartbeat_callback: Arc<dyn Fn() -> WorkerHeartbeat + Send + Sync>,
1174
+ }
1175
+
1176
+ impl WorkerHeartbeatManager {
1177
+ fn new(
1178
+ config: WorkerConfig,
1179
+ worker_instance_key: Uuid,
1180
+ heartbeat_interval: Duration,
1181
+ telemetry_instance: Option<WorkerTelemetry>,
1182
+ heartbeat_manager_metrics: HeartbeatMetrics,
1183
+ ) -> Self {
1184
+ let start_time = Some(SystemTime::now().into());
1185
+ let worker_heartbeat_callback: HeartbeatFn = Arc::new(move || {
1186
+ let deployment_version = config.computed_deployment_version().map(|dv| {
1187
+ deployment::v1::WorkerDeploymentVersion {
1188
+ deployment_name: dv.deployment_name,
1189
+ build_id: dv.build_id,
1190
+ }
1191
+ });
1192
+
1193
+ let mut plugins: Vec<_> = config.plugins.clone().into_iter().collect();
1194
+ plugins.sort_by(|a, b| a.name.cmp(&b.name));
1195
+
1196
+ let mut worker_heartbeat = WorkerHeartbeat {
1197
+ worker_instance_key: worker_instance_key.to_string(),
1198
+ host_info: Some(WorkerHostInfo {
1199
+ host_name: gethostname().to_string_lossy().to_string(),
1200
+ process_id: std::process::id().to_string(),
1201
+ current_host_cpu_usage: heartbeat_manager_metrics.sys_info.used_cpu_percent()
1202
+ as f32,
1203
+ current_host_mem_usage: heartbeat_manager_metrics.sys_info.used_mem_percent()
1204
+ as f32,
1205
+
1206
+ // Set by SharedNamespaceWorker because it relies on the client
1207
+ process_key: String::new(),
1208
+ }),
1209
+ task_queue: config.task_queue.clone(),
1210
+ deployment_version,
1211
+
1212
+ status: (*heartbeat_manager_metrics.status.read()) as i32,
1213
+ start_time,
1214
+ plugins,
1215
+
1216
+ // Some Metrics dependent fields are set below, and
1217
+ // some fields like sdk_name, sdk_version, and worker_identity, must be set by
1218
+ // SharedNamespaceWorker because they rely on the client, and
1219
+ // need to be pulled from the current client used by SharedNamespaceWorker
1220
+ ..Default::default()
1221
+ };
1222
+
1223
+ if let Some(in_mem) = heartbeat_manager_metrics.in_mem_metrics.as_ref() {
1224
+ worker_heartbeat.total_sticky_cache_hit =
1225
+ in_mem.total_sticky_cache_hit.load(Ordering::Relaxed) as i32;
1226
+ worker_heartbeat.total_sticky_cache_miss =
1227
+ in_mem.total_sticky_cache_miss.load(Ordering::Relaxed) as i32;
1228
+ worker_heartbeat.current_sticky_cache_size =
1229
+ in_mem.sticky_cache_size.load(Ordering::Relaxed) as i32;
1230
+
1231
+ worker_heartbeat.workflow_poller_info = Some(WorkerPollerInfo {
1232
+ current_pollers: in_mem
1233
+ .num_pollers
1234
+ .wft_current_pollers
1235
+ .load(Ordering::Relaxed) as i32,
1236
+ last_successful_poll_time: heartbeat_manager_metrics
1237
+ .wf_last_suc_poll_time
1238
+ .load()
1239
+ .map(|time| time.into()),
1240
+ is_autoscaling: config.workflow_task_poller_behavior.is_autoscaling(),
1241
+ });
1242
+ worker_heartbeat.workflow_sticky_poller_info = Some(WorkerPollerInfo {
1243
+ current_pollers: in_mem
1244
+ .num_pollers
1245
+ .sticky_wft_current_pollers
1246
+ .load(Ordering::Relaxed) as i32,
1247
+ last_successful_poll_time: heartbeat_manager_metrics
1248
+ .wf_sticky_last_suc_poll_time
1249
+ .load()
1250
+ .map(|time| time.into()),
1251
+ is_autoscaling: config.workflow_task_poller_behavior.is_autoscaling(),
1252
+ });
1253
+ worker_heartbeat.activity_poller_info = Some(WorkerPollerInfo {
1254
+ current_pollers: in_mem
1255
+ .num_pollers
1256
+ .activity_current_pollers
1257
+ .load(Ordering::Relaxed) as i32,
1258
+ last_successful_poll_time: heartbeat_manager_metrics
1259
+ .act_last_suc_poll_time
1260
+ .load()
1261
+ .map(|time| time.into()),
1262
+ is_autoscaling: config.activity_task_poller_behavior.is_autoscaling(),
1263
+ });
1264
+ worker_heartbeat.nexus_poller_info = Some(WorkerPollerInfo {
1265
+ current_pollers: in_mem
1266
+ .num_pollers
1267
+ .nexus_current_pollers
1268
+ .load(Ordering::Relaxed) as i32,
1269
+ last_successful_poll_time: heartbeat_manager_metrics
1270
+ .nexus_last_suc_poll_time
1271
+ .load()
1272
+ .map(|time| time.into()),
1273
+ is_autoscaling: config.nexus_task_poller_behavior.is_autoscaling(),
1274
+ });
1275
+
1276
+ worker_heartbeat.workflow_task_slots_info = make_slots_info(
1277
+ &heartbeat_manager_metrics.wft_slots,
1278
+ in_mem.worker_task_slots_available.workflow_worker.clone(),
1279
+ in_mem.worker_task_slots_used.workflow_worker.clone(),
1280
+ in_mem.workflow_task_execution_latency.clone(),
1281
+ in_mem.workflow_task_execution_failed.clone(),
1282
+ );
1283
+ worker_heartbeat.activity_task_slots_info = make_slots_info(
1284
+ &heartbeat_manager_metrics.act_slots,
1285
+ in_mem.worker_task_slots_available.activity_worker.clone(),
1286
+ in_mem.worker_task_slots_used.activity_worker.clone(),
1287
+ in_mem.activity_execution_latency.clone(),
1288
+ in_mem.activity_execution_failed.clone(),
1289
+ );
1290
+ worker_heartbeat.nexus_task_slots_info = make_slots_info(
1291
+ &heartbeat_manager_metrics.nexus_slots,
1292
+ in_mem.worker_task_slots_available.nexus_worker.clone(),
1293
+ in_mem.worker_task_slots_used.nexus_worker.clone(),
1294
+ in_mem.nexus_task_execution_latency.clone(),
1295
+ in_mem.nexus_task_execution_failed.clone(),
1296
+ );
1297
+ worker_heartbeat.local_activity_slots_info = make_slots_info(
1298
+ &heartbeat_manager_metrics.la_slots,
1299
+ in_mem
1300
+ .worker_task_slots_available
1301
+ .local_activity_worker
1302
+ .clone(),
1303
+ in_mem.worker_task_slots_used.local_activity_worker.clone(),
1304
+ in_mem.local_activity_execution_latency.clone(),
1305
+ in_mem.local_activity_execution_failed.clone(),
1306
+ );
1307
+ }
1308
+ worker_heartbeat
1309
+ });
1310
+
1311
+ WorkerHeartbeatManager {
1312
+ heartbeat_interval,
1313
+ telemetry: telemetry_instance,
1314
+ heartbeat_callback: worker_heartbeat_callback,
1315
+ }
1316
+ }
1317
+ }
1318
+
1319
+ pub(crate) struct PostActivateHookData<'a> {
1320
+ pub(crate) run_id: &'a str,
1321
+ pub(crate) replaying: bool,
1322
+ }
1323
+
1324
+ pub(crate) enum TaskPollers {
1325
+ Real,
1326
+ #[cfg(any(feature = "test-utilities", test))]
1327
+ Mocked {
1328
+ wft_stream: Option<BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>>,
1329
+ act_poller: Option<BoxedPoller<PollActivityTaskQueueResponse>>,
1330
+ nexus_poller: Option<BoxedPoller<PollNexusTaskQueueResponse>>,
1331
+ },
1332
+ }
1333
+
1334
+ fn wft_poller_behavior(config: &WorkerConfig, is_sticky: bool) -> PollerBehavior {
1335
+ fn calc_max_nonsticky(max_polls: usize, ratio: f32) -> usize {
1336
+ ((max_polls as f32 * ratio) as usize).max(1)
1337
+ }
1338
+
1339
+ if let PollerBehavior::SimpleMaximum(m) = config.workflow_task_poller_behavior {
1340
+ if !is_sticky {
1341
+ PollerBehavior::SimpleMaximum(calc_max_nonsticky(
1342
+ m,
1343
+ config.nonsticky_to_sticky_poll_ratio,
1344
+ ))
1345
+ } else {
1346
+ PollerBehavior::SimpleMaximum(
1347
+ m.saturating_sub(calc_max_nonsticky(m, config.nonsticky_to_sticky_poll_ratio))
1348
+ .max(1),
1349
+ )
1350
+ }
1351
+ } else {
1352
+ config.workflow_task_poller_behavior
1353
+ }
1354
+ }
1355
+
1356
+ fn make_slots_info<SK>(
1357
+ dealer: &MeteredPermitDealer<SK>,
1358
+ slots_available: Arc<AtomicU64>,
1359
+ slots_used: Arc<AtomicU64>,
1360
+ total_processed: Arc<AtomicU64>,
1361
+ total_failed: Arc<AtomicU64>,
1362
+ ) -> Option<WorkerSlotsInfo>
1363
+ where
1364
+ SK: SlotKind + 'static,
1365
+ {
1366
+ Some(WorkerSlotsInfo {
1367
+ current_available_slots: i32::try_from(slots_available.load(Ordering::Relaxed))
1368
+ .unwrap_or(-1),
1369
+ current_used_slots: i32::try_from(slots_used.load(Ordering::Relaxed)).unwrap_or(-1),
1370
+ slot_supplier_kind: dealer.slot_supplier_kind().to_string(),
1371
+ total_processed_tasks: i32::try_from(total_processed.load(Ordering::Relaxed))
1372
+ .unwrap_or(i32::MIN),
1373
+ total_failed_tasks: i32::try_from(total_failed.load(Ordering::Relaxed)).unwrap_or(i32::MIN),
1374
+
1375
+ // Filled in by heartbeat later
1376
+ last_interval_processed_tasks: 0,
1377
+ last_interval_failure_tasks: 0,
1378
+ })
1379
+ }
1380
+
1381
+ #[cfg(test)]
1382
+ mod tests {
1383
+ use super::*;
1384
+ use crate::{
1385
+ advance_fut,
1386
+ test_help::test_worker_cfg,
1387
+ worker::client::mocks::{mock_manual_worker_client, mock_worker_client},
1388
+ };
1389
+ use futures_util::FutureExt;
1390
+ use temporalio_common::{
1391
+ protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse,
1392
+ worker::PollerBehavior,
1393
+ };
1394
+
1395
+ #[tokio::test]
1396
+ async fn activity_timeouts_maintain_permit() {
1397
+ let mut mock_client = mock_worker_client();
1398
+ mock_client
1399
+ .expect_poll_activity_task()
1400
+ .returning(|_, _| Ok(PollActivityTaskQueueResponse::default()));
1401
+
1402
+ let cfg = test_worker_cfg()
1403
+ .max_outstanding_activities(5_usize)
1404
+ .build()
1405
+ .unwrap();
1406
+ let worker = Worker::new_test(cfg, mock_client);
1407
+ let fut = worker.poll_activity_task();
1408
+ advance_fut!(fut);
1409
+ assert_eq!(
1410
+ worker.at_task_mgr.as_ref().unwrap().unused_permits(),
1411
+ Some(5)
1412
+ );
1413
+ }
1414
+
1415
+ #[tokio::test]
1416
+ async fn activity_errs_dont_eat_permits() {
1417
+ // Return one error followed by simulating waiting on the poll, otherwise the poller will
1418
+ // loop very fast and be in some indeterminate state.
1419
+ let mut mock_client = mock_manual_worker_client();
1420
+ mock_client
1421
+ .expect_poll_activity_task()
1422
+ .returning(|_, _| async { Err(tonic::Status::internal("ahhh")) }.boxed())
1423
+ .times(1);
1424
+ mock_client
1425
+ .expect_poll_activity_task()
1426
+ .returning(|_, _| future::pending().boxed());
1427
+
1428
+ let cfg = test_worker_cfg()
1429
+ .max_outstanding_activities(5_usize)
1430
+ .build()
1431
+ .unwrap();
1432
+ let worker = Worker::new_test(cfg, mock_client);
1433
+ assert!(worker.activity_poll().await.is_err());
1434
+ assert_eq!(worker.at_task_mgr.unwrap().unused_permits(), Some(5));
1435
+ }
1436
+
1437
+ #[test]
1438
+ fn max_polls_calculated_properly() {
1439
+ let cfg = test_worker_cfg()
1440
+ .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(5_usize))
1441
+ .build()
1442
+ .unwrap();
1443
+ assert_eq!(
1444
+ wft_poller_behavior(&cfg, false),
1445
+ PollerBehavior::SimpleMaximum(1)
1446
+ );
1447
+ assert_eq!(
1448
+ wft_poller_behavior(&cfg, true),
1449
+ PollerBehavior::SimpleMaximum(4)
1450
+ );
1451
+ }
1452
+
1453
+ #[test]
1454
+ fn max_polls_zero_is_err() {
1455
+ assert!(
1456
+ test_worker_cfg()
1457
+ .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(0_usize))
1458
+ .build()
1459
+ .is_err()
1460
+ );
1461
+ }
1462
+ }