@temporalio/core-bridge 1.6.0 → 1.7.1

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 (138) hide show
  1. package/Cargo.lock +520 -456
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +8 -3
  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/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +1 -1
  13. package/sdk-core/.github/workflows/heavy.yml +1 -0
  14. package/sdk-core/README.md +13 -7
  15. package/sdk-core/client/src/lib.rs +27 -9
  16. package/sdk-core/client/src/metrics.rs +17 -8
  17. package/sdk-core/client/src/raw.rs +3 -3
  18. package/sdk-core/core/Cargo.toml +3 -4
  19. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  20. package/sdk-core/core/src/abstractions.rs +197 -18
  21. package/sdk-core/core/src/core_tests/activity_tasks.rs +137 -45
  22. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  23. package/sdk-core/core/src/core_tests/determinism.rs +212 -2
  24. package/sdk-core/core/src/core_tests/local_activities.rs +183 -36
  25. package/sdk-core/core/src/core_tests/queries.rs +32 -14
  26. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  27. package/sdk-core/core/src/core_tests/workflow_tasks.rs +340 -51
  28. package/sdk-core/core/src/ephemeral_server/mod.rs +110 -8
  29. package/sdk-core/core/src/internal_flags.rs +141 -0
  30. package/sdk-core/core/src/lib.rs +14 -9
  31. package/sdk-core/core/src/replay/mod.rs +16 -27
  32. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  33. package/sdk-core/core/src/telemetry/mod.rs +38 -14
  34. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  35. package/sdk-core/core/src/test_help/mod.rs +65 -13
  36. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  37. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  38. package/sdk-core/core/src/worker/activities/local_activities.rs +122 -6
  39. package/sdk-core/core/src/worker/activities.rs +347 -173
  40. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  41. package/sdk-core/core/src/worker/client.rs +18 -2
  42. package/sdk-core/core/src/worker/mod.rs +137 -44
  43. package/sdk-core/core/src/worker/workflow/history_update.rs +132 -51
  44. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +207 -166
  45. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +6 -7
  46. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
  47. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +157 -82
  48. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +12 -12
  49. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
  50. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +13 -15
  51. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +170 -60
  52. package/sdk-core/core/src/worker/workflow/machines/mod.rs +24 -16
  53. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +6 -8
  54. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +320 -204
  55. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +10 -13
  56. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +15 -23
  57. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +187 -46
  58. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +237 -111
  59. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -13
  60. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +10 -6
  61. package/sdk-core/core/src/worker/workflow/managed_run.rs +81 -62
  62. package/sdk-core/core/src/worker/workflow/mod.rs +341 -79
  63. package/sdk-core/core/src/worker/workflow/run_cache.rs +18 -11
  64. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +15 -3
  65. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -0
  66. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +75 -52
  67. package/sdk-core/core-api/Cargo.toml +0 -1
  68. package/sdk-core/core-api/src/lib.rs +13 -7
  69. package/sdk-core/core-api/src/telemetry.rs +4 -6
  70. package/sdk-core/core-api/src/worker.rs +5 -0
  71. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +80 -55
  72. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +22 -68
  73. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  74. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  75. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  76. package/sdk-core/protos/api_upstream/Makefile +1 -1
  77. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  78. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  80. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  81. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  83. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  84. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  87. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  88. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  89. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  90. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  91. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  92. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  93. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  94. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  95. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  96. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  97. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +65 -60
  98. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  99. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  100. package/sdk-core/sdk/Cargo.toml +1 -1
  101. package/sdk-core/sdk/src/lib.rs +21 -5
  102. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  103. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  104. package/sdk-core/sdk/src/workflow_future.rs +9 -3
  105. package/sdk-core/sdk-core-protos/src/history_builder.rs +114 -89
  106. package/sdk-core/sdk-core-protos/src/history_info.rs +6 -1
  107. package/sdk-core/sdk-core-protos/src/lib.rs +205 -64
  108. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  109. package/sdk-core/test-utils/src/lib.rs +32 -5
  110. package/sdk-core/tests/heavy_tests.rs +10 -43
  111. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. package/sdk-core/tests/integ_tests/polling_tests.rs +3 -8
  115. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -2
  116. package/sdk-core/tests/integ_tests/visibility_tests.rs +34 -23
  117. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +97 -81
  118. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -0
  120. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  121. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -1
  122. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -0
  123. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +25 -3
  124. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  125. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +30 -0
  126. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +64 -0
  127. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -0
  129. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -1
  130. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +7 -2
  131. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -7
  132. package/sdk-core/tests/integ_tests/workflow_tests.rs +8 -8
  133. package/sdk-core/tests/main.rs +16 -25
  134. package/sdk-core/tests/runner.rs +11 -9
  135. package/src/conversions.rs +14 -8
  136. package/src/runtime.rs +9 -8
  137. package/ts/index.ts +8 -6
  138. package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
@@ -34,6 +34,7 @@ use temporal_sdk_core::{
34
34
  replay::HistoryForReplay,
35
35
  ClientOptions, ClientOptionsBuilder, CoreRuntime, WorkerConfig, WorkerConfigBuilder,
36
36
  };
37
+ use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
37
38
  use temporal_sdk_core_api::{
38
39
  telemetry::{
39
40
  Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
@@ -50,6 +51,7 @@ use temporal_sdk_core_protos::{
50
51
  workflow_completion::WorkflowActivationCompletion,
51
52
  },
52
53
  temporal::api::{common::v1::Payload, history::v1::History},
54
+ DEFAULT_ACTIVITY_TYPE,
53
55
  };
54
56
  use tokio::sync::{mpsc::unbounded_channel, OnceCell};
55
57
  use url::Url;
@@ -58,8 +60,8 @@ pub const NAMESPACE: &str = "default";
58
60
  pub const TEST_Q: &str = "q";
59
61
  /// The env var used to specify where the integ tests should point
60
62
  pub const INTEG_SERVER_TARGET_ENV_VAR: &str = "TEMPORAL_SERVICE_ADDRESS";
61
- /// This env var is set (to any value) if temporalite is in use
62
- pub const INTEG_TEMPORALITE_USED_ENV_VAR: &str = "INTEG_TEMPORALITE_ON";
63
+ /// This env var is set (to any value) if temporal CLI dev server is in use
64
+ pub const INTEG_TEMPORAL_DEV_SERVER_USED_ENV_VAR: &str = "INTEG_TEMPORAL_DEV_SERVER_ON";
63
65
  /// This env var is set (to any value) if the test server is in use
64
66
  pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
65
67
 
@@ -301,6 +303,11 @@ impl CoreWfStarter {
301
303
  self
302
304
  }
303
305
 
306
+ pub fn no_remote_activities(&mut self) -> &mut Self {
307
+ self.worker_config.no_remote_activities = true;
308
+ self
309
+ }
310
+
304
311
  pub fn enable_wf_state_input_recording(&mut self) -> &mut Self {
305
312
  let (ser_tx, ser_rx) = unbounded_channel();
306
313
  let worker_cfg_clone = self.worker_config.clone();
@@ -569,7 +576,7 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
569
576
 
570
577
  pub fn default_cached_download() -> EphemeralExe {
571
578
  EphemeralExe::CachedDownload {
572
- version: EphemeralExeVersion::Default {
579
+ version: EphemeralExeVersion::SDKDefault {
573
580
  sdk_name: "sdk-rust".to_string(),
574
581
  sdk_version: "0.1.0".to_string(),
575
582
  },
@@ -588,7 +595,7 @@ pub fn schedule_activity_cmd(
588
595
  ScheduleActivity {
589
596
  seq,
590
597
  activity_id: activity_id.to_string(),
591
- activity_type: "test_activity".to_string(),
598
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
592
599
  task_queue: task_q.to_owned(),
593
600
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
594
601
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -609,7 +616,7 @@ pub fn schedule_local_activity_cmd(
609
616
  ScheduleLocalActivity {
610
617
  seq,
611
618
  activity_id: activity_id.to_string(),
612
- activity_type: "test_activity".to_string(),
619
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
613
620
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
614
621
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
615
622
  schedule_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -680,3 +687,23 @@ where
680
687
  .unwrap();
681
688
  }
682
689
  }
690
+
691
+ /// Initiate shutdown, drain the pollers, and wait for shutdown to complete.
692
+ pub async fn drain_pollers_and_shutdown(worker: &Arc<dyn CoreWorker>) {
693
+ worker.initiate_shutdown();
694
+ tokio::join!(
695
+ async {
696
+ assert!(matches!(
697
+ worker.poll_activity_task().await.unwrap_err(),
698
+ PollActivityError::ShutDown
699
+ ));
700
+ },
701
+ async {
702
+ assert!(matches!(
703
+ worker.poll_workflow_activation().await.unwrap_err(),
704
+ PollWfError::ShutDown,
705
+ ));
706
+ }
707
+ );
708
+ worker.shutdown().await;
709
+ }
@@ -1,11 +1,9 @@
1
- use assert_matches::assert_matches;
2
1
  use futures::{future::join_all, sink, stream::FuturesUnordered, StreamExt};
3
2
  use std::time::{Duration, Instant};
4
3
  use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowOptions};
5
4
  use temporal_sdk::{ActContext, ActivityOptions, WfContext, WorkflowResult};
6
5
  use temporal_sdk_core_protos::coresdk::{
7
- activity_result::ActivityExecutionResult, activity_task::activity_task as act_task,
8
- workflow_commands::ActivityCancellationType, ActivityTaskCompletion, AsJsonPayloadExt,
6
+ workflow_commands::ActivityCancellationType, AsJsonPayloadExt,
9
7
  };
10
8
  use temporal_sdk_core_test_utils::{workflows::la_problem_workflow, CoreWfStarter};
11
9
 
@@ -25,18 +23,16 @@ async fn activity_load() {
25
23
 
26
24
  let activity_id = "act-1";
27
25
  let activity_timeout = Duration::from_secs(8);
28
- let payload_dat = b"hello".to_vec();
29
26
  let task_queue = starter.get_task_queue().to_owned();
30
27
 
31
- let pd = payload_dat.clone();
32
28
  let wf_fn = move |ctx: WfContext| {
33
29
  let task_queue = task_queue.clone();
34
- let payload_dat = pd.clone();
35
-
30
+ let payload = "yo".as_json_payload().unwrap();
36
31
  async move {
37
32
  let activity = ActivityOptions {
38
33
  activity_id: Some(activity_id.to_string()),
39
34
  activity_type: "test_activity".to_string(),
35
+ input: payload.clone(),
40
36
  task_queue,
41
37
  schedule_to_start_timeout: Some(activity_timeout),
42
38
  start_to_close_timeout: Some(activity_timeout),
@@ -46,7 +42,7 @@ async fn activity_load() {
46
42
  ..Default::default()
47
43
  };
48
44
  let res = ctx.activity(activity).await.unwrap_ok_payload();
49
- assert_eq!(res.data, payload_dat);
45
+ assert_eq!(res.data, payload.data);
50
46
  Ok(().into())
51
47
  }
52
48
  };
@@ -54,6 +50,10 @@ async fn activity_load() {
54
50
  let starting = Instant::now();
55
51
  let wf_type = "activity_load";
56
52
  worker.register_wf(wf_type.to_owned(), wf_fn);
53
+ worker.register_activity(
54
+ "test_activity",
55
+ |_ctx: ActContext, echo: String| async move { Ok(echo) },
56
+ );
57
57
  join_all((0..CONCURRENCY).map(|i| {
58
58
  let worker = &worker;
59
59
  let wf_id = format!("activity_load_{i}");
@@ -73,42 +73,8 @@ async fn activity_load() {
73
73
  dbg!(starting.elapsed());
74
74
 
75
75
  let running = Instant::now();
76
- let core = starter.get_worker().await;
77
76
 
78
- // Poll for and complete all activities
79
- let c2 = core.clone();
80
- let all_acts = async move {
81
- let mut act_complete_futs = vec![];
82
- for _ in 0..CONCURRENCY {
83
- let task = c2.poll_activity_task().await.unwrap();
84
- assert_matches!(
85
- task.variant,
86
- Some(act_task::Variant::Start(ref start_activity)) => {
87
- assert_eq!(start_activity.activity_type, "test_activity")
88
- }
89
- );
90
- let pd = payload_dat.clone();
91
- let core = c2.clone();
92
- act_complete_futs.push(tokio::spawn(async move {
93
- core.complete_activity_task(ActivityTaskCompletion {
94
- task_token: task.task_token,
95
- result: Some(ActivityExecutionResult::ok(pd.into())),
96
- })
97
- .await
98
- .unwrap()
99
- }));
100
- }
101
- join_all(act_complete_futs)
102
- .await
103
- .into_iter()
104
- .for_each(|h| h.unwrap());
105
- };
106
- tokio::join! {
107
- async {
108
- worker.run_until_done().await.unwrap();
109
- },
110
- all_acts
111
- };
77
+ worker.run_until_done().await.unwrap();
112
78
  dbg!(running.elapsed());
113
79
  }
114
80
 
@@ -262,6 +228,7 @@ pub async fn many_parallel_timers_longhist(ctx: WfContext) -> WorkflowResult<()>
262
228
  async fn can_paginate_long_history() {
263
229
  let wf_name = "can_paginate_long_history";
264
230
  let mut starter = CoreWfStarter::new(wf_name);
231
+ starter.no_remote_activities();
265
232
  // Do not use sticky queues so we are forced to paginate once history gets long
266
233
  starter.max_cached_workflows(0);
267
234
 
@@ -1,13 +1,35 @@
1
1
  use std::time::{SystemTime, UNIX_EPOCH};
2
2
  use temporal_client::{ClientOptionsBuilder, TestService, WorkflowService};
3
3
  use temporal_sdk_core::ephemeral_server::{
4
- EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
5
- TestServerConfigBuilder,
4
+ EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporalDevServerConfigBuilder,
5
+ TemporaliteConfigBuilder, TestServerConfigBuilder,
6
6
  };
7
7
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
8
8
  use temporal_sdk_core_test_utils::{default_cached_download, NAMESPACE};
9
9
  use url::Url;
10
10
 
11
+ #[tokio::test]
12
+ async fn temporal_cli_default() {
13
+ let config = TemporalDevServerConfigBuilder::default()
14
+ .exe(default_cached_download())
15
+ .build()
16
+ .unwrap();
17
+ let mut server = config.start_server().await.unwrap();
18
+ assert_ephemeral_server(&server).await;
19
+ server.shutdown().await.unwrap();
20
+ }
21
+
22
+ #[tokio::test]
23
+ async fn temporal_cli_fixed() {
24
+ let config = TemporalDevServerConfigBuilder::default()
25
+ .exe(fixed_cached_download("v0.4.0"))
26
+ .build()
27
+ .unwrap();
28
+ let mut server = config.start_server().await.unwrap();
29
+ assert_ephemeral_server(&server).await;
30
+ server.shutdown().await.unwrap();
31
+ }
32
+
11
33
  #[tokio::test]
12
34
  async fn temporalite_default() {
13
35
  let config = TemporaliteConfigBuilder::default()
@@ -22,7 +44,7 @@ async fn temporalite_default() {
22
44
  #[tokio::test]
23
45
  async fn temporalite_fixed() {
24
46
  let config = TemporaliteConfigBuilder::default()
25
- .exe(fixed_cached_download("v0.1.1"))
47
+ .exe(fixed_cached_download("v0.2.0"))
26
48
  .build()
27
49
  .unwrap();
28
50
  let mut server = config.start_server().await.unwrap();
@@ -17,9 +17,11 @@ use temporal_sdk_core_protos::{
17
17
  common::v1::{Payload, RetryPolicy},
18
18
  enums::v1::TimeoutType,
19
19
  },
20
+ DEFAULT_ACTIVITY_TYPE,
20
21
  };
21
22
  use temporal_sdk_core_test_utils::{
22
- init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
23
+ drain_pollers_and_shutdown, init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter,
24
+ WorkerTestHelpers,
23
25
  };
24
26
  use tokio::time::sleep;
25
27
 
@@ -49,7 +51,7 @@ async fn activity_heartbeat() {
49
51
  assert_matches!(
50
52
  task.variant,
51
53
  Some(activity_task::Variant::Start(start_activity)) => {
52
- assert_eq!(start_activity.activity_type, "test_activity".to_string())
54
+ assert_eq!(start_activity.activity_type, DEFAULT_ACTIVITY_TYPE.to_string())
53
55
  }
54
56
  );
55
57
  // Heartbeat timeout is set to 1 second, this loop is going to send heartbeat every 100ms.
@@ -169,7 +171,7 @@ async fn many_act_fails_with_heartbeats() {
169
171
  },]
170
172
  );
171
173
  core.complete_execution(&task.run_id).await;
172
- core.shutdown().await;
174
+ drain_pollers_and_shutdown(&core).await;
173
175
  }
174
176
 
175
177
  #[tokio::test]
@@ -1,18 +1,34 @@
1
- use temporal_client::WorkflowService;
2
- use temporal_sdk_core::CoreRuntime;
3
- use temporal_sdk_core_api::telemetry::MetricsExporter;
4
- use temporal_sdk_core_protos::temporal::api::workflowservice::v1::ListNamespacesRequest;
5
- use temporal_sdk_core_test_utils::{get_integ_server_options, get_integ_telem_options};
1
+ use std::{sync::Arc, time::Duration};
2
+ use temporal_client::{WorkflowClientTrait, WorkflowOptions, WorkflowService};
3
+ use temporal_sdk_core::{init_worker, CoreRuntime};
4
+ use temporal_sdk_core_api::{telemetry::MetricsExporter, worker::WorkerConfigBuilder, Worker};
5
+ use temporal_sdk_core_protos::{
6
+ coresdk::{
7
+ activity_result::ActivityExecutionResult,
8
+ workflow_commands::{ScheduleActivity, ScheduleLocalActivity},
9
+ workflow_completion::WorkflowActivationCompletion,
10
+ ActivityTaskCompletion,
11
+ },
12
+ temporal::api::{enums::v1::WorkflowIdReusePolicy, workflowservice::v1::ListNamespacesRequest},
13
+ };
14
+ use temporal_sdk_core_test_utils::{get_integ_server_options, get_integ_telem_options, NAMESPACE};
15
+ use tokio::sync::Barrier;
16
+
17
+ static ANY_PORT: &str = "127.0.0.1:0";
18
+
19
+ async fn get_text(endpoint: String) -> String {
20
+ reqwest::get(endpoint).await.unwrap().text().await.unwrap()
21
+ }
6
22
 
7
23
  #[tokio::test]
8
24
  async fn prometheus_metrics_exported() {
9
25
  let mut telemopts = get_integ_telem_options();
10
- let addr = "127.0.0.1:10919";
11
- telemopts.metrics = Some(MetricsExporter::Prometheus(addr.parse().unwrap()));
26
+ telemopts.metrics = Some(MetricsExporter::Prometheus(ANY_PORT.parse().unwrap()));
12
27
  let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
28
+ let addr = rt.telemetry().prom_port().unwrap();
13
29
  let opts = get_integ_server_options();
14
30
  let mut raw_client = opts
15
- .connect_no_namespace(rt.metric_meter(), None)
31
+ .connect_no_namespace(rt.metric_meter().as_deref(), None)
16
32
  .await
17
33
  .unwrap();
18
34
  assert!(raw_client.get_client().capabilities().is_some());
@@ -22,16 +38,202 @@ async fn prometheus_metrics_exported() {
22
38
  .await
23
39
  .unwrap();
24
40
 
25
- let body = reqwest::get(format!("http://{addr}/metrics"))
26
- .await
27
- .unwrap()
28
- .text()
29
- .await
30
- .unwrap();
41
+ let body = get_text(format!("http://{addr}/metrics")).await;
31
42
  assert!(body.contains(
32
- "request_latency_count{operation=\"ListNamespaces\",service_name=\"temporal-core-sdk\"} 1"
43
+ "temporal_request_latency_count{operation=\"ListNamespaces\",service_name=\"temporal-core-sdk\"} 1"
33
44
  ));
34
45
  assert!(body.contains(
35
- "request_latency_count{operation=\"GetSystemInfo\",service_name=\"temporal-core-sdk\"} 1"
46
+ "temporal_request_latency_count{operation=\"GetSystemInfo\",service_name=\"temporal-core-sdk\"} 1"
36
47
  ));
37
48
  }
49
+
50
+ #[tokio::test]
51
+ async fn one_slot_worker_reports_available_slot() {
52
+ let mut telemopts = get_integ_telem_options();
53
+ let tq = "one_slot_worker_tq";
54
+ telemopts.metrics = Some(MetricsExporter::Prometheus(ANY_PORT.parse().unwrap()));
55
+ let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
56
+ let addr = rt.telemetry().prom_port().unwrap();
57
+
58
+ let worker_cfg = WorkerConfigBuilder::default()
59
+ .namespace(NAMESPACE)
60
+ .task_queue(tq)
61
+ .worker_build_id("test_build_id")
62
+ .max_cached_workflows(1_usize)
63
+ .max_outstanding_activities(1_usize)
64
+ .max_outstanding_local_activities(1_usize)
65
+ .max_outstanding_workflow_tasks(1_usize)
66
+ .build()
67
+ .unwrap();
68
+
69
+ let client = Arc::new(
70
+ get_integ_server_options()
71
+ .connect(worker_cfg.namespace.clone(), None, None)
72
+ .await
73
+ .expect("Must connect"),
74
+ );
75
+ let worker = init_worker(&rt, worker_cfg, client.clone()).expect("Worker inits cleanly");
76
+ let wf_task_barr = Barrier::new(2);
77
+ let act_task_barr = Barrier::new(2);
78
+
79
+ let wf_polling = async {
80
+ let task = worker.poll_workflow_activation().await.unwrap();
81
+ wf_task_barr.wait().await;
82
+ wf_task_barr.wait().await;
83
+ worker
84
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
85
+ task.run_id,
86
+ ScheduleActivity {
87
+ seq: 1,
88
+ activity_id: "1".to_string(),
89
+ activity_type: "test_act".to_string(),
90
+ task_queue: tq.to_string(),
91
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
92
+ ..Default::default()
93
+ }
94
+ .into(),
95
+ ))
96
+ .await
97
+ .unwrap();
98
+ wf_task_barr.wait().await;
99
+
100
+ let task = worker.poll_workflow_activation().await.unwrap();
101
+ worker
102
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
103
+ task.run_id,
104
+ ScheduleLocalActivity {
105
+ seq: 2,
106
+ activity_id: "2".to_string(),
107
+ activity_type: "test_act".to_string(),
108
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
109
+ ..Default::default()
110
+ }
111
+ .into(),
112
+ ))
113
+ .await
114
+ .unwrap();
115
+ };
116
+
117
+ let act_polling = async {
118
+ let task = worker.poll_activity_task().await.unwrap();
119
+ act_task_barr.wait().await;
120
+ worker
121
+ .complete_activity_task(ActivityTaskCompletion {
122
+ task_token: task.task_token,
123
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
124
+ })
125
+ .await
126
+ .unwrap();
127
+ act_task_barr.wait().await;
128
+
129
+ let task = worker.poll_activity_task().await.unwrap();
130
+ act_task_barr.wait().await;
131
+ act_task_barr.wait().await;
132
+ worker
133
+ .complete_activity_task(ActivityTaskCompletion {
134
+ task_token: task.task_token,
135
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
136
+ })
137
+ .await
138
+ .unwrap();
139
+ act_task_barr.wait().await;
140
+ };
141
+
142
+ let testing = async {
143
+ // Wait just a beat for the poller to initiate
144
+ tokio::time::sleep(Duration::from_millis(50)).await;
145
+ let body = get_text(format!("http://{addr}/metrics")).await;
146
+ assert!(body.contains(&format!(
147
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
148
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
149
+ worker_type=\"WorkflowWorker\"}} 1"
150
+ )));
151
+
152
+ // Start a workflow so that a task will get delivered
153
+ client
154
+ .start_workflow(
155
+ vec![],
156
+ tq.to_owned(),
157
+ "one_slot_metric_test".to_owned(),
158
+ "whatever".to_string(),
159
+ None,
160
+ WorkflowOptions {
161
+ id_reuse_policy: WorkflowIdReusePolicy::TerminateIfRunning,
162
+ execution_timeout: Some(Duration::from_secs(5)),
163
+ ..Default::default()
164
+ },
165
+ )
166
+ .await
167
+ .unwrap();
168
+
169
+ wf_task_barr.wait().await;
170
+
171
+ // At this point the workflow task is outstanding, so there should be 0 slots, and
172
+ // the activities haven't started, so there should be 1 each.
173
+ let body = get_text(format!("http://{addr}/metrics")).await;
174
+ assert!(body.contains(&format!(
175
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
176
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
177
+ worker_type=\"WorkflowWorker\"}} 0"
178
+ )));
179
+ assert!(body.contains(&format!(
180
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
181
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
182
+ worker_type=\"ActivityWorker\"}} 1"
183
+ )));
184
+ assert!(body.contains(&format!(
185
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
186
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
187
+ worker_type=\"LocalActivityWorker\"}} 1"
188
+ )));
189
+
190
+ // Now we allow the complete to proceed. Once it goes through, there should be 1 WFT slot
191
+ // open but 0 activity slots
192
+ wf_task_barr.wait().await;
193
+ wf_task_barr.wait().await;
194
+ // Sometimes the recording takes an extra bit. 🤷
195
+ tokio::time::sleep(Duration::from_millis(100)).await;
196
+ let body = get_text(format!("http://{addr}/metrics")).await;
197
+ assert!(body.contains(&format!(
198
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
199
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
200
+ worker_type=\"WorkflowWorker\"}} 1"
201
+ )));
202
+ assert!(body.contains(&format!(
203
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
204
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
205
+ worker_type=\"ActivityWorker\"}} 0"
206
+ )));
207
+
208
+ // Now complete the activity and watch it go up
209
+ act_task_barr.wait().await;
210
+ // Wait for completion to finish
211
+ act_task_barr.wait().await;
212
+ let body = get_text(format!("http://{addr}/metrics")).await;
213
+ assert!(body.contains(&format!(
214
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
215
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
216
+ worker_type=\"ActivityWorker\"}} 1"
217
+ )));
218
+
219
+ // Proceed to local activity command
220
+ act_task_barr.wait().await;
221
+ // Ensure that, once we have the LA task, slots are 0
222
+ let body = get_text(format!("http://{addr}/metrics")).await;
223
+ assert!(body.contains(&format!(
224
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
225
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
226
+ worker_type=\"LocalActivityWorker\"}} 0"
227
+ )));
228
+ // When completion is done, we have 1 again
229
+ act_task_barr.wait().await;
230
+ act_task_barr.wait().await;
231
+ let body = get_text(format!("http://{addr}/metrics")).await;
232
+ assert!(body.contains(&format!(
233
+ "temporal_worker_task_slots_available{{namespace=\"{NAMESPACE}\",\
234
+ service_name=\"temporal-core-sdk\",task_queue=\"one_slot_worker_tq\",\
235
+ worker_type=\"LocalActivityWorker\"}} 1"
236
+ )));
237
+ };
238
+ tokio::join!(wf_polling, act_polling, testing);
239
+ }
@@ -40,15 +40,10 @@ async fn out_of_order_completion_doesnt_hang() {
40
40
  )
41
41
  .await
42
42
  .unwrap();
43
- // Poll activity and verify that it's been scheduled with correct parameters, we don't expect to
44
- // complete it in this test as activity is try-cancelled.
43
+ // Poll activity and verify that it's been scheduled, we don't expect to complete it in this
44
+ // test as activity is try-cancelled.
45
45
  let activity_task = core.poll_activity_task().await.unwrap();
46
- assert_matches!(
47
- activity_task.variant,
48
- Some(act_task::Variant::Start(start_activity)) => {
49
- assert_eq!(start_activity.activity_type, "test_activity".to_string())
50
- }
51
- );
46
+ assert_matches!(activity_task.variant, Some(act_task::Variant::Start(_)));
52
47
  // Poll workflow task and verify that activity has failed.
53
48
  let task = core.poll_workflow_activation().await.unwrap();
54
49
  assert_matches!(
@@ -10,7 +10,9 @@ use temporal_sdk_core_protos::{
10
10
  },
11
11
  temporal::api::{failure::v1::Failure, query::v1::WorkflowQuery},
12
12
  };
13
- use temporal_sdk_core_test_utils::{init_core_and_create_wf, WorkerTestHelpers};
13
+ use temporal_sdk_core_test_utils::{
14
+ drain_pollers_and_shutdown, init_core_and_create_wf, WorkerTestHelpers,
15
+ };
14
16
 
15
17
  #[tokio::test]
16
18
  async fn simple_query_legacy() {
@@ -205,7 +207,7 @@ async fn query_after_execution_complete(#[case] do_evict: bool) {
205
207
  query_futs.push(do_workflow(true).map(|_| ()).boxed());
206
208
  }
207
209
  while query_futs.next().await.is_some() {}
208
- core.shutdown().await;
210
+ drain_pollers_and_shutdown(core).await;
209
211
  }
210
212
 
211
213
  #[tokio::test]
@@ -8,7 +8,8 @@ use temporal_sdk_core_protos::coresdk::workflow_activation::{
8
8
  workflow_activation_job, WorkflowActivationJob,
9
9
  };
10
10
  use temporal_sdk_core_test_utils::{
11
- get_integ_server_options, CoreWfStarter, WorkerTestHelpers, NAMESPACE,
11
+ drain_pollers_and_shutdown, get_integ_server_options, CoreWfStarter, WorkerTestHelpers,
12
+ NAMESPACE,
12
13
  };
13
14
  use tokio::time::sleep;
14
15
 
@@ -51,29 +52,39 @@ async fn client_list_open_closed_workflow_executions() {
51
52
 
52
53
  // Complete workflow
53
54
  core.complete_execution(&task.run_id).await;
54
- core.shutdown().await;
55
+ drain_pollers_and_shutdown(&core).await;
55
56
 
56
- // List above CLOSED workflow
57
- let start_time_filter = StartTimeFilter {
58
- earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
59
- latest_time: Some(latest).and_then(|t| t.try_into().ok()),
60
- };
61
- let filter = ListClosedFilters::ExecutionFilter(WorkflowExecutionFilter {
62
- workflow_id: wf_name.clone(),
63
- run_id,
64
- });
65
- let closed_workflows = client
66
- .list_closed_workflow_executions(
67
- 1,
68
- Default::default(),
69
- Some(start_time_filter),
70
- Some(filter),
71
- )
72
- .await
73
- .unwrap();
74
- assert_eq!(closed_workflows.executions.len(), 1);
75
- let workflow = closed_workflows.executions[0].clone();
76
- assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
57
+ // List above CLOSED workflow. Visibility doesn't always update immediately so we give this a
58
+ // few tries.
59
+ let mut passed = false;
60
+ for _ in 1..=5 {
61
+ let closed_workflows = client
62
+ .list_closed_workflow_executions(
63
+ 1,
64
+ Default::default(),
65
+ Some(StartTimeFilter {
66
+ earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
67
+ latest_time: Some(latest).and_then(|t| t.try_into().ok()),
68
+ }),
69
+ Some(ListClosedFilters::ExecutionFilter(
70
+ WorkflowExecutionFilter {
71
+ workflow_id: wf_name.clone(),
72
+ run_id: run_id.clone(),
73
+ },
74
+ )),
75
+ )
76
+ .await
77
+ .unwrap();
78
+ if closed_workflows.executions.len() == 1 {
79
+ let workflow = &closed_workflows.executions[0];
80
+ if workflow.execution.as_ref().unwrap().workflow_id == wf_name {
81
+ passed = true;
82
+ break;
83
+ }
84
+ }
85
+ sleep(Duration::from_millis(100)).await;
86
+ }
87
+ assert!(passed);
77
88
  }
78
89
 
79
90
  #[tokio::test]