@temporalio/core-bridge 0.20.2 → 0.22.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 (50) hide show
  1. package/Cargo.lock +137 -127
  2. package/index.d.ts +7 -2
  3. package/package.json +3 -3
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/x86_64-apple-darwin/index.node +0 -0
  6. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  7. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  8. package/sdk-core/.buildkite/docker/docker-compose.yaml +5 -4
  9. package/sdk-core/client/Cargo.toml +1 -0
  10. package/sdk-core/client/src/lib.rs +52 -9
  11. package/sdk-core/client/src/raw.rs +9 -1
  12. package/sdk-core/client/src/retry.rs +12 -1
  13. package/sdk-core/client/src/workflow_handle/mod.rs +183 -0
  14. package/sdk-core/core/src/abstractions.rs +10 -3
  15. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -9
  16. package/sdk-core/core/src/core_tests/determinism.rs +8 -19
  17. package/sdk-core/core/src/core_tests/local_activities.rs +22 -32
  18. package/sdk-core/core/src/core_tests/queries.rs +272 -5
  19. package/sdk-core/core/src/core_tests/workers.rs +4 -34
  20. package/sdk-core/core/src/core_tests/workflow_tasks.rs +197 -41
  21. package/sdk-core/core/src/pending_activations.rs +11 -0
  22. package/sdk-core/core/src/telemetry/mod.rs +1 -1
  23. package/sdk-core/core/src/test_help/mod.rs +57 -7
  24. package/sdk-core/core/src/worker/mod.rs +64 -15
  25. package/sdk-core/core/src/workflow/machines/mod.rs +1 -1
  26. package/sdk-core/core/src/workflow/machines/timer_state_machine.rs +2 -2
  27. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +14 -3
  28. package/sdk-core/core/src/workflow/mod.rs +5 -2
  29. package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +47 -2
  30. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +16 -2
  31. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +252 -125
  32. package/sdk-core/core-api/src/worker.rs +9 -0
  33. package/sdk-core/sdk/Cargo.toml +1 -0
  34. package/sdk-core/sdk/src/activity_context.rs +223 -0
  35. package/sdk-core/sdk/src/interceptors.rs +8 -2
  36. package/sdk-core/sdk/src/lib.rs +167 -122
  37. package/sdk-core/sdk-core-protos/src/history_info.rs +3 -7
  38. package/sdk-core/test-utils/Cargo.toml +1 -0
  39. package/sdk-core/test-utils/src/lib.rs +78 -37
  40. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +11 -4
  41. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +0 -1
  42. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +0 -3
  43. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +33 -17
  44. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +10 -1
  45. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +0 -1
  46. package/sdk-core/tests/integ_tests/workflow_tests.rs +71 -3
  47. package/sdk-core/tests/load_tests.rs +80 -6
  48. package/src/errors.rs +9 -2
  49. package/src/lib.rs +39 -16
  50. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
@@ -175,7 +175,7 @@ where
175
175
  event: HistoryEvent,
176
176
  has_next_event: bool,
