@temporalio/core-bridge 0.15.0 → 0.17.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 (63) hide show
  1. package/Cargo.lock +13 -12
  2. package/index.d.ts +14 -0
  3. package/index.node +0 -0
  4. package/package.json +7 -5
  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/scripts/build.js +70 -33
  11. package/sdk-core/Cargo.toml +1 -0
  12. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  13. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +8 -9
  14. package/sdk-core/fsm/rustfsm_trait/Cargo.toml +1 -1
  15. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +1 -1
  16. package/sdk-core/sdk-core-protos/src/lib.rs +36 -48
  17. package/sdk-core/src/core_tests/activity_tasks.rs +5 -5
  18. package/sdk-core/src/core_tests/child_workflows.rs +55 -29
  19. package/sdk-core/src/core_tests/mod.rs +3 -3
  20. package/sdk-core/src/core_tests/retry.rs +14 -8
  21. package/sdk-core/src/core_tests/workflow_tasks.rs +244 -2
  22. package/sdk-core/src/errors.rs +11 -9
  23. package/sdk-core/src/lib.rs +12 -2
  24. package/sdk-core/src/machines/activity_state_machine.rs +44 -5
  25. package/sdk-core/src/machines/child_workflow_state_machine.rs +31 -11
  26. package/sdk-core/src/machines/complete_workflow_state_machine.rs +1 -1
  27. package/sdk-core/src/machines/continue_as_new_workflow_state_machine.rs +1 -1
  28. package/sdk-core/src/machines/mod.rs +18 -23
  29. package/sdk-core/src/machines/patch_state_machine.rs +8 -8
  30. package/sdk-core/src/machines/signal_external_state_machine.rs +22 -1
  31. package/sdk-core/src/machines/timer_state_machine.rs +21 -3
  32. package/sdk-core/src/machines/transition_coverage.rs +3 -3
  33. package/sdk-core/src/machines/workflow_machines.rs +11 -11
  34. package/sdk-core/src/pending_activations.rs +19 -20
  35. package/sdk-core/src/pollers/gateway.rs +15 -7
  36. package/sdk-core/src/pollers/poll_buffer.rs +6 -5
  37. package/sdk-core/src/pollers/retry.rs +7 -5
  38. package/sdk-core/src/prototype_rust_sdk/workflow_context.rs +61 -46
  39. package/sdk-core/src/prototype_rust_sdk/workflow_future.rs +13 -12
  40. package/sdk-core/src/prototype_rust_sdk.rs +16 -22
  41. package/sdk-core/src/telemetry/metrics.rs +2 -4
  42. package/sdk-core/src/telemetry/mod.rs +6 -7
  43. package/sdk-core/src/test_help/canned_histories.rs +16 -93
  44. package/sdk-core/src/test_help/history_builder.rs +61 -2
  45. package/sdk-core/src/test_help/history_info.rs +21 -2
  46. package/sdk-core/src/test_help/mod.rs +24 -33
  47. package/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +246 -138
  48. package/sdk-core/src/worker/activities.rs +46 -45
  49. package/sdk-core/src/worker/config.rs +11 -0
  50. package/sdk-core/src/worker/dispatcher.rs +5 -5
  51. package/sdk-core/src/worker/mod.rs +46 -28
  52. package/sdk-core/src/workflow/driven_workflow.rs +3 -3
  53. package/sdk-core/src/workflow/history_update.rs +1 -1
  54. package/sdk-core/src/workflow/mod.rs +1 -1
  55. package/sdk-core/src/workflow/workflow_tasks/cache_manager.rs +13 -17
  56. package/sdk-core/src/workflow/workflow_tasks/concurrency_manager.rs +4 -8
  57. package/sdk-core/src/workflow/workflow_tasks/mod.rs +14 -19
  58. package/sdk-core/test_utils/src/lib.rs +2 -2
  59. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +61 -1
  60. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +2 -2
  61. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +2 -2
  62. package/src/conversions.rs +17 -0
  63. package/releases/x86_64-pc-windows-gnu/index.node +0 -0
