@temporalio/core-bridge 1.14.2-canary-release-testing.0 → 1.16.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 (233) hide show
  1. package/Cargo.lock +794 -650
  2. package/bridge-macros/src/derive_tryintojs.rs +40 -0
  3. package/lib/native.d.ts +24 -3
  4. package/package.json +4 -4
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  11. package/sdk-core/AGENTS.md +42 -31
  12. package/sdk-core/Cargo.toml +4 -1
  13. package/sdk-core/README.md +19 -13
  14. package/sdk-core/crates/client/Cargo.toml +4 -0
  15. package/sdk-core/crates/client/README.md +139 -0
  16. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  17. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  18. package/sdk-core/crates/client/src/errors.rs +294 -0
  19. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +370 -159
  20. package/sdk-core/crates/client/src/lib.rs +920 -1326
  21. package/sdk-core/crates/client/src/metrics.rs +24 -33
  22. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  23. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  24. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  25. package/sdk-core/crates/client/src/retry.rs +99 -54
  26. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +104 -29
  27. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  28. package/sdk-core/crates/common/Cargo.toml +62 -3
  29. package/sdk-core/crates/common/build.rs +742 -12
  30. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  31. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/create-release.yml +0 -5
  32. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  33. package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
  34. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  35. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +3329 -2647
  36. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +2734 -708
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +27 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
  43. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
  44. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
  45. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +63 -15
  46. package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
  47. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  48. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +111 -17
  49. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +21 -0
  50. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +20 -1
  51. package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
  52. package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  53. package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
  54. package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
  55. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
  56. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +347 -23
  57. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +242 -43
  58. package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
  59. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +9 -2
  60. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +8 -0
  61. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +22 -5
  62. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  63. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  64. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  65. package/sdk-core/crates/common/src/lib.rs +15 -211
  66. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  67. package/sdk-core/crates/common/src/priority.rs +110 -0
  68. package/sdk-core/crates/common/src/protos/canned_histories.rs +19 -0
  69. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  70. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  71. package/sdk-core/crates/common/src/protos/mod.rs +134 -27
  72. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  73. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  74. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +11 -16
  75. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  76. package/sdk-core/crates/common/src/telemetry/metrics.rs +272 -225
  77. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  78. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  79. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  80. package/sdk-core/crates/common/src/telemetry.rs +278 -19
  81. package/sdk-core/crates/common/src/worker.rs +68 -636
  82. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  83. package/sdk-core/crates/macros/Cargo.toml +5 -1
  84. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  85. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  86. package/sdk-core/crates/macros/src/lib.rs +138 -512
  87. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  88. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  89. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  90. package/sdk-core/crates/sdk/README.md +415 -0
  91. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  92. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  93. package/sdk-core/crates/sdk/src/lib.rs +759 -442
  94. package/sdk-core/crates/sdk/src/workflow_context/options.rs +64 -35
  95. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  96. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  97. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  98. package/sdk-core/crates/sdk-core/Cargo.toml +59 -65
  99. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +45 -54
  100. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  101. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  102. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  103. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +22 -21
  104. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  105. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  106. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  107. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +647 -27
  108. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +46 -41
  109. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +13 -16
  110. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  111. package/sdk-core/crates/sdk-core/src/lib.rs +60 -123
  112. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  113. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +411 -32
  114. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  115. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  116. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +183 -198
  117. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -281
  118. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +35 -16
  119. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  120. package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
  121. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  122. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  123. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +11 -5
  124. package/sdk-core/crates/sdk-core/src/worker/client.rs +104 -86
  125. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +10 -14
  126. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1175 -241
  127. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +150 -23
  128. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  129. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  130. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +25 -27
  131. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +64 -44
  132. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  133. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
  134. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  135. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +28 -4
  136. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +20 -41
  137. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +50 -9
  138. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  139. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  140. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  141. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  142. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  143. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  144. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  145. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  146. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  147. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  148. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  149. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  150. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  151. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  152. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  153. package/sdk-core/crates/sdk-core/tests/common/mod.rs +281 -236
  154. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  155. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +9 -14
  156. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -66
  157. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +306 -268
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +37 -38
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +49 -40
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +447 -300
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +157 -157
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -463
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +389 -265
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +250 -185
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -49
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +437 -327
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -58
  175. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -30
  176. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -251
  177. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  178. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +110 -46
  179. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -149
  180. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -32
  181. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1040
  182. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -43
  183. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +402 -245
  184. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +343 -207
  185. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  186. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  187. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +155 -140
  188. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -113
  189. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -44
  190. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -48
  191. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -56
  192. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +365 -242
  193. package/sdk-core/crates/sdk-core/tests/main.rs +22 -16
  194. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +233 -187
  195. package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
  196. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +73 -27
  197. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  198. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  199. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  200. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  201. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  202. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  203. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  204. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  205. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  206. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  207. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +8 -1
  208. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +37 -26
  209. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +180 -87
  210. package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
  211. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +10 -16
  212. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -67
  213. package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
  214. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +57 -22
  215. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +108 -12
  216. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +9 -52
  217. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +74 -91
  218. package/sdk-core/rustfmt.toml +2 -1
  219. package/src/client.rs +206 -289
  220. package/src/helpers/try_into_js.rs +88 -2
  221. package/src/metrics.rs +277 -35
  222. package/src/runtime.rs +94 -45
  223. package/src/testing.rs +9 -16
  224. package/src/worker.rs +86 -68
  225. package/ts/native.ts +39 -3
  226. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  227. package/sdk-core/crates/common/src/errors.rs +0 -85
  228. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  229. package/sdk-core/crates/macros/LICENSE.txt +0 -21
  230. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  231. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  232. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  233. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -1,11 +1,12 @@
1
1
  use crate::{
2
2
  common::{
3
- ANY_PORT, CoreWfStarter, NAMESPACE, OTEL_URL_ENV_VAR, PROMETHEUS_QUERY_API,
4
- get_integ_runtime_options, get_integ_server_options, get_integ_telem_options, prom_metrics,
3
+ ANY_PORT, CoreWfStarter, NAMESPACE, OTEL_URL_ENV_VAR, PROMETHEUS_QUERY_API, eventually,
4
+ get_integ_client, get_integ_connection, get_integ_runtime_options,
5
+ get_integ_server_options, get_integ_telem_options, prom_metrics,
5
6
  },
6
7
  integ_tests::mk_nexus_endpoint,
7
8
  };
8
- use anyhow::anyhow;
9
+ use anyhow::{anyhow, bail};
9
10
  use assert_matches::assert_matches;
