@temporalio/core-bridge 1.5.2 → 1.7.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 (194) hide show
  1. package/Cargo.lock +304 -112
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +9 -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/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +2 -4
  13. package/sdk-core/.cargo/config.toml +5 -2
  14. package/sdk-core/.github/workflows/heavy.yml +29 -0
  15. package/sdk-core/Cargo.toml +1 -1
  16. package/sdk-core/README.md +20 -10
  17. package/sdk-core/client/src/lib.rs +215 -39
  18. package/sdk-core/client/src/metrics.rs +17 -8
  19. package/sdk-core/client/src/raw.rs +4 -4
  20. package/sdk-core/client/src/retry.rs +32 -20
  21. package/sdk-core/core/Cargo.toml +25 -12
  22. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  23. package/sdk-core/core/src/abstractions.rs +204 -14
  24. package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
  25. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  26. package/sdk-core/core/src/core_tests/determinism.rs +165 -2
  27. package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
  28. package/sdk-core/core/src/core_tests/queries.rs +34 -16
  29. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
  32. package/sdk-core/core/src/internal_flags.rs +155 -0
  33. package/sdk-core/core/src/lib.rs +16 -9
  34. package/sdk-core/core/src/protosext/mod.rs +1 -1
  35. package/sdk-core/core/src/replay/mod.rs +16 -27
  36. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  37. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  38. package/sdk-core/core/src/telemetry/mod.rs +60 -21
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  40. package/sdk-core/core/src/test_help/mod.rs +73 -14
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
  44. package/sdk-core/core/src/worker/activities.rs +350 -175
  45. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  46. package/sdk-core/core/src/worker/client.rs +18 -2
  47. package/sdk-core/core/src/worker/mod.rs +183 -64
  48. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  49. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  50. package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
  51. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
  53. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
  54. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
  55. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
  56. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
  57. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  65. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
  66. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
  68. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
  69. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
  70. package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
  71. package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
  72. package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  73. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
  74. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  75. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  76. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  77. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
  78. package/sdk-core/core-api/Cargo.toml +2 -1
  79. package/sdk-core/core-api/src/errors.rs +1 -34
  80. package/sdk-core/core-api/src/lib.rs +19 -9
  81. package/sdk-core/core-api/src/telemetry.rs +4 -6
  82. package/sdk-core/core-api/src/worker.rs +19 -1
  83. package/sdk-core/etc/deps.svg +115 -140
  84. package/sdk-core/etc/regen-depgraph.sh +5 -0
  85. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
  86. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
  87. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  88. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  89. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  90. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  91. package/sdk-core/protos/api_upstream/Makefile +6 -6
  92. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  93. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  94. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  95. package/sdk-core/protos/api_upstream/go.mod +6 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
  98. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
  103. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  106. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  107. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  108. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  109. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  110. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  111. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  112. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  113. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  114. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
  115. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  116. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
  117. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  118. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  119. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  120. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  121. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  122. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  123. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  124. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  125. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  126. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  127. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
  128. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
  129. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  130. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  131. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  132. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  133. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  134. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  135. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
  136. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  137. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  138. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  139. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  140. package/sdk-core/sdk/Cargo.toml +5 -4
  141. package/sdk-core/sdk/src/lib.rs +108 -26
  142. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  143. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  144. package/sdk-core/sdk/src/workflow_future.rs +16 -15
  145. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  146. package/sdk-core/sdk-core-protos/build.rs +36 -2
  147. package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
  148. package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
  149. package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
  150. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  151. package/sdk-core/test-utils/Cargo.toml +3 -1
  152. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  153. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  154. package/sdk-core/test-utils/src/lib.rs +82 -23
  155. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  156. package/sdk-core/test-utils/src/workflows.rs +29 -0
  157. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  158. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  159. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  160. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  161. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  162. package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  163. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  164. package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  165. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
  166. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  167. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  168. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  169. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  170. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  171. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
  172. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  173. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
  174. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
  175. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  176. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  177. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  178. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  179. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
  180. package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
  181. package/sdk-core/tests/main.rs +3 -13
  182. package/sdk-core/tests/runner.rs +75 -36
  183. package/sdk-core/tests/wf_input_replay.rs +32 -0
  184. package/src/conversions.rs +14 -8
  185. package/src/runtime.rs +9 -8
  186. package/ts/index.ts +8 -6
  187. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  188. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  189. package/sdk-core/bridge-ffi/build.rs +0 -25
  190. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  191. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  192. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  193. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  194. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -23,6 +23,6 @@ async fn main() -> Result<(), anyhow::Error> {
23
23
  .expect("history field must be populated");
24
24
  // Serialize history to file
25
25
  let byteified = hist.encode_to_vec();
26
- tokio::fs::write(format!("{}_history.bin", wf_id), &byteified).await?;
26
+ tokio::fs::write(format!("{wf_id}_history.bin"), &byteified).await?;
27
27
  Ok(())
28
28
  }