177
177
  ) -> Result<Vec<MachineResponse>, WFMachinesError> {
178
- debug!(
178
+ trace!(
179
179
  event = %event,
180
180
  machine_name = %self.name(),
181
181
  state = %self.state(),
@@ -336,7 +336,7 @@ mod test {
336
336
  happy_wfm.shutdown().await.unwrap();
337
337
  }
338
338
 
339
- #[tokio::test(flavor = "multi_thread")]
339
+ #[tokio::test]
340
340
  async fn mismatched_timer_ids_errors() {
341
341
  let func = WorkflowFunction::new(|command_sink: WfContext| async move {
342
342
  command_sink.timer(Duration::from_secs(5)).await;
@@ -403,7 +403,7 @@ mod test {
403
403
  wfm.shutdown().await.unwrap();
404
404
  }
405
405
 
406
- #[tokio::test(flavor = "multi_thread")]
406
+ #[tokio::test]
407
407
  async fn cancel_before_sent_to_server() {
408
408
  let func = WorkflowFunction::new(|ctx: WfContext| async move {
409
409
  let cancel_timer_fut = ctx.timer(Duration::from_secs(500));
@@ -252,7 +252,11 @@ impl WorkflowMachines {
252
252
 
253
253
  /// Let this workflow know that something we've been waiting locally on has resolved, like a
254
254
  /// local activity or side effect
255
- pub(crate) fn local_resolution(&mut self, resolution: LocalResolution) -> Result<()> {
255
+ ///
256
+ /// Returns true if the resolution did anything. EX: If the activity is already canceled and
257
+ /// used the TryCancel or Abandon modes, the resolution is uninteresting.
258
+ pub(crate) fn local_resolution(&mut self, resolution: LocalResolution) -> Result<bool> {
259
+ let mut result_important = true;
256
260
  match resolution {
257
261
  LocalResolution::LocalActivity(LocalActivityResolution {
258
262
  seq,
@@ -268,6 +272,9 @@ impl WorkflowMachines {
268
272
  if let Machines::LocalActivityMachine(ref mut lam) = *mach {
269
273
  let resps =
270
274
  lam.try_resolve(result, runtime, attempt, backoff, original_schedule_time)?;
275
+ if resps.is_empty() {
276
+ result_important = false;
277
+ }
271
278
  self.process_machine_responses(mk, resps)?;
272
279
  } else {
273
280
  return Err(WFMachinesError::Nondeterminism(format!(
@@ -279,7 +286,7 @@ impl WorkflowMachines {
279
286
  self.local_activity_data.done_executing(seq);
280
287
  }
281
288
  }
282
- Ok(())
289
+ Ok(result_important)
283
290
  }
284
291
 
285
292
  /// Drain all queued local activities that need executing or cancellation
@@ -456,7 +463,7 @@ impl WorkflowMachines {
456
463
  event: HistoryEvent,
457
464
  has_next_event: bool,
458
465
  ) -> Result<()> {
459
- debug!(
466
+ trace!(
460
467
  event = %event,
461
468
  "handling non-stateful event"
462
469
  );
@@ -470,6 +477,10 @@ impl WorkflowMachines {
470
477
  if let Some(st) = event.event_time {
471
478
  let as_systime: SystemTime = st.try_into()?;
472
479
  self.workflow_start_time = Some(as_systime);
480
+ // Set the workflow time to be the event time of the first event, so that
481
+ // if there is a query issued before first WFT started event, there is some
482
+ // workflow time set.
483
+ self.set_current_time(as_systime);
473
484
  }
474
485
  // Notify the lang sdk that it's time to kick off a workflow
475
486
  self.drive_me.start(
@@ -96,7 +96,10 @@ impl WorkflowManager {
96
96
 
97
97
  /// Let this workflow know that something we've been waiting locally on has resolved, like a
98
98
  /// local activity or side effect
99
- pub fn notify_of_local_result(&mut self, resolved: LocalResolution) -> Result<()> {
99
+ ///
100
+ /// Returns true if the resolution did anything. EX: If the activity is already canceled and
101
+ /// used the TryCancel or Abandon modes, the resolution is uninteresting.
102
+ pub fn notify_of_local_result(&mut self, resolved: LocalResolution) -> Result<bool> {
100
103
  self.machines.local_resolution(resolved)
101
104
  }
102
105
 
@@ -407,7 +410,7 @@ pub mod managed_wf {
407
410
  &mut self,
408
411
  seq_num: u32,
409
412
  result: ActivityExecutionResult,
410
- ) -> Result<()> {
413
+ ) -> Result<bool> {
411
414
  self.mgr
412
415
  .notify_of_local_result(LocalResolution::LocalActivity(LocalActivityResolution {
413
416
  seq: seq_num,
@@ -1,5 +1,13 @@
1
1
  use crate::{telemetry::metrics::MetricsContext, workflow::WorkflowCachingPolicy};
2
2
  use lru::LruCache;
3
+ use std::{
4
+ future::Future,
5
+ sync::{
6
+ atomic::{AtomicUsize, Ordering},
7
+ Arc,
8
+ },
9
+ };
10
+ use tokio::sync::Notify;
3
11
 
4
12
  /// Helps to maintain an LRU ordering in which workflow runs have been accessed so that old runs may
5
13
  /// be evicted once we reach the cap.
@@ -7,6 +15,9 @@ use lru::LruCache;
7
15
  pub(crate) struct WorkflowCacheManager {
8
16
  cache: LruCache<String, ()>,
9
17
  metrics: MetricsContext,
18
+ cap_notify: Arc<Notify>,
19
+ cache_size: Arc<AtomicUsize>,
20
+ cap_mutex: Arc<tokio::sync::Mutex<()>>,
10
21
  }
11
22
 
12
23
  impl WorkflowCacheManager {
@@ -20,6 +31,9 @@ impl WorkflowCacheManager {
20
31
  Self {
21
32
  cache: LruCache::new(cap),
22
33
  metrics,
34
+ cap_notify: Arc::new(Notify::new()),
35
+ cache_size: Arc::new(AtomicUsize::new(0)),
36
+ cap_mutex: Arc::new(tokio::sync::Mutex::new(())),
23
37
  }
24
38
  }
25
39
 
@@ -28,6 +42,30 @@ impl WorkflowCacheManager {
28
42
  Self::new(policy, Default::default())
29
43
  }
30
44
 
45
+ /// Resolves once there is an open slot in the cache. The passed in closure can be used to
46
+ /// exit the wait loop if it returns true even if the cache is not below the limit. It will be
47
+ /// re-evaluated every time there is an insert or remove to the cache, even if it did not change
48
+ /// the size.
49
+ pub fn wait_for_capacity<Fun>(&self, early_exit: Fun) -> Option<impl Future<Output = ()>>
50
+ where
51
+ Fun: Fn() -> bool,
52
+ {
53
+ if self.cache.cap() == 0 {
54
+ return None;
55
+ }
56
+
57
+ let size = self.cache_size.clone();
58
+ let notify = self.cap_notify.clone();
59
+ let cap = self.cache.cap();
60
+ let mx = self.cap_mutex.clone();
61
+ Some(async move {
62
+ let _l = mx.lock().await;
63
+ while !early_exit() && size.load(Ordering::Acquire) >= cap {
64
+ notify.notified().await;
65
+ }
66
+ })
67
+ }
68
+
31
69
  /// Inserts a record associated with the run id into the lru cache.
32
70
  /// Once cache reaches capacity, overflow records will be returned back to the caller.
33
71
  pub fn insert(&mut self, run_id: &str) -> Option<String> {
@@ -44,7 +82,7 @@ impl WorkflowCacheManager {
44
82
  not_cached.then(|| maybe_got_evicted).flatten()
45
83
  };
46
84
 
47
- self.metrics.cache_size(self.cache.len() as u64);
85
+ self.size_changed();
48
86
 
49
87
  res
50
88
  }
@@ -56,7 +94,14 @@ impl WorkflowCacheManager {
56
94
 
57
95
  pub fn remove(&mut self, run_id: &str) {
58
96
  self.cache.pop(run_id);
59
- self.metrics.cache_size(self.cache.len() as u64);
97
+ self.size_changed();
98
+ }
99
+
100
+ fn size_changed(&self) {
101
+ let size = self.cache.len();
102
+ self.metrics.cache_size(size as u64);
103
+ self.cache_size.store(size, Ordering::Release);
104
+ self.cap_notify.notify_one();
60
105
  }
61
106
  }
62
107
 
@@ -154,7 +154,7 @@ impl WorkflowConcurrencyManager {
154
154
  pub fn complete_wft(
155
155
  &self,
156
156
  run_id: &str,
157
- send_wft_complete_to_srv: bool,
157
+ sent_wft_complete_to_srv: bool,
158
158
  ) -> Option<OutstandingTask> {
159
159
  // If the WFT completion wasn't sent to the server, but we did see the final event, we still
160
160
  // want to clear the workflow task. This can really only happen in replay testing, where we
@@ -164,7 +164,7 @@ impl WorkflowConcurrencyManager {
164
164
  let saw_final = self
165
165
  .access_sync(run_id, |wfm| wfm.machines.have_seen_terminal_event)
166
166
  .unwrap_or_default();
167
- if !saw_final && !send_wft_complete_to_srv {
167
+ if !saw_final && !sent_wft_complete_to_srv {
168
168
  return None;
169
169
  }
170
170
 
@@ -313,6 +313,7 @@ impl WorkflowConcurrencyManager {
313
313
  val.and_then(|v| v.buffered_resp.take())
314
314
  }
315
315
 
316
+ /// Sounds the total number of outstanding workflow tasks
316
317
  pub fn outstanding_wft(&self) -> usize {
317
318
  self.runs
318
319
  .read()
@@ -320,6 +321,19 @@ impl WorkflowConcurrencyManager {
320
321
  .filter(|(_, run)| run.wft.is_some())
321
322
  .count()
322
323
  }
324
+
325
+ /// Returns number of currently cached workflows
326
+ pub fn cached_workflows(&self) -> usize {
327
+ self.runs.read().len()
328
+ }
329
+
330
+ /// Returns true if any outstanding activation contains an eviction
331
+ pub fn are_outstanding_evictions(&self) -> bool {
332
+ self.runs
333
+ .read()
334
+ .values()
335
+ .any(|mr| mr.activation.map(|a| a.has_eviction()).unwrap_or_default())
336
+ }
323
337
  }
324
338
 
325
339
  #[cfg(test)]