@temporalio/core-bridge 1.4.3 → 1.5.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 (123) hide show
  1. package/Cargo.lock +327 -419
  2. package/Cargo.toml +1 -1
  3. package/index.js +25 -2
  4. package/lib/errors.d.ts +22 -0
  5. package/lib/errors.js +65 -0
  6. package/lib/errors.js.map +1 -0
  7. package/lib/index.d.ts +440 -0
  8. package/lib/index.js +8 -0
  9. package/lib/index.js.map +1 -0
  10. package/package.json +11 -5
  11. package/releases/aarch64-apple-darwin/index.node +0 -0
  12. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  13. package/releases/x86_64-apple-darwin/index.node +0 -0
  14. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  15. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  16. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  17. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  18. package/sdk-core/bridge-ffi/Cargo.toml +1 -1
  19. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -25
  20. package/sdk-core/bridge-ffi/src/lib.rs +29 -108
  21. package/sdk-core/bridge-ffi/src/wrappers.rs +35 -25
  22. package/sdk-core/client/Cargo.toml +1 -1
  23. package/sdk-core/client/src/lib.rs +12 -20
  24. package/sdk-core/client/src/raw.rs +9 -8
  25. package/sdk-core/client/src/retry.rs +100 -23
  26. package/sdk-core/core/Cargo.toml +5 -5
  27. package/sdk-core/core/benches/workflow_replay.rs +13 -10
  28. package/sdk-core/core/src/abstractions.rs +22 -22
  29. package/sdk-core/core/src/core_tests/activity_tasks.rs +78 -1
  30. package/sdk-core/core/src/core_tests/local_activities.rs +228 -6
  31. package/sdk-core/core/src/core_tests/queries.rs +247 -89
  32. package/sdk-core/core/src/core_tests/workers.rs +2 -2
  33. package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  34. package/sdk-core/core/src/core_tests/workflow_tasks.rs +46 -27
  35. package/sdk-core/core/src/lib.rs +139 -32
  36. package/sdk-core/core/src/replay/mod.rs +185 -41
  37. package/sdk-core/core/src/telemetry/log_export.rs +190 -0
  38. package/sdk-core/core/src/telemetry/metrics.rs +184 -139
  39. package/sdk-core/core/src/telemetry/mod.rs +296 -318
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +4 -3
  41. package/sdk-core/core/src/test_help/mod.rs +15 -8
  42. package/sdk-core/core/src/worker/activities/local_activities.rs +2 -1
  43. package/sdk-core/core/src/worker/activities.rs +40 -23
  44. package/sdk-core/core/src/worker/client/mocks.rs +1 -1
  45. package/sdk-core/core/src/worker/client.rs +30 -4
  46. package/sdk-core/core/src/worker/mod.rs +22 -18
  47. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +10 -19
  48. package/sdk-core/core/src/worker/workflow/history_update.rs +99 -25
  49. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -5
  50. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -5
  51. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -5
  52. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -5
  53. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -5
  54. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +2 -6
  55. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -5
  56. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +18 -21
  57. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -38
  58. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  59. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -5
  60. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -5
  61. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -5
  62. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +8 -2
  63. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +1 -5
  64. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +232 -216
  65. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -6
  66. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +4 -4
  67. package/sdk-core/core/src/worker/workflow/managed_run.rs +13 -5
  68. package/sdk-core/core/src/worker/workflow/mod.rs +84 -30
  69. package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
  70. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +56 -11
  71. package/sdk-core/core-api/Cargo.toml +4 -3
  72. package/sdk-core/core-api/src/lib.rs +1 -43
  73. package/sdk-core/core-api/src/telemetry.rs +147 -0
  74. package/sdk-core/core-api/src/worker.rs +13 -0
  75. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  76. package/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  77. package/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  78. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  79. package/sdk-core/protos/api_upstream/buf.yaml +0 -3
  80. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +3 -7
  81. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +8 -0
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -2
  83. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +2 -0
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +13 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +19 -59
  87. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +0 -19
  88. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +108 -29
  89. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  90. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  91. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +47 -8
  92. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +15 -1
  93. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
  94. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +8 -1
  95. package/sdk-core/sdk/src/interceptors.rs +36 -3
  96. package/sdk-core/sdk/src/lib.rs +7 -4
  97. package/sdk-core/sdk/src/workflow_context.rs +13 -2
  98. package/sdk-core/sdk-core-protos/src/history_builder.rs +47 -1
  99. package/sdk-core/sdk-core-protos/src/history_info.rs +22 -22
  100. package/sdk-core/sdk-core-protos/src/lib.rs +49 -27
  101. package/sdk-core/test-utils/Cargo.toml +1 -0
  102. package/sdk-core/test-utils/src/lib.rs +81 -29
  103. package/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +0 -13
  105. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +145 -4
  106. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +106 -20
  108. package/sdk-core/tests/integ_tests/workflow_tests.rs +18 -8
  109. package/sdk-core/tests/main.rs +6 -4
  110. package/src/conversions.rs +52 -47
  111. package/src/errors.rs +28 -86
  112. package/src/helpers.rs +3 -4
  113. package/src/lib.rs +2 -2
  114. package/src/runtime.rs +132 -61
  115. package/src/testing.rs +7 -4
  116. package/src/worker.rs +67 -50
  117. package/ts/errors.ts +55 -0
  118. package/{index.d.ts → ts/index.ts} +121 -15
  119. package/sdk-core/core/src/log_export.rs +0 -62
  120. package/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  121. package/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  122. package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  123. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +0 -40
