@temporalio/core-bridge 0.14.0 → 0.16.4

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 (75) hide show
  1. package/Cargo.lock +162 -38
  2. package/Cargo.toml +3 -3
  3. package/index.d.ts +14 -1
  4. package/index.node +0 -0
  5. package/package.json +8 -5
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/{x86_64-pc-windows-gnu → aarch64-unknown-linux-gnu}/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/scripts/build.js +77 -34
  12. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  13. package/sdk-core/Cargo.toml +6 -5
  14. package/sdk-core/fsm/Cargo.toml +1 -1
  15. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +2 -2
  16. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +8 -9
  17. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +13 -7
  18. package/sdk-core/fsm/rustfsm_trait/Cargo.toml +2 -2
  19. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +1 -1
  20. package/sdk-core/protos/local/workflow_activation.proto +6 -3
  21. package/sdk-core/sdk-core-protos/Cargo.toml +4 -4
  22. package/sdk-core/sdk-core-protos/src/lib.rs +38 -50
  23. package/sdk-core/src/core_tests/activity_tasks.rs +5 -5
  24. package/sdk-core/src/core_tests/child_workflows.rs +55 -29
  25. package/sdk-core/src/core_tests/determinism.rs +19 -9
  26. package/sdk-core/src/core_tests/mod.rs +3 -3
  27. package/sdk-core/src/core_tests/retry.rs +14 -8
  28. package/sdk-core/src/core_tests/workers.rs +1 -1
  29. package/sdk-core/src/core_tests/workflow_tasks.rs +347 -4
  30. package/sdk-core/src/errors.rs +27 -44
  31. package/sdk-core/src/lib.rs +13 -3
  32. package/sdk-core/src/machines/activity_state_machine.rs +44 -5
  33. package/sdk-core/src/machines/child_workflow_state_machine.rs +31 -11
  34. package/sdk-core/src/machines/complete_workflow_state_machine.rs +1 -1
  35. package/sdk-core/src/machines/continue_as_new_workflow_state_machine.rs +1 -1
  36. package/sdk-core/src/machines/mod.rs +18 -23
  37. package/sdk-core/src/machines/patch_state_machine.rs +8 -8
  38. package/sdk-core/src/machines/signal_external_state_machine.rs +22 -1
  39. package/sdk-core/src/machines/timer_state_machine.rs +21 -3
  40. package/sdk-core/src/machines/transition_coverage.rs +3 -3
  41. package/sdk-core/src/machines/workflow_machines.rs +11 -11
  42. package/sdk-core/src/pending_activations.rs +27 -22
  43. package/sdk-core/src/pollers/gateway.rs +15 -7
  44. package/sdk-core/src/pollers/poll_buffer.rs +6 -5
  45. package/sdk-core/src/pollers/retry.rs +153 -120
  46. package/sdk-core/src/prototype_rust_sdk/workflow_context.rs +61 -46
  47. package/sdk-core/src/prototype_rust_sdk/workflow_future.rs +13 -12
  48. package/sdk-core/src/prototype_rust_sdk.rs +17 -23
  49. package/sdk-core/src/telemetry/metrics.rs +2 -4
  50. package/sdk-core/src/telemetry/mod.rs +6 -7
  51. package/sdk-core/src/test_help/canned_histories.rs +17 -93
  52. package/sdk-core/src/test_help/history_builder.rs +61 -2
  53. package/sdk-core/src/test_help/history_info.rs +21 -2
  54. package/sdk-core/src/test_help/mod.rs +26 -34
  55. package/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +246 -138
  56. package/sdk-core/src/worker/activities.rs +46 -45
  57. package/sdk-core/src/worker/config.rs +11 -0
  58. package/sdk-core/src/worker/dispatcher.rs +5 -5
  59. package/sdk-core/src/worker/mod.rs +86 -56
  60. package/sdk-core/src/workflow/driven_workflow.rs +3 -3
  61. package/sdk-core/src/workflow/history_update.rs +1 -1
  62. package/sdk-core/src/workflow/mod.rs +2 -1
  63. package/sdk-core/src/workflow/workflow_tasks/cache_manager.rs +13 -17
  64. package/sdk-core/src/workflow/workflow_tasks/concurrency_manager.rs +10 -18
  65. package/sdk-core/src/workflow/workflow_tasks/mod.rs +72 -57
  66. package/sdk-core/test_utils/Cargo.toml +1 -1
  67. package/sdk-core/test_utils/src/lib.rs +2 -2
  68. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +61 -1
  69. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +2 -2
  70. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +49 -0
  71. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +2 -2
  72. package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
  73. package/src/conversions.rs +17 -0
  74. package/src/errors.rs +0 -7
  75. package/src/lib.rs +0 -20
