@temporalio/core-bridge 1.14.1 → 1.15.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 (135) hide show
  1. package/Cargo.lock +648 -606
  2. package/bridge-macros/src/derive_tryintojs.rs +40 -0
  3. package/lib/native.d.ts +23 -2
  4. package/package.json +12 -13
  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/.cargo/multi-worker-manual-test +0 -0
  11. package/sdk-core/AGENTS.md +2 -2
  12. package/sdk-core/Cargo.toml +1 -1
  13. package/sdk-core/README.md +5 -5
  14. package/sdk-core/crates/client/src/raw.rs +90 -0
  15. package/sdk-core/crates/client/src/worker/mod.rs +103 -28
  16. package/sdk-core/crates/common/Cargo.toml +1 -1
  17. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/create-release.yml +0 -5
  18. package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
  19. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +3 -0
  20. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +2738 -2452
  21. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1657 -124
  22. package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
  23. package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  24. package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
  25. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +26 -0
  26. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
  27. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
  28. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
  29. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
  30. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +62 -15
  31. package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
  32. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +107 -17
  33. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +15 -0
  34. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -0
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +285 -19
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +154 -10
  42. package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +5 -0
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +17 -0
  46. package/sdk-core/crates/common/src/lib.rs +3 -3
  47. package/sdk-core/crates/common/src/protos/canned_histories.rs +16 -0
  48. package/sdk-core/crates/common/src/protos/mod.rs +12 -0
  49. package/sdk-core/crates/common/src/telemetry/metrics.rs +6 -4
  50. package/sdk-core/crates/common/src/telemetry.rs +14 -15
  51. package/sdk-core/crates/common/src/worker.rs +66 -99
  52. package/sdk-core/crates/common/tests/worker_task_types_test.rs +9 -9
  53. package/sdk-core/crates/sdk/src/lib.rs +10 -8
  54. package/sdk-core/crates/sdk/src/workflow_context/options.rs +19 -0
  55. package/sdk-core/crates/sdk-core/Cargo.toml +2 -1
  56. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +4 -19
  57. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +9 -6
  58. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +166 -13
  59. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +42 -33
  60. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +6 -9
  61. package/sdk-core/crates/sdk-core/src/lib.rs +20 -13
  62. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +301 -21
  63. package/sdk-core/crates/sdk-core/src/telemetry/log_export.rs +7 -10
  64. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +4 -2
  65. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +2 -3
  66. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +30 -8
  67. package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
  68. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +3 -1
  69. package/sdk-core/crates/sdk-core/src/worker/client.rs +2 -6
  70. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +4 -4
  71. package/sdk-core/crates/sdk-core/src/worker/mod.rs +92 -53
  72. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +5 -0
  73. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +12 -14
  74. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +36 -36
  75. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
  76. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +12 -1
  77. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +6 -23
  78. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +46 -3
  79. package/sdk-core/crates/sdk-core/tests/common/mod.rs +45 -45
  80. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +7 -10
  81. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +3 -5
  82. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +34 -42
  83. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +21 -26
  84. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +1 -0
  85. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +147 -72
  86. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +27 -48
  87. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +5 -15
  88. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +61 -66
  89. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +16 -14
  90. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +15 -21
  91. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +16 -19
  92. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -3
  93. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -3
  94. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -12
  95. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +14 -9
  96. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -3
  97. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +2 -6
  98. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +4 -8
  99. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -3
  100. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +11 -13
  101. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +11 -27
  102. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +3 -5
  103. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -12
  104. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +7 -13
  105. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +4 -12
  106. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -3
  107. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +16 -30
  108. package/sdk-core/crates/sdk-core/tests/main.rs +6 -2
  109. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +40 -49
  110. package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
  111. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +28 -13
  112. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +1 -0
  113. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +24 -13
  114. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +103 -19
  115. package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
  116. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +1 -2
  117. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -66
  118. package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
  119. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +46 -11
  120. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +103 -7
  121. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +6 -48
  122. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +13 -17
  123. package/sdk-core/docker-cgroup-tests.sh +0 -0
  124. package/sdk-core/etc/cargo-tokio-console.sh +0 -0
  125. package/sdk-core/etc/integ-with-otel.sh +0 -0
  126. package/sdk-core/etc/regen-depgraph.sh +0 -0
  127. package/src/client.rs +30 -0
  128. package/src/helpers/try_into_js.rs +88 -2
  129. package/src/metrics.rs +272 -22
  130. package/src/runtime.rs +91 -41
  131. package/src/testing.rs +9 -16
  132. package/src/worker.rs +76 -55
  133. package/ts/native.ts +38 -2
  134. package/LICENSE +0 -21
  135. package/sdk-core/crates/macros/LICENSE.txt +0 -21