@@ -14,6 +14,7 @@ async-trait = "0.1"
14
14
  base64 = "0.13"
15
15
  futures = "0.3"
16
16
  log = "0.4"
17
+ once_cell = "1.16"
17
18
  parking_lot = "0.12"
18
19
  prost = "0.11"
19
20
  prost-types = "0.11"
@@ -6,7 +6,7 @@ extern crate tracing;
6
6
 
7
7
  pub mod canned_histories;
8
8
 
9
- use crate::stream::TryStreamExt;
9
+ use crate::stream::{Stream, TryStreamExt};
10
10
  use futures::{future, stream, stream::FuturesUnordered, StreamExt};
11
11
  use parking_lot::Mutex;
12
12
  use prost::Message;
@@ -18,14 +18,23 @@ use std::{
18
18
  use temporal_client::{
19
19
  Client, RetryClient, WorkflowClientTrait, WorkflowExecutionInfo, WorkflowOptions,
20
20
  };
21
- use temporal_sdk::{interceptors::WorkerInterceptor, IntoActivityFunc, Worker, WorkflowFunction};
21
+ use temporal_sdk::{
22
+ interceptors::{FailOnNondeterminismInterceptor, WorkerInterceptor},
23
+ IntoActivityFunc, Worker, WorkflowFunction,
24
+ };
22
25
  use temporal_sdk_core::{
23
26
  ephemeral_server::{EphemeralExe, EphemeralExeVersion},
24
- init_replay_worker, init_worker, telemetry_init, ClientOptions, ClientOptionsBuilder, Logger,
25
- MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
26
- TraceExporter, WorkerConfig, WorkerConfigBuilder,
27
+ init_replay_worker, init_worker,
28
+ replay::HistoryForReplay,
29
+ ClientOptions, ClientOptionsBuilder, CoreRuntime, WorkerConfig, WorkerConfigBuilder,
30
+ };
31
+ use temporal_sdk_core_api::{
32
+ telemetry::{
33
+ Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
34
+ TraceExportConfig, TraceExporter,
35
+ },
36
+ Worker as CoreWorker,
27
37
  };
28
- use temporal_sdk_core_api::Worker as CoreWorker;
29
38
  use temporal_sdk_core_protos::{
30
39
  coresdk::{
31
40
  workflow_commands::{
@@ -61,21 +70,43 @@ pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
61
70
  starter
62
71
  }
63
72
 
64
- /// Create a worker replay instance preloaded with a provided history. Returns the worker impl
65
- /// and the task queue name as in [init_core_and_create_wf].
66
- pub fn init_core_replay_preloaded(
67
- test_name: &str,
68
- history: &History,
69
- ) -> (Arc<dyn CoreWorker>, String) {
70
- telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
73
+ /// Create a worker replay instance preloaded with provided histories. Returns the worker impl.
74
+ pub fn init_core_replay_preloaded<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
75
+ where
76
+ I: IntoIterator<Item = HistoryForReplay> + 'static,
77
+ <I as IntoIterator>::IntoIter: Send,
78
+ {
79
+ init_core_replay_stream(test_name, stream::iter(histories))
80
+ }
81
+ pub fn init_core_replay_stream<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
82
+ where
83
+ I: Stream<Item = HistoryForReplay> + Send + 'static,
84
+ {
71
85
  let worker_cfg = WorkerConfigBuilder::default()
72
86
  .namespace(NAMESPACE)
73
87
  .task_queue(test_name)
74
88
  .worker_build_id("test_bin_id")
75
89
  .build()
76
90
  .expect("Configuration options construct properly");
77
- let worker = init_replay_worker(worker_cfg, history).expect("Replay worker must init properly");
78
- (Arc::new(worker), test_name.to_string())
91
+ let worker =
92
+ init_replay_worker(worker_cfg, histories).expect("Replay worker must init properly");
93
+ Arc::new(worker)
94
+ }
95
+ pub fn replay_sdk_worker<I>(histories: I) -> Worker
96
+ where
97
+ I: IntoIterator<Item = HistoryForReplay> + 'static,
98
+ <I as IntoIterator>::IntoIter: Send,
99
+ {
100
+ replay_sdk_worker_stream(stream::iter(histories))
101
+ }
102
+ pub fn replay_sdk_worker_stream<I>(histories: I) -> Worker
103
+ where
104
+ I: Stream<Item = HistoryForReplay> + Send + 'static,
105
+ {
106
+ let core = init_core_replay_stream("replay_worker_test", histories);
107
+ let mut worker = Worker::new_from_core(core, "replay_q".to_string());
108
+ worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
109
+ worker
79
110
  }
80
111
 
81
112
  /// Load history from a file containing the protobuf serialization of it
@@ -87,11 +118,21 @@ pub async fn history_from_proto_binary(path_from_root: &str) -> Result<History,
87
118
  Ok(History::decode(&*bytes)?)
88
119
  }
89
120
 
121
+ static INTEG_TESTS_RT: once_cell::sync::OnceCell<CoreRuntime> = once_cell::sync::OnceCell::new();
122
+ pub fn init_integ_telem() {
123
+ INTEG_TESTS_RT.get_or_init(|| {
124
+ let telemetry_options = get_integ_telem_options();
125
+ let rt =
126
+ CoreRuntime::new_assume_tokio(telemetry_options).expect("Core runtime inits cleanly");
127
+ let _ = tracing::subscriber::set_global_default(rt.trace_subscriber());
128
+ rt
129
+ });
130
+ }
131
+
90
132
  /// Implements a builder pattern to help integ tests initialize core and create workflows
91
133
  pub struct CoreWfStarter {
92
134
  /// Used for both the task queue and workflow id
93
135
  task_queue_name: String,
94
- telemetry_options: TelemetryOptions,
95
136
  pub worker_config: WorkerConfig,
96
137
  wft_timeout: Option<Duration>,
97
138
  initted_worker: OnceCell<InitializedWorker>,
@@ -110,9 +151,9 @@ impl CoreWfStarter {
110
151
  }
111
152
 
112
153
  pub fn new_tq_name(task_queue: &str) -> Self {
154
+ init_integ_telem();
113
155
  Self {
114
156
  task_queue_name: task_queue.to_owned(),
115
- telemetry_options: get_integ_telem_options(),
116
157
  worker_config: WorkerConfigBuilder::default()
117
158
  .namespace(NAMESPACE)
118
159
  .task_queue(task_queue)
@@ -181,19 +222,21 @@ impl CoreWfStarter {
181
222
  &mut self,
182
223
  wf_id: impl Into<String>,
183
224
  run_id: impl Into<String>,
184
- // TODO: Need not be passed in
185
225
  worker: &mut Worker,
186
226
  ) -> Result<(), anyhow::Error> {
227
+ let wf_id = wf_id.into();
187
228
  // Fetch history and replay it
188
229
  let history = self
189
230
  .get_client()
190
231
  .await
191
- .get_workflow_execution_history(wf_id.into(), Some(run_id.into()), vec![])
232
+ .get_workflow_execution_history(wf_id.clone(), Some(run_id.into()), vec![])
192
233
  .await?
193
234
  .history
194
235
  .expect("history field must be populated");
195
- let (replay_worker, _) = init_core_replay_preloaded(worker.task_queue(), &history);
236
+ let with_id = HistoryForReplay::new(history, wf_id);
237
+ let replay_worker = init_core_replay_preloaded(worker.task_queue(), [with_id]);
196
238
  worker.with_new_core_worker(replay_worker);
239
+ worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
197
240
  worker.run().await.unwrap();
198
241
  Ok(())
199
242
  }
@@ -239,14 +282,18 @@ impl CoreWfStarter {
239
282
  async fn get_or_init(&mut self) -> &InitializedWorker {
240
283
  self.initted_worker
241
284
  .get_or_init(|| async {
242
- telemetry_init(&self.telemetry_options).expect("Telemetry inits cleanly");
243
285
  let client = Arc::new(
244
286
  get_integ_server_options()
245
287
  .connect(self.worker_config.namespace.clone(), None, None)
246
288
  .await
247
289
  .expect("Must connect"),
248
290
  );
249
- let worker = init_worker(self.worker_config.clone(), client.clone());
291
+ let worker = init_worker(
292
+ INTEG_TESTS_RT.get().unwrap(),
293
+ self.worker_config.clone(),
294
+ client.clone(),
295
+ )
296
+ .expect("Worker inits cleanly");
250
297
  InitializedWorker {
251
298
  worker: Arc::new(worker),
252
299
  client,
@@ -442,7 +489,6 @@ impl WorkerInterceptor for TestWorkerCompletionIceptor {
442
489
 
443
490
  /// Returns the client options used to connect to the server used for integration tests.
444
491
  pub fn get_integ_server_options() -> ClientOptions {
445
- telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
446
492
  let temporal_server_address = match env::var(INTEG_SERVER_TARGET_ENV_VAR) {
447
493
  Ok(addr) => addr,
448
494
  Err(_) => "http://localhost:7233".to_owned(),
@@ -459,6 +505,8 @@ pub fn get_integ_server_options() -> ClientOptions {
459
505
 
460
506
  pub fn get_integ_telem_options() -> TelemetryOptions {
461
507
  let mut ob = TelemetryOptionsBuilder::default();
508
+ let filter_string =
509
+ env::var("RUST_LOG").unwrap_or_else(|_| "temporal_sdk_core=INFO".to_string());
462
510
  if let Some(url) = env::var(OTEL_URL_ENV_VAR)
463
511
  .ok()
464
512
  .map(|x| x.parse::<Url>().unwrap())
@@ -466,8 +514,12 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
466
514
  let opts = OtelCollectorOptions {
467
515
  url,
468
516
  headers: Default::default(),
517
+ metric_periodicity: None,
469
518
  };
470
- ob.tracing(TraceExporter::Otel(opts.clone()));
519
+ ob.tracing(TraceExportConfig {
520
+ filter: filter_string.clone(),
521
+ exporter: TraceExporter::Otel(opts.clone()),
522
+ });
471
523
  ob.metrics(MetricsExporter::Otel(opts));
472
524
  }
473
525
  if let Some(addr) = env::var(PROM_ENABLE_ENV_VAR)
@@ -476,10 +528,11 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
476
528
  {
477
529
  ob.metrics(MetricsExporter::Prometheus(addr));
478
530
  }
479
- ob.tracing_filter(env::var("RUST_LOG").unwrap_or_else(|_| "temporal_sdk_core=INFO".to_string()))
480
- .logging(Logger::Console)
481
- .build()
482
- .unwrap()
531
+ ob.logging(Logger::Console {
532
+ filter: filter_string,
533
+ })
534
+ .build()
535
+ .unwrap()
483
536
  }
484
537
 
485
538
  pub fn default_cached_download() -> EphemeralExe {
@@ -504,7 +557,6 @@ pub fn schedule_activity_cmd(
504
557
  seq,
505
558
  activity_id: activity_id.to_string(),
506
559
  activity_type: "test_activity".to_string(),
507
- namespace: NAMESPACE.to_owned(),
508
560
  task_queue: task_q.to_owned(),
509
561
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
510
562
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -0,0 +1,37 @@
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};
6
+
7
+ #[tokio::test]
8
+ async fn prometheus_metrics_exported() {
9
+ let mut telemopts = get_integ_telem_options();
10
+ let addr = "127.0.0.1:10919";
11
+ telemopts.metrics = Some(MetricsExporter::Prometheus(addr.parse().unwrap()));
12
+ let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
13
+ let opts = get_integ_server_options();
14
+ let mut raw_client = opts
15
+ .connect_no_namespace(rt.metric_meter(), None)
16
+ .await
17
+ .unwrap();
18
+ assert!(raw_client.get_client().capabilities().is_some());
19
+
20
+ let _ = raw_client
21
+ .list_namespaces(ListNamespacesRequest::default())
22
+ .await
23
+ .unwrap();
24
+
25
+ let body = reqwest::get(format!("http://{}/metrics", addr))
26
+ .await
27
+ .unwrap()
28
+ .text()
29
+ .await
30
+ .unwrap();
31
+ assert!(body.contains(
32
+ "request_latency_count{operation=\"ListNamespaces\",service_name=\"temporal-core-sdk\"} 1"
33
+ ));
34
+ assert!(body.contains(
35
+ "request_latency_count{operation=\"GetSystemInfo\",service_name=\"temporal-core-sdk\"} 1"
36
+ ));
37
+ }
@@ -3,7 +3,6 @@ use futures::future::join_all;
3
3
  use std::time::Duration;
4
4
  use temporal_client::WorkflowOptions;
5
5
  use temporal_sdk::{WfContext, WorkflowResult};
6
- use temporal_sdk_core_api::errors::PollWfError;
7
6
  use temporal_sdk_core_protos::coresdk::{
8
7
  activity_task::activity_task as act_task,
9
8
  workflow_activation::{workflow_activation_job, FireTimer, WorkflowActivationJob},
@@ -132,15 +131,3 @@ async fn can_paginate_long_history() {
132
131
  .unwrap();
133
132
  worker.run_until_done().await.unwrap();
134
133
  }
135
-
136
- // TODO: Takes ages now, fix somehow
137
- #[tokio::test]
138
- async fn poll_of_nonexistent_namespace_is_fatal() {
139
- let mut starter = CoreWfStarter::new("whatever_yo");
140
- starter.worker_config.namespace = "I do not exist".to_string();
141
- let worker = starter.get_worker().await;
142
- assert_matches!(
143
- worker.poll_workflow_activation().await,
144
- Err(PollWfError::TonicError(_))
145
- );
146
- }
@@ -1,19 +1,24 @@
1
1
  use anyhow::anyhow;
2
2
  use futures::future::join_all;
3
+ use futures_util::stream::{FuturesUnordered, StreamExt};
3
4
  use std::time::Duration;
4
- use temporal_client::WorkflowOptions;
5
+ use temporal_client::{WorkflowClientTrait, WorkflowOptions};
5
6
  use temporal_sdk::{
6
- interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, CancellableFuture,
7
- LocalActivityOptions, WfContext, WorkflowResult,
7
+ interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, ActivityOptions,
8
+ CancellableFuture, LocalActivityOptions, WfContext, WorkflowResult,
8
9
  };
10
+ use temporal_sdk_core::replay::HistoryForReplay;
9
11
  use temporal_sdk_core_protos::{
10
12
  coresdk::{
11
13
  workflow_commands::ActivityCancellationType,
12
14
  workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
13
15
  },
14
16
  temporal::api::common::v1::RetryPolicy,
17
+ TestHistoryBuilder,
18
+ };
19
+ use temporal_sdk_core_test_utils::{
20
+ history_from_proto_binary, init_integ_telem, replay_sdk_worker, CoreWfStarter,
15
21
  };
16
- use temporal_sdk_core_test_utils::CoreWfStarter;
17
22
  use tokio_util::sync::CancellationToken;
18
23
 
19
24
  pub async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
@@ -632,3 +637,139 @@ async fn repro_nondeterminism_with_timer_bug() {
632
637
  .await
633
638
  .unwrap();
634
639
  }
640
+
641
+ async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
642
+ ctx.local_activity(LocalActivityOptions {
643
+ activity_type: "delay".to_string(),
644
+ input: "hi".as_json_payload().expect("serializes fine"),
645
+ retry_policy: RetryPolicy {
646
+ initial_interval: Some(prost_dur!(from_micros(15))),
647
+ backoff_coefficient: 1_000.,
648
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
649
+ maximum_attempts: 4,
650
+ non_retryable_error_types: vec![],
651
+ },
652
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
653
+ ..Default::default()
654
+ })
655
+ .await;
656
+ ctx.activity(ActivityOptions {
657
+ activity_type: "delay".to_string(),
658
+ start_to_close_timeout: Some(Duration::from_secs(20)),
659
+ input: "hi!".as_json_payload().expect("serializes fine"),
660
+ ..Default::default()
661
+ })
662
+ .await;
663
+ Ok(().into())
664
+ }
665
+
666
+ // Expensive to run - worth enabling on a stress/regression pipeline.
667
+ #[ignore]
668
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
669
+ async fn evict_while_la_running_no_interference() {
670
+ let wf_name = "evict_while_la_running_no_interference";
671
+ let mut starter = CoreWfStarter::new(wf_name);
672
+ starter.max_local_at(20);
673
+ starter.max_cached_workflows(20);
674
+ let mut worker = starter.worker().await;
675
+
676
+ worker.register_wf(wf_name.to_owned(), la_problem_workflow);
677
+ worker.register_activity("delay", |_: ActContext, _: String| async {
678
+ tokio::time::sleep(Duration::from_secs(15)).await;
679
+ Ok(())
680
+ });
681
+
682
+ let client = starter.get_client().await;
683
+ let subfs = FuturesUnordered::new();
684
+ for i in 1..100 {
685
+ let wf_id = format!("{}-{}", wf_name, i);
686
+ let run_id = worker
687
+ .submit_wf(
688
+ &wf_id,
689
+ wf_name.to_owned(),
690
+ vec![],
691
+ WorkflowOptions::default(),
692
+ )
693
+ .await
694
+ .unwrap();
695
+ let cw = worker.core_worker.clone();
696
+ let client = client.clone();
697
+ subfs.push(async move {
698
+ // Evict the workflow
699
+ tokio::time::sleep(Duration::from_secs(1)).await;
700
+ cw.request_workflow_eviction(&run_id);
701
+ // Wake up workflow by sending signal
702
+ client
703
+ .signal_workflow_execution(
704
+ wf_id,
705
+ run_id.clone(),
706
+ "whaatever".to_string(),
707
+ None,
708
+ None,
709
+ )
710
+ .await
711
+ .unwrap();
712
+ });
713
+ }
714
+ let runf = async {
715
+ worker.run_until_done().await.unwrap();
716
+ };
717
+ tokio::join!(subfs.collect::<Vec<_>>(), runf);
718
+ }
719
+
720
+ #[rstest::rstest]
721
+ #[tokio::test]
722
+ async fn weird_la_nondeterminism_repro(#[values(true, false)] fix_hist: bool) {
723
+ init_integ_telem();
724
+ let mut hist = history_from_proto_binary(
725
+ "histories/evict_while_la_running_no_interference-85_history.bin",
726
+ )
727
+ .await
728
+ .unwrap();
729
+ if fix_hist {
730
+ // Replace broken ending with accurate ending
731
+ hist.events.truncate(20);
732
+ let mut thb = TestHistoryBuilder::from_history(hist.events);
733
+ thb.add_workflow_task_completed();
734
+ thb.add_workflow_execution_completed();
735
+ hist = thb.get_full_history_info().unwrap().into();
736
+ }
737
+
738
+ let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
739
+ worker.register_wf(
740
+ "evict_while_la_running_no_interference",
741
+ la_problem_workflow,
742
+ );
743
+ worker.register_activity("delay", |_: ActContext, _: String| async {
744
+ tokio::time::sleep(Duration::from_secs(15)).await;
745
+ Ok(())
746
+ });
747
+ worker.run().await.unwrap();
748
+ }
749
+
750
+ #[tokio::test]
751
+ async fn second_weird_la_nondeterminism_repro() {
752
+ init_integ_telem();
753
+ let mut hist = history_from_proto_binary(
754
+ "histories/evict_while_la_running_no_interference-23_history.bin",
755
+ )
756
+ .await
757
+ .unwrap();
758
+ // Chop off uninteresting ending
759
+ hist.events.truncate(24);
760
+ let mut thb = TestHistoryBuilder::from_history(hist.events);
761
+ // thb.add_workflow_task_completed();
762
+ thb.add_workflow_execution_completed();
763
+ hist = thb.get_full_history_info().unwrap().into();
764
+
765
+ let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
766
+ worker.register_wf(
767
+ "evict_while_la_running_no_interference",
768
+ la_problem_workflow,
769
+ );
770
+ worker.register_activity("delay", |_: ActContext, _: String| async {
771
+ tokio::time::sleep(Duration::from_secs(15)).await;
772
+ Ok(())
773
+ });
774
+ worker.run().await.unwrap();
775
+ }
@@ -0,0 +1,53 @@
1
+ use temporal_client::WorkflowClientTrait;
2
+ use temporal_sdk::{WfContext, WorkflowResult};
3
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt};
4
+ use temporal_sdk_core_test_utils::CoreWfStarter;
5
+ use uuid::Uuid;
6
+
7
+ static FIELD_A: &str = "cat_name";
8
+ static FIELD_B: &str = "cute_level";
9
+
10
+ async fn memo_upserter(ctx: WfContext) -> WorkflowResult<()> {
11
+ ctx.upsert_memo([
12
+ (FIELD_A.to_string(), "enchi".as_json_payload().unwrap()),
13
+ (FIELD_B.to_string(), 9001.as_json_payload().unwrap()),
14
+ ]);
15
+ Ok(().into())
16
+ }
17
+
18
+ #[tokio::test]
19
+ async fn sends_modify_wf_props() {
20
+ let wf_name = "can_upsert_memo";
21
+ let wf_id = Uuid::new_v4();
22
+ let mut starter = CoreWfStarter::new(wf_name);
23
+ let mut worker = starter.worker().await;
24
+
25
+ worker.register_wf(wf_name, memo_upserter);
26
+ let run_id = worker
27
+ .submit_wf(wf_id.to_string(), wf_name, vec![], Default::default())
28
+ .await
29
+ .unwrap();
30
+ worker.run_until_done().await.unwrap();
31
+
32
+ let memo = starter
33
+ .get_client()
34
+ .await
35
+ .describe_workflow_execution(wf_id.to_string(), Some(run_id))
36
+ .await
37
+ .unwrap()
38
+ .workflow_execution_info
39
+ .unwrap()
40
+ .memo
41
+ .unwrap()
42
+ .fields;
43
+ let catname = memo.get(FIELD_A).unwrap();
44
+ let cuteness = memo.get(FIELD_B).unwrap();
45
+ for payload in [catname, cuteness] {
46
+ assert_eq!(
47
+ &b"json/plain".to_vec(),
48
+ payload.metadata.get("encoding").unwrap()
49
+ );
50
+ }
51
+ assert_eq!("enchi", String::from_json_payload(catname).unwrap());
52
+ assert_eq!(9001, usize::from_json_payload(cuteness).unwrap());
53
+ }