10
11
  use std::{
11
12
  collections::HashMap,
@@ -15,15 +16,15 @@ use std::{
15
16
  time::Duration,
16
17
  };
17
18
  use temporalio_client::{
18
- REQUEST_LATENCY_HISTOGRAM_NAME, WorkflowClientTrait, WorkflowOptions, WorkflowService,
19
+ Connection, NamespacedClient, REQUEST_LATENCY_HISTOGRAM_NAME, UntypedQuery, UntypedWorkflow,
20
+ WorkflowExecutionInfo, WorkflowQueryOptions, WorkflowStartOptions, grpc::WorkflowService,
19
21
  };
20
22
  use temporalio_common::{
21
- Worker,
22
- errors::PollError,
23
+ data_converters::RawValue,
23
24
  prost_dur,
24
25
  protos::{
25
26
  coresdk::{
26
- ActivityTaskCompletion, AsJsonPayloadExt,
27
+ ActivityTaskCompletion,
27
28
  activity_result::ActivityExecutionResult,
28
29
  nexus::{NexusTaskCompletion, nexus_task, nexus_task_completion},
29
30
  workflow_activation::{WorkflowActivationJob, workflow_activation_job},
@@ -36,38 +37,47 @@ use temporalio_common::{
36
37
  },
37
38
  temporal::api::{
38
39
  common::v1::RetryPolicy,
39
- enums::v1::{NexusHandlerErrorRetryBehavior, WorkflowIdReusePolicy},
40
+ enums::v1::{
41
+ NexusHandlerErrorRetryBehavior, WorkflowIdConflictPolicy, WorkflowIdReusePolicy,
42
+ },
40
43
  failure::v1::Failure,
41
- nexus,
42
- nexus::v1::{
43
- HandlerError, StartOperationResponse, UnsuccessfulOperationError, request::Variant,
44
- start_operation_response,
44
+ nexus::{
45
+ self,
46
+ v1::{
47
+ HandlerError, StartOperationResponse, UnsuccessfulOperationError,
48
+ request::Variant, start_operation_response,
49
+ },
45
50
  },
46
- query::v1::WorkflowQuery,
47
51
  workflowservice::v1::{DescribeNamespaceRequest, ListNamespacesRequest},
48
52
  },
49
53
  },
50
54
  telemetry::{
51
- HistogramBucketOverrides, OtelCollectorOptionsBuilder, OtlpProtocol,
52
- PrometheusExporterOptionsBuilder, TaskQueueLabelStrategy, TelemetryOptionsBuilder,
55
+ HistogramBucketOverrides, OtelCollectorOptions, OtlpProtocol, PrometheusExporterOptions,
56
+ TaskQueueLabelStrategy, TelemetryOptions, build_otlp_metric_exporter,
53
57
  metrics::{
54
58
  CoreMeter, CounterBase, Gauge, GaugeBase, HistogramBase, MetricKeyValue,
55
- MetricParameters, MetricParametersBuilder, NewAttributes,
59
+ MetricParameters, NewAttributes, WORKFLOW_TASK_EXECUTION_LATENCY_HISTOGRAM_NAME,
56
60
  },
61
+ start_prometheus_metric_exporter,
57
62
  },
58
- worker::{
59
- PollerBehavior, SlotKind, SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext,
60
- SlotSupplier, SlotSupplierPermit, WorkerConfigBuilder, WorkerTaskTypes,
61
- WorkerVersioningStrategy, WorkflowSlotKind,
62
- },
63
+ worker::WorkerTaskTypes,
63
64
  };
65
+ use temporalio_macros::{activities, workflow, workflow_methods};
64
66
  use temporalio_sdk::{
65
- ActContext, ActivityError, ActivityOptions, CancellableFuture, LocalActivityOptions,
66
- NexusOperationOptions, WfContext,
67
+ ActivityOptions, CancellableFuture, LocalActivityOptions, NexusOperationOptions,
68
+ WorkflowContext, WorkflowResult,
69
+ activities::{ActivityContext, ActivityError},
67
70
  };
68
71
  use temporalio_sdk_core::{
69
- CoreRuntime, FixedSizeSlotSupplier, TokioRuntimeBuilder, TunerBuilder, init_worker,
70
- telemetry::{WORKFLOW_TASK_EXECUTION_LATENCY_HISTOGRAM_NAME, build_otlp_metric_exporter},
72
+ CoreRuntime, FixedSizeSlotSupplier, PollError, PollerBehavior, SlotKind, SlotMarkUsedContext,
73
+ SlotReleaseContext, SlotReservationContext, SlotSupplier, SlotSupplierPermit,
74
+ TokioRuntimeBuilder, TunerBuilder, WorkerConfig, WorkerVersioningStrategy, WorkflowSlotKind,
75
+ init_worker,
76
+ replay::TestHistoryBuilder,
77
+ test_help::{
78
+ MockPollCfg, ResponseType, TemporalMeter, WorkerExt, WorkerTestHelpers, build_mock_pollers,
79
+ mock_worker, mock_worker_client,
80
+ },
71
81
  };
72
82
  use tokio::{join, sync::Barrier};
73
83
  use tonic::IntoRequest;
@@ -83,30 +93,30 @@ async fn prometheus_metrics_exported(
83
93
  #[values(true, false)] use_seconds_latency: bool,
84
94
  #[values(true, false)] custom_buckets: bool,
85
95
  ) {
86
- let mut opts_builder = PrometheusExporterOptionsBuilder::default();
87
- opts_builder
96
+ let opts = PrometheusExporterOptions::builder()
88
97
  .global_tags(HashMap::from([("global".to_string(), "hi!".to_string())]))
89
98
  .socket_addr(ANY_PORT.parse().unwrap())
90
- .use_seconds_for_durations(use_seconds_latency);
91
- if custom_buckets {
92
- opts_builder.histogram_bucket_overrides(HistogramBucketOverrides {
93
- overrides: {
94
- let mut hm = HashMap::new();
95
- hm.insert(REQUEST_LATENCY_HISTOGRAM_NAME.to_string(), vec![1337.0]);
96
- hm
97
- },
98
- });
99
- }
100
- let (telemopts, addr, _aborter) = prom_metrics(Some(opts_builder.build().unwrap()));
99
+ .use_seconds_for_durations(use_seconds_latency)
100
+ .histogram_bucket_overrides(if custom_buckets {
101
+ HistogramBucketOverrides {
102
+ overrides: {
103
+ let mut hm = HashMap::new();
104
+ hm.insert(REQUEST_LATENCY_HISTOGRAM_NAME.to_string(), vec![1337.0]);
105
+ hm
106
+ },
107
+ }
108
+ } else {
109
+ Default::default()
110
+ })
111
+ .build();
112
+ let (telemopts, addr, _aborter) = prom_metrics(Some(opts));
101
113
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
102
- let opts = get_integ_server_options();
103
- let mut raw_client = opts
104
- .connect_no_namespace(rt.telemetry().get_temporal_metric_meter())
105
- .await
106
- .unwrap();
107
- assert!(raw_client.get_client().capabilities().is_some());
114
+ let mut opts = get_integ_server_options();
115
+ opts.metrics_meter = rt.telemetry().get_temporal_metric_meter();
116
+ let mut connection = Connection::connect(opts).await.unwrap();
117
+ assert!(connection.capabilities().is_some());
108
118
 
109
- let _ = raw_client
119
+ let _ = connection
110
120
  .list_namespaces(ListNamespacesRequest::default().into_request())
111
121
  .await
112
122
  .unwrap();
@@ -138,8 +148,8 @@ async fn prometheus_metrics_exported(
138
148
  assert!(body.contains("temporal_request{"));
139
149
  // Verify non-temporal metrics meter does not prefix
140
150
  let mm = rt.telemetry().get_metric_meter().unwrap();
141
- let g = mm.inner.gauge(MetricParameters::from("mygauge"));
142
- let attrs = mm.inner.new_attributes(NewAttributes::new(vec![]));
151
+ let g = mm.gauge(MetricParameters::from("mygauge"));
152
+ let attrs = mm.new_attributes(NewAttributes::new(vec![]));
143
153
  g.record(42, &attrs);
144
154
  let body = get_text(format!("http://{addr}/metrics")).await;
145
155
  assert!(body.contains("\nmygauge{global=\"hi!\"} 42"));
@@ -151,7 +161,7 @@ async fn one_slot_worker_reports_available_slot() {
151
161
  let tq = "one_slot_worker_tq";
152
162
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
153
163
 
154
- let worker_cfg = WorkerConfigBuilder::default()
164
+ let worker_cfg = WorkerConfig::builder()
155
165
  .namespace(NAMESPACE)
156
166
  .task_queue(tq)
157
167
  .versioning_strategy(WorkerVersioningStrategy::None {
@@ -168,13 +178,9 @@ async fn one_slot_worker_reports_available_slot() {
168
178
  .build()
169
179
  .unwrap();
170
180
 
171
- let client = Arc::new(
172
- get_integ_server_options()
173
- .connect(worker_cfg.namespace.clone(), None)
174
- .await
175
- .expect("Must connect"),
176
- );
177
- let worker = init_worker(&rt, worker_cfg, client.clone()).expect("Worker inits cleanly");
181
+ let connection = get_integ_connection(None).await;
182
+ let client = Arc::new(get_integ_client(worker_cfg.namespace.clone(), None).await);
183
+ let worker = init_worker(&rt, worker_cfg, connection).expect("Worker inits cleanly");
178
184
  let wf_task_barr = Barrier::new(2);
179
185
  let act_task_barr = Barrier::new(2);
180
186
 
@@ -273,16 +279,13 @@ async fn one_slot_worker_reports_available_slot() {
273
279
  // Start a workflow so that a task will get delivered
274
280
  client
275
281
  .start_workflow(
276
- vec![],
277
- tq.to_owned(),
278
- "one_slot_metric_test".to_owned(),
279
- "whatever".to_string(),
280
- None,
281
- WorkflowOptions {
282
- id_reuse_policy: WorkflowIdReusePolicy::TerminateIfRunning,
283
- execution_timeout: Some(Duration::from_secs(5)),
284
- ..Default::default()
285
- },
282
+ UntypedWorkflow::new("whatever"),
283
+ RawValue::default(),
284
+ WorkflowStartOptions::new(tq.to_owned(), "one_slot_metric_test".to_owned())
285
+ .id_conflict_policy(WorkflowIdConflictPolicy::TerminateExisting)
286
+ .id_reuse_policy(WorkflowIdReusePolicy::AllowDuplicate)
287
+ .execution_timeout(Duration::from_secs(5))
288
+ .build(),
286
289
  )
287
290
  .await
288
291
  .unwrap();
@@ -407,7 +410,7 @@ async fn query_of_closed_workflow_doesnt_tick_terminal_metric(
407
410
  let mut starter =
408
411
  CoreWfStarter::new_with_runtime("query_of_closed_workflow_doesnt_tick_terminal_metric", rt);
409
412
  // Disable cache to ensure replay happens completely
410
- starter.worker_config.max_cached_workflows(0_usize);
413
+ starter.sdk_config.max_cached_workflows = 0_usize;
411
414
  let worker = starter.get_worker().await;
412
415
  let run_id = starter.start_wf().await;
413
416
  let task = worker.poll_workflow_activation().await.unwrap();
@@ -464,18 +467,20 @@ async fn query_of_closed_workflow_doesnt_tick_terminal_metric(
464
467
  // Query the now-closed workflow
465
468
  let client = starter.get_client().await;
466
469
  let queryer = async {
467
- client
468
- .query_workflow_execution(
469
- starter.get_wf_id().to_string(),
470
- run_id,
471
- WorkflowQuery {
472
- query_type: "fake_query".to_string(),
473
- query_args: None,
474
- header: None,
475
- },
476
- )
477
- .await
478
- .unwrap();
470
+ WorkflowExecutionInfo {
471
+ namespace: client.namespace(),
472
+ workflow_id: starter.get_wf_id().to_string(),
473
+ run_id: Some(run_id),
474
+ first_execution_run_id: None,
475
+ }
476
+ .bind_untyped(client.clone())
477
+ .query(
478
+ UntypedQuery::new("fake_query"),
479
+ RawValue::empty(),
480
+ WorkflowQueryOptions::default(),
481
+ )
482
+ .await
483
+ .unwrap();
479
484
  };
480
485
  let query_reply = async {
481
486
  // Need to re-complete b/c replay
@@ -535,13 +540,11 @@ fn runtime_new() {
535
540
  let (telemopts, addr, _aborter) = prom_metrics(None);
536
541
  rt.telemetry_mut()
537
542
  .attach_late_init_metrics(telemopts.metrics.unwrap());
538
- let opts = get_integ_server_options();
543
+ let mut opts = get_integ_server_options();
544
+ opts.metrics_meter = rt.telemetry().get_temporal_metric_meter();
539
545
  handle.block_on(async {
540
- let mut raw_client = opts
541
- .connect_no_namespace(rt.telemetry().get_temporal_metric_meter())
542
- .await
543
- .unwrap();
544
- assert!(raw_client.get_client().capabilities().is_some());
546
+ let mut raw_client = Connection::connect(opts).await.unwrap();
547
+ assert!(raw_client.capabilities().is_some());
545
548
  let _ = raw_client
546
549
  .list_namespaces(ListNamespacesRequest::default().into_request())
547
550
  .await
@@ -558,7 +561,7 @@ async fn latency_metrics(
558
561
  #[values(true, false)] show_units: bool,
559
562
  ) {
560
563
  let (telemopts, addr, _aborter) = prom_metrics(Some(
561
- PrometheusExporterOptionsBuilder::default()
564
+ PrometheusExporterOptions::builder()
562
565
  .socket_addr(ANY_PORT.parse().unwrap())
563
566
  .use_seconds_for_durations(use_seconds_latency)
564
567
  .unit_suffix(show_units)
@@ -572,8 +575,7 @@ async fn latency_metrics(
572
575
  hm
573
576
  },
574
577
  })
575
- .build()
576
- .unwrap(),
578
+ .build(),
577
579
  ));
578
580
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
579
581
  let mut starter = CoreWfStarter::new_with_runtime("latency_metrics", rt);
@@ -630,11 +632,11 @@ async fn latency_metrics(
630
632
  async fn request_fail_codes() {
631
633
  let (telemopts, addr, _aborter) = prom_metrics(None);
632
634
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
633
- let opts = get_integ_server_options();
634
- let mut client = opts
635
- .connect(NAMESPACE, rt.telemetry().get_temporal_metric_meter())
636
- .await
637
- .unwrap();
635
+ let mut client = get_integ_client(
636
+ NAMESPACE.to_string(),
637
+ rt.telemetry().get_temporal_metric_meter(),
638
+ )
639
+ .await;
638
640
 
639
641
  // Describe namespace w/ invalid argument (unset namespace field)
640
642
  WorkflowService::describe_namespace(
@@ -663,25 +665,20 @@ async fn request_fail_codes_otel() {
663
665
  .ok()
664
666
  .map(|x| x.parse::<Url>().unwrap())
665
667
  {
666
- let opts = OtelCollectorOptionsBuilder::default()
667
- .url(url)
668
- .build()
669
- .unwrap();
668
+ let opts = OtelCollectorOptions::builder().url(url).build();
670
669
  build_otlp_metric_exporter(opts).unwrap()
671
670
  } else {
672
671
  // skip
673
672
  return;
674
673
  };
675
- let mut telemopts = TelemetryOptionsBuilder::default();
676
674
  let exporter = Arc::new(exporter);
677
- telemopts.metrics(exporter as Arc<dyn CoreMeter>);
678
- let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts.build().unwrap()))
679
- .unwrap();
680
- let opts = get_integ_server_options();
681
- let mut client = opts
682
- .connect(NAMESPACE, rt.telemetry().get_temporal_metric_meter())
683
- .await
684
- .unwrap();
675
+ let telemopts = TelemetryOptions::builder().metrics(exporter as Arc<dyn CoreMeter>);
676
+ let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts.build())).unwrap();
677
+ let mut client = get_integ_client(
678
+ NAMESPACE.to_string(),
679
+ rt.telemetry().get_temporal_metric_meter(),
680
+ )
681
+ .await;
685
682
 
686
683
  for _ in 0..10 {
687
684
  // Describe namespace w/ invalid argument (unset namespace field)
@@ -717,18 +714,16 @@ async fn docker_metrics_with_prometheus(
717
714
  );
718
715
 
719
716
  // Configure the OTLP exporter with HTTP
720
- let opts = OtelCollectorOptionsBuilder::default()
717
+ let opts = OtelCollectorOptions::builder()
721
718
  .url(otel_collector_addr.parse().unwrap())
722
719
  .protocol(otel_protocol)
723
720
  .global_tags(HashMap::from([("test_id".to_string(), test_uid.clone())]))
724
- .build()
725
- .unwrap();
721
+ .build();
726
722
  let exporter = Arc::new(build_otlp_metric_exporter(opts).unwrap());
727
- let telemopts = TelemetryOptionsBuilder::default()
723
+ let telemopts = TelemetryOptions::builder()
728
724
  .metrics(exporter as Arc<dyn CoreMeter>)
729
725
  .metric_prefix(test_uid.clone())
730
- .build()
731
- .unwrap();
726
+ .build();
732
727
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
733
728
  let test_name = "docker_metrics_with_prometheus";
734
729
  let mut starter = CoreWfStarter::new_with_runtime(test_name, rt);
@@ -746,38 +741,48 @@ async fn docker_metrics_with_prometheus(
746
741
  .unwrap();
747
742
 
748
743
  let client = starter.get_client().await;
749
- client.list_namespaces().await.unwrap();
750
-
751
- // Give Prometheus time to scrape metrics
752
- tokio::time::sleep(Duration::from_secs(2)).await;
753
-
754
- // Query Prometheus API for metrics
755
- let client = reqwest::Client::new();
756
- let query = format!("temporal_sdk_{}num_pollers", test_uid.clone());
757
- let response = client
758
- .get(PROMETHEUS_QUERY_API)
759
- .query(&[("query", query)])
760
- .send()
761
- .await
762
- .unwrap()
763
- .json::<serde_json::Value>()
764
- .await
765
- .unwrap();
744
+ WorkflowService::list_namespaces(
745
+ &mut client.clone(),
746
+ ListNamespacesRequest::default().into_request(),
747
+ )
748
+ .await
749
+ .unwrap();
766
750
 
767
- // Validate the Prometheus response
768
- if let Some(data) = response["data"]["result"].as_array() {
769
- assert!(!data.is_empty(), "No metrics found for query: {test_uid}");
770
- assert_eq!(data[0]["metric"]["exported_job"], "temporal-core-sdk");
771
- assert_eq!(data[0]["metric"]["job"], "otel-collector");
772
- assert!(
773
- data[0]["metric"]["task_queue"]
774
- .as_str()
775
- .unwrap()
776
- .starts_with(test_name)
777
- );
778
- } else {
779
- panic!("Invalid Prometheus response: {response:?}");
780
- }
751
+ eventually(
752
+ || async {
753
+ // Query Prometheus API for metrics
754
+ let client = reqwest::Client::new();
755
+ let query = format!("temporal_sdk_{}num_pollers", test_uid.clone());
756
+ let response = client
757
+ .get(PROMETHEUS_QUERY_API)
758
+ .query(&[("query", query.clone())])
759
+ .send()
760
+ .await?
761
+ .json::<serde_json::Value>()
762
+ .await?;
763
+
764
+ // Validate the Prometheus response
765
+ if let Some(data) = response["data"]["result"].as_array() {
766
+ if data.is_empty() {
767
+ bail!("No metrics found for query: {query}");
768
+ }
769
+ assert_eq!(data[0]["metric"]["exported_job"], "temporal-core-sdk");
770
+ assert_eq!(data[0]["metric"]["job"], "otel-collector");
771
+ assert!(
772
+ data[0]["metric"]["task_queue"]
773
+ .as_str()
774
+ .unwrap()
775
+ .starts_with(test_name)
776
+ );
777
+ } else {
778
+ bail!("Invalid Prometheus response: {response:?}");
779
+ }
780
+ Ok(())
781
+ },
782
+ Duration::from_secs(45),
783
+ )
784
+ .await
785
+ .unwrap();
781
786
  }
782
787
 
783
788
  #[tokio::test]
@@ -786,86 +791,110 @@ async fn activity_metrics() {
786
791
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
787
792
  let wf_name = "activity_metrics";
788
793
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
789
- starter
790
- .worker_config
791
- .graceful_shutdown_period(Duration::from_secs(1));
792
- let task_queue = starter.get_task_queue().to_owned();
794
+ starter.sdk_config.graceful_shutdown_period = Some(Duration::from_secs(1));
795
+
796
+ struct PassFailActivities;
797
+ #[activities]
798
+ impl PassFailActivities {
799
+ #[activity(name = "pass_fail_act")]
800
+ async fn pass_fail_act(ctx: ActivityContext, i: String) -> Result<String, ActivityError> {
801
+ match i.as_str() {
802
+ "pass" => Ok("pass".to_string()),
803
+ "cancel" => {
804
+ ctx.cancelled().await;
805
+ Err(ActivityError::cancelled())
806
+ }
807
+ _ => Err(anyhow!("fail").into()),
808
+ }
809
+ }
810
+ }
811
+
812
+ starter.sdk_config.register_activities(PassFailActivities);
793
813
  let mut worker = starter.worker().await;
794
814
 
795
- worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
796
- let normal_act_pass = ctx.activity(ActivityOptions {
797
- activity_type: "pass_fail_act".to_string(),
798
- input: "pass".as_json_payload().expect("serializes fine"),
799
- start_to_close_timeout: Some(Duration::from_secs(1)),
800
- ..Default::default()
801
- });
802
- let normal_act_fail = ctx.activity(ActivityOptions {
803
- activity_type: "pass_fail_act".to_string(),
804
- input: "fail".as_json_payload().expect("serializes fine"),
805
- start_to_close_timeout: Some(Duration::from_secs(1)),
806
- retry_policy: Some(RetryPolicy {
807
- maximum_attempts: 1,
808
- ..Default::default()
809
- }),
810
- ..Default::default()
811
- });
812
- join!(normal_act_pass, normal_act_fail);
813
- let local_act_pass = ctx.local_activity(LocalActivityOptions {
814
- activity_type: "pass_fail_act".to_string(),
815
- input: "pass".as_json_payload().expect("serializes fine"),
816
- ..Default::default()
817
- });
818
- let local_act_fail = ctx.local_activity(LocalActivityOptions {
819
- activity_type: "pass_fail_act".to_string(),
820
- input: "fail".as_json_payload().expect("serializes fine"),
821
- retry_policy: RetryPolicy {
822
- maximum_attempts: 1,
823
- ..Default::default()
824
- },
825
- ..Default::default()
826
- });
827
- let local_act_cancel = ctx.local_activity(LocalActivityOptions {
828
- activity_type: "pass_fail_act".to_string(),
829
- input: "cancel".as_json_payload().expect("serializes fine"),
830
- retry_policy: RetryPolicy {
831
- maximum_attempts: 1,
832
- ..Default::default()
833
- },
834
- ..Default::default()
835
- });
836
- join!(local_act_pass, local_act_fail);
837
- // TODO: Currently takes a WFT b/c of https://github.com/temporalio/sdk-core/issues/856
838
- local_act_cancel.cancel(&ctx);
839
- local_act_cancel.await;
840
- Ok(().into())
841
- });
842
- worker.register_activity("pass_fail_act", |ctx: ActContext, i: String| async move {
843
- match i.as_str() {
844
- "pass" => Ok("pass"),
845
- "cancel" => {
846
- ctx.cancelled().await;
847
- Err(ActivityError::cancelled())
848
- }
849
- _ => Err(anyhow!("fail").into()),
815
+ #[workflow]
816
+ #[derive(Default)]
817
+ struct ActivityMetricsWf;
818
+
819
+ #[workflow_methods]
820
+ impl ActivityMetricsWf {
821
+ #[run]
822
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
823
+ let normal_act_pass = ctx.start_activity(
824
+ PassFailActivities::pass_fail_act,
825
+ "pass".to_string(),
826
+ ActivityOptions {
827
+ start_to_close_timeout: Some(Duration::from_secs(1)),
828
+ ..Default::default()
829
+ },
830
+ );
831
+ let normal_act_fail = ctx.start_activity(
832
+ PassFailActivities::pass_fail_act,
833
+ "fail".to_string(),
834
+ ActivityOptions {
835
+ start_to_close_timeout: Some(Duration::from_secs(1)),
836
+ retry_policy: Some(RetryPolicy {
837
+ maximum_attempts: 1,
838
+ ..Default::default()
839
+ }),
840
+ ..Default::default()
841
+ },
842
+ );
843
+ let _ = join!(normal_act_pass, normal_act_fail);
844
+ let local_act_pass = ctx.start_local_activity(
845
+ PassFailActivities::pass_fail_act,
846
+ "pass".to_string(),
847
+ LocalActivityOptions::default(),
848
+ );
849
+ let local_act_fail = ctx.start_local_activity(
850
+ PassFailActivities::pass_fail_act,
851
+ "fail".to_string(),
852
+ LocalActivityOptions {
853
+ retry_policy: RetryPolicy {
854
+ maximum_attempts: 1,
855
+ ..Default::default()
856
+ },
857
+ ..Default::default()
858
+ },
859
+ );
860
+ let local_act_cancel = ctx.start_local_activity(
861
+ PassFailActivities::pass_fail_act,
862
+ "cancel".to_string(),
863
+ LocalActivityOptions {
864
+ retry_policy: RetryPolicy {
865
+ maximum_attempts: 1,
866
+ ..Default::default()
867
+ },
868
+ ..Default::default()
869
+ },
870
+ );
871
+ let _ = join!(local_act_pass, local_act_fail);
872
+ // TODO: Currently takes a WFT b/c of https://github.com/temporalio/sdk-core/issues/856
873
+ local_act_cancel.cancel();
874
+ let _ = local_act_cancel.await;
875
+ Ok(())
850
876
  }
851
- });
877
+ }
852
878
 
879
+ worker.register_workflow::<ActivityMetricsWf>();
880
+ let task_queue = starter.get_task_queue().to_owned();
881
+ let workflow_id = wf_name.to_owned();
853
882
  worker
854
- .submit_wf(
855
- wf_name.to_owned(),
856
- wf_name.to_owned(),
857
- vec![],
858
- WorkflowOptions::default(),
883
+ .submit_workflow(
884
+ ActivityMetricsWf::run,
885
+ (),
886
+ WorkflowStartOptions::new(task_queue.clone(), workflow_id).build(),
859
887
  )
860
888
  .await
861
889
  .unwrap();
862
890
  worker.run_until_done().await.unwrap();
863
891
 
864
892
  let body = get_text(format!("http://{addr}/metrics")).await;
893
+ let wf_type = ActivityMetricsWf::name();
865
894
  assert!(body.contains(&format!(
866
895
  "temporal_activity_execution_failed{{activity_type=\"pass_fail_act\",\
867
896
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
868
- task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 1"
897
+ task_queue=\"{task_queue}\",workflow_type=\"{wf_type}\"}} 1"
869
898
  )));
870
899
  assert!(body.contains(&format!(
871
900
  "temporal_activity_schedule_to_start_latency_count{{\
@@ -875,42 +904,42 @@ async fn activity_metrics() {
875
904
  assert!(body.contains(&format!(
876
905
  "temporal_activity_execution_latency_count{{activity_type=\"pass_fail_act\",\
877
906
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
878
- task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 2"
907
+ task_queue=\"{task_queue}\",workflow_type=\"{wf_type}\"}} 2"
879
908
  )));
880
909
  assert!(body.contains(&format!(
881
910
  "temporal_activity_succeed_endtoend_latency_count{{activity_type=\"pass_fail_act\",\
882
911
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
883
- task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 1"
912
+ task_queue=\"{task_queue}\",workflow_type=\"{wf_type}\"}} 1"
884
913
  )));
885
914
 
886
915
  assert!(body.contains(&format!(
887
916
  "temporal_local_activity_total{{activity_type=\"pass_fail_act\",namespace=\"{NAMESPACE}\",\
888
917
  service_name=\"temporal-core-sdk\",task_queue=\"{task_queue}\",\
889
- workflow_type=\"{wf_name}\"}} 3"
918
+ workflow_type=\"{wf_type}\"}} 3"
890
919
  )));
891
920
  assert!(body.contains(&format!(
892
921
  "temporal_local_activity_execution_failed{{activity_type=\"pass_fail_act\",\
893
922
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
894
923
  task_queue=\"{task_queue}\",\
895
- workflow_type=\"{wf_name}\"}} 1"
924
+ workflow_type=\"{wf_type}\"}} 1"
896
925
  )));
897
926
  assert!(body.contains(&format!(
898
927
  "temporal_local_activity_execution_cancelled{{activity_type=\"pass_fail_act\",\
899
928
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
900
929
  task_queue=\"{task_queue}\",\
901
- workflow_type=\"{wf_name}\"}} 1"
930
+ workflow_type=\"{wf_type}\"}} 1"
902
931
  )));
903
932
  assert!(body.contains(&format!(
904
933
  "temporal_local_activity_execution_latency_count{{activity_type=\"pass_fail_act\",\
905
934
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
906
935
  task_queue=\"{task_queue}\",\
907
- workflow_type=\"{wf_name}\"}} 3"
936
+ workflow_type=\"{wf_type}\"}} 3"
908
937
  )));
909
938
  assert!(body.contains(&format!(
910
939
  "temporal_local_activity_succeed_endtoend_latency_count{{activity_type=\"pass_fail_act\",\
911
940
  namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
912
941
  task_queue=\"{task_queue}\",\
913
- workflow_type=\"{wf_name}\"}} 1"
942
+ workflow_type=\"{wf_type}\"}} 1"
914
943
  )));
915
944
  }
916
945
 
@@ -920,25 +949,30 @@ async fn nexus_metrics() {
920
949
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
921
950
  let wf_name = "nexus_metrics";
922
951
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
923
- starter.worker_config.task_types(WorkerTaskTypes {
952
+ starter.sdk_config.task_types = WorkerTaskTypes {
924
953
  enable_workflows: true,
925
954
  enable_local_activities: false,
926
955
  enable_remote_activities: false,
927
956
  enable_nexus: true,
928
- });
929
- let task_queue = starter.get_task_queue().to_owned();
957
+ };
930
958
  let mut worker = starter.worker().await;
931
959
  let core_worker = starter.get_worker().await;
932
960
  let endpoint = mk_nexus_endpoint(&mut starter).await;
933
961
 
934
- worker.register_wf(wf_name.to_string(), move |ctx: WfContext| {
935
- let partial_op = NexusOperationOptions {
936
- endpoint: endpoint.clone(),
937
- service: "mysvc".to_string(),
938
- operation: "myop".to_string(),
939
- ..Default::default()
940
- };
941
- async move {
962
+ #[workflow]
963
+ #[derive(Default)]
964
+ struct NexusMetricsWf;
965
+
966
+ #[workflow_methods]
967
+ impl NexusMetricsWf {
968
+ #[run]
969
+ async fn run(ctx: &mut WorkflowContext<Self>, endpoint: String) -> WorkflowResult<()> {
970
+ let partial_op = NexusOperationOptions {
971
+ endpoint: endpoint.clone(),
972
+ service: "mysvc".to_string(),
973
+ operation: "myop".to_string(),
974
+ ..Default::default()
975
+ };
942
976
  join!(
943
977
  async {
944
978
  ctx.start_nexus_operation(partial_op.clone())
@@ -973,11 +1007,21 @@ async fn nexus_metrics() {
973
1007
  .await;
974
1008
  }
975
1009
  );
976
- Ok(().into())
1010
+ Ok(())
977
1011
  }
978
- });
1012
+ }
979
1013
 
980
- starter.start_with_worker(wf_name, &mut worker).await;
1014
+ worker.register_workflow::<NexusMetricsWf>();
1015
+ let task_queue = starter.get_task_queue().to_owned();
1016
+ let workflow_id = wf_name.to_owned();
1017
+ worker
1018
+ .submit_workflow(
1019
+ NexusMetricsWf::run,
1020
+ endpoint,
1021
+ WorkflowStartOptions::new(task_queue.clone(), workflow_id).build(),
1022
+ )
1023
+ .await
1024
+ .unwrap();
981
1025
 
982
1026
  let nexus_polling = async {
983
1027
  for _ in 0..5 {
@@ -1010,6 +1054,7 @@ async fn nexus_metrics() {
1010
1054
  variant: Some(nexus::v1::response::Variant::StartOperation(
1011
1055
  StartOperationResponse {
1012
1056
  variant: Some(
1057
+ #[allow(deprecated)]
1013
1058
  start_operation_response::Variant::OperationError(
1014
1059
  UnsuccessfulOperationError {
1015
1060
  operation_state: "failed".to_string(),
@@ -1024,7 +1069,9 @@ async fn nexus_metrics() {
1024
1069
  )),
1025
1070
  })
1026
1071
  }
1027
- Some(p) if p == "handler-fail".into() => {
1072
+ Some(p) if p == "handler-fail".into() =>
1073
+ {
1074
+ #[allow(deprecated)]
1028
1075
  nexus_task_completion::Status::Error(HandlerError {
1029
1076
  error_type: "BAD_REQUEST".to_string(),
1030
1077
  failure: Some(nexus::v1::Failure {
@@ -1102,22 +1149,29 @@ async fn evict_on_complete_does_not_count_as_forced_eviction() {
1102
1149
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1103
1150
  let wf_name = "evict_on_complete_does_not_count_as_forced_eviction";
1104
1151
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
1105
- starter
1106
- .worker_config
1107
- .task_types(WorkerTaskTypes::workflow_only());
1152
+ starter.sdk_config.task_types = WorkerTaskTypes::workflow_only();
1108
1153
  let mut worker = starter.worker().await;
1109
1154
 
1110
- worker.register_wf(
1111
- wf_name.to_string(),
1112
- |_: WfContext| async move { Ok(().into()) },
1113
- );
1155
+ #[workflow]
1156
+ #[derive(Default)]
1157
+ struct EvictOnCompleteWf;
1158
+
1159
+ #[workflow_methods]
1160
+ impl EvictOnCompleteWf {
1161
+ #[run]
1162
+ async fn run(_ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
1163
+ Ok(())
1164
+ }
1165
+ }
1114
1166
 
1167
+ worker.register_workflow::<EvictOnCompleteWf>();
1168
+ let task_queue = starter.get_task_queue().to_owned();
1169
+ let workflow_id = wf_name.to_owned();
1115
1170
  worker
1116
- .submit_wf(
1117
- wf_name.to_owned(),
1118
- wf_name.to_owned(),
1119
- vec![],
1120
- WorkflowOptions::default(),
1171
+ .submit_workflow(
1172
+ EvictOnCompleteWf::run,
1173
+ (),
1174
+ WorkflowStartOptions::new(task_queue, workflow_id).build(),
1121
1175
  )
1122
1176
  .await
1123
1177
  .unwrap();
@@ -1187,29 +1241,34 @@ async fn metrics_available_from_custom_slot_supplier() {
1187
1241
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1188
1242
  let mut starter =
1189
1243
  CoreWfStarter::new_with_runtime("metrics_available_from_custom_slot_supplier", rt);
1190
- starter
1191
- .worker_config
1192
- .task_types(WorkerTaskTypes::workflow_only());
1193
- starter.worker_config.clear_max_outstanding_opts();
1244
+ starter.sdk_config.task_types = WorkerTaskTypes::workflow_only();
1194
1245
  let mut tb = TunerBuilder::default();
1195
1246
  tb.workflow_slot_supplier(Arc::new(MetricRecordingSlotSupplier::<WorkflowSlotKind> {
1196
1247
  inner: FixedSizeSlotSupplier::new(5),
1197
1248
  metrics: OnceLock::new(),
1198
1249
  }));
1199
- starter.worker_config.tuner(Arc::new(tb.build()));
1250
+ starter.sdk_config.tuner = Arc::new(tb.build());
1200
1251
  let mut worker = starter.worker().await;
1201
1252
 
1202
- worker.register_wf(
1203
- "s_wf".to_string(),
1204
- |_: WfContext| async move { Ok(().into()) },
1205
- );
1253
+ #[workflow]
1254
+ #[derive(Default)]
1255
+ struct CustomSlotSupplierWf;
1256
+
1257
+ #[workflow_methods]
1258
+ impl CustomSlotSupplierWf {
1259
+ #[run]
1260
+ async fn run(_ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
1261
+ Ok(())
1262
+ }
1263
+ }
1206
1264
 
1265
+ worker.register_workflow::<CustomSlotSupplierWf>();
1266
+ let task_queue = starter.get_task_queue().to_owned();
1207
1267
  worker
1208
- .submit_wf(
1209
- "s_wf".to_owned(),
1210
- "s_wf".to_owned(),
1211
- vec![],
1212
- WorkflowOptions::default(),
1268
+ .submit_workflow(
1269
+ CustomSlotSupplierWf::run,
1270
+ (),
1271
+ WorkflowStartOptions::new(task_queue, "s_wf".to_owned()).build(),
1213
1272
  )
1214
1273
  .await
1215
1274
  .unwrap();
@@ -1314,17 +1373,11 @@ async fn test_prometheus_metric_format_consistency() {
1314
1373
 
1315
1374
  #[tokio::test]
1316
1375
  async fn prometheus_label_nonsense() {
1317
- let mut opts_builder = PrometheusExporterOptionsBuilder::default();
1318
- opts_builder.socket_addr(ANY_PORT.parse().unwrap());
1319
- let (telemopts, addr, _aborter) = prom_metrics(Some(opts_builder.build().unwrap()));
1376
+ let opts_builder = PrometheusExporterOptions::builder().socket_addr(ANY_PORT.parse().unwrap());
1377
+ let (telemopts, addr, _aborter) = prom_metrics(Some(opts_builder.build()));
1320
1378
  let meter = telemopts.metrics.clone().unwrap();
1321
1379
 
1322
- let ctr = meter.counter(
1323
- MetricParametersBuilder::default()
1324
- .name("some_counter")
1325
- .build()
1326
- .unwrap(),
1327
- );
1380
+ let ctr = meter.counter(MetricParameters::builder().name("some_counter").build());
1328
1381
  let a1 = meter.new_attributes(NewAttributes::from([MetricKeyValue::new("thing", "foo")]));
1329
1382
  let a2 = meter.new_attributes(NewAttributes::from([MetricKeyValue::new("blerp", "baz")]));
1330
1383
  ctr.add(1, &a1);
@@ -1347,36 +1400,41 @@ async fn sticky_queue_label_strategy(
1347
1400
  strategy: TaskQueueLabelStrategy,
1348
1401
  ) {
1349
1402
  let (mut telemopts, addr, _aborter) = prom_metrics(Some(
1350
- PrometheusExporterOptionsBuilder::default()
1403
+ PrometheusExporterOptions::builder()
1351
1404
  .socket_addr(ANY_PORT.parse().unwrap())
1352
- .build()
1353
- .unwrap(),
1405
+ .build(),
1354
1406
  ));
1355
1407
  telemopts.task_queue_label_strategy = strategy;
1356
1408
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1357
1409
  let wf_name = format!("sticky_queue_label_strategy_{strategy:?}");
1358
1410
  let mut starter = CoreWfStarter::new_with_runtime(&wf_name, rt);
1359
1411
  // Enable sticky queues by setting a reasonable cache size
1360
- starter.worker_config.max_cached_workflows(10_usize);
1361
- starter
1362
- .worker_config
1363
- .task_types(WorkerTaskTypes::workflow_only());
1364
- let task_queue = starter.get_task_queue().to_owned();
1412
+ starter.sdk_config.max_cached_workflows = 10_usize;
1413
+ starter.sdk_config.task_types = WorkerTaskTypes::workflow_only();
1365
1414
  let mut worker = starter.worker().await;
1366
1415
 
1367
- worker.register_wf(wf_name.clone(), |ctx: WfContext| async move {
1368
- ctx.timer(Duration::from_millis(1)).await;
1369
- Ok(().into())
1370
- });
1416
+ #[workflow]
1417
+ #[derive(Default)]
1418
+ struct StickyQueueLabelStrategyWf;
1419
+
1420
+ #[workflow_methods]
1421
+ impl StickyQueueLabelStrategyWf {
1422
+ #[run]
1423
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
1424
+ ctx.timer(Duration::from_millis(1)).await;
1425
+ Ok(())
1426
+ }
1427
+ }
1428
+
1429
+ worker.register_workflow::<StickyQueueLabelStrategyWf>();
1430
+ let task_queue = starter.get_task_queue().to_owned();
1371
1431
  worker
1372
- .submit_wf(
1373
- wf_name.clone(),
1374
- wf_name,
1375
- vec![],
1376
- WorkflowOptions {
1377
- enable_eager_workflow_start: false,
1378
- ..Default::default()
1379
- },
1432
+ .submit_workflow(
1433
+ StickyQueueLabelStrategyWf::run,
1434
+ (),
1435
+ WorkflowStartOptions::new(task_queue.clone(), wf_name.clone())
1436
+ .enable_eager_workflow_start(false)
1437
+ .build(),
1380
1438
  )
1381
1439
  .await
1382
1440
  .unwrap();
@@ -1436,28 +1494,34 @@ async fn resource_based_tuner_metrics() {
1436
1494
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1437
1495
  let wf_name = "resource_based_tuner_metrics";
1438
1496
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
1439
- starter
1440
- .worker_config
1441
- .task_types(WorkerTaskTypes::workflow_only());
1442
- starter.worker_config.clear_max_outstanding_opts();
1443
-
1497
+ starter.sdk_config.task_types = WorkerTaskTypes::workflow_only();
1444
1498
  // Create a resource-based tuner with reasonable thresholds
1445
1499
  let tuner = ResourceBasedTuner::new(0.8, 0.8);
1446
- starter.worker_config.tuner(Arc::new(tuner));
1500
+ starter.sdk_config.tuner = Arc::new(tuner);
1447
1501
 
1448
1502
  let mut worker = starter.worker().await;
1449
1503
 
1450
- worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
1451
- ctx.timer(Duration::from_millis(100)).await;
1452
- Ok(().into())
1453
- });
1504
+ #[workflow]
1505
+ #[derive(Default)]
1506
+ struct ResourceBasedTunerMetricsWf;
1454
1507
 
1508
+ #[workflow_methods]
1509
+ impl ResourceBasedTunerMetricsWf {
1510
+ #[run]
1511
+ async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
1512
+ ctx.timer(Duration::from_millis(100)).await;
1513
+ Ok(())
1514
+ }
1515
+ }
1516
+
1517
+ worker.register_workflow::<ResourceBasedTunerMetricsWf>();
1518
+ let task_queue = starter.get_task_queue().to_owned();
1519
+ let workflow_id = wf_name.to_owned();
1455
1520
  worker
1456
- .submit_wf(
1457
- wf_name.to_owned(),
1458
- wf_name.to_owned(),
1459
- vec![],
1460
- WorkflowOptions::default(),
1521
+ .submit_workflow(
1522
+ ResourceBasedTunerMetricsWf::run,
1523
+ (),
1524
+ WorkflowStartOptions::new(task_queue, workflow_id).build(),
1461
1525
  )
1462
1526
  .await
1463
1527
  .unwrap();
@@ -1487,3 +1551,86 @@ async fn resource_based_tuner_metrics() {
1487
1551
  "CPU PID output metric should be present"
1488
1552
  );
1489
1553
  }
1554
+
1555
+ #[tokio::test]
1556
+ async fn terminal_metric_not_recorded_on_rejected_completion() {
1557
+ let prom_info = start_prometheus_metric_exporter(
1558
+ PrometheusExporterOptions::builder()
1559
+ .socket_addr(ANY_PORT.parse().unwrap())
1560
+ .build(),
1561
+ )
1562
+ .unwrap();
1563
+ let addr = prom_info.bound_addr;
1564
+ let _abort = prom_info.abort_handle;
1565
+ let meter = TemporalMeter::new(
1566
+ prom_info.meter as Arc<dyn CoreMeter>,
1567
+ NewAttributes { attributes: vec![] },
1568
+ TaskQueueLabelStrategy::UseNormal,
1569
+ );
1570
+
1571
+ // Build a simple workflow history: start + WFT
1572
+ let mut t = TestHistoryBuilder::default();
1573
+ t.add_by_type(
1574
+ temporalio_common::protos::temporal::api::enums::v1::EventType::WorkflowExecutionStarted,
1575
+ );
1576
+ t.add_workflow_task_scheduled_and_started();
1577
+
1578
+ // First WFT completion is rejected by server, second succeeds
1579
+ let mut call_count = 0;
1580
+ let mut mh = MockPollCfg::from_resp_batches(
1581
+ "fake_wf_id",
1582
+ t,
1583
+ [ResponseType::AllHistory, ResponseType::AllHistory],
1584
+ mock_worker_client(),
1585
+ );
1586
+ mh.completion_mock_fn = Some(Box::new(move |_| {
1587
+ call_count += 1;
1588
+ if call_count == 1 {
1589
+ Err(tonic::Status::not_found("Workflow task not found"))
1590
+ } else {
1591
+ Ok(Default::default())
1592
+ }
1593
+ }));
1594
+
1595
+ let mut mock = build_mock_pollers(mh);
1596
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
1597
+ mock.set_temporal_meter(meter);
1598
+ let core = mock_worker(mock);
1599
+
1600
+ // First attempt: get WFT activation and complete the workflow
1601
+ let act = core.poll_workflow_activation().await.unwrap();
1602
+ core.complete_execution(&act.run_id).await;
1603
+
1604
+ // Handle eviction (triggered by NotFound error from server)
1605
+ core.handle_eviction().await;
1606
+
1607
+ // Second attempt (replay after eviction): complete workflow again
1608
+ let act = core.poll_workflow_activation().await.unwrap();
1609
+ core.complete_execution(&act.run_id).await;
1610
+
1611
+ core.drain_pollers_and_shutdown().await;
1612
+
1613
+ // The workflow_completed metric should be recorded exactly once — only for
1614
+ // the successful server response, not for the rejected first attempt.
1615
+ let body = get_text(format!("http://{addr}/metrics")).await;
1616
+ let matching_line = body.lines().find(|l| l.starts_with("workflow_completed{"));
1617
+ match matching_line {
1618
+ Some(line) => {
1619
+ assert!(
1620
+ line.ends_with(" 1"),
1621
+ "Expected workflow_completed count of 1, got: {line}"
1622
+ );
1623
+ assert!(
1624
+ line.contains("workflow_type=\"default_wf_type\""),
1625
+ "Expected workflow_type label on metric, got: {line}"
1626
+ );
1627
+ }
1628
+ None => panic!(
1629
+ "workflow_completed metric not found. Available metrics:\n{}",
1630
+ body.lines()
1631
+ .filter(|l| l.contains("workflow"))
1632
+ .collect::<Vec<_>>()
1633
+ .join("\n")
1634
+ ),
1635
+ }
1636
+ }