@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,1061 @@
1
+ use crate::common::{ANY_PORT, CoreWfStarter, eventually, get_integ_telem_options};
2
+ use anyhow::anyhow;
3
+ use crossbeam_utils::atomic::AtomicCell;
4
+ use futures_util::StreamExt;
5
+ use prost_types::{Duration as PbDuration, Timestamp};
6
+ use std::{
7
+ collections::HashSet,
8
+ env,
9
+ sync::{
10
+ Arc,
11
+ atomic::{AtomicU64, Ordering},
12
+ },
13
+ time::{Duration, SystemTime, UNIX_EPOCH},
14
+ };
15
+ use temporalio_client::{
16
+ Client, NamespacedClient, RetryClient, WfClientExt, WorkflowClientTrait, WorkflowService,
17
+ };
18
+ use temporalio_common::{
19
+ prost_dur,
20
+ protos::{
21
+ coresdk::{AsJsonPayloadExt, FromJsonPayloadExt},
22
+ temporal::api::{
23
+ common::v1::RetryPolicy,
24
+ enums::v1::WorkerStatus,
25
+ worker::v1::{PluginInfo, WorkerHeartbeat},
26
+ workflowservice::v1::{DescribeWorkerRequest, ListWorkersRequest},
27
+ },
28
+ },
29
+ telemetry::{
30
+ OtelCollectorOptionsBuilder, PrometheusExporterOptionsBuilder, TelemetryOptionsBuilder,
31
+ },
32
+ worker::PollerBehavior,
33
+ };
34
+ use temporalio_sdk::{ActContext, ActivityOptions, WfContext};
35
+ use temporalio_sdk_core::{
36
+ CoreRuntime, ResourceBasedTuner, ResourceSlotOptions, RuntimeOptionsBuilder,
37
+ telemetry::{build_otlp_metric_exporter, start_prometheus_metric_exporter},
38
+ };
39
+ use tokio::{sync::Notify, time::sleep};
40
+ use tonic::IntoRequest;
41
+ use url::Url;
42
+
43
+ fn within_two_minutes_ts(ts: Timestamp) -> bool {
44
+ let ts_time = UNIX_EPOCH + Duration::new(ts.seconds as u64, ts.nanos as u32);
45
+
46
+ let now = SystemTime::now();
47
+ // ts should be at most 2 minutes before the current time
48
+ now.duration_since(ts_time).unwrap() <= Duration::from_secs(2 * 60)
49
+ }
50
+
51
+ fn within_duration(dur: PbDuration, threshold: Duration) -> bool {
52
+ let std_dur = Duration::new(dur.seconds as u64, dur.nanos as u32);
53
+ std_dur <= threshold
54
+ }
55
+
56
+ fn new_no_metrics_starter(wf_name: &str) -> CoreWfStarter {
57
+ let runtimeopts = RuntimeOptionsBuilder::default()
58
+ .telemetry_options(TelemetryOptionsBuilder::default().build().unwrap())
59
+ .heartbeat_interval(Some(Duration::from_secs(1)))
60
+ .build()
61
+ .unwrap();
62
+ CoreWfStarter::new_with_runtime(wf_name, CoreRuntime::new_assume_tokio(runtimeopts).unwrap())
63
+ }
64
+
65
+ fn to_system_time(ts: Timestamp) -> SystemTime {
66
+ UNIX_EPOCH + Duration::new(ts.seconds as u64, ts.nanos as u32)
67
+ }
68
+
69
+ async fn list_worker_heartbeats(
70
+ client: &Arc<RetryClient<Client>>,
71
+ query: impl Into<String>,
72
+ ) -> Vec<WorkerHeartbeat> {
73
+ let mut raw_client = client.as_ref().clone();
74
+ WorkflowService::list_workers(
75
+ &mut raw_client,
76
+ ListWorkersRequest {
77
+ namespace: client.namespace().to_owned(),
78
+ page_size: 200,
79
+ next_page_token: Vec::new(),
80
+ query: query.into(),
81
+ }
82
+ .into_request(),
83
+ )
84
+ .await
85
+ .unwrap()
86
+ .into_inner()
87
+ .workers_info
88
+ .into_iter()
89
+ .filter_map(|info| info.worker_heartbeat)
90
+ .collect()
91
+ }
92
+
93
+ // Tests that rely on Prometheus running in a docker container need to start
94
+ // with `docker_` and set the `DOCKER_PROMETHEUS_RUNNING` env variable to run
95
+ #[rstest::rstest]
96
+ #[tokio::test]
97
+ async fn docker_worker_heartbeat_basic(#[values("otel", "prom", "no_metrics")] backing: &str) {
98
+ if env::var("DOCKER_PROMETHEUS_RUNNING").is_err() {
99
+ return;
100
+ }
101
+ let telemopts = if backing == "no_metrics" {
102
+ TelemetryOptionsBuilder::default().build().unwrap()
103
+ } else {
104
+ get_integ_telem_options()
105
+ };
106
+ let runtimeopts = RuntimeOptionsBuilder::default()
107
+ .telemetry_options(telemopts)
108
+ .heartbeat_interval(Some(Duration::from_secs(1)))
109
+ .build()
110
+ .unwrap();
111
+ let mut rt = CoreRuntime::new_assume_tokio(runtimeopts).unwrap();
112
+ match backing {
113
+ "otel" => {
114
+ let url = Some("grpc://localhost:4317")
115
+ .map(|x| x.parse::<Url>().unwrap())
116
+ .unwrap();
117
+ let mut opts_build = OtelCollectorOptionsBuilder::default();
118
+ let opts = opts_build.url(url).build().unwrap();
119
+ rt.telemetry_mut()
120
+ .attach_late_init_metrics(Arc::new(build_otlp_metric_exporter(opts).unwrap()));
121
+ }
122
+ "prom" => {
123
+ let mut opts_build = PrometheusExporterOptionsBuilder::default();
124
+ opts_build.socket_addr(ANY_PORT.parse().unwrap());
125
+ let opts = opts_build.build().unwrap();
126
+ rt.telemetry_mut()
127
+ .attach_late_init_metrics(start_prometheus_metric_exporter(opts).unwrap().meter);
128
+ }
129
+ "no_metrics" => {}
130
+ _ => unreachable!(),
131
+ }
132
+ let wf_name = format!("worker_heartbeat_basic_{backing}");
133
+ let mut starter = CoreWfStarter::new_with_runtime(&wf_name, rt);
134
+ starter
135
+ .worker_config
136
+ .max_outstanding_workflow_tasks(5_usize)
137
+ .max_cached_workflows(5_usize)
138
+ .max_outstanding_activities(5_usize)
139
+ .plugins(
140
+ [
141
+ PluginInfo {
142
+ name: "plugin1".to_string(),
143
+ version: "1".to_string(),
144
+ },
145
+ PluginInfo {
146
+ name: "plugin2".to_string(),
147
+ version: "2".to_string(),
148
+ },
149
+ ]
150
+ .into_iter()
151
+ .collect::<HashSet<_>>(),
152
+ );
153
+ let mut worker = starter.worker().await;
154
+ let worker_instance_key = worker.worker_instance_key();
155
+
156
+ worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
157
+ ctx.activity(ActivityOptions {
158
+ activity_type: "pass_fail_act".to_string(),
159
+ input: "pass".as_json_payload().expect("serializes fine"),
160
+ start_to_close_timeout: Some(Duration::from_secs(5)),
161
+ ..Default::default()
162
+ })
163
+ .await;
164
+ Ok(().into())
165
+ });
166
+
167
+ let acts_started = Arc::new(Notify::new());
168
+ let acts_done = Arc::new(Notify::new());
169
+
170
+ let acts_started_act = acts_started.clone();
171
+ let acts_done_act = acts_done.clone();
172
+ worker.register_activity("pass_fail_act", move |_ctx: ActContext, i: String| {
173
+ let acts_started = acts_started_act.clone();
174
+ let acts_done = acts_done_act.clone();
175
+ async move {
176
+ acts_started.notify_one();
177
+ acts_done.notified().await;
178
+ Ok(i)
179
+ }
180
+ });
181
+
182
+ starter
183
+ .start_with_worker(wf_name.clone(), &mut worker)
184
+ .await;
185
+
186
+ let start_time = AtomicCell::new(None);
187
+ let heartbeat_time = AtomicCell::new(None);
188
+
189
+ let test_fut = async {
190
+ // Give enough time to ensure heartbeat interval has been hit
191
+ tokio::time::sleep(Duration::from_millis(1500)).await;
192
+ acts_started.notified().await;
193
+ let client = starter.get_client().await;
194
+ let mut raw_client = (*client).clone();
195
+ let workers_list = WorkflowService::list_workers(
196
+ &mut raw_client,
197
+ ListWorkersRequest {
198
+ namespace: client.namespace().to_owned(),
199
+ page_size: 100,
200
+ next_page_token: Vec::new(),
201
+ query: String::new(),
202
+ }
203
+ .into_request(),
204
+ )
205
+ .await
206
+ .unwrap()
207
+ .into_inner();
208
+ let worker_info = workers_list
209
+ .workers_info
210
+ .iter()
211
+ .find(|worker_info| {
212
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
213
+ hb.worker_instance_key == worker_instance_key.to_string()
214
+ } else {
215
+ false
216
+ }
217
+ })
218
+ .unwrap();
219
+ let heartbeat = worker_info.worker_heartbeat.as_ref().unwrap();
220
+ assert_eq!(
221
+ heartbeat.worker_instance_key,
222
+ worker_instance_key.to_string()
223
+ );
224
+ in_activity_checks(heartbeat, &start_time, &heartbeat_time);
225
+ acts_done.notify_one();
226
+ };
227
+
228
+ let runner = async move {
229
+ worker.run_until_done().await.unwrap();
230
+ };
231
+ tokio::join!(test_fut, runner);
232
+
233
+ let client = starter.get_client().await;
234
+ let mut raw_client = (*client).clone();
235
+ let workers_list = WorkflowService::list_workers(
236
+ &mut raw_client,
237
+ ListWorkersRequest {
238
+ namespace: client.namespace().to_owned(),
239
+ page_size: 100,
240
+ next_page_token: Vec::new(),
241
+ query: String::new(),
242
+ }
243
+ .into_request(),
244
+ )
245
+ .await
246
+ .unwrap()
247
+ .into_inner();
248
+ // Since list_workers finds all workers in the namespace, must find specific worker used in this
249
+ // test
250
+ let worker_info = workers_list
251
+ .workers_info
252
+ .iter()
253
+ .find(|worker_info| {
254
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
255
+ hb.worker_instance_key == worker_instance_key.to_string()
256
+ } else {
257
+ false
258
+ }
259
+ })
260
+ .unwrap();
261
+ let heartbeat = worker_info.worker_heartbeat.as_ref().unwrap();
262
+ after_shutdown_checks(heartbeat, &wf_name, &start_time, &heartbeat_time);
263
+ }
264
+
265
+ // Tests that rely on Prometheus running in a docker container need to start
266
+ // with `docker_` and set the `DOCKER_PROMETHEUS_RUNNING` env variable to run
267
+ #[tokio::test]
268
+ async fn docker_worker_heartbeat_tuner() {
269
+ if env::var("DOCKER_PROMETHEUS_RUNNING").is_err() {
270
+ return;
271
+ }
272
+ let runtimeopts = RuntimeOptionsBuilder::default()
273
+ .telemetry_options(get_integ_telem_options())
274
+ .heartbeat_interval(Some(Duration::from_secs(1)))
275
+ .build()
276
+ .unwrap();
277
+ let mut rt = CoreRuntime::new_assume_tokio(runtimeopts).unwrap();
278
+
279
+ let url = Some("grpc://localhost:4317")
280
+ .map(|x| x.parse::<Url>().unwrap())
281
+ .unwrap();
282
+ let mut opts_build = OtelCollectorOptionsBuilder::default();
283
+ let opts = opts_build.url(url).build().unwrap();
284
+
285
+ rt.telemetry_mut()
286
+ .attach_late_init_metrics(Arc::new(build_otlp_metric_exporter(opts).unwrap()));
287
+ let wf_name = "worker_heartbeat_tuner";
288
+ let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
289
+ let mut tuner = ResourceBasedTuner::new(0.0, 0.0);
290
+ tuner
291
+ .with_workflow_slots_options(ResourceSlotOptions::new(2, 10, Duration::from_millis(0)))
292
+ .with_activity_slots_options(ResourceSlotOptions::new(5, 10, Duration::from_millis(50)));
293
+ starter
294
+ .worker_config
295
+ .workflow_task_poller_behavior(PollerBehavior::Autoscaling {
296
+ minimum: 1,
297
+ maximum: 200,
298
+ initial: 5,
299
+ })
300
+ .nexus_task_poller_behavior(PollerBehavior::Autoscaling {
301
+ minimum: 1,
302
+ maximum: 200,
303
+ initial: 5,
304
+ })
305
+ .clear_max_outstanding_opts()
306
+ .tuner(Arc::new(tuner));
307
+ let mut worker = starter.worker().await;
308
+ let worker_instance_key = worker.worker_instance_key();
309
+
310
+ // Run a workflow
311
+ worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
312
+ ctx.activity(ActivityOptions {
313
+ activity_type: "pass_fail_act".to_string(),
314
+ input: "pass".as_json_payload().expect("serializes fine"),
315
+ start_to_close_timeout: Some(Duration::from_secs(1)),
316
+ ..Default::default()
317
+ })
318
+ .await;
319
+ Ok(().into())
320
+ });
321
+ worker.register_activity("pass_fail_act", |_ctx: ActContext, i: String| async move {
322
+ Ok(i)
323
+ });
324
+
325
+ starter.start_with_worker(wf_name, &mut worker).await;
326
+ worker.run_until_done().await.unwrap();
327
+
328
+ let client = starter.get_client().await;
329
+ let mut raw_client = (*client).clone();
330
+ let workers_list = WorkflowService::list_workers(
331
+ &mut raw_client,
332
+ ListWorkersRequest {
333
+ namespace: client.namespace().to_owned(),
334
+ page_size: 100,
335
+ next_page_token: Vec::new(),
336
+ query: String::new(),
337
+ }
338
+ .into_request(),
339
+ )
340
+ .await
341
+ .unwrap()
342
+ .into_inner();
343
+ // Since list_workers finds all workers in the namespace, must find specific worker used in this
344
+ // test
345
+ let worker_info = workers_list
346
+ .workers_info
347
+ .iter()
348
+ .find(|worker_info| {
349
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
350
+ hb.worker_instance_key == worker_instance_key.to_string()
351
+ } else {
352
+ false
353
+ }
354
+ })
355
+ .unwrap();
356
+ let heartbeat = worker_info.worker_heartbeat.as_ref().unwrap();
357
+ assert!(heartbeat.task_queue.starts_with(wf_name));
358
+
359
+ assert_eq!(
360
+ heartbeat
361
+ .workflow_task_slots_info
362
+ .clone()
363
+ .unwrap()
364
+ .slot_supplier_kind,
365
+ "ResourceBased"
366
+ );
367
+ assert_eq!(
368
+ heartbeat
369
+ .activity_task_slots_info
370
+ .clone()
371
+ .unwrap()
372
+ .slot_supplier_kind,
373
+ "ResourceBased"
374
+ );
375
+ assert_eq!(
376
+ heartbeat
377
+ .nexus_task_slots_info
378
+ .clone()
379
+ .unwrap()
380
+ .slot_supplier_kind,
381
+ "ResourceBased"
382
+ );
383
+ assert_eq!(
384
+ heartbeat
385
+ .local_activity_slots_info
386
+ .clone()
387
+ .unwrap()
388
+ .slot_supplier_kind,
389
+ "ResourceBased"
390
+ );
391
+
392
+ let workflow_poller_info = heartbeat.workflow_poller_info.unwrap();
393
+ assert!(workflow_poller_info.is_autoscaling);
394
+ assert!(within_two_minutes_ts(
395
+ workflow_poller_info.last_successful_poll_time.unwrap()
396
+ ));
397
+ let sticky_poller_info = heartbeat.workflow_sticky_poller_info.unwrap();
398
+ assert!(sticky_poller_info.is_autoscaling);
399
+ assert!(within_two_minutes_ts(
400
+ sticky_poller_info.last_successful_poll_time.unwrap()
401
+ ));
402
+ let nexus_poller_info = heartbeat.nexus_poller_info.unwrap();
403
+ assert!(nexus_poller_info.is_autoscaling);
404
+ assert!(nexus_poller_info.last_successful_poll_time.is_none());
405
+ let activity_poller_info = heartbeat.activity_poller_info.unwrap();
406
+ assert!(!activity_poller_info.is_autoscaling);
407
+ assert!(within_two_minutes_ts(
408
+ activity_poller_info.last_successful_poll_time.unwrap()
409
+ ));
410
+ }
411
+
412
+ fn in_activity_checks(
413
+ heartbeat: &WorkerHeartbeat,
414
+ start_time: &AtomicCell<Option<Timestamp>>,
415
+ heartbeat_time: &AtomicCell<Option<Timestamp>>,
416
+ ) {
417
+ assert_eq!(heartbeat.status, WorkerStatus::Running as i32);
418
+
419
+ let workflow_task_slots = heartbeat.workflow_task_slots_info.clone().unwrap();
420
+ assert_eq!(workflow_task_slots.total_processed_tasks, 1);
421
+ assert_eq!(workflow_task_slots.current_available_slots, 5);
422
+ assert_eq!(workflow_task_slots.current_used_slots, 0);
423
+ assert_eq!(workflow_task_slots.slot_supplier_kind, "Fixed");
424
+ let activity_task_slots = heartbeat.activity_task_slots_info.clone().unwrap();
425
+ assert_eq!(activity_task_slots.current_available_slots, 4);
426
+ assert_eq!(activity_task_slots.current_used_slots, 1);
427
+ assert_eq!(activity_task_slots.slot_supplier_kind, "Fixed");
428
+ let nexus_task_slots = heartbeat.nexus_task_slots_info.clone().unwrap();
429
+ assert_eq!(nexus_task_slots.current_available_slots, 0);
430
+ assert_eq!(nexus_task_slots.current_used_slots, 0);
431
+ assert_eq!(nexus_task_slots.slot_supplier_kind, "Fixed");
432
+ let local_activity_task_slots = heartbeat.local_activity_slots_info.clone().unwrap();
433
+ assert_eq!(local_activity_task_slots.current_available_slots, 100);
434
+ assert_eq!(local_activity_task_slots.current_used_slots, 0);
435
+ assert_eq!(local_activity_task_slots.slot_supplier_kind, "Fixed");
436
+
437
+ let workflow_poller_info = heartbeat.workflow_poller_info.unwrap();
438
+ assert_eq!(workflow_poller_info.current_pollers, 1);
439
+ let sticky_poller_info = heartbeat.workflow_sticky_poller_info.unwrap();
440
+ assert_ne!(sticky_poller_info.current_pollers, 0);
441
+ let nexus_poller_info = heartbeat.nexus_poller_info.unwrap();
442
+ assert_eq!(nexus_poller_info.current_pollers, 0);
443
+ let activity_poller_info = heartbeat.activity_poller_info.unwrap();
444
+ assert_ne!(activity_poller_info.current_pollers, 0);
445
+ assert_ne!(heartbeat.current_sticky_cache_size, 0);
446
+ start_time.store(Some(heartbeat.start_time.unwrap()));
447
+ heartbeat_time.store(Some(heartbeat.heartbeat_time.unwrap()));
448
+ }
449
+
450
+ fn after_shutdown_checks(
451
+ heartbeat: &WorkerHeartbeat,
452
+ wf_name: &str,
453
+ start_time: &AtomicCell<Option<Timestamp>>,
454
+ heartbeat_time: &AtomicCell<Option<Timestamp>>,
455
+ ) {
456
+ assert_eq!(heartbeat.worker_identity, "integ_tester");
457
+ let host_info = heartbeat.host_info.clone().unwrap();
458
+ assert!(!host_info.host_name.is_empty());
459
+ assert!(!host_info.process_key.is_empty());
460
+ assert!(!host_info.process_id.is_empty());
461
+ assert!(host_info.current_host_cpu_usage >= 0.0);
462
+ assert!(host_info.current_host_mem_usage >= 0.0);
463
+
464
+ assert!(heartbeat.task_queue.starts_with(wf_name));
465
+ assert_eq!(
466
+ heartbeat.deployment_version.clone().unwrap().build_id,
467
+ "test_build_id"
468
+ );
469
+ assert_eq!(heartbeat.sdk_name, "temporal-core");
470
+ assert_eq!(heartbeat.sdk_version, "0.1.0");
471
+ assert_eq!(heartbeat.status, WorkerStatus::Shutdown as i32);
472
+
473
+ assert_eq!(start_time.load().unwrap(), heartbeat.start_time.unwrap());
474
+ assert_ne!(
475
+ heartbeat_time.load().unwrap(),
476
+ heartbeat.heartbeat_time.unwrap()
477
+ );
478
+ assert!(within_two_minutes_ts(heartbeat.start_time.unwrap()));
479
+ assert!(within_two_minutes_ts(heartbeat.heartbeat_time.unwrap()));
480
+ assert!(
481
+ to_system_time(heartbeat_time.load().unwrap())
482
+ < to_system_time(heartbeat.heartbeat_time.unwrap())
483
+ );
484
+ assert!(within_duration(
485
+ heartbeat.elapsed_since_last_heartbeat.unwrap(),
486
+ Duration::from_millis(2000)
487
+ ));
488
+
489
+ let workflow_task_slots = heartbeat.workflow_task_slots_info.clone().unwrap();
490
+ assert_eq!(workflow_task_slots.current_available_slots, 5);
491
+ assert_eq!(workflow_task_slots.current_used_slots, 1);
492
+ assert_eq!(workflow_task_slots.total_processed_tasks, 2);
493
+ assert_eq!(workflow_task_slots.slot_supplier_kind, "Fixed");
494
+ let activity_task_slots = heartbeat.activity_task_slots_info.clone().unwrap();
495
+ assert_eq!(activity_task_slots.current_available_slots, 5);
496
+ assert_eq!(workflow_task_slots.current_used_slots, 1);
497
+ assert_eq!(activity_task_slots.slot_supplier_kind, "Fixed");
498
+ assert_eq!(activity_task_slots.last_interval_processed_tasks, 1);
499
+ let nexus_task_slots = heartbeat.nexus_task_slots_info.clone().unwrap();
500
+ assert_eq!(nexus_task_slots.current_available_slots, 0);
501
+ assert_eq!(nexus_task_slots.current_used_slots, 0);
502
+ assert_eq!(nexus_task_slots.slot_supplier_kind, "Fixed");
503
+ let local_activity_task_slots = heartbeat.local_activity_slots_info.clone().unwrap();
504
+ assert_eq!(local_activity_task_slots.current_available_slots, 100);
505
+ assert_eq!(local_activity_task_slots.current_used_slots, 0);
506
+ assert_eq!(local_activity_task_slots.slot_supplier_kind, "Fixed");
507
+
508
+ let workflow_poller_info = heartbeat.workflow_poller_info.unwrap();
509
+ assert!(!workflow_poller_info.is_autoscaling);
510
+ assert!(within_two_minutes_ts(
511
+ workflow_poller_info.last_successful_poll_time.unwrap()
512
+ ));
513
+ let sticky_poller_info = heartbeat.workflow_sticky_poller_info.unwrap();
514
+ assert!(!sticky_poller_info.is_autoscaling);
515
+ assert!(within_two_minutes_ts(
516
+ sticky_poller_info.last_successful_poll_time.unwrap()
517
+ ));
518
+ let nexus_poller_info = heartbeat.nexus_poller_info.unwrap();
519
+ assert!(!nexus_poller_info.is_autoscaling);
520
+ assert!(nexus_poller_info.last_successful_poll_time.is_none());
521
+ let activity_poller_info = heartbeat.activity_poller_info.unwrap();
522
+ assert!(!activity_poller_info.is_autoscaling);
523
+ assert!(within_two_minutes_ts(
524
+ activity_poller_info.last_successful_poll_time.unwrap()
525
+ ));
526
+
527
+ assert_eq!(heartbeat.total_sticky_cache_hit, 2);
528
+ assert_eq!(heartbeat.current_sticky_cache_size, 0);
529
+ assert_eq!(
530
+ heartbeat.plugins,
531
+ vec![
532
+ PluginInfo {
533
+ name: "plugin1".to_string(),
534
+ version: "1".to_string()
535
+ },
536
+ PluginInfo {
537
+ name: "plugin2".to_string(),
538
+ version: "2".to_string()
539
+ }
540
+ ]
541
+ );
542
+ }
543
+
544
+ #[tokio::test]
545
+ async fn worker_heartbeat_sticky_cache_miss() {
546
+ let wf_name = "worker_heartbeat_cache_miss";
547
+ let mut starter = new_no_metrics_starter(wf_name);
548
+ starter
549
+ .worker_config
550
+ .max_cached_workflows(1_usize)
551
+ .max_outstanding_workflow_tasks(2_usize);
552
+
553
+ let mut worker = starter.worker().await;
554
+ worker.fetch_results = false;
555
+ let worker_key = worker.worker_instance_key().to_string();
556
+ let worker_core = worker.core_worker.clone();
557
+ let submitter = worker.get_submitter_handle();
558
+ let wf_opts = starter.workflow_options.clone();
559
+ let client = starter.get_client().await;
560
+ let client_for_orchestrator = client.clone();
561
+
562
+ static HISTORY_WF1_ACTIVITY_STARTED: Notify = Notify::const_new();
563
+ static HISTORY_WF1_ACTIVITY_FINISH: Notify = Notify::const_new();
564
+ static HISTORY_WF2_ACTIVITY_STARTED: Notify = Notify::const_new();
565
+ static HISTORY_WF2_ACTIVITY_FINISH: Notify = Notify::const_new();
566
+
567
+ worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
568
+ let wf_marker = ctx
569
+ .get_args()
570
+ .first()
571
+ .and_then(|p| String::from_json_payload(p).ok())
572
+ .unwrap_or_else(|| "wf1".to_string());
573
+
574
+ ctx.activity(ActivityOptions {
575
+ activity_type: "sticky_cache_history_act".to_string(),
576
+ input: wf_marker.clone().as_json_payload().expect("serialize"),
577
+ start_to_close_timeout: Some(Duration::from_secs(5)),
578
+ ..Default::default()
579
+ })
580
+ .await;
581
+
582
+ Ok(().into())
583
+ });
584
+ worker.register_activity(
585
+ "sticky_cache_history_act",
586
+ |_ctx: ActContext, marker: String| async move {
587
+ match marker.as_str() {
588
+ "wf1" => {
589
+ HISTORY_WF1_ACTIVITY_STARTED.notify_one();
590
+ HISTORY_WF1_ACTIVITY_FINISH.notified().await;
591
+ }
592
+ "wf2" => {
593
+ HISTORY_WF2_ACTIVITY_STARTED.notify_one();
594
+ HISTORY_WF2_ACTIVITY_FINISH.notified().await;
595
+ }
596
+ _ => {}
597
+ }
598
+ Ok(marker)
599
+ },
600
+ );
601
+
602
+ let wf1_id = format!("{wf_name}_wf1");
603
+ let wf2_id = format!("{wf_name}_wf2");
604
+
605
+ let orchestrator = async move {
606
+ let wf1_run = submitter
607
+ .submit_wf(
608
+ wf1_id.clone(),
609
+ wf_name.to_string(),
610
+ vec!["wf1".to_string().as_json_payload().unwrap()],
611
+ wf_opts.clone(),
612
+ )
613
+ .await
614
+ .unwrap();
615
+
616
+ HISTORY_WF1_ACTIVITY_STARTED.notified().await;
617
+
618
+ let wf2_run = submitter
619
+ .submit_wf(
620
+ wf2_id.clone(),
621
+ wf_name.to_string(),
622
+ vec!["wf2".to_string().as_json_payload().unwrap()],
623
+ wf_opts,
624
+ )
625
+ .await
626
+ .unwrap();
627
+
628
+ HISTORY_WF2_ACTIVITY_STARTED.notified().await;
629
+
630
+ HISTORY_WF1_ACTIVITY_FINISH.notify_one();
631
+ let handle1 = client_for_orchestrator.get_untyped_workflow_handle(wf1_id, wf1_run);
632
+ handle1
633
+ .get_workflow_result(Default::default())
634
+ .await
635
+ .expect("wf1 result");
636
+
637
+ HISTORY_WF2_ACTIVITY_FINISH.notify_one();
638
+ let handle2 = client_for_orchestrator.get_untyped_workflow_handle(wf2_id, wf2_run);
639
+ handle2
640
+ .get_workflow_result(Default::default())
641
+ .await
642
+ .expect("wf2 result");
643
+
644
+ worker_core.initiate_shutdown();
645
+ };
646
+
647
+ let mut worker_runner = worker;
648
+ let runner = async move {
649
+ worker_runner.run_until_done().await.unwrap();
650
+ };
651
+
652
+ tokio::join!(orchestrator, runner);
653
+
654
+ sleep(Duration::from_secs(2)).await;
655
+ let mut heartbeats =
656
+ list_worker_heartbeats(&client, format!("WorkerInstanceKey=\"{worker_key}\"")).await;
657
+ assert_eq!(heartbeats.len(), 1);
658
+ let heartbeat = heartbeats.pop().unwrap();
659
+
660
+ assert!(heartbeat.total_sticky_cache_miss >= 1);
661
+ assert_eq!(heartbeat.worker_instance_key, worker_key);
662
+ }
663
+
664
+ #[tokio::test]
665
+ async fn worker_heartbeat_multiple_workers() {
666
+ let wf_name = "worker_heartbeat_multi_workers";
667
+ let mut starter = new_no_metrics_starter(wf_name);
668
+ starter
669
+ .worker_config
670
+ .max_outstanding_workflow_tasks(5_usize)
671
+ .max_cached_workflows(5_usize);
672
+
673
+ let client = starter.get_client().await;
674
+ let starting_hb_len = list_worker_heartbeats(&client, String::new()).await.len();
675
+
676
+ let mut worker_a = starter.worker().await;
677
+ worker_a.register_wf(wf_name.to_string(), |_ctx: WfContext| async move {
678
+ Ok(().into())
679
+ });
680
+ worker_a.register_activity("failing_act", |_ctx: ActContext, _: String| async move {
681
+ Ok(())
682
+ });
683
+
684
+ let mut starter_b = starter.clone_no_worker();
685
+ let mut worker_b = starter_b.worker().await;
686
+ worker_b.register_wf(wf_name.to_string(), |_ctx: WfContext| async move {
687
+ Ok(().into())
688
+ });
689
+ worker_b.register_activity("failing_act", |_ctx: ActContext, _: String| async move {
690
+ Ok(())
691
+ });
692
+
693
+ let worker_a_key = worker_a.worker_instance_key().to_string();
694
+ let worker_b_key = worker_b.worker_instance_key().to_string();
695
+ let _ = starter.start_with_worker(wf_name, &mut worker_a).await;
696
+ worker_a.run_until_done().await.unwrap();
697
+
698
+ let _ = starter_b.start_with_worker(wf_name, &mut worker_b).await;
699
+ worker_b.run_until_done().await.unwrap();
700
+
701
+ sleep(Duration::from_secs(2)).await;
702
+
703
+ let all = list_worker_heartbeats(&client, String::new()).await;
704
+ let keys: HashSet<_> = all
705
+ .iter()
706
+ .map(|hb| hb.worker_instance_key.clone())
707
+ .collect();
708
+ assert!(keys.contains(&worker_a_key));
709
+ assert!(keys.contains(&worker_b_key));
710
+
711
+ // Verify both heartbeats contain the same shared process_key
712
+ let process_keys: HashSet<_> = all
713
+ .iter()
714
+ .filter_map(|hb| hb.host_info.as_ref().map(|info| info.process_key.clone()))
715
+ .collect();
716
+ assert!(process_keys.len() > starting_hb_len);
717
+
718
+ let filtered =
719
+ list_worker_heartbeats(&client, format!("WorkerInstanceKey=\"{worker_a_key}\"")).await;
720
+ assert_eq!(filtered.len(), 1);
721
+ assert_eq!(filtered[0].worker_instance_key, worker_a_key);
722
+
723
+ // Verify describe worker gives the same heartbeat as listworker
724
+ let mut raw_client = client.as_ref().clone();
725
+ let describe_worker_a = WorkflowService::describe_worker(
726
+ &mut raw_client,
727
+ DescribeWorkerRequest {
728
+ namespace: client.namespace().to_owned(),
729
+ worker_instance_key: worker_a_key.to_string(),
730
+ }
731
+ .into_request(),
732
+ )
733
+ .await
734
+ .unwrap()
735
+ .into_inner()
736
+ .worker_info
737
+ .unwrap()
738
+ .worker_heartbeat
739
+ .unwrap();
740
+ assert_eq!(describe_worker_a, filtered[0]);
741
+
742
+ let filtered_b =
743
+ list_worker_heartbeats(&client, format!("WorkerInstanceKey = \"{worker_b_key}\"")).await;
744
+ assert_eq!(filtered_b.len(), 1);
745
+ assert_eq!(filtered_b[0].worker_instance_key, worker_b_key);
746
+ let describe_worker_b = WorkflowService::describe_worker(
747
+ &mut raw_client,
748
+ DescribeWorkerRequest {
749
+ namespace: client.namespace().to_owned(),
750
+ worker_instance_key: worker_b_key.to_string(),
751
+ }
752
+ .into_request(),
753
+ )
754
+ .await
755
+ .unwrap()
756
+ .into_inner()
757
+ .worker_info
758
+ .unwrap()
759
+ .worker_heartbeat
760
+ .unwrap();
761
+ assert_eq!(describe_worker_b, filtered_b[0]);
762
+ }
763
+
764
+ #[tokio::test]
765
+ async fn worker_heartbeat_failure_metrics() {
766
+ const WORKFLOW_CONTINUE_SIGNAL: &str = "workflow-continue";
767
+
768
+ let wf_name = "worker_heartbeat_failure_metrics";
769
+ let mut starter = new_no_metrics_starter(wf_name);
770
+ starter.worker_config.max_outstanding_activities(5_usize);
771
+
772
+ let mut worker = starter.worker().await;
773
+ let worker_instance_key = worker.worker_instance_key();
774
+ static ACT_COUNT: AtomicU64 = AtomicU64::new(0);
775
+ static WF_COUNT: AtomicU64 = AtomicU64::new(0);
776
+ static ACT_FAIL: Notify = Notify::const_new();
777
+ static WF_FAIL: Notify = Notify::const_new();
778
+ worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
779
+ let _ = ctx
780
+ .activity(ActivityOptions {
781
+ activity_type: "failing_act".to_string(),
782
+ input: "boom".as_json_payload().expect("serialize"),
783
+ start_to_close_timeout: Some(Duration::from_secs(5)),
784
+ retry_policy: Some(RetryPolicy {
785
+ initial_interval: Some(prost_dur!(from_millis(10))),
786
+ backoff_coefficient: 1.0,
787
+ maximum_attempts: 4,
788
+ ..Default::default()
789
+ }),
790
+ ..Default::default()
791
+ })
792
+ .await;
793
+
794
+ if WF_COUNT.load(Ordering::Relaxed) == 0 {
795
+ WF_COUNT.fetch_add(1, Ordering::Relaxed);
796
+ WF_FAIL.notify_one();
797
+ panic!("expected WF panic");
798
+ }
799
+
800
+ // Signal here to avoid workflow from completing and shutdown heartbeat from sending
801
+ // before we check workflow_slots.last_interval_failure_tasks
802
+ let mut proceed_signal = ctx.make_signal_channel(WORKFLOW_CONTINUE_SIGNAL);
803
+ proceed_signal.next().await.unwrap();
804
+ Ok(().into())
805
+ });
806
+
807
+ worker.register_activity("failing_act", |_ctx: ActContext, _: String| async move {
808
+ if ACT_COUNT.load(Ordering::Relaxed) == 3 {
809
+ return Ok(());
810
+ }
811
+ ACT_COUNT.fetch_add(1, Ordering::Relaxed);
812
+ ACT_FAIL.notify_one();
813
+ Err(anyhow!("Expected error").into())
814
+ });
815
+
816
+ let worker_key = worker_instance_key.to_string();
817
+ starter.workflow_options.retry_policy = Some(RetryPolicy {
818
+ maximum_attempts: 2,
819
+ ..Default::default()
820
+ });
821
+
822
+ let _ = starter.start_with_worker(wf_name, &mut worker).await;
823
+
824
+ let test_fut = async {
825
+ ACT_FAIL.notified().await;
826
+ let client = starter.get_client().await;
827
+ eventually(
828
+ || async {
829
+ let mut raw_client = (*client).clone();
830
+
831
+ let workers_list = WorkflowService::list_workers(
832
+ &mut raw_client,
833
+ ListWorkersRequest {
834
+ namespace: client.namespace().to_owned(),
835
+ page_size: 100,
836
+ next_page_token: Vec::new(),
837
+ query: String::new(),
838
+ }
839
+ .into_request(),
840
+ )
841
+ .await
842
+ .unwrap()
843
+ .into_inner();
844
+ let worker_info = workers_list
845
+ .workers_info
846
+ .iter()
847
+ .find(|worker_info| {
848
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
849
+ hb.worker_instance_key == worker_instance_key.to_string()
850
+ } else {
851
+ false
852
+ }
853
+ })
854
+ .unwrap();
855
+ let heartbeat = worker_info.worker_heartbeat.as_ref().unwrap();
856
+ assert_eq!(
857
+ heartbeat.worker_instance_key,
858
+ worker_instance_key.to_string()
859
+ );
860
+ let activity_slots = heartbeat.activity_task_slots_info.clone().unwrap();
861
+ if activity_slots.last_interval_failure_tasks >= 1 {
862
+ return Ok(());
863
+ }
864
+ Err("activity_slots.last_interval_failure_tasks still 0, retrying")
865
+ },
866
+ Duration::from_millis(1500),
867
+ )
868
+ .await
869
+ .unwrap();
870
+
871
+ WF_FAIL.notified().await;
872
+
873
+ eventually(
874
+ || async {
875
+ let mut raw_client = (*client).clone();
876
+ let workers_list = WorkflowService::list_workers(
877
+ &mut raw_client,
878
+ ListWorkersRequest {
879
+ namespace: client.namespace().to_owned(),
880
+ page_size: 100,
881
+ next_page_token: Vec::new(),
882
+ query: String::new(),
883
+ }
884
+ .into_request(),
885
+ )
886
+ .await
887
+ .unwrap()
888
+ .into_inner();
889
+ let worker_info = workers_list
890
+ .workers_info
891
+ .iter()
892
+ .find(|worker_info| {
893
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
894
+ hb.worker_instance_key == worker_instance_key.to_string()
895
+ } else {
896
+ false
897
+ }
898
+ })
899
+ .unwrap();
900
+
901
+ let heartbeat = worker_info.worker_heartbeat.as_ref().unwrap();
902
+ let workflow_slots = heartbeat.workflow_task_slots_info.clone().unwrap();
903
+ if workflow_slots.last_interval_failure_tasks >= 1 {
904
+ return Ok(());
905
+ }
906
+ Err("workflow_slots.last_interval_failure_tasks still 0, retrying")
907
+ },
908
+ Duration::from_millis(1500),
909
+ )
910
+ .await
911
+ .unwrap();
912
+ client
913
+ .signal_workflow_execution(
914
+ starter.get_wf_id().to_string(),
915
+ String::new(),
916
+ WORKFLOW_CONTINUE_SIGNAL.to_string(),
917
+ None,
918
+ None,
919
+ )
920
+ .await
921
+ .unwrap();
922
+ };
923
+
924
+ let runner = async move {
925
+ worker.run_until_done().await.unwrap();
926
+ };
927
+ tokio::join!(test_fut, runner);
928
+
929
+ let client = starter.get_client().await;
930
+ let mut heartbeats =
931
+ list_worker_heartbeats(&client, format!("WorkerInstanceKey=\"{worker_key}\"")).await;
932
+ assert_eq!(heartbeats.len(), 1);
933
+ let heartbeat = heartbeats.pop().unwrap();
934
+
935
+ let activity_slots = heartbeat.activity_task_slots_info.unwrap();
936
+ assert_eq!(activity_slots.total_failed_tasks, 3);
937
+
938
+ let workflow_slots = heartbeat.workflow_task_slots_info.unwrap();
939
+ assert_eq!(workflow_slots.total_failed_tasks, 1);
940
+ }
941
+
942
+ #[tokio::test]
943
+ async fn worker_heartbeat_no_runtime_heartbeat() {
944
+ let wf_name = "worker_heartbeat_no_runtime_heartbeat";
945
+ let runtimeopts = RuntimeOptionsBuilder::default()
946
+ .telemetry_options(get_integ_telem_options())
947
+ .heartbeat_interval(None) // Turn heartbeating off
948
+ .build()
949
+ .unwrap();
950
+ let rt = CoreRuntime::new_assume_tokio(runtimeopts).unwrap();
951
+ let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
952
+ let mut worker = starter.worker().await;
953
+ let worker_instance_key = worker.worker_instance_key();
954
+
955
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
956
+ ctx.activity(ActivityOptions {
957
+ activity_type: "pass_fail_act".to_string(),
958
+ input: "pass".as_json_payload().expect("serializes fine"),
959
+ start_to_close_timeout: Some(Duration::from_secs(1)),
960
+ ..Default::default()
961
+ })
962
+ .await;
963
+ Ok(().into())
964
+ });
965
+
966
+ worker.register_activity("pass_fail_act", |_ctx: ActContext, i: String| async move {
967
+ Ok(i)
968
+ });
969
+
970
+ starter
971
+ .start_with_worker(wf_name.to_owned(), &mut worker)
972
+ .await;
973
+
974
+ worker.run_until_done().await.unwrap();
975
+ let client = starter.get_client().await;
976
+ let mut raw_client = (*client).clone();
977
+ let workers_list = WorkflowService::list_workers(
978
+ &mut raw_client,
979
+ ListWorkersRequest {
980
+ namespace: client.namespace().to_owned(),
981
+ page_size: 100,
982
+ next_page_token: Vec::new(),
983
+ query: String::new(),
984
+ }
985
+ .into_request(),
986
+ )
987
+ .await
988
+ .unwrap()
989
+ .into_inner();
990
+
991
+ // Ensure worker has not ever heartbeated
992
+ let heartbeat = workers_list.workers_info.iter().find(|worker_info| {
993
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
994
+ hb.worker_instance_key == worker_instance_key.to_string()
995
+ } else {
996
+ false
997
+ }
998
+ });
999
+ assert!(heartbeat.is_none());
1000
+ }
1001
+
1002
+ #[tokio::test]
1003
+ async fn worker_heartbeat_skip_client_worker_set_check() {
1004
+ let wf_name = "worker_heartbeat_skip_client_worker_set_check";
1005
+ let runtimeopts = RuntimeOptionsBuilder::default()
1006
+ .telemetry_options(get_integ_telem_options())
1007
+ .heartbeat_interval(Some(Duration::from_secs(1)))
1008
+ .build()
1009
+ .unwrap();
1010
+ let rt = CoreRuntime::new_assume_tokio(runtimeopts).unwrap();
1011
+ let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
1012
+ starter.worker_config.skip_client_worker_set_check(true);
1013
+ let mut worker = starter.worker().await;
1014
+ let worker_instance_key = worker.worker_instance_key();
1015
+
1016
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
1017
+ ctx.activity(ActivityOptions {
1018
+ activity_type: "pass_fail_act".to_string(),
1019
+ input: "pass".as_json_payload().expect("serializes fine"),
1020
+ start_to_close_timeout: Some(Duration::from_secs(1)),
1021
+ ..Default::default()
1022
+ })
1023
+ .await;
1024
+ Ok(().into())
1025
+ });
1026
+
1027
+ worker.register_activity("pass_fail_act", |_ctx: ActContext, i: String| async move {
1028
+ Ok(i)
1029
+ });
1030
+
1031
+ starter
1032
+ .start_with_worker(wf_name.to_owned(), &mut worker)
1033
+ .await;
1034
+
1035
+ worker.run_until_done().await.unwrap();
1036
+ let client = starter.get_client().await;
1037
+ let mut raw_client = (*client).clone();
1038
+ let workers_list = WorkflowService::list_workers(
1039
+ &mut raw_client,
1040
+ ListWorkersRequest {
1041
+ namespace: client.namespace().to_owned(),
1042
+ page_size: 100,
1043
+ next_page_token: Vec::new(),
1044
+ query: String::new(),
1045
+ }
1046
+ .into_request(),
1047
+ )
1048
+ .await
1049
+ .unwrap()
1050
+ .into_inner();
1051
+
1052
+ // Ensure worker still heartbeats
1053
+ let heartbeat = workers_list.workers_info.iter().find(|worker_info| {
1054
+ if let Some(hb) = worker_info.worker_heartbeat.as_ref() {
1055
+ hb.worker_instance_key == worker_instance_key.to_string()
1056
+ } else {
1057
+ false
1058
+ }
1059
+ });
1060
+ assert!(heartbeat.is_some());
1061
+ }