@@ -1,5 +1,3 @@
1
- extern crate proc_macro;
2
-
3
1
  use proc_macro::TokenStream;
4
2
  use quote::{quote, quote_spanned};
5
3
  use std::collections::{hash_map::Entry, HashMap, HashSet};
@@ -82,7 +80,7 @@ use syn::{
82
80
  /// ReadingCard { card_data: data.clone() }.into(),
83
81
  /// SharedState { last_id: Some(data) }
84
82
  /// )
85
- /// }
83
+ /// }
86
84
  /// }
87
85
  /// }
88
86
  /// }
@@ -200,7 +198,7 @@ impl Parse for StateMachineDefinition {
200
198
  // Parse visibility if present
201
199
  let visibility = input.parse()?;
202
200
  // parse the state machine name, command type, and error type
203
- let (name, command_type, error_type, shared_state_type) = parse_machine_types(&input)
201
+ let (name, command_type, error_type, shared_state_type) = parse_machine_types(input)
204
202
  .map_err(|mut e| {
205
203
  e.combine(Error::new(
206
204
  e.span(),
@@ -235,7 +233,7 @@ impl Parse for StateMachineDefinition {
235
233
  }
236
234
  }
237
235
 
238
- fn parse_machine_types(input: &ParseStream) -> Result<(Ident, Ident, Ident, Option<Type>)> {
236
+ fn parse_machine_types(input: ParseStream) -> Result<(Ident, Ident, Ident, Option<Type>)> {
239
237
  let _: kw::name = input.parse()?;
240
238
  let name: Ident = input.parse()?;
241
239
  input.parse::<Token![;]>()?;
@@ -443,7 +441,7 @@ impl StateMachineDefinition {
443
441
  },
444
442
  multi_dests => {
445
443
  let string_dests: Vec<_> = multi_dests.iter()
446
- .map(|i| i.to_string()).collect();
444
+ .map(ToString::to_string).collect();
447
445
  let enum_ident = Ident::new(&string_dests.join("Or"),
448
446
  multi_dests[0].span());
449
447
  let multi_dest_enum = quote! {
@@ -558,6 +556,7 @@ impl StateMachineDefinition {
558
556
  fn state(&self) -> &Self::State {
559
557
  &self.state
560
558
  }
559
+
561
560
  fn set_state(&mut self, new: Self::State) {
562
561
  self.state = new
563
562
  }
@@ -590,7 +589,7 @@ impl StateMachineDefinition {
590
589
  #trait_impl
591
590
  };
592
591
 
593
- output.into()
592
+ TokenStream::from(output)
594
593
  }
595
594
 
596
595
  fn all_states(&self) -> HashSet<Ident> {
@@ -628,7 +627,7 @@ impl StateMachineDefinition {
628
627
  /// Merge transition's dest state lists for those with the same from state & handler
629
628
  fn merge_transition_dests(transitions: Vec<Transition>) -> Vec<Transition> {
630
629
  let mut map = HashMap::<_, Transition>::new();
631
- transitions.into_iter().for_each(|t| {
630
+ for t in transitions {
632
631
  // We want to use the transition sans-destinations as the key
633
632
  let without_dests = {
634
633
  let mut wd = t.clone();
@@ -643,6 +642,6 @@ fn merge_transition_dests(transitions: Vec<Transition>) -> Vec<Transition> {
643
642
  v.insert(t);
644
643
  }
645
644
  }
646
- });
645
+ }
647
646
  map.into_iter().map(|(_, v)| v).collect()
648
647
  }
@@ -1,8 +1,14 @@
1
1
  error[E0277]: the trait bound `One: From<Two>` is not satisfied
2
- --> $DIR/no_handle_conversions_require_into_fail.rs:11:18
3
- |
4
- 11 | Two --(B)--> One;
5
- | ^^^ the trait `From<Two>` is not implemented for `One`
6
- |
7
- = note: required because of the requirements on the impl of `Into<One>` for `Two`
8
- = note: required by `TransitionResult::<Sm, Ds>::from`
2
+ --> tests/trybuild/no_handle_conversions_require_into_fail.rs:11:18
3
+ |
4
+ 11 | Two --(B)--> One;
5
+ | ^^^ the trait `From<Two>` is not implemented for `One`
6
+ |
7
+ = note: required because of the requirements on the impl of `Into<One>` for `Two`
8
+ note: required by `TransitionResult::<Sm, Ds>::from`
9
+ --> $WORKSPACE/fsm/rustfsm_trait/src/lib.rs
10
+ |
11
+ | / pub fn from<CurrentState>(current_state: CurrentState) -> Self
12
+ | | where
13
+ | | CurrentState: Into<Ds>,
14
+ | |_______________________________^
@@ -2,7 +2,7 @@
2
2
  name = "rustfsm_trait"
3
3
  version = "0.1.0"
4
4
  authors = ["Spencer Judge <spencer@temporal.io>"]
5
- edition = "2018"
5
+ edition = "2021"
6
6
  license-file = "LICENSE.txt"
7
7
  description = "Trait sub-crate of the `rustfsm` crate"
8
8
 
@@ -11,4 +11,4 @@ description = "Trait sub-crate of the `rustfsm` crate"
11
11
  [dependencies]
12
12
 
13
13
  [package.metadata.workspaces]
14
- independent = true
14
+ independent = true
@@ -112,7 +112,7 @@ where
112
112
  pub fn unwrap(self) -> Vec<M::Command> {
113
113
  match self {
114
114
  Self::Ok { commands } => commands,
115
- _ => panic!("Transition was not successful!"),
115
+ Self::InvalidTransition => panic!("Transition was not successful!"),
116
116
  }
117
117
  }
118
118
  }
@@ -59,9 +59,8 @@ message WFActivationJob {
59
59
  /// after performing the activation.
60
60
  ///
61
61
  /// If other job variant are present in the list, this variant will be the last job in the
62
- /// job list. The boolean value is irrelevant, since the variant type is what matters. It
63
- /// will be set to true if this is the variant.
64
- bool remove_from_cache = 50;
62
+ /// job list. The string value is a reason for eviction.
63
+ RemoveFromCache remove_from_cache = 50;
65
64
  }
66
65
  }
67
66
 
@@ -187,3 +186,7 @@ message ResolveRequestCancelExternalWorkflow {
187
186
  /// type / info.
188
187
  temporal.api.failure.v1.Failure failure = 2;
189
188
  }
189
+
190
+ message RemoveFromCache {
191
+ string reason = 1;
192
+ }
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "temporal-sdk-core-protos"
3
3
  version = "0.1.0"
4
- edition = "2018"
4
+ edition = "2021"
5
5
  authors = ["Spencer Judge <spencer@temporal.io>"]
6
6
  license-file = "LICENSE.txt"
7
7
  description = "Protobuf definitions for Temporal SDKs Core/Lang interface"
@@ -12,9 +12,9 @@ categories = ["development-tools"]
12
12
 
13
13
  [dependencies]
14
14
  derive_more = "0.99"
15
- prost = "0.8"
16
- prost-types = "0.8"
17
- tonic = "0.5"
15
+ prost = "0.9"
16
+ prost-types = "0.9"
17
+ tonic = "0.6"
18
18
 
19
19
  [build-dependencies]
20
20
  tonic-build = "0.5"
@@ -38,7 +38,7 @@ pub mod coresdk {
38
38
  activity_id: String,
39
39
  reason: ActivityCancelReason,
40
40
  ) -> Self {
41
- ActivityTask {
41
+ Self {
42
42
  task_token,
43
43
  activity_id,
44
44
  variant: Some(activity_task::Variant::Cancel(Cancel {
@@ -75,7 +75,7 @@ pub mod coresdk {
75
75
  })),
76
76
  }
77
77
  }
78
- pub fn will_complete_async() -> Self {
78
+ pub const fn will_complete_async() -> Self {
79
79
  Self {
80
80
  status: Some(activity_result::Status::WillCompleteAsync(
81
81
  WillCompleteAsync {},
@@ -139,13 +139,13 @@ pub mod coresdk {
139
139
  use std::fmt::{Display, Formatter};
140
140
 
141
141
  tonic::include_proto!("coresdk.workflow_activation");
142
- pub fn create_evict_activation(run_id: String) -> WfActivation {
142
+ pub fn create_evict_activation(run_id: String, reason: String) -> WfActivation {
143
143
  WfActivation {
144
144
  timestamp: None,
145
145
  run_id,
146
146
  is_replaying: false,
147
147
  jobs: vec![WfActivationJob::from(
148
- wf_activation_job::Variant::RemoveFromCache(true),
148
+ wf_activation_job::Variant::RemoveFromCache(RemoveFromCache { reason }),
149
149
  )],
150
150
  }
151
151
  }
@@ -195,7 +195,7 @@ pub mod coresdk {
195
195
  "jobs: {})",
196
196
  self.jobs
197
197
  .iter()
198
- .map(|j| j.to_string())
198
+ .map(ToString::to_string)
199
199
  .collect::<Vec<_>>()
200
200
  .as_slice()
201
201
  .join(", ")
@@ -264,18 +264,18 @@ pub mod coresdk {
264
264
  }
265
265
 
266
266
  pub mod workflow_completion {
267
- use crate::coresdk::workflow_completion::wf_activation_completion::Status;
268
267
  tonic::include_proto!("coresdk.workflow_completion");
269
268
 
270
269
  impl wf_activation_completion::Status {
271
- pub fn is_success(&self) -> bool {
270
+ pub const fn is_success(&self) -> bool {
272
271
  match &self {
273
- Status::Successful(_) => true,
274
- Status::Failed(_) => false,
272
+ Self::Successful(_) => true,
273
+ Self::Failed(_) => false,
275
274
  }
276
275
  }
277
276
  }
278
277
  }
278
+
279
279
  pub mod child_workflow {
280
280
  tonic::include_proto!("coresdk.child_workflow");
281
281
  }
@@ -370,7 +370,7 @@ pub mod coresdk {
370
370
 
371
371
  impl From<wf_activation_job::Variant> for WfActivationJob {
372
372
  fn from(a: wf_activation_job::Variant) -> Self {
373
- WfActivationJob { variant: Some(a) }
373
+ Self { variant: Some(a) }
374
374
  }
375
375
  }
376
376
 
@@ -382,7 +382,7 @@ pub mod coresdk {
382
382
 
383
383
  impl From<workflow_command::Variant> for WorkflowCommand {
384
384
  fn from(v: workflow_command::Variant) -> Self {
385
- WorkflowCommand { variant: Some(v) }
385
+ Self { variant: Some(v) }
386
386
  }
387
387
  }
388
388
 
@@ -591,7 +591,7 @@ pub mod coresdk {
591
591
  }
592
592
 
593
593
  impl ActivityResult {
594
- pub fn ok(result: Payload) -> Self {
594
+ pub const fn ok(result: Payload) -> Self {
595
595
  Self {
596
596
  status: Some(activity_result::activity_result::Status::Completed(
597
597
  activity_result::Success {
@@ -615,24 +615,18 @@ pub mod coresdk {
615
615
  .workflow_execution
616
616
  .map(|we| (we.workflow_id, we.run_id))
617
617
  .unwrap_or_default();
618
- ActivityTask {
618
+ Self {
619
619
  task_token: r.task_token,
620
620
  activity_id: r.activity_id,
621
621
  variant: Some(activity_task::activity_task::Variant::Start(
622
622
  activity_task::Start {
623
623
  workflow_namespace: r.workflow_namespace,
624
- workflow_type: r
625
- .workflow_type
626
- .map(|wt| wt.name)
627
- .unwrap_or_else(|| "".to_string()),
624
+ workflow_type: r.workflow_type.map_or_else(|| "".to_string(), |wt| wt.name),
628
625
  workflow_execution: Some(common::WorkflowExecution {
629
626
  workflow_id,
630
627
  run_id,
631
628
  }),
632
- activity_type: r
633
- .activity_type
634
- .map(|at| at.name)
635
- .unwrap_or_else(|| "".to_string()),
629
+ activity_type: r.activity_type.map_or_else(|| "".to_string(), |at| at.name),
636
630
  header_fields: r.header.map(Into::into).unwrap_or_default(),
637
631
  input: Vec::from_payloads(r.input),
638
632
  heartbeat_details: Vec::from_payloads(r.heartbeat_details),
@@ -664,7 +658,7 @@ pub mod coresdk {
664
658
 
665
659
  impl From<common::WorkflowExecution> for WorkflowExecution {
666
660
  fn from(exc: common::WorkflowExecution) -> Self {
667
- WorkflowExecution {
661
+ Self {
668
662
  workflow_id: exc.workflow_id,
669
663
  run_id: exc.run_id,
670
664
  }
@@ -767,7 +761,7 @@ pub mod coresdk {
767
761
  T: AsRef<[u8]>,
768
762
  {
769
763
  fn from(v: T) -> Self {
770
- Payloads {
764
+ Self {
771
765
  payloads: vec![v.into()],
772
766
  }
773
767
  }
@@ -786,10 +780,10 @@ pub mod coresdk {
786
780
  match v.payloads.pop() {
787
781
  None => Err(PayloadsToPayloadError::NoPayload),
788
782
  Some(p) => {
789
- if !v.payloads.is_empty() {
790
- Err(PayloadsToPayloadError::MoreThanOnePayload)
791
- } else {
783
+ if v.payloads.is_empty() {
792
784
  Ok(p.into())
785
+ } else {
786
+ Err(PayloadsToPayloadError::MoreThanOnePayload)
793
787
  }
794
788
  }
795
789
  }
@@ -1091,26 +1085,22 @@ pub mod temporal {
1091
1085
  impl HistoryEvent {
1092
1086
  /// Returns true if this is an event created to mirror a command
1093
1087
  pub fn is_command_event(&self) -> bool {
1094
- if let Some(et) = EventType::from_i32(self.event_type) {
1095
- match et {
1096
- EventType::ActivityTaskScheduled
1097
- | EventType::ActivityTaskCancelRequested
1098
- | EventType::MarkerRecorded
1099
- | EventType::RequestCancelExternalWorkflowExecutionInitiated
1100
- | EventType::SignalExternalWorkflowExecutionInitiated
1101
- | EventType::StartChildWorkflowExecutionInitiated
1102
- | EventType::TimerCanceled
1103
- | EventType::TimerStarted
1104
- | EventType::UpsertWorkflowSearchAttributes
1105
- | EventType::WorkflowExecutionCanceled
1106
- | EventType::WorkflowExecutionCompleted
1107
- | EventType::WorkflowExecutionContinuedAsNew
1108
- | EventType::WorkflowExecutionFailed => true,
1109
- _ => false,
1110
- }
1111
- } else {
1112
- false
1113
- }
1088
+ EventType::from_i32(self.event_type).map_or(false, |et| match et {
1089
+ EventType::ActivityTaskScheduled
1090
+ | EventType::ActivityTaskCancelRequested
1091
+ | EventType::MarkerRecorded
1092
+ | EventType::RequestCancelExternalWorkflowExecutionInitiated
1093
+ | EventType::SignalExternalWorkflowExecutionInitiated
1094
+ | EventType::StartChildWorkflowExecutionInitiated
1095
+ | EventType::TimerCanceled
1096
+ | EventType::TimerStarted
1097
+ | EventType::UpsertWorkflowSearchAttributes
1098
+ | EventType::WorkflowExecutionCanceled
1099
+ | EventType::WorkflowExecutionCompleted
1100
+ | EventType::WorkflowExecutionContinuedAsNew
1101
+ | EventType::WorkflowExecutionFailed => true,
1102
+ _ => false,
1103
+ })
1114
1104
  }
1115
1105
 
1116
1106
  /// Returns the command's initiating event id, if present. This is the id of the
@@ -1262,16 +1252,14 @@ pub mod temporal {
1262
1252
  let last_event = self
1263
1253
  .history
1264
1254
  .as_ref()
1265
- .map(|h| h.events.last().map(|he| he.event_id))
1266
- .flatten()
1255
+ .and_then(|h| h.events.last().map(|he| he.event_id))
1267
1256
  .unwrap_or(0);
1268
1257
  write!(
1269
1258
  f,
1270
1259
  "PollWFTQResp(run_id: {}, attempt: {}, last_event: {})",
1271
1260
  self.workflow_execution
1272
1261
  .as_ref()
1273
- .map(|we| we.run_id.as_str())
1274
- .unwrap_or(""),
1262
+ .map_or("", |we| we.run_id.as_str()),
1275
1263
  self.attempt,
1276
1264
  last_event
1277
1265
  )
@@ -76,7 +76,7 @@ async fn max_activities_respected() {
76
76
  core.register_worker(
77
77
  WorkerConfigBuilder::default()
78
78
  .task_queue(TEST_Q)
79
- .max_outstanding_activities(2usize)
79
+ .max_outstanding_activities(2_usize)
80
80
  .build()
81
81
  .unwrap(),
82
82
  )
@@ -163,7 +163,7 @@ async fn heartbeats_report_cancels_only_once() {
163
163
  core.record_activity_heartbeat(ActivityHeartbeat {
164
164
  task_token: act.task_token.clone(),
165
165
  task_queue: TEST_Q.to_string(),
166
- details: vec![vec![1u8, 2, 3].into()],
166
+ details: vec![vec![1_u8, 2, 3].into()],
167
167
  });
168
168
  // We have to wait a beat for the heartbeat to be processed
169
169
  sleep(Duration::from_millis(10)).await;
@@ -183,7 +183,7 @@ async fn heartbeats_report_cancels_only_once() {
183
183
  core.record_activity_heartbeat(ActivityHeartbeat {
184
184
  task_token: act.task_token,
185
185
  task_queue: TEST_Q.to_string(),
186
- details: vec![vec![1u8, 2, 3].into()],
186
+ details: vec![vec![1_u8, 2, 3].into()],
187
187
  });
188
188
  sleep(Duration::from_millis(10)).await;
189
189
  // Since cancels always come before new tasks, if we get a new non-cancel task, we did not
@@ -251,7 +251,7 @@ async fn activity_cancel_interrupts_poll() {
251
251
  core.record_activity_heartbeat(ActivityHeartbeat {
252
252
  task_token: act.task_token,
253
253
  task_queue: TEST_Q.to_string(),
254
- details: vec![vec![1u8, 2, 3].into()],
254
+ details: vec![vec![1_u8, 2, 3].into()],
255
255
  });
256
256
  last_finisher.store(1, Ordering::SeqCst);
257
257
  },
@@ -358,7 +358,7 @@ async fn many_concurrent_heartbeat_cancels() {
358
358
  .task_queue(TEST_Q)
359
359
  .max_outstanding_activities(CONCURRENCY_NUM)
360
360
  // Only 1 poll at a time to avoid over-polling and running out of responses
361
- .max_concurrent_at_polls(1usize)
361
+ .max_concurrent_at_polls(1_usize)
362
362
  .build()
363
363
  .unwrap(),
364
364
  )
@@ -1,8 +1,15 @@
1
1
  use crate::{
2
- prototype_rust_sdk::{ChildWorkflowOptions, WfContext, WorkflowFunction, WorkflowResult},
3
- test_help::canned_histories,
2
+ pollers::MockServerGatewayApis,
3
+ prototype_rust_sdk::{
4
+ ChildWorkflowOptions, TestRustWorker, WfContext, WorkflowFunction, WorkflowResult,
5
+ },
6
+ test_help::{
7
+ build_mock_pollers, canned_histories, mock_core, MockPollCfg, ResponseType,
8
+ DEFAULT_WORKFLOW_TYPE, TEST_Q,
9
+ },
4
10
  workflow::managed_wf::ManagedWFFunc,
5
11
  };
12
+ use std::sync::Arc;
6
13
  use temporal_sdk_core_protos::coresdk::child_workflow::{
7
14
  child_workflow_result, ChildWorkflowCancellationType,
8
15
  };
@@ -10,33 +17,52 @@ use tokio::join;
10
17
 
11
18
  const SIGNAME: &str = "SIGNAME";
12
19
 
13
- async fn parent_wf(mut ctx: WfContext) -> WorkflowResult<()> {
14
- let child = ctx.child_workflow(ChildWorkflowOptions {
15
- workflow_id: "child-id-1".to_string(),
16
- workflow_type: "child".to_string(),
17
- ..Default::default()
18
- });
19
-
20
- let start_res = child
21
- .start(&mut ctx)
22
- .await
23
- .as_started()
24
- .expect("Child should get started");
25
- let sigfut = start_res.signal(&mut ctx, SIGNAME, b"Hi!");
26
- let resfut = start_res.result(&mut ctx);
27
- let (sigres, res) = join!(sigfut, resfut);
28
- sigres.expect("signal result is ok");
29
- res.status.expect("child wf result is ok");
30
- Ok(().into())
31
- }
32
-
20
+ #[rstest::rstest]
21
+ #[case::signal_then_result(true)]
22
+ #[case::signal_and_result_concurrent(false)]
33
23
  #[tokio::test]
34
- async fn signal_child_workflow() {
35
- let func = WorkflowFunction::new(parent_wf);
24
+ async fn signal_child_workflow(#[case] serial: bool) {
25
+ let wf_id = "fakeid";
26
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
36
27
  let t = canned_histories::single_child_workflow_signaled("child-id-1", SIGNAME);
37
- let mut wfm = ManagedWFFunc::new(t, func, vec![]);
38
- wfm.process_all_activations().await.unwrap();
39
- wfm.shutdown().await.unwrap();
28
+ let mock = MockServerGatewayApis::new();
29
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
30
+ let mock = build_mock_pollers(mh);
31
+ let core = mock_core(mock);
32
+ let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
33
+
34
+ let wf = move |mut ctx: WfContext| async move {
35
+ let child = ctx.child_workflow(ChildWorkflowOptions {
36
+ workflow_id: "child-id-1".to_string(),
37
+ workflow_type: "child".to_string(),
38
+ ..Default::default()
39
+ });
40
+
41
+ let start_res = child
42
+ .start(&mut ctx)
43
+ .await
44
+ .into_started()
45
+ .expect("Child should get started");
46
+ let (sigres, res) = if serial {
47
+ let sigres = start_res.signal(&mut ctx, SIGNAME, b"Hi!").await;
48
+ let res = start_res.result().await;
49
+ (sigres, res)
50
+ } else {
51
+ let sigfut = start_res.signal(&mut ctx, SIGNAME, b"Hi!");
52
+ let resfut = start_res.result();
53
+ join!(sigfut, resfut)
54
+ };
55
+ sigres.expect("signal result is ok");
56
+ res.status.expect("child wf result is ok");
57
+ Ok(().into())
58
+ };
59
+
60
+ worker.register_wf(wf_type.to_owned(), wf);
61
+ worker
62
+ .submit_wf(wf_id.to_owned(), wf_type.to_owned(), vec![])
63
+ .await
64
+ .unwrap();
65
+ worker.run_until_done().await.unwrap();
40
66
  }
41
67
 
42
68
  async fn parent_cancels_child_wf(mut ctx: WfContext) -> WorkflowResult<()> {
@@ -50,10 +76,10 @@ async fn parent_cancels_child_wf(mut ctx: WfContext) -> WorkflowResult<()> {
50
76
  let start_res = child
51
77
  .start(&mut ctx)
52
78
  .await
53
- .as_started()
79
+ .into_started()
54
80
  .expect("Child should get started");
55
81
  let cancel_fut = start_res.cancel(&mut ctx);
56
- let resfut = start_res.result(&mut ctx);
82
+ let resfut = start_res.result();
57
83
  let (cancel_res, res) = join!(cancel_fut, resfut);
58
84
  cancel_res.expect("cancel result is ok");
59
85
  let stat = res.status.expect("child wf result is ok");
@@ -8,7 +8,7 @@ use crate::{
8
8
  };
9
9
  use std::{
10
10
  sync::{
11
- atomic::{AtomicBool, Ordering},
11
+ atomic::{AtomicBool, AtomicUsize, Ordering},
12
12
  Arc,
13
13
  },
14
14
  time::Duration,
@@ -64,7 +64,13 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
64
64
  let wf_type = DEFAULT_WORKFLOW_TYPE;
65
65
  let t = canned_histories::single_timer_wf_completes("1");
66
66
  let mock = MockServerGatewayApis::new();
67
- let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
67
+ let mut mh = MockPollCfg::from_resp_batches(
68
+ wf_id,
69
+ t,
70
+ // Two polls are needed, since the first will fail
71
+ [ResponseType::AllHistory, ResponseType::AllHistory],
72
+ mock,
73
+ );
68
74
  // We should see one wft failure which has nondeterminism cause
69
75
  mh.num_expected_fails = Some(1);
70
76
  mh.expect_fail_wft_matcher =
@@ -78,10 +84,13 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
78
84
  let core = mock_core(mock);
79
85
  let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
80
86
 
81
- // The workflow is replaying all of history, so the when it schedules an extra timer it should
82
- // not have, it causes a nondeterminism error.
83
- worker.register_wf(wf_type.to_owned(), |mut ctx: WfContext| async move {
84
- ctx.timer(Duration::from_secs(1)).await;
87
+ let started_count: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
88
+ worker.register_wf(wf_type.to_owned(), move |mut ctx: WfContext| async move {
89
+ // The workflow is replaying all of history, so the when it schedules an extra timer it
90
+ // should not have, it causes a nondeterminism error.
91
+ if started_count.fetch_add(1, Ordering::Relaxed) == 0 {
92
+ ctx.timer(Duration::from_secs(1)).await;
93
+ }
85
94
  ctx.timer(Duration::from_secs(1)).await;
86
95
  Ok(().into())
87
96
  });
@@ -90,7 +99,8 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
90
99
  .submit_wf(wf_id.to_owned(), wf_type.to_owned(), vec![])
91
100
  .await
92
101
  .unwrap();
93
- // TODO: Shouldn't return error, should just queue eviction per
94
- // https://github.com/temporalio/sdk-core/issues/171
95
- assert!(worker.run_until_done().await.is_err());
102
+ worker.run_until_done().await.unwrap();
103
+ // Started count is two since we start, restart once due to error, then we unblock the real
104
+ // timer and proceed without restarting
105
+ assert_eq!(2, started_count.load(Ordering::Relaxed));
96
106
  }
@@ -65,7 +65,7 @@ async fn shutdown_interrupts_both_polls() {
65
65
  mock_gateway
66
66
  .expect_poll_workflow_task()
67
67
  .times(1)
68
- .returning(move |_| {
68
+ .returning(move |_, _| {
69
69
  async move {
70
70
  BARR.wait().await;
71
71
  sleep(Duration::from_secs(1)).await;
@@ -91,8 +91,8 @@ async fn shutdown_interrupts_both_polls() {
91
91
  WorkerConfigBuilder::default()
92
92
  .task_queue(TEST_Q)
93
93
  // Need only 1 concurrent pollers for mock expectations to work here
94
- .max_concurrent_wft_polls(1usize)
95
- .max_concurrent_at_polls(1usize)
94
+ .max_concurrent_wft_polls(1_usize)
95
+ .max_concurrent_at_polls(1_usize)
96
96
  .build()
97
97
  .unwrap(),
98
98
  )