@@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
4
4
  use temporalio_client::{ClientOptions, TestService, WorkflowService};
5
5
  use temporalio_common::protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
6
6
  use temporalio_sdk_core::ephemeral_server::{
7
- EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporalDevServerConfigBuilder,
7
+ EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporalDevServerConfig,
8
8
  default_cached_download,
9
9
  };
10
10
  use tonic::IntoRequest;
@@ -12,10 +12,9 @@ use url::Url;
12
12
 
13
13
  #[tokio::test]
14
14
  async fn temporal_cli_default() {
15
- let config = TemporalDevServerConfigBuilder::default()
15
+ let config = TemporalDevServerConfig::builder()
16
16
  .exe(default_cached_download())
17
- .build()
18
- .unwrap();
17
+ .build();
19
18
  let mut server = config.start_server().await.unwrap();
20
19
  assert_ephemeral_server(&server).await;
21
20
 
@@ -28,10 +27,9 @@ async fn temporal_cli_default() {
28
27
 
29
28
  #[tokio::test]
30
29
  async fn temporal_cli_fixed() {
31
- let config = TemporalDevServerConfigBuilder::default()
30
+ let config = TemporalDevServerConfig::builder()
32
31
  .exe(fixed_cached_download("v1.2.0"))
33
- .build()
34
- .unwrap();
32
+ .build();
35
33
  let mut server = config.start_server().await.unwrap();
36
34
  assert_ephemeral_server(&server).await;
37
35
  server.shutdown().await.unwrap();
@@ -41,11 +39,10 @@ async fn temporal_cli_fixed() {
41
39
  async fn temporal_cli_shutdown_port_reuse() {
42
40
  // Start, test shutdown, do again immediately on same port to ensure we can
43
41
  // reuse after shutdown
44
- let config = TemporalDevServerConfigBuilder::default()
42
+ let config = TemporalDevServerConfig::builder()
45
43
  .exe(default_cached_download())
46
- .port(Some(10123))
47
- .build()
48
- .unwrap();
44
+ .port(10123)
45
+ .build();
49
46
  let mut server = config.start_server().await.unwrap();
50
47
  assert_ephemeral_server(&server).await;
51
48
  server.shutdown().await.unwrap();
@@ -66,10 +63,11 @@ async fn temporal_cli_shutdown_port_reuse() {
66
63
  #[ignore]
67
64
  async fn temporal_cli_concurrent_starts() -> Result<(), Box<dyn std::error::Error>> {
68
65
  stream::iter((0..80).map(|_| {
69
- TemporalDevServerConfigBuilder::default()
70
- .exe(default_cached_download())
71
- .build()
72
- .map_err(anyhow::Error::from)
66
+ Ok::<TemporalDevServerConfig, Box<dyn std::error::Error>>(
67
+ TemporalDevServerConfig::builder()
68
+ .exe(default_cached_download())
69
+ .build(),
70
+ )
73
71
  }))
74
72
  .try_for_each_concurrent(8, |config| async move {
75
73
  let mut server = config.start_server().await?;
@@ -85,14 +83,13 @@ async fn temporal_cli_concurrent_starts() -> Result<(), Box<dyn std::error::Erro
85
83
  #[cfg(not(all(target_os = "linux", any(target_arch = "arm", target_arch = "aarch64"))))]
86
84
  mod test_server {
87
85
  use super::*;
88
- use temporalio_sdk_core::ephemeral_server::TestServerConfigBuilder;
86
+ use temporalio_sdk_core::ephemeral_server::TestServerConfig;
89
87
 
90
88
  #[tokio::test]
91
89
  async fn test_server_default() {
92
- let config = TestServerConfigBuilder::default()
90
+ let config = TestServerConfig::builder()
93
91
  .exe(default_cached_download())
94
- .build()
95
- .unwrap();
92
+ .build();
96
93
  let mut server = config.start_server().await.unwrap();
97
94
  assert_ephemeral_server(&server).await;
98
95
  server.shutdown().await.unwrap();
@@ -100,10 +97,9 @@ mod test_server {
100
97
 
101
98
  #[tokio::test]
102
99
  async fn test_server_fixed() {
103
- let config = TestServerConfigBuilder::default()
100
+ let config = TestServerConfig::builder()
104
101
  .exe(fixed_cached_download("v1.16.0"))
105
- .build()
106
- .unwrap();
102
+ .build();
107
103
  let mut server = config.start_server().await.unwrap();
108
104
  assert_ephemeral_server(&server).await;
109
105
  server.shutdown().await.unwrap();
@@ -113,11 +109,10 @@ mod test_server {
113
109
  async fn test_server_shutdown_port_reuse() {
114
110
  // Start, test shutdown, do again immediately on same port to ensure we can
115
111
  // reuse after shutdown
116
- let config = TestServerConfigBuilder::default()
112
+ let config = TestServerConfig::builder()
117
113
  .exe(default_cached_download())
118
- .port(Some(10124))
119
- .build()
120
- .unwrap();
114
+ .port(10124)
115
+ .build();
121
116
  let mut server = config.start_server().await.unwrap();
122
117
  assert_ephemeral_server(&server).await;
123
118
  server.shutdown().await.unwrap();
@@ -72,6 +72,7 @@ async fn activity_heartbeat() {
72
72
  let response_payload = Payload {
73
73
  data: b"hello ".to_vec(),
74
74
  metadata: Default::default(),
75
+ external_payloads: Default::default(),
75
76
  };
76
77
  // Complete activity successfully.
77
78
  core.complete_activity_task(ActivityTaskCompletion {
@@ -48,17 +48,17 @@ use temporalio_common::{
48
48
  },
49
49
  },
50
50
  telemetry::{
51
- HistogramBucketOverrides, OtelCollectorOptionsBuilder, OtlpProtocol,
52
- PrometheusExporterOptionsBuilder, TaskQueueLabelStrategy, TelemetryOptionsBuilder,
51
+ HistogramBucketOverrides, OtelCollectorOptions, OtlpProtocol, PrometheusExporterOptions,
52
+ TaskQueueLabelStrategy, TelemetryOptions,
53
53
  metrics::{
54
54
  CoreMeter, CounterBase, Gauge, GaugeBase, HistogramBase, MetricKeyValue,
55
- MetricParameters, MetricParametersBuilder, NewAttributes,
55
+ MetricParameters, NewAttributes,
56
56
  },
57
57
  },
58
58
  worker::{
59
59
  PollerBehavior, SlotKind, SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext,
60
- SlotSupplier, SlotSupplierPermit, WorkerConfigBuilder, WorkerTaskTypes,
61
- WorkerVersioningStrategy, WorkflowSlotKind,
60
+ SlotSupplier, SlotSupplierPermit, WorkerConfig, WorkerTaskTypes, WorkerVersioningStrategy,
61
+ WorkflowSlotKind,
62
62
  },
63
63
  };
64
64
  use temporalio_sdk::{
@@ -67,7 +67,15 @@ use temporalio_sdk::{
67
67
  };
68
68
  use temporalio_sdk_core::{
69
69
  CoreRuntime, FixedSizeSlotSupplier, TokioRuntimeBuilder, TunerBuilder, init_worker,
70
- telemetry::{WORKFLOW_TASK_EXECUTION_LATENCY_HISTOGRAM_NAME, build_otlp_metric_exporter},
70
+ replay::TestHistoryBuilder,
71
+ telemetry::{
72
+ WORKFLOW_TASK_EXECUTION_LATENCY_HISTOGRAM_NAME, build_otlp_metric_exporter,
73
+ start_prometheus_metric_exporter,
74
+ },
75
+ test_help::{
76
+ MockPollCfg, ResponseType, TemporalMeter, WorkerExt, WorkerTestHelpers, build_mock_pollers,
77
+ mock_worker, mock_worker_client,
78
+ },
71
79
  };
72
80
  use tokio::{join, sync::Barrier};
73
81
  use tonic::IntoRequest;
@@ -83,21 +91,23 @@ async fn prometheus_metrics_exported(
83
91
  #[values(true, false)] use_seconds_latency: bool,
84
92
  #[values(true, false)] custom_buckets: bool,
85
93
  ) {
86
- let mut opts_builder = PrometheusExporterOptionsBuilder::default();
87
- opts_builder
94
+ let opts = PrometheusExporterOptions::builder()
88
95
  .global_tags(HashMap::from([("global".to_string(), "hi!".to_string())]))
89
96
  .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()));
97
+ .use_seconds_for_durations(use_seconds_latency)
98
+ .histogram_bucket_overrides(if custom_buckets {
99
+ HistogramBucketOverrides {
100
+ overrides: {
101
+ let mut hm = HashMap::new();
102
+ hm.insert(REQUEST_LATENCY_HISTOGRAM_NAME.to_string(), vec![1337.0]);
103
+ hm
104
+ },
105
+ }
106
+ } else {
107
+ Default::default()
108
+ })
109
+ .build();
110
+ let (telemopts, addr, _aborter) = prom_metrics(Some(opts));
101
111
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
102
112
  let opts = get_integ_server_options();
103
113
  let mut raw_client = opts
@@ -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 {
@@ -279,6 +289,7 @@ async fn one_slot_worker_reports_available_slot() {
279
289
  "whatever".to_string(),
280
290
  None,
281
291
  WorkflowOptions {
292
+ #[allow(deprecated)]
282
293
  id_reuse_policy: WorkflowIdReusePolicy::TerminateIfRunning,
283
294
  execution_timeout: Some(Duration::from_secs(5)),
284
295
  ..Default::default()
@@ -407,7 +418,7 @@ async fn query_of_closed_workflow_doesnt_tick_terminal_metric(
407
418
  let mut starter =
408
419
  CoreWfStarter::new_with_runtime("query_of_closed_workflow_doesnt_tick_terminal_metric", rt);
409
420
  // Disable cache to ensure replay happens completely
410
- starter.worker_config.max_cached_workflows(0_usize);
421
+ starter.worker_config.max_cached_workflows = 0_usize;
411
422
  let worker = starter.get_worker().await;
412
423
  let run_id = starter.start_wf().await;
413
424
  let task = worker.poll_workflow_activation().await.unwrap();
@@ -558,7 +569,7 @@ async fn latency_metrics(
558
569
  #[values(true, false)] show_units: bool,
559
570
  ) {
560
571
  let (telemopts, addr, _aborter) = prom_metrics(Some(
561
- PrometheusExporterOptionsBuilder::default()
572
+ PrometheusExporterOptions::builder()
562
573
  .socket_addr(ANY_PORT.parse().unwrap())
563
574
  .use_seconds_for_durations(use_seconds_latency)
564
575
  .unit_suffix(show_units)
@@ -572,8 +583,7 @@ async fn latency_metrics(
572
583
  hm
573
584
  },
574
585
  })
575
- .build()
576
- .unwrap(),
586
+ .build(),
577
587
  ));
578
588
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
579
589
  let mut starter = CoreWfStarter::new_with_runtime("latency_metrics", rt);
@@ -663,20 +673,15 @@ async fn request_fail_codes_otel() {
663
673
  .ok()
664
674
  .map(|x| x.parse::<Url>().unwrap())
665
675
  {
666
- let opts = OtelCollectorOptionsBuilder::default()
667
- .url(url)
668
- .build()
669
- .unwrap();
676
+ let opts = OtelCollectorOptions::builder().url(url).build();
670
677
  build_otlp_metric_exporter(opts).unwrap()
671
678
  } else {
672
679
  // skip
673
680
  return;
674
681
  };
675
- let mut telemopts = TelemetryOptionsBuilder::default();
676
682
  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();
683
+ let telemopts = TelemetryOptions::builder().metrics(exporter as Arc<dyn CoreMeter>);
684
+ let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts.build())).unwrap();
680
685
  let opts = get_integ_server_options();
681
686
  let mut client = opts
682
687
  .connect(NAMESPACE, rt.telemetry().get_temporal_metric_meter())
@@ -717,18 +722,16 @@ async fn docker_metrics_with_prometheus(
717
722
  );
718
723
 
719
724
  // Configure the OTLP exporter with HTTP
720
- let opts = OtelCollectorOptionsBuilder::default()
725
+ let opts = OtelCollectorOptions::builder()
721
726
  .url(otel_collector_addr.parse().unwrap())
722
727
  .protocol(otel_protocol)
723
728
  .global_tags(HashMap::from([("test_id".to_string(), test_uid.clone())]))
724
- .build()
725
- .unwrap();
729
+ .build();
726
730
  let exporter = Arc::new(build_otlp_metric_exporter(opts).unwrap());
727
- let telemopts = TelemetryOptionsBuilder::default()
731
+ let telemopts = TelemetryOptions::builder()
728
732
  .metrics(exporter as Arc<dyn CoreMeter>)
729
733
  .metric_prefix(test_uid.clone())
730
- .build()
731
- .unwrap();
734
+ .build();
732
735
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
733
736
  let test_name = "docker_metrics_with_prometheus";
734
737
  let mut starter = CoreWfStarter::new_with_runtime(test_name, rt);
@@ -786,9 +789,7 @@ async fn activity_metrics() {
786
789
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
787
790
  let wf_name = "activity_metrics";
788
791
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
789
- starter
790
- .worker_config
791
- .graceful_shutdown_period(Duration::from_secs(1));
792
+ starter.worker_config.graceful_shutdown_period = Some(Duration::from_secs(1));
792
793
  let task_queue = starter.get_task_queue().to_owned();
793
794
  let mut worker = starter.worker().await;
794
795
 
@@ -920,12 +921,12 @@ async fn nexus_metrics() {
920
921
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
921
922
  let wf_name = "nexus_metrics";
922
923
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
923
- starter.worker_config.task_types(WorkerTaskTypes {
924
+ starter.worker_config.task_types = WorkerTaskTypes {
924
925
  enable_workflows: true,
925
926
  enable_local_activities: false,
926
927
  enable_remote_activities: false,
927
928
  enable_nexus: true,
928
- });
929
+ };
929
930
  let task_queue = starter.get_task_queue().to_owned();
930
931
  let mut worker = starter.worker().await;
931
932
  let core_worker = starter.get_worker().await;
@@ -1102,9 +1103,7 @@ async fn evict_on_complete_does_not_count_as_forced_eviction() {
1102
1103
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1103
1104
  let wf_name = "evict_on_complete_does_not_count_as_forced_eviction";
1104
1105
  let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
1105
- starter
1106
- .worker_config
1107
- .task_types(WorkerTaskTypes::workflow_only());
1106
+ starter.worker_config.task_types = WorkerTaskTypes::workflow_only();
1108
1107
  let mut worker = starter.worker().await;
1109
1108
 
1110
1109
  worker.register_wf(
@@ -1187,16 +1186,17 @@ async fn metrics_available_from_custom_slot_supplier() {
1187
1186
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1188
1187
  let mut starter =
1189
1188
  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();
1189
+ starter.worker_config.task_types = WorkerTaskTypes::workflow_only();
1190
+ starter.worker_config.max_outstanding_workflow_tasks = None;
1191
+ starter.worker_config.max_outstanding_local_activities = None;
1192
+ starter.worker_config.max_outstanding_activities = None;
1193
+ starter.worker_config.max_outstanding_nexus_tasks = None;
1194
1194
  let mut tb = TunerBuilder::default();
1195
1195
  tb.workflow_slot_supplier(Arc::new(MetricRecordingSlotSupplier::<WorkflowSlotKind> {
1196
1196
  inner: FixedSizeSlotSupplier::new(5),
1197
1197
  metrics: OnceLock::new(),
1198
1198
  }));
1199
- starter.worker_config.tuner(Arc::new(tb.build()));
1199
+ starter.worker_config.tuner = Some(Arc::new(tb.build()));
1200
1200
  let mut worker = starter.worker().await;
1201
1201
 
1202
1202
  worker.register_wf(
@@ -1314,17 +1314,11 @@ async fn test_prometheus_metric_format_consistency() {
1314
1314
 
1315
1315
  #[tokio::test]
1316
1316
  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()));
1317
+ let opts_builder = PrometheusExporterOptions::builder().socket_addr(ANY_PORT.parse().unwrap());
1318
+ let (telemopts, addr, _aborter) = prom_metrics(Some(opts_builder.build()));
1320
1319
  let meter = telemopts.metrics.clone().unwrap();
1321
1320
 
1322
- let ctr = meter.counter(
1323
- MetricParametersBuilder::default()
1324
- .name("some_counter")
1325
- .build()
1326
- .unwrap(),
1327
- );
1321
+ let ctr = meter.counter(MetricParameters::builder().name("some_counter").build());
1328
1322
  let a1 = meter.new_attributes(NewAttributes::from([MetricKeyValue::new("thing", "foo")]));
1329
1323
  let a2 = meter.new_attributes(NewAttributes::from([MetricKeyValue::new("blerp", "baz")]));
1330
1324
  ctr.add(1, &a1);
@@ -1347,20 +1341,17 @@ async fn sticky_queue_label_strategy(
1347
1341
  strategy: TaskQueueLabelStrategy,
1348
1342
  ) {
1349
1343
  let (mut telemopts, addr, _aborter) = prom_metrics(Some(
1350
- PrometheusExporterOptionsBuilder::default()
1344
+ PrometheusExporterOptions::builder()
1351
1345
  .socket_addr(ANY_PORT.parse().unwrap())
1352
- .build()
1353
- .unwrap(),
1346
+ .build(),
1354
1347
  ));
1355
1348
  telemopts.task_queue_label_strategy = strategy;
1356
1349
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1357
1350
  let wf_name = format!("sticky_queue_label_strategy_{strategy:?}");
1358
1351
  let mut starter = CoreWfStarter::new_with_runtime(&wf_name, rt);
1359
1352
  // 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());
1353
+ starter.worker_config.max_cached_workflows = 10_usize;
1354
+ starter.worker_config.task_types = WorkerTaskTypes::workflow_only();
1364
1355
  let task_queue = starter.get_task_queue().to_owned();
1365
1356
  let mut worker = starter.worker().await;
1366
1357
 
@@ -1436,14 +1427,15 @@ async fn resource_based_tuner_metrics() {
1436
1427
  let rt = CoreRuntime::new_assume_tokio(get_integ_runtime_options(telemopts)).unwrap();
1437
1428
  let wf_name = "resource_based_tuner_metrics";
1438
1429
  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();
1430
+ starter.worker_config.task_types = WorkerTaskTypes::workflow_only();
1431
+ starter.worker_config.max_outstanding_workflow_tasks = None;
1432
+ starter.worker_config.max_outstanding_local_activities = None;
1433
+ starter.worker_config.max_outstanding_activities = None;
1434
+ starter.worker_config.max_outstanding_nexus_tasks = None;
1443
1435
 
1444
1436
  // Create a resource-based tuner with reasonable thresholds
1445
1437
  let tuner = ResourceBasedTuner::new(0.8, 0.8);
1446
- starter.worker_config.tuner(Arc::new(tuner));
1438
+ starter.worker_config.tuner = Some(Arc::new(tuner));
1447
1439
 
1448
1440
  let mut worker = starter.worker().await;
1449
1441
 
@@ -1487,3 +1479,86 @@ async fn resource_based_tuner_metrics() {
1487
1479
  "CPU PID output metric should be present"
1488
1480
  );
1489
1481
  }
1482
+
1483
+ #[tokio::test]
1484
+ async fn terminal_metric_not_recorded_on_rejected_completion() {
1485
+ let prom_info = start_prometheus_metric_exporter(
1486
+ PrometheusExporterOptions::builder()
1487
+ .socket_addr(ANY_PORT.parse().unwrap())
1488
+ .build(),
1489
+ )
1490
+ .unwrap();
1491
+ let addr = prom_info.bound_addr;
1492
+ let _abort = prom_info.abort_handle;
1493
+ let meter = TemporalMeter::new(
1494
+ prom_info.meter as Arc<dyn CoreMeter>,
1495
+ NewAttributes { attributes: vec![] },
1496
+ TaskQueueLabelStrategy::UseNormal,
1497
+ );
1498
+
1499
+ // Build a simple workflow history: start + WFT
1500
+ let mut t = TestHistoryBuilder::default();
1501
+ t.add_by_type(
1502
+ temporalio_common::protos::temporal::api::enums::v1::EventType::WorkflowExecutionStarted,
1503
+ );
1504
+ t.add_workflow_task_scheduled_and_started();
1505
+
1506
+ // First WFT completion is rejected by server, second succeeds
1507
+ let mut call_count = 0;
1508
+ let mut mh = MockPollCfg::from_resp_batches(
1509
+ "fake_wf_id",
1510
+ t,
1511
+ [ResponseType::AllHistory, ResponseType::AllHistory],
1512
+ mock_worker_client(),
1513
+ );
1514
+ mh.completion_mock_fn = Some(Box::new(move |_| {
1515
+ call_count += 1;
1516
+ if call_count == 1 {
1517
+ Err(tonic::Status::not_found("Workflow task not found"))
1518
+ } else {
1519
+ Ok(Default::default())
1520
+ }
1521
+ }));
1522
+
1523
+ let mut mock = build_mock_pollers(mh);
1524
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
1525
+ mock.set_temporal_meter(meter);
1526
+ let core = mock_worker(mock);
1527
+
1528
+ // First attempt: get WFT activation and complete the workflow
1529
+ let act = core.poll_workflow_activation().await.unwrap();
1530
+ core.complete_execution(&act.run_id).await;
1531
+
1532
+ // Handle eviction (triggered by NotFound error from server)
1533
+ core.handle_eviction().await;
1534
+
1535
+ // Second attempt (replay after eviction): complete workflow again
1536
+ let act = core.poll_workflow_activation().await.unwrap();
1537
+ core.complete_execution(&act.run_id).await;
1538
+
1539
+ core.drain_pollers_and_shutdown().await;
1540
+
1541
+ // The workflow_completed metric should be recorded exactly once — only for
1542
+ // the successful server response, not for the rejected first attempt.
1543
+ let body = get_text(format!("http://{addr}/metrics")).await;
1544
+ let matching_line = body.lines().find(|l| l.starts_with("workflow_completed{"));
1545
+ match matching_line {
1546
+ Some(line) => {
1547
+ assert!(
1548
+ line.ends_with(" 1"),
1549
+ "Expected workflow_completed count of 1, got: {line}"
1550
+ );
1551
+ assert!(
1552
+ line.contains("workflow_type=\"default_wf_type\""),
1553
+ "Expected workflow_type label on metric, got: {line}"
1554
+ );
1555
+ }
1556
+ None => panic!(
1557
+ "workflow_completed metric not found. Available metrics:\n{}",
1558
+ body.lines()
1559
+ .filter(|l| l.contains("workflow"))
1560
+ .collect::<Vec<_>>()
1561
+ .join("\n")
1562
+ ),
1563
+ }
1564
+ }
@@ -29,13 +29,13 @@ use temporalio_common::{
29
29
  temporal::api::enums::v1::EventType,
30
30
  test_utils::schedule_activity_cmd,
31
31
  },
32
- telemetry::{Logger, TelemetryOptionsBuilder},
32
+ telemetry::{Logger, TelemetryOptions},
33
33
  worker::PollerBehavior,
34
34
  };
35
35
  use temporalio_sdk::{ActivityOptions, WfContext};
36
36
  use temporalio_sdk_core::{
37
- ClientOptions, CoreRuntime, RuntimeOptionsBuilder,
38
- ephemeral_server::{TemporalDevServerConfigBuilder, default_cached_download},
37
+ ClientOptions, CoreRuntime, RuntimeOptions,
38
+ ephemeral_server::{TemporalDevServerConfig, default_cached_download},
39
39
  init_worker,
40
40
  telemetry::CoreLogStreamConsumer,
41
41
  test_help::{NAMESPACE, WorkerTestHelpers, drain_pollers_and_shutdown},
@@ -125,15 +125,14 @@ async fn out_of_order_completion_doesnt_hang() {
125
125
  async fn switching_worker_client_changes_poll() {
126
126
  // Start two servers
127
127
  info!("Starting servers");
128
- let server_config = TemporalDevServerConfigBuilder::default()
128
+ let server_config = TemporalDevServerConfig::builder()
129
129
  .exe(default_cached_download())
130
130
  // We need to lower the poll timeout so the poll call rolls over
131
131
  .extra_args(vec![
132
132
  "--dynamic-config-value".to_string(),
133
133
  "matching.longPollExpirationInterval=\"1s\"".to_string(),
134
134
  ])
135
- .build()
136
- .unwrap();
135
+ .build();
137
136
  let mut server1 = server_config
138
137
  .start_server_with_output(Stdio::null(), Stdio::null())
139
138
  .await
@@ -191,16 +190,10 @@ async fn switching_worker_client_changes_poll() {
191
190
  .unwrap();
192
191
 
193
192
  // Create a worker only on the first server
194
- let worker = init_worker(
195
- init_integ_telem().unwrap(),
196
- integ_worker_config("my-task-queue")
197
- // We want a cache so we don't get extra remove-job activations
198
- .max_cached_workflows(100_usize)
199
- .build()
200
- .unwrap(),
201
- client1.clone(),
202
- )
203
- .unwrap();
193
+ let mut config = integ_worker_config("my-task-queue");
194
+ // We want a cache so we don't get extra remove-job activations
195
+ config.max_cached_workflows = 100_usize;
196
+ let worker = init_worker(init_integ_telem().unwrap(), config, client1.clone()).unwrap();
204
197
 
205
198
  // Poll for first task, confirm it's first wf, complete, and wait for complete
206
199
  info!("Doing initial poll");
@@ -252,24 +245,18 @@ async fn small_workflow_slots_and_pollers(#[values(false, true)] use_autoscaling
252
245
  let wf_name = "only_one_workflow_slot_and_two_pollers";
253
246
  let mut starter = CoreWfStarter::new(wf_name);
254
247
  if use_autoscaling {
255
- starter
256
- .worker_config
257
- .workflow_task_poller_behavior(PollerBehavior::Autoscaling {
258
- minimum: 1,
259
- maximum: 5,
260
- initial: 1,
261
- });
248
+ starter.worker_config.workflow_task_poller_behavior = PollerBehavior::Autoscaling {
249
+ minimum: 1,
250
+ maximum: 5,
251
+ initial: 1,
252
+ };
262
253
  } else {
263
- starter
264
- .worker_config
265
- .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(2));
254
+ starter.worker_config.workflow_task_poller_behavior = PollerBehavior::SimpleMaximum(2);
266
255
  }
267
- starter
268
- .worker_config
269
- .max_outstanding_workflow_tasks(2_usize)
270
- .max_outstanding_local_activities(1_usize)
271
- .activity_task_poller_behavior(PollerBehavior::SimpleMaximum(1))
272
- .max_outstanding_activities(1_usize);
256
+ starter.worker_config.max_outstanding_workflow_tasks = Some(2_usize);
257
+ starter.worker_config.max_outstanding_local_activities = Some(1_usize);
258
+ starter.worker_config.activity_task_poller_behavior = PollerBehavior::SimpleMaximum(1);
259
+ starter.worker_config.max_outstanding_activities = Some(1_usize);
273
260
  let mut worker = starter.worker().await;
274
261
  worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
275
262
  for _ in 0..3 {
@@ -330,14 +317,13 @@ async fn small_workflow_slots_and_pollers(#[values(false, true)] use_autoscaling
330
317
  #[tokio::test]
331
318
  async fn replace_client_works_after_polling_failure() {
332
319
  let (log_consumer, mut log_rx) = CoreLogStreamConsumer::new(100);
333
- let telem_opts = TelemetryOptionsBuilder::default()
320
+ let telem_opts = TelemetryOptions::builder()
334
321
  .logging(Logger::Push {
335
322
  filter: "OFF,temporalio_client=DEBUG".into(),
336
323
  consumer: Arc::new(log_consumer),
337
324
  })
338
- .build()
339
- .unwrap();
340
- let runtime_opts = RuntimeOptionsBuilder::default()
325
+ .build();
326
+ let runtime_opts = RuntimeOptions::builder()
341
327
  .telemetry_options(telem_opts)
342
328
  .build()
343
329
  .unwrap();
@@ -376,7 +362,7 @@ async fn replace_client_works_after_polling_failure() {
376
362
 
377
363
  // Starting a second dev server for the worker to connect to initially. Later this server will be shut down
378
364
  // and the worker client replaced with a client connected to the main integration test server.
379
- let initial_server_config = integ_dev_server_config(vec![]).build().unwrap();
365
+ let initial_server_config = integ_dev_server_config(vec![], false);
380
366
  let initial_server = Arc::new(Mutex::new(Some(
381
367
  initial_server_config
382
368
  .start_server_with_output(Stdio::null(), Stdio::null())
@@ -405,17 +391,10 @@ async fn replace_client_works_after_polling_failure() {
405
391
  let wf_name = "replace_client_works_after_polling_failure";
406
392
  let task_queue = format!("{wf_name}_tq");
407
393
 
408
- let worker = Arc::new(
409
- init_worker(
410
- &rt,
411
- integ_worker_config(&task_queue)
412
- .max_cached_workflows(100_usize)
413
- .build()
414
- .unwrap(),
415
- client_for_initial_server.clone(),
416
- )
417
- .unwrap(),
418
- );
394
+ let mut config = integ_worker_config(&task_queue);
395
+ config.max_cached_workflows = 100_usize;
396
+ let worker =
397
+ Arc::new(init_worker(&rt, config, client_for_initial_server.clone()).unwrap());
419
398
 
420
399
  // Polling the initial server the first time is successful.
421
400
  let wf_1 = client_for_initial_server