@@ -21,7 +21,7 @@ use crate::{
21
21
  use crossbeam::queue::SegQueue;
22
22
  use futures::FutureExt;
23
23
  use parking_lot::Mutex;
24
- use std::{fmt::Debug, ops::DerefMut, time::Instant};
24
+ use std::{fmt::Debug, time::Instant};
25
25
  use temporal_sdk_core_protos::coresdk::{
26
26
  workflow_activation::{
27
27
  create_evict_activation, create_query_activation, wf_activation_job, QueryWorkflow,
@@ -77,9 +77,9 @@ pub(crate) enum OutstandingActivation {
77
77
  }
78
78
 
79
79
  impl OutstandingActivation {
80
- fn has_eviction(&self) -> bool {
80
+ const fn has_eviction(self) -> bool {
81
81
  matches!(
82
- &self,
82
+ self,
83
83
  OutstandingActivation::Normal {
84
84
  contains_eviction: true
85
85
  }
@@ -303,13 +303,13 @@ impl WorkflowTaskManager {
303
303
  )
304
304
  .expect("Workflow machines must exist, we just created/updated them");
305
305
 
306
- if !next_activation.jobs.is_empty() {
306
+ if next_activation.jobs.is_empty() {
307
+ NewWfTaskOutcome::Autocomplete
308
+ } else {
307
309
  if let Err(wme) = self.insert_outstanding_activation(&next_activation) {
308
310
  return NewWfTaskOutcome::Evict(wme.into());
309
311
  }
310
312
  NewWfTaskOutcome::IssueActivation(next_activation)
311
- } else {
312
- NewWfTaskOutcome::Autocomplete
313
313
  }
314
314
  }
315
315
 
@@ -406,7 +406,9 @@ impl WorkflowTaskManager {
406
406
  query_responses,
407
407
  },
408
408
  })
409
- } else if !query_responses.is_empty() {
409
+ } else if query_responses.is_empty() {
410
+ None
411
+ } else {
410
412
  Some(ServerCommandsWithWorkflowInfo {
411
413
  task_token,
412
414
  action: ActivationAction::WftComplete {
@@ -414,8 +416,6 @@ impl WorkflowTaskManager {
414
416
  query_responses,
415
417
  },
416
418
  })
417
- } else {
418
- None
419
419
  }
420
420
  };
421
421
  Ok(ret)
@@ -447,13 +447,9 @@ impl WorkflowTaskManager {
447
447
  FailedActivationOutcome::ReportLegacyQueryFailure(tt)
448
448
  } else {
449
449
  // Blow up any cached data associated with the workflow
450
- let should_report =
451
- if let Some(attempt) = self.request_eviction(run_id, "Activation failed by lang") {
452
- // Only report to server if the last task wasn't also a failure (avoid spam)
453
- attempt <= 1
454
- } else {
455
- true
456
- };
450
+ let should_report = self
451
+ .request_eviction(run_id, "Activation failed by lang")
452
+ .map_or(true, |attempt| attempt <= 1);
457
453
  if should_report {
458
454
  FailedActivationOutcome::Report(tt)
459
455
  } else {
@@ -541,11 +537,10 @@ impl WorkflowTaskManager {
541
537
  if !just_evicted {
542
538
  // Check if there was a legacy query which must be fulfilled, and if there is create
543
539
  // a new pending activation for it.
544
- if let Some(ref mut ot) = self
540
+ if let Some(ref mut ot) = &mut *self
545
541
  .workflow_machines
546
542
  .get_task_mut(run_id)
547
543
  .expect("Machine must exist")
548
- .deref_mut()
549
544
  {
550
545
  if let Some(query) = ot.legacy_query.take() {
551
546
  let na = create_query_activation(run_id.to_string(), [query]);
@@ -623,7 +618,7 @@ impl WorkflowTaskManager {
623
618
  fn activation_has_eviction(&self, run_id: &str) -> bool {
624
619
  self.workflow_machines
625
620
  .get_activation(run_id)
626
- .map(|oa| oa.has_eviction())
621
+ .map(OutstandingActivation::has_eviction)
627
622
  .unwrap_or_default()
628
623
  }
629
624
  }
@@ -46,7 +46,7 @@ impl CoreWfStarter {
46
46
  .unwrap(),
47
47
  worker_config: WorkerConfigBuilder::default()
48
48
  .task_queue(task_queue)
49
- .max_cached_workflows(1000usize)
49
+ .max_cached_workflows(1000_usize)
50
50
  .build()
51
51
  .unwrap(),
52
52
  wft_timeout: None,
@@ -63,7 +63,7 @@ impl CoreWfStarter {
63
63
  }
64
64
 
65
65
  pub async fn shutdown(&mut self) {
66
- self.get_core().await.shutdown().await
66
+ self.get_core().await.shutdown().await;
67
67
  }
68
68
 
69
69
  pub async fn get_core(&mut self) -> Arc<dyn Core> {
@@ -8,7 +8,7 @@ use temporal_sdk_core_protos::{
8
8
  workflow_activation::{wf_activation_job, FireTimer, ResolveActivity, WfActivationJob},
9
9
  workflow_commands::{ActivityCancellationType, RequestCancelActivity, StartTimer},
10
10
  workflow_completion::WfActivationCompletion,
11
- ActivityTaskCompletion, IntoCompletion,
11
+ ActivityHeartbeat, ActivityTaskCompletion, IntoCompletion,
12
12
  },
13
13
  temporal::api::{
14
14
  common::v1::{ActivityType, Payloads},
@@ -647,3 +647,63 @@ async fn async_activity_completion_workflow() {
647
647
  );
648
648
  core.complete_execution(&task_q, &task.run_id).await;
649
649
  }
650
+
651
+ #[tokio::test]
652
+ async fn activity_cancelled_after_heartbeat_times_out() {
653
+ let test_name = "activity_cancelled_after_heartbeat_times_out";
654
+ let (core, task_q) = init_core_and_create_wf(test_name).await;
655
+ let activity_id = "act-1";
656
+ let task = core.poll_workflow_activation(&task_q).await.unwrap();
657
+ // Complete workflow task and schedule activity
658
+ core.complete_workflow_activation(
659
+ schedule_activity_cmd(
660
+ 0,
661
+ &task_q,
662
+ activity_id,
663
+ ActivityCancellationType::WaitCancellationCompleted,
664
+ Duration::from_secs(60),
665
+ Duration::from_secs(1),
666
+ )
667
+ .into_completion(task_q.clone(), task.run_id),
668
+ )
669
+ .await
670
+ .unwrap();
671
+ // Poll activity and verify that it's been scheduled with correct parameters
672
+ let task = core.poll_activity_task(&task_q).await.unwrap();
673
+ assert_matches!(
674
+ task.variant,
675
+ Some(act_task::Variant::Start(start_activity)) => {
676
+ assert_eq!(start_activity.activity_type, "test_activity".to_string())
677
+ }
678
+ );
679
+ // Delay the heartbeat
680
+ sleep(Duration::from_secs(2)).await;
681
+ core.record_activity_heartbeat(ActivityHeartbeat {
682
+ task_token: task.task_token.clone(),
683
+ task_queue: task_q.to_string(),
684
+ details: vec![],
685
+ });
686
+
687
+ // Verify activity got cancelled
688
+ let cancel_task = core.poll_activity_task(&task_q).await.unwrap();
689
+ assert_eq!(cancel_task.task_token, task.task_token.clone());
690
+ assert_matches!(cancel_task.variant, Some(act_task::Variant::Cancel(_)));
691
+
692
+ // Complete activity with cancelled result
693
+ core.complete_activity_task(ActivityTaskCompletion {
694
+ task_token: task.task_token.clone(),
695
+ task_queue: task_q.to_string(),
696
+ result: Some(ActivityResult::cancel_from_details(None)),
697
+ })
698
+ .await
699
+ .unwrap();
700
+
701
+ // Verify shutdown completes
702
+ core.shutdown_worker(task_q.as_str()).await;
703
+ core.shutdown().await;
704
+ // Cleanup just in case
705
+ core.server_gateway()
706
+ .terminate_workflow_execution(test_name.to_string(), None)
707
+ .await
708
+ .unwrap();
709
+ }
@@ -20,9 +20,9 @@ async fn parent_wf(mut ctx: WfContext) -> WorkflowResult<()> {
20
20
  let started = child
21
21
  .start(&mut ctx)
22
22
  .await
23
- .as_started()
23
+ .into_started()
24
24
  .expect("Child chould start OK");
25
- match started.result(&mut ctx).await.status {
25
+ match started.result().await.status {
26
26
  Some(child_workflow_result::Status::Completed(Success { .. })) => Ok(().into()),
27
27
  _ => Err(anyhow!("Unexpected child WF status")),
28
28
  }
@@ -78,13 +78,13 @@ async fn signals_child(mut ctx: WfContext) -> WorkflowResult<()> {
78
78
  })
79
79
  .start(&mut ctx)
80
80
  .await
81
- .as_started()
81
+ .into_started()
82
82
  .expect("Must start ok");
83
83
  started_child
84
84
  .signal(&mut ctx, SIGNAME, b"hiya!")
85
85
  .await
86
86
  .unwrap();
87
- started_child.result(&mut ctx).await.status.unwrap();
87
+ started_child.result().await.status.unwrap();
88
88
  Ok(().into())
89
89
  }
90
90
 
@@ -276,6 +276,21 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
276
276
  ) as u64);
277
277
  let max_cached_workflows =
278
278
  js_value_getter!(cx, self, "maxCachedWorkflows", JsNumber) as usize;
279
+
280
+ let max_heartbeat_throttle_interval = Duration::from_millis(js_value_getter!(
281
+ cx,
282
+ self,
283
+ "maxHeartbeatThrottleIntervalMs",
284
+ JsNumber
285
+ ) as u64);
286
+
287
+ let default_heartbeat_throttle_interval = Duration::from_millis(js_value_getter!(
288
+ cx,
289
+ self,
290
+ "defaultHeartbeatThrottleIntervalMs",
291
+ JsNumber
292
+ ) as u64);
293
+
279
294
  Ok(WorkerConfig {
280
295
  no_remote_activities: false, // TODO: make this configurable once Core implements local activities
281
296
  max_concurrent_at_polls,
@@ -286,6 +301,8 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
286
301
  nonsticky_to_sticky_poll_ratio,
287
302
  sticky_queue_schedule_to_start_timeout,
288
303
  task_queue,
304
+ max_heartbeat_throttle_interval,
305
+ default_heartbeat_throttle_interval,
289
306
  })
290
307
  }
291
308
  }