@@ -5,8 +5,14 @@
5
5
  extern crate tracing;
6
6
 
7
7
  pub mod canned_histories;
8
+ pub mod wf_input_saver;
9
+ pub mod workflows;
8
10
 
9
- use crate::stream::{Stream, TryStreamExt};
11
+ use crate::{
12
+ stream::{Stream, TryStreamExt},
13
+ wf_input_saver::stream_to_file,
14
+ };
15
+ use base64::{prelude::BASE64_STANDARD, Engine};
10
16
  use futures::{future, stream, stream::FuturesUnordered, StreamExt};
11
17
  use parking_lot::Mutex;
12
18
  use prost::Message;
@@ -28,6 +34,7 @@ use temporal_sdk_core::{
28
34
  replay::HistoryForReplay,
29
35
  ClientOptions, ClientOptionsBuilder, CoreRuntime, WorkerConfig, WorkerConfigBuilder,
30
36
  };
37
+ use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
31
38
  use temporal_sdk_core_api::{
32
39
  telemetry::{
33
40
  Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
@@ -44,16 +51,17 @@ use temporal_sdk_core_protos::{
44
51
  workflow_completion::WorkflowActivationCompletion,
45
52
  },
46
53
  temporal::api::{common::v1::Payload, history::v1::History},
54
+ DEFAULT_ACTIVITY_TYPE,
47
55
  };
48
- use tokio::sync::OnceCell;
56
+ use tokio::sync::{mpsc::unbounded_channel, OnceCell};
49
57
  use url::Url;
50
58
 
51
59
  pub const NAMESPACE: &str = "default";
52
60
  pub const TEST_Q: &str = "q";
53
61
  /// The env var used to specify where the integ tests should point
54
62
  pub const INTEG_SERVER_TARGET_ENV_VAR: &str = "TEMPORAL_SERVICE_ADDRESS";
55
- /// This env var is set (to any value) if temporalite is in use
56
- 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";
57
65
  /// This env var is set (to any value) if the test server is in use
58
66
  pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
59
67
 
@@ -61,6 +69,15 @@ pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
61
69
  const OTEL_URL_ENV_VAR: &str = "TEMPORAL_INTEG_OTEL_URL";
62
70
  /// If set, enable direct scraping of prom metrics on the specified port
63
71
  const PROM_ENABLE_ENV_VAR: &str = "TEMPORAL_INTEG_PROM_PORT";
72
+ #[macro_export]
73
+ macro_rules! prost_dur {
74
+ ($dur_call:ident $args:tt) => {
75
+ std::time::Duration::$dur_call$args
76
+ .try_into()
77
+ .expect("test duration fits")
78
+ };
79
+ }
80
+
64
81
  /// Create a worker instance which will use the provided test name to base the task queue and wf id
65
82
  /// upon. Returns the instance and the task queue name (which is also the workflow id).
66
83
  pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
@@ -82,6 +99,7 @@ pub fn init_core_replay_stream<I>(test_name: &str, histories: I) -> Arc<dyn Core
82
99
  where
83
100
  I: Stream<Item = HistoryForReplay> + Send + 'static,
84
101
  {
102
+ init_integ_telem();
85
103
  let worker_cfg = WorkerConfigBuilder::default()
86
104
  .namespace(NAMESPACE)
87
105
  .task_queue(test_name)
@@ -134,7 +152,8 @@ pub struct CoreWfStarter {
134
152
  /// Used for both the task queue and workflow id
135
153
  task_queue_name: String,
136
154
  pub worker_config: WorkerConfig,
137
- wft_timeout: Option<Duration>,
155
+ /// Options to use when starting workflow(s)
156
+ pub workflow_options: WorkflowOptions,
138
157
  initted_worker: OnceCell<InitializedWorker>,
139
158
  }
140
159
  struct InitializedWorker {
@@ -144,14 +163,10 @@ struct InitializedWorker {
144
163
 
145
164
  impl CoreWfStarter {
146
165
  pub fn new(test_name: &str) -> Self {
147
- let rand_bytes: Vec<u8> = rand::thread_rng().sample_iter(&Standard).take(6).collect();
148
- let task_q_salt = base64::encode(rand_bytes);
149
- let task_queue = format!("{}_{}", test_name, task_q_salt);
150
- Self::new_tq_name(&task_queue)
151
- }
152
-
153
- pub fn new_tq_name(task_queue: &str) -> Self {
154
166
  init_integ_telem();
167
+ let rand_bytes: Vec<u8> = rand::thread_rng().sample_iter(&Standard).take(6).collect();
168
+ let task_q_salt = BASE64_STANDARD.encode(rand_bytes);
169
+ let task_queue = format!("{test_name}_{task_q_salt}");
155
170
  Self {
156
171
  task_queue_name: task_queue.to_owned(),
157
172
  worker_config: WorkerConfigBuilder::default()
@@ -161,8 +176,8 @@ impl CoreWfStarter {
161
176
  .max_cached_workflows(1000_usize)
162
177
  .build()
163
178
  .unwrap(),
164
- wft_timeout: None,
165
179
  initted_worker: OnceCell::new(),
180
+ workflow_options: Default::default(),
166
181
  }
167
182
  }
168
183
 
@@ -189,13 +204,27 @@ impl CoreWfStarter {
189
204
  }
190
205
 
191
206
  /// Start the workflow defined by the builder and return run id
192
- pub async fn start_wf(&self) -> String {
193
- self.start_wf_with_id(self.task_queue_name.clone(), WorkflowOptions::default())
207
+ pub async fn start_wf(&mut self) -> String {
208
+ self.start_wf_with_id(self.task_queue_name.clone()).await
209
+ }
210
+
211
+ pub async fn start_with_worker(
212
+ &self,
213
+ wf_name: impl Into<String>,
214
+ worker: &mut TestWorker,
215
+ ) -> String {
216
+ worker
217
+ .submit_wf(
218
+ self.task_queue_name.clone(),
219
+ wf_name.into(),
220
+ vec![],
221
+ self.workflow_options.clone(),
222
+ )
194
223
  .await
224
+ .unwrap()
195
225
  }
196
226
 
197
- pub async fn start_wf_with_id(&self, workflow_id: String, mut opts: WorkflowOptions) -> String {
198
- opts.task_timeout = opts.task_timeout.or(self.wft_timeout);
227
+ pub async fn start_wf_with_id(&self, workflow_id: String) -> String {
199
228
  self.initted_worker
200
229
  .get()
201
230
  .expect(
@@ -209,7 +238,7 @@ impl CoreWfStarter {
209
238
  workflow_id,
210
239
  self.task_queue_name.clone(),
211
240
  None,
212
- opts,
241
+ self.workflow_options.clone(),
213
242
  )
214
243
  .await
215
244
  .unwrap()
@@ -274,8 +303,18 @@ impl CoreWfStarter {
274
303
  self
275
304
  }
276
305
 
277
- pub fn wft_timeout(&mut self, timeout: Duration) -> &mut Self {
278
- self.wft_timeout = Some(timeout);
306
+ pub fn no_remote_activities(&mut self) -> &mut Self {
307
+ self.worker_config.no_remote_activities = true;
308
+ self
309
+ }
310
+
311
+ pub fn enable_wf_state_input_recording(&mut self) -> &mut Self {
312
+ let (ser_tx, ser_rx) = unbounded_channel();
313
+ let worker_cfg_clone = self.worker_config.clone();
314
+ tokio::spawn(async move {
315
+ stream_to_file(&worker_cfg_clone, ser_rx).await.unwrap();
316
+ });
317
+ self.worker_config.wf_state_inputs = Some(ser_tx);
279
318
  self
280
319
  }
281
320
 
@@ -537,7 +576,7 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
537
576
 
538
577
  pub fn default_cached_download() -> EphemeralExe {
539
578
  EphemeralExe::CachedDownload {
540
- version: EphemeralExeVersion::Default {
579
+ version: EphemeralExeVersion::SDKDefault {
541
580
  sdk_name: "sdk-rust".to_string(),
542
581
  sdk_version: "0.1.0".to_string(),
543
582
  },
@@ -556,7 +595,7 @@ pub fn schedule_activity_cmd(
556
595
  ScheduleActivity {
557
596
  seq,
558
597
  activity_id: activity_id.to_string(),
559
- activity_type: "test_activity".to_string(),
598
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
560
599
  task_queue: task_q.to_owned(),
561
600
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
562
601
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -577,7 +616,7 @@ pub fn schedule_local_activity_cmd(
577
616
  ScheduleLocalActivity {
578
617
  seq,
579
618
  activity_id: activity_id.to_string(),
580
- activity_type: "test_activity".to_string(),
619
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
581
620
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
582
621
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
583
622
  schedule_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -648,3 +687,23 @@ where
648
687
  .unwrap();
649
688
  }
650
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
+ }
@@ -0,0 +1,50 @@
1
+ use anyhow::anyhow;
2
+ use bytes::BytesMut;
3
+ use futures::{stream::BoxStream, SinkExt, StreamExt};
4
+ use prost::bytes::Bytes;
5
+ use std::path::Path;
6
+ use temporal_sdk_core_api::worker::WorkerConfig;
7
+ use tokio::{fs::File, sync::mpsc::UnboundedReceiver};
8
+ use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec};
9
+
10
+ pub struct WFStateReplayData {
11
+ pub config: WorkerConfig,
12
+ pub inputs: BoxStream<'static, Result<BytesMut, std::io::Error>>,
13
+ }
14
+
15
+ pub async fn stream_to_file(
16
+ config: &WorkerConfig,
17
+ mut rcv: UnboundedReceiver<Vec<u8>>,
18
+ ) -> Result<(), anyhow::Error> {
19
+ let file = File::create("wf_inputs").await?;
20
+ let mut transport = FramedWrite::new(file, ldc());
21
+ // First write the worker config, since things like cache size affect how many evictions there
22
+ // will be, etc.
23
+ transport.send(rmp_serde::to_vec(config)?.into()).await?;
24
+ while let Some(v) = rcv.recv().await {
25
+ transport.send(Bytes::from(v)).await?;
26
+ }
27
+ Ok(())
28
+ }
29
+
30
+ pub async fn read_from_file(path: impl AsRef<Path>) -> Result<WFStateReplayData, anyhow::Error> {
31
+ let file = File::open(path).await?;
32
+ let mut framed_read = FramedRead::new(file, ldc());
33
+ // Deserialize the worker config first
34
+ let config = framed_read
35
+ .next()
36
+ .await
37
+ .ok_or_else(|| anyhow!("Replay data file is empty"))??;
38
+ let config = rmp_serde::from_slice(config.as_ref())?;
39
+
40
+ Ok(WFStateReplayData {
41
+ config,
42
+ inputs: framed_read.boxed(),
43
+ })
44
+ }
45
+
46
+ fn ldc() -> LengthDelimitedCodec {
47
+ LengthDelimitedCodec::builder()
48
+ .length_field_type::<u16>()
49
+ .new_codec()
50
+ }
@@ -0,0 +1,29 @@
1
+ use crate::prost_dur;
2
+ use std::time::Duration;
3
+ use temporal_sdk::{ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
4
+ use temporal_sdk_core_protos::{coresdk::AsJsonPayloadExt, temporal::api::common::v1::RetryPolicy};
5
+
6
+ pub async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
7
+ ctx.local_activity(LocalActivityOptions {
8
+ activity_type: "delay".to_string(),
9
+ input: "hi".as_json_payload().expect("serializes fine"),
10
+ retry_policy: RetryPolicy {
11
+ initial_interval: Some(prost_dur!(from_micros(15))),
12
+ backoff_coefficient: 1_000.,
13
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
14
+ maximum_attempts: 4,
15
+ non_retryable_error_types: vec![],
16
+ },
17
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
18
+ ..Default::default()
19
+ })
20
+ .await;
21
+ ctx.activity(ActivityOptions {
22
+ activity_type: "delay".to_string(),
23
+ start_to_close_timeout: Some(Duration::from_secs(20)),
24
+ input: "hi!".as_json_payload().expect("serializes fine"),
25
+ ..Default::default()
26
+ })
27
+ .await;
28
+ Ok(().into())
29
+ }
@@ -0,0 +1,130 @@
1
+ use futures_util::{sink, stream::FuturesUnordered, FutureExt, StreamExt};
2
+ use rand::{prelude::Distribution, rngs::SmallRng, Rng, SeedableRng};
3
+ use std::{future, time::Duration};
4
+ use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowOptions};
5
+ use temporal_sdk::{ActContext, ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
6
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt, IntoPayloadsExt};
7
+ use temporal_sdk_core_test_utils::CoreWfStarter;
8
+ use tokio_util::sync::CancellationToken;
9
+
10
+ const FUZZY_SIG: &str = "fuzzy_sig";
11
+
12
+ #[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
13
+ enum FuzzyWfAction {
14
+ Shutdown,
15
+ DoAct,
16
+ DoLocalAct,
17
+ }
18
+
19
+ struct FuzzyWfActionSampler;
20
+ impl Distribution<FuzzyWfAction> for FuzzyWfActionSampler {
21
+ fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> FuzzyWfAction {
22
+ let v: u8 = rng.gen_range(1..=2);
23
+ match v {
24
+ 1 => FuzzyWfAction::DoAct,
25
+ 2 => FuzzyWfAction::DoLocalAct,
26
+ _ => unreachable!(),
27
+ }
28
+ }
29
+ }
30
+
31
+ async fn echo(_ctx: ActContext, echo_me: String) -> Result<String, anyhow::Error> {
32
+ Ok(echo_me)
33
+ }
34
+
35
+ async fn fuzzy_wf_def(ctx: WfContext) -> WorkflowResult<()> {
36
+ let sigchan = ctx
37
+ .make_signal_channel(FUZZY_SIG)
38
+ .map(|sd| FuzzyWfAction::from_json_payload(&sd.input[0]).expect("Can deserialize signal"));
39
+ let done = CancellationToken::new();
40
+ let done_setter = done.clone();
41
+
42
+ sigchan
43
+ .take_until(done.cancelled())
44
+ .for_each_concurrent(None, |action| {
45
+ let fut = match action {
46
+ FuzzyWfAction::DoAct => ctx
47
+ .activity(ActivityOptions {
48
+ activity_type: "echo_activity".to_string(),
49
+ start_to_close_timeout: Some(Duration::from_secs(5)),
50
+ input: "hi!".as_json_payload().expect("serializes fine"),
51
+ ..Default::default()
52
+ })
53
+ .map(|_| ())
54
+ .boxed(),
55
+ FuzzyWfAction::DoLocalAct => ctx
56
+ .local_activity(LocalActivityOptions {
57
+ activity_type: "echo_activity".to_string(),
58
+ start_to_close_timeout: Some(Duration::from_secs(5)),
59
+ input: "hi!".as_json_payload().expect("serializes fine"),
60
+ ..Default::default()
61
+ })
62
+ .map(|_| ())
63
+ .boxed(),
64
+ FuzzyWfAction::Shutdown => {
65
+ done_setter.cancel();
66
+ future::ready(()).boxed()
67
+ }
68
+ };
69
+ fut
70
+ })
71
+ .await;
72
+
73
+ Ok(().into())
74
+ }
75
+
76
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
77
+ async fn fuzzy_workflow() {
78
+ let num_workflows = 200;
79
+ let wf_name = "fuzzy_wf";
80
+ let mut starter = CoreWfStarter::new("fuzzy_workflow");
81
+ starter.max_wft(25).max_cached_workflows(25).max_at(25);
82
+ // .enable_wf_state_input_recording();
83
+ let mut worker = starter.worker().await;
84
+ worker.register_wf(wf_name.to_owned(), fuzzy_wf_def);
85
+ worker.register_activity("echo_activity", echo);
86
+ let client = starter.get_client().await;
87
+
88
+ let mut workflow_handles = vec![];
89
+ for i in 0..num_workflows {
90
+ let wfid = format!("{wf_name}_{i}");
91
+ let rid = worker
92
+ .submit_wf(
93
+ wfid.clone(),
94
+ wf_name.to_owned(),
95
+ vec![],
96
+ WorkflowOptions::default(),
97
+ )
98
+ .await
99
+ .unwrap();
100
+ workflow_handles.push(client.get_untyped_workflow_handle(wfid, rid));
101
+ }
102
+
103
+ let rng = SmallRng::seed_from_u64(523189);
104
+ let mut actions: Vec<FuzzyWfAction> = rng.sample_iter(FuzzyWfActionSampler).take(15).collect();
105
+ actions.push(FuzzyWfAction::Shutdown);
106
+
107
+ let sig_sender = async {
108
+ for action in actions {
109
+ let sends: FuturesUnordered<_> = (0..num_workflows)
110
+ .map(|i| {
111
+ client.signal_workflow_execution(
112
+ format!("{wf_name}_{i}"),
113
+ "".to_string(),
114
+ FUZZY_SIG.to_string(),
115
+ [action.as_json_payload().expect("Serializes ok")].into_payloads(),
116
+ None,
117
+ )
118
+ })
119
+ .collect();
120
+ sends
121
+ .map(|_| Ok(()))
122
+ .forward(sink::drain())
123
+ .await
124
+ .expect("Sending signals works");
125
+ tokio::time::sleep(Duration::from_secs(1)).await;
126
+ }
127
+ };
128
+ let (r1, _) = tokio::join!(worker.run_until_done(), sig_sender);
129
+ r1.unwrap();
130
+ }
@@ -1,17 +1,17 @@
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
- use temporal_sdk::{ActContext, ActivityOptions, WfContext};
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
- use temporal_sdk_core_test_utils::CoreWfStarter;
8
+ use temporal_sdk_core_test_utils::{workflows::la_problem_workflow, CoreWfStarter};
9
+
10
+ mod fuzzy_workflow;
11
11
 
12
12
  #[tokio::test]
13
13
  async fn activity_load() {
14
- const CONCURRENCY: usize = 1000;
14
+ const CONCURRENCY: usize = 512;
15
15
 
16
16
  let mut starter = CoreWfStarter::new("activity_load");
17
17
  starter
@@ -23,18 +23,16 @@ async fn activity_load() {
23
23
 
24
24
  let activity_id = "act-1";
25
25
  let activity_timeout = Duration::from_secs(8);
26
- let payload_dat = b"hello".to_vec();
27
26
  let task_queue = starter.get_task_queue().to_owned();
28
27
 
29
- let pd = payload_dat.clone();
30
28
  let wf_fn = move |ctx: WfContext| {
31
29
  let task_queue = task_queue.clone();
32
- let payload_dat = pd.clone();
33
-
30
+ let payload = "yo".as_json_payload().unwrap();
34
31
  async move {
35
32
  let activity = ActivityOptions {
36
33
  activity_id: Some(activity_id.to_string()),
37
34
  activity_type: "test_activity".to_string(),
35
+ input: payload.clone(),
38
36
  task_queue,
39
37
  schedule_to_start_timeout: Some(activity_timeout),
40
38
  start_to_close_timeout: Some(activity_timeout),
@@ -44,7 +42,7 @@ async fn activity_load() {
44
42
  ..Default::default()
45
43
  };
46
44
  let res = ctx.activity(activity).await.unwrap_ok_payload();
47
- assert_eq!(res.data, payload_dat);
45
+ assert_eq!(res.data, payload.data);
48
46
  Ok(().into())
49
47
  }
50
48
  };
@@ -52,9 +50,13 @@ async fn activity_load() {
52
50
  let starting = Instant::now();
53
51
  let wf_type = "activity_load";
54
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
+ );
55
57
  join_all((0..CONCURRENCY).map(|i| {
56
58
  let worker = &worker;
57
- let wf_id = format!("activity_load_{}", i);
59
+ let wf_id = format!("activity_load_{i}");
58
60
  async move {
59
61
  worker
60
62
  .submit_wf(
@@ -71,42 +73,8 @@ async fn activity_load() {
71
73
  dbg!(starting.elapsed());
72
74
 
73
75
  let running = Instant::now();
74
- let core = starter.get_worker().await;
75
-
76
- // Poll for and complete all activities
77
- let c2 = core.clone();
78
- let all_acts = async move {
79
- let mut act_complete_futs = vec![];
80
- for _ in 0..CONCURRENCY {
81
- let task = c2.poll_activity_task().await.unwrap();
82
- assert_matches!(
83
- task.variant,
84
- Some(act_task::Variant::Start(ref start_activity)) => {
85
- assert_eq!(start_activity.activity_type, "test_activity")
86
- }
87
- );
88
- let pd = payload_dat.clone();
89
- let core = c2.clone();
90
- act_complete_futs.push(tokio::spawn(async move {
91
- core.complete_activity_task(ActivityTaskCompletion {
92
- task_token: task.task_token,
93
- result: Some(ActivityExecutionResult::ok(pd.into())),
94
- })
95
- .await
96
- .unwrap()
97
- }));
98
- }
99
- join_all(act_complete_futs)
100
- .await
101
- .into_iter()
102
- .for_each(|h| h.unwrap());
103
- };
104
- tokio::join! {
105
- async {
106
- worker.run_until_done().await.unwrap();
107
- },
108
- all_acts
109
- };
76
+
77
+ worker.run_until_done().await.unwrap();
110
78
  dbg!(running.elapsed());
111
79
  }
112
80
 
@@ -153,7 +121,7 @@ async fn workflow_load() {
153
121
 
154
122
  let mut workflow_handles = vec![];
155
123
  for i in 0..num_workflows {
156
- let wfid = format!("{}_{}", wf_name, i);
124
+ let wfid = format!("{wf_name}_{i}");
157
125
  let rid = worker
158
126
  .submit_wf(
159
127
  wfid.clone(),
@@ -171,7 +139,7 @@ async fn workflow_load() {
171
139
  let sends: FuturesUnordered<_> = (0..num_workflows)
172
140
  .map(|i| {
173
141
  client.signal_workflow_execution(
174
- format!("{}_{}", wf_name, i),
142
+ format!("{wf_name}_{i}"),
175
143
  "".to_string(),
176
144
  SIGNAME.to_string(),
177
145
  None,
@@ -187,5 +155,111 @@ async fn workflow_load() {
187
155
  tokio::time::sleep(Duration::from_secs(2)).await;
188
156
  }
189
157
  };
190
- tokio::select! { r1 = worker.run_until_done() => {r1.unwrap()}, _ = sig_sender => {}};
158
+ tokio::select! { r1 = worker.run_until_done() => {r1.unwrap()}, _ = sig_sender => {}}
159
+ }
160
+
161
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
162
+ async fn evict_while_la_running_no_interference() {
163
+ let wf_name = "evict_while_la_running_no_interference";
164
+ let mut starter = CoreWfStarter::new(wf_name);
165
+ starter.max_local_at(20);
166
+ starter.max_cached_workflows(20);
167
+ // Though it doesn't make sense to set wft higher than cached workflows, leaving this commented
168
+ // introduces more instability that can be useful in the test.
169
+ // starter.max_wft(20);
170
+ let mut worker = starter.worker().await;
171
+
172
+ worker.register_wf(wf_name.to_owned(), la_problem_workflow);
173
+ worker.register_activity("delay", |_: ActContext, _: String| async {
174
+ tokio::time::sleep(Duration::from_secs(15)).await;
175
+ Ok(())
176
+ });
177
+
178
+ let client = starter.get_client().await;
179
+ let subfs = FuturesUnordered::new();
180
+ for i in 1..100 {
181
+ let wf_id = format!("{wf_name}-{i}");
182
+ let run_id = worker
183
+ .submit_wf(
184
+ &wf_id,
185
+ wf_name.to_owned(),
186
+ vec![],
187
+ WorkflowOptions::default(),
188
+ )
189
+ .await
190
+ .unwrap();
191
+ let cw = worker.core_worker.clone();
192
+ let client = client.clone();
193
+ subfs.push(async move {
194
+ // Evict the workflow
195
+ tokio::time::sleep(Duration::from_secs(1)).await;
196
+ cw.request_workflow_eviction(&run_id);
197
+ // Wake up workflow by sending signal
198
+ client
199
+ .signal_workflow_execution(
200
+ wf_id,
201
+ run_id.clone(),
202
+ "whaatever".to_string(),
203
+ None,
204
+ None,
205
+ )
206
+ .await
207
+ .unwrap();
208
+ });
209
+ }
210
+ let runf = async {
211
+ worker.run_until_done().await.unwrap();
212
+ };
213
+ tokio::join!(subfs.collect::<Vec<_>>(), runf);
214
+ }
215
+
216
+ pub async fn many_parallel_timers_longhist(ctx: WfContext) -> WorkflowResult<()> {
217
+ for _ in 0..120 {
218
+ let mut futs = vec![];
219
+ for _ in 0..100 {
220
+ futs.push(ctx.timer(Duration::from_millis(100)));
221
+ }
222
+ join_all(futs).await;
223
+ }
224
+ Ok(().into())
225
+ }
226
+
227
+ #[tokio::test]
228
+ async fn can_paginate_long_history() {
229
+ let wf_name = "can_paginate_long_history";
230
+ let mut starter = CoreWfStarter::new(wf_name);
231
+ starter.no_remote_activities();
232
+ // Do not use sticky queues so we are forced to paginate once history gets long
233
+ starter.max_cached_workflows(0);
234
+
235
+ let mut worker = starter.worker().await;
236
+ worker.register_wf(wf_name.to_owned(), many_parallel_timers_longhist);
237
+ let run_id = worker
238
+ .submit_wf(
239
+ wf_name.to_owned(),
240
+ wf_name.to_owned(),
241
+ vec![],
242
+ WorkflowOptions::default(),
243
+ )
244
+ .await
245
+ .unwrap();
246
+ let client = starter.get_client().await;
247
+ tokio::spawn(async move {
248
+ loop {
249
+ for _ in 0..10 {
250
+ client
251
+ .signal_workflow_execution(
252
+ wf_name.to_owned(),
253
+ run_id.clone(),
254
+ "sig".to_string(),
255
+ None,
256
+ None,
257
+ )
258
+ .await
259
+ .unwrap();
260
+ }
261
+ tokio::time::sleep(Duration::from_secs(3)).await;
262
+ }
263
+ });
264
+ worker.run_until_done().await.unwrap();
191
265
  }