@temporalio/core-bridge 0.23.0 → 1.0.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 (135) hide show
  1. package/Cargo.lock +118 -15
  2. package/Cargo.toml +2 -1
  3. package/LICENSE.md +1 -1
  4. package/README.md +1 -1
  5. package/index.d.ts +47 -18
  6. package/package.json +7 -7
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/sdk-core/.buildkite/docker/docker-compose.yaml +4 -2
  13. package/sdk-core/ARCHITECTURE.md +9 -7
  14. package/sdk-core/README.md +5 -1
  15. package/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -3
  17. package/sdk-core/client/src/lib.rs +26 -8
  18. package/sdk-core/client/src/raw.rs +166 -54
  19. package/sdk-core/client/src/retry.rs +9 -4
  20. package/sdk-core/client/src/workflow_handle/mod.rs +4 -2
  21. package/sdk-core/core/Cargo.toml +2 -0
  22. package/sdk-core/core/src/abstractions.rs +137 -16
  23. package/sdk-core/core/src/core_tests/activity_tasks.rs +258 -63
  24. package/sdk-core/core/src/core_tests/child_workflows.rs +1 -2
  25. package/sdk-core/core/src/core_tests/determinism.rs +2 -2
  26. package/sdk-core/core/src/core_tests/local_activities.rs +8 -7
  27. package/sdk-core/core/src/core_tests/queries.rs +146 -60
  28. package/sdk-core/core/src/core_tests/replay_flag.rs +1 -1
  29. package/sdk-core/core/src/core_tests/workers.rs +39 -23
  30. package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +387 -280
  32. package/sdk-core/core/src/lib.rs +6 -4
  33. package/sdk-core/core/src/pollers/poll_buffer.rs +16 -10
  34. package/sdk-core/core/src/protosext/mod.rs +6 -6
  35. package/sdk-core/core/src/retry_logic.rs +1 -1
  36. package/sdk-core/core/src/telemetry/metrics.rs +21 -7
  37. package/sdk-core/core/src/telemetry/mod.rs +18 -4
  38. package/sdk-core/core/src/test_help/mod.rs +341 -109
  39. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +18 -9
  40. package/sdk-core/core/src/worker/activities/local_activities.rs +19 -16
  41. package/sdk-core/core/src/worker/activities.rs +156 -29
  42. package/sdk-core/core/src/worker/client.rs +1 -0
  43. package/sdk-core/core/src/worker/mod.rs +132 -659
  44. package/sdk-core/core/src/{workflow → worker/workflow}/bridge.rs +1 -1
  45. package/sdk-core/core/src/{workflow → worker/workflow}/driven_workflow.rs +1 -1
  46. package/sdk-core/core/src/{workflow → worker/workflow}/history_update.rs +16 -2
  47. package/sdk-core/core/src/{workflow → worker/workflow}/machines/activity_state_machine.rs +39 -4
  48. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_external_state_machine.rs +5 -2
  49. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_workflow_state_machine.rs +1 -1
  50. package/sdk-core/core/src/{workflow → worker/workflow}/machines/child_workflow_state_machine.rs +2 -4
  51. package/sdk-core/core/src/{workflow → worker/workflow}/machines/complete_workflow_state_machine.rs +0 -0
  52. package/sdk-core/core/src/{workflow → worker/workflow}/machines/continue_as_new_workflow_state_machine.rs +1 -1
  53. package/sdk-core/core/src/{workflow → worker/workflow}/machines/fail_workflow_state_machine.rs +0 -0
  54. package/sdk-core/core/src/{workflow → worker/workflow}/machines/local_activity_state_machine.rs +2 -5
  55. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mod.rs +1 -1
  56. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  57. package/sdk-core/core/src/{workflow → worker/workflow}/machines/patch_state_machine.rs +1 -1
  58. package/sdk-core/core/src/{workflow → worker/workflow}/machines/side_effect_state_machine.rs +0 -0
  59. package/sdk-core/core/src/{workflow → worker/workflow}/machines/signal_external_state_machine.rs +4 -2
  60. package/sdk-core/core/src/{workflow → worker/workflow}/machines/timer_state_machine.rs +1 -2
  61. package/sdk-core/core/src/{workflow → worker/workflow}/machines/transition_coverage.rs +1 -1
  62. package/sdk-core/core/src/{workflow → worker/workflow}/machines/upsert_search_attributes_state_machine.rs +5 -7
  63. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines/local_acts.rs +2 -2
  64. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines.rs +40 -16
  65. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_task_state_machine.rs +0 -0
  66. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  67. package/sdk-core/core/src/worker/workflow/managed_run.rs +627 -0
  68. package/sdk-core/core/src/worker/workflow/mod.rs +1115 -0
  69. package/sdk-core/core/src/worker/workflow/run_cache.rs +143 -0
  70. package/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  71. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +936 -0
  72. package/sdk-core/core-api/src/errors.rs +3 -10
  73. package/sdk-core/core-api/src/lib.rs +2 -1
  74. package/sdk-core/core-api/src/worker.rs +26 -2
  75. package/sdk-core/etc/dynamic-config.yaml +2 -0
  76. package/sdk-core/integ-with-otel.sh +1 -1
  77. package/sdk-core/protos/api_upstream/Makefile +4 -4
  78. package/sdk-core/protos/api_upstream/api-linter.yaml +2 -0
  79. package/sdk-core/protos/api_upstream/buf.yaml +8 -9
  80. package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -1
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  83. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +3 -1
  85. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +3 -0
  87. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +32 -4
  88. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +69 -19
  89. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +13 -0
  90. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +163 -0
  91. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +97 -0
  92. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  93. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +25 -0
  94. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +180 -3
  95. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +53 -3
  96. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +2 -2
  97. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +6 -5
  98. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -1
  99. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +2 -1
  100. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +0 -64
  101. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -1
  102. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +11 -8
  103. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +30 -25
  104. package/sdk-core/sdk/src/activity_context.rs +12 -5
  105. package/sdk-core/sdk/src/app_data.rs +37 -0
  106. package/sdk-core/sdk/src/lib.rs +76 -43
  107. package/sdk-core/sdk/src/workflow_context/options.rs +8 -6
  108. package/sdk-core/sdk/src/workflow_context.rs +14 -19
  109. package/sdk-core/sdk/src/workflow_future.rs +11 -6
  110. package/sdk-core/sdk-core-protos/src/history_builder.rs +19 -5
  111. package/sdk-core/sdk-core-protos/src/history_info.rs +11 -6
  112. package/sdk-core/sdk-core-protos/src/lib.rs +74 -176
  113. package/sdk-core/test-utils/src/lib.rs +85 -72
  114. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -9
  115. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -0
  116. package/sdk-core/tests/integ_tests/queries_tests.rs +39 -22
  117. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +49 -4
  118. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  120. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +74 -13
  121. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +19 -0
  122. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  123. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -3
  124. package/sdk-core/tests/integ_tests/workflow_tests.rs +10 -23
  125. package/sdk-core/tests/load_tests.rs +8 -3
  126. package/sdk-core/tests/main.rs +2 -1
  127. package/src/conversions.rs +47 -39
  128. package/src/errors.rs +10 -21
  129. package/src/lib.rs +342 -325
  130. package/sdk-core/core/src/pending_activations.rs +0 -173
  131. package/sdk-core/core/src/worker/wft_delivery.rs +0 -81
  132. package/sdk-core/core/src/workflow/mod.rs +0 -478
  133. package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +0 -194
  134. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +0 -418
  135. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +0 -989
@@ -1,6 +1,6 @@
1
1
  use crate::{
2
2
  telemetry::VecDisplayer,
3
- workflow::{WFCommand, WorkflowFetcher},
3
+ worker::workflow::{WFCommand, WorkflowFetcher},
4
4
  };
5
5
  use std::sync::mpsc::{self, Receiver, Sender};
6
6
 
@@ -1,4 +1,4 @@
1
- use crate::workflow::{WFCommand, WorkflowStartedInfo};
1
+ use crate::worker::workflow::{WFCommand, WorkflowStartedInfo};
2
2
  use std::collections::VecDeque;
3
3
  use temporal_sdk_core_protos::{
4
4
  coresdk::workflow_activation::{
@@ -5,6 +5,7 @@ use crate::{
5
5
  use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
6
6
  use std::{
7
7
  collections::VecDeque,
8
+ fmt::Debug,
8
9
  future::Future,
9
10
  pin::Pin,
10
11
  sync::Arc,
@@ -15,6 +16,7 @@ use temporal_sdk_core_protos::temporal::api::{
15
16
  history::v1::{History, HistoryEvent},
16
17
  workflowservice::v1::GetWorkflowExecutionHistoryResponse,
17
18
  };
19
+ use tracing::Instrument;
18
20
 
19
21
  /// A slimmed down version of a poll workflow task response which includes just the info needed
20
22
  /// by [WorkflowManager]. History events are expected to be consumed from it and applied to the
@@ -28,6 +30,15 @@ pub struct HistoryUpdate {
28
30
  buffered: VecDeque<HistoryEvent>,
29
31
  pub previous_started_event_id: i64,
30
32
  }
33
+ impl Debug for HistoryUpdate {
34
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35
+ write!(
36
+ f,
37
+ "HistoryUpdate(previous_started_event_id: {})",
38
+ self.previous_started_event_id
39
+ )
40
+ }
41
+ }
31
42
 
32
43
  pub struct HistoryPaginator {
33
44
  // Potentially this could actually be a ref w/ lifetime here
@@ -130,8 +141,11 @@ impl Stream for HistoryPaginator {
130
141
  let gw = self.client.clone();
131
142
  let wid = self.wf_id.clone();
132
143
  let rid = self.run_id.clone();
133
- let resp_fut =
134
- async move { gw.get_workflow_execution_history(wid, Some(rid), npt).await };
144
+ let resp_fut = async move {
145
+ gw.get_workflow_execution_history(wid, Some(rid), npt)
146
+ .instrument(span!(tracing::Level::TRACE, "fetch_history_in_paginator"))
147
+ .await
148
+ };
135
149
  self.open_history_request.insert(resp_fut.boxed())
136
150
  };
137
151
 
@@ -9,13 +9,12 @@ use std::convert::{TryFrom, TryInto};
9
9
  use temporal_sdk_core_protos::{
10
10
  coresdk::{
11
11
  activity_result::{self as ar, activity_resolution, ActivityResolution, Cancellation},
12
- common::Payload,
13
12
  workflow_activation::ResolveActivity,
14
13
  workflow_commands::{ActivityCancellationType, ScheduleActivity},
15
14
  },
16
15
  temporal::api::{
17
16
  command::v1::{command, Command, RequestCancelActivityTaskCommandAttributes},
18
- common::v1::{ActivityType, Payloads},
17
+ common::v1::{ActivityType, Payload, Payloads},
19
18
  enums::v1::{CommandType, EventType, RetryState},
20
19
  failure::v1::{
21
20
  self as failure, failure::FailureInfo, ActivityFailureInfo, CanceledFailureInfo,
@@ -77,6 +76,10 @@ fsm! {
77
76
  shared on_activity_task_timed_out) --> TimedOut;
78
77
  StartedActivityCancelEventRecorded --(ActivityTaskCanceled(ActivityTaskCanceledEventAttributes),
79
78
  shared on_activity_task_canceled) --> Canceled;
79
+
80
+ Canceled --(ActivityTaskStarted(i64), shared on_activity_task_started) --> Canceled;
81
+ Canceled --(ActivityTaskCompleted(ActivityTaskCompletedEventAttributes),
82
+ shared on_activity_task_completed) --> Canceled;
80
83
  }
81
84
 
82
85
  #[derive(Debug, derive_more::Display)]
@@ -637,6 +640,39 @@ pub(super) struct TimedOut {}
637
640
 
638
641
  #[derive(Default, Clone)]
639
642
  pub(super) struct Canceled {}
643
+ impl Canceled {
644
+ pub(super) fn on_activity_task_started(
645
+ self,
646
+ dat: SharedState,
647
+ seq_num: i64,
648
+ ) -> ActivityMachineTransition<Canceled> {
649
+ // Abandoned activities might start anyway. Ignore the result.
650
+ if dat.cancellation_type == ActivityCancellationType::Abandon {
651
+ TransitionResult::default()
652
+ } else {
653
+ TransitionResult::Err(WFMachinesError::Nondeterminism(format!(
654
+ "Non-Abandon cancel mode activities cannot be started after being cancelled. \
655
+ Seq: {:?}",
656
+ seq_num
657
+ )))
658
+ }
659
+ }
660
+ pub(super) fn on_activity_task_completed(
661
+ self,
662
+ dat: SharedState,
663
+ attrs: ActivityTaskCompletedEventAttributes,
664
+ ) -> ActivityMachineTransition<Canceled> {
665
+ // Abandoned activities might complete anyway. Ignore the result.
666
+ if dat.cancellation_type == ActivityCancellationType::Abandon {
667
+ TransitionResult::default()
668
+ } else {
669
+ TransitionResult::Err(WFMachinesError::Nondeterminism(format!(
670
+ "Non-Abandon cancel mode activities cannot be completed after being cancelled: {:?}",
671
+ attrs
672
+ )))
673
+ }
674
+ }
675
+ }
640
676
 
641
677
  fn create_request_cancel_activity_task_command<S>(
642
678
  dat: SharedState,
@@ -747,8 +783,7 @@ fn convert_payloads(
747
783
  mod test {
748
784
  use super::*;
749
785
  use crate::{
750
- replay::TestHistoryBuilder, test_help::canned_histories,
751
- workflow::managed_wf::ManagedWFFunc,
786
+ replay::TestHistoryBuilder, test_help::canned_histories, worker::workflow::ManagedWFFunc,
752
787
  };
753
788
  use rstest::{fixture, rstest};
754
789
  use std::mem::discriminant;
@@ -58,6 +58,7 @@ pub(super) fn new_external_cancel(
58
58
  seq: u32,
59
59
  workflow_execution: NamespacedWorkflowExecution,
60
60
  only_child: bool,
61
+ reason: String,
61
62
  ) -> NewMachineWithCommand {
62
63
  let mut s = CancelExternalMachine {
63
64
  state: Created {}.into(),
@@ -73,6 +74,7 @@ pub(super) fn new_external_cancel(
73
74
  // Apparently this is effectively deprecated at this point
74
75
  control: "".to_string(),
75
76
  child_workflow_only: only_child,
77
+ reason,
76
78
  },
77
79
  );
78
80
  let cmd = Command {
@@ -191,7 +193,8 @@ impl WFMachinesAdapter for CancelExternalMachine {
191
193
  CancelExternalCommand::Failed(f) => {
192
194
  let reason = match f {
193
195
  CancelExternalWorkflowExecutionFailedCause::Unspecified => "unknown",
194
- CancelExternalWorkflowExecutionFailedCause::ExternalWorkflowExecutionNotFound => "not found"
196
+ CancelExternalWorkflowExecutionFailedCause::ExternalWorkflowExecutionNotFound
197
+ | CancelExternalWorkflowExecutionFailedCause::NamespaceNotFound => "not found"
195
198
  };
196
199
  vec![ResolveRequestCancelExternalWorkflow {
197
200
  seq: self.shared_state.seq,
@@ -230,7 +233,7 @@ impl Cancellable for CancelExternalMachine {}
230
233
  #[cfg(test)]
231
234
  mod tests {
232
235
  use super::*;
233
- use crate::{replay::TestHistoryBuilder, workflow::managed_wf::ManagedWFFunc};
236
+ use crate::{replay::TestHistoryBuilder, worker::workflow::ManagedWFFunc};
234
237
  use temporal_sdk::{WfContext, WorkflowFunction, WorkflowResult};
235
238
 
236
239
  async fn cancel_sender(ctx: WfContext) -> WorkflowResult<()> {
@@ -122,7 +122,7 @@ impl Cancellable for CancelWorkflowMachine {}
122
122
  #[cfg(test)]
123
123
  mod tests {
124
124
  use super::*;
125
- use crate::{test_help::canned_histories, workflow::managed_wf::ManagedWFFunc};
125
+ use crate::{test_help::canned_histories, worker::workflow::ManagedWFFunc};
126
126
  use std::time::Duration;
127
127
  use temporal_sdk::{WfContext, WfExitValue, WorkflowFunction, WorkflowResult};
128
128
  use temporal_sdk_core_protos::coresdk::workflow_activation::{
@@ -9,7 +9,6 @@ use temporal_sdk_core_protos::{
9
9
  child_workflow::{
10
10
  self as wfr, child_workflow_result::Status as ChildWorkflowStatus, ChildWorkflowResult,
11
11
  },
12
- common::Payload,
13
12
  workflow_activation::{
14
13
  resolve_child_workflow_execution_start, ResolveChildWorkflowExecution,
15
14
  ResolveChildWorkflowExecutionStart, ResolveChildWorkflowExecutionStartCancelled,
@@ -19,7 +18,7 @@ use temporal_sdk_core_protos::{
19
18
  },
20
19
  temporal::api::{
21
20
  command::v1::Command,
22
- common::v1::{Payloads, WorkflowExecution, WorkflowType},
21
+ common::v1::{Payload, Payloads, WorkflowExecution, WorkflowType},
23
22
  enums::v1::{
24
23
  CommandType, EventType, RetryState, StartChildWorkflowExecutionFailedCause, TimeoutType,
25
24
  },
@@ -616,8 +615,7 @@ fn convert_payloads(
616
615
  mod test {
617
616
  use super::*;
618
617
  use crate::{
619
- replay::TestHistoryBuilder, test_help::canned_histories,
620
- workflow::managed_wf::ManagedWFFunc,
618
+ replay::TestHistoryBuilder, test_help::canned_histories, worker::workflow::ManagedWFFunc,
621
619
  };
622
620
  use anyhow::anyhow;
623
621
  use rstest::{fixture, rstest};
@@ -121,7 +121,7 @@ impl Cancellable for ContinueAsNewWorkflowMachine {}
121
121
  #[cfg(test)]
122
122
  mod tests {
123
123
  use super::*;
124
- use crate::{test_help::canned_histories, workflow::managed_wf::ManagedWFFunc};
124
+ use crate::{test_help::canned_histories, worker::workflow::ManagedWFFunc};
125
125
  use std::time::Duration;
126
126
  use temporal_sdk::{WfContext, WfExitValue, WorkflowFunction, WorkflowResult};
127
127
 
@@ -105,9 +105,7 @@ impl From<CompleteLocalActivityData> for ResolveDat {
105
105
  fn from(d: CompleteLocalActivityData) -> Self {
106
106
  ResolveDat {
107
107
  result: match d.result {
108
- Ok(res) => LocalActivityExecutionResult::Completed(Success {
109
- result: Some(res.into()),
110
- }),
108
+ Ok(res) => LocalActivityExecutionResult::Completed(Success { result: Some(res) }),
111
109
  Err(fail) => {
112
110
  if matches!(fail.failure_info, Some(FailureInfo::CanceledFailureInfo(_))) {
113
111
  LocalActivityExecutionResult::Cancelled(Cancellation {
@@ -784,8 +782,7 @@ impl From<LocalActivityExecutionResult> for ActivityResolution {
784
782
  mod tests {
785
783
  use super::*;
786
784
  use crate::{
787
- replay::TestHistoryBuilder, test_help::canned_histories,
788
- workflow::managed_wf::ManagedWFFunc,
785
+ replay::TestHistoryBuilder, test_help::canned_histories, worker::workflow::ManagedWFFunc,
789
786
  };
790
787
  use rstest::rstest;
791
788
  use std::time::Duration;
@@ -234,7 +234,7 @@ where
234
234
  {
235
235
  if !commands.is_empty() {
236
236
  debug!(commands=%commands.display(), state=%machine.state(),
237
- machine_name=%StateMachine::name(machine), "Machine produced commands");
237
+ machine_name=%TemporalStateMachine::kind(machine), "Machine produced commands");
238
238
  }
239
239
  let mut machine_responses = vec![];
240
240
  for cmd in commands {
@@ -223,7 +223,7 @@ impl TryFrom<HistoryEvent> for PatchMachineEvents {
223
223
  mod tests {
224
224
  use crate::{
225
225
  replay::TestHistoryBuilder,
226
- workflow::{machines::WFMachinesError, managed_wf::ManagedWFFunc},
226
+ worker::workflow::{machines::WFMachinesError, ManagedWFFunc},
227
227
  };
228
228
  use rstest::rstest;
229
229
  use std::time::Duration;
@@ -234,7 +234,9 @@ impl WFMachinesAdapter for SignalExternalMachine {
234
234
  SignalExternalCommand::Failed(f) => {
235
235
  let reason = match f {
236
236
  SignalExternalWorkflowExecutionFailedCause::Unspecified => "unknown",
237
- SignalExternalWorkflowExecutionFailedCause::ExternalWorkflowExecutionNotFound => "it was not found"
237
+ SignalExternalWorkflowExecutionFailedCause::ExternalWorkflowExecutionNotFound
238
+ | SignalExternalWorkflowExecutionFailedCause::NamespaceNotFound =>
239
+ "it was not found"
238
240
  };
239
241
  vec![ResolveSignalExternalWorkflow {
240
242
  seq: self.shared_state.seq,
@@ -306,7 +308,7 @@ impl Cancellable for SignalExternalMachine {
306
308
  #[cfg(test)]
307
309
  mod tests {
308
310
  use super::*;
309
- use crate::{replay::TestHistoryBuilder, workflow::managed_wf::ManagedWFFunc};
311
+ use crate::{replay::TestHistoryBuilder, worker::workflow::ManagedWFFunc};
310
312
  use std::mem::discriminant;
311
313
  use temporal_sdk::{
312
314
  CancellableFuture, SignalWorkflowOptions, WfContext, WorkflowFunction, WorkflowResult,
@@ -274,8 +274,7 @@ impl Cancellable for TimerMachine {
274
274
  mod test {
275
275
  use super::*;
276
276
  use crate::{
277
- replay::TestHistoryBuilder, test_help::canned_histories,
278
- workflow::managed_wf::ManagedWFFunc,
277
+ replay::TestHistoryBuilder, test_help::canned_histories, worker::workflow::ManagedWFFunc,
279
278
  };
280
279
  use rstest::{fixture, rstest};
281
280
  use std::{mem::discriminant, time::Duration};
@@ -67,7 +67,7 @@ fn spawn_save_coverage_at_end() -> SyncSender<(String, CoveredTransition)> {
67
67
  #[cfg(test)]
68
68
  mod machine_coverage_report {
69
69
  use super::*;
70
- use crate::workflow::machines::{
70
+ use crate::worker::workflow::machines::{
71
71
  activity_state_machine::ActivityMachine,
72
72
  cancel_external_state_machine::CancelExternalMachine,
73
73
  cancel_workflow_state_machine::CancelWorkflowMachine,
@@ -2,7 +2,7 @@ use super::{
2
2
  workflow_machines::{MachineResponse, WFMachinesError},
3
3
  NewMachineWithCommand,
4
4
  };
5
- use crate::workflow::machines::{Cancellable, EventInfo, MachineKind, WFMachinesAdapter};
5
+ use crate::worker::workflow::machines::{Cancellable, EventInfo, MachineKind, WFMachinesAdapter};
6
6
  use rustfsm::{fsm, TransitionResult};
7
7
  use temporal_sdk_core_protos::{
8
8
  coresdk::workflow_commands::UpsertWorkflowSearchAttributes,
@@ -161,16 +161,14 @@ impl From<Created> for CommandIssued {
161
161
 
162
162
  #[cfg(test)]
163
163
  mod tests {
164
+ use super::{super::OnEventWrapper, *};
165
+ use crate::{replay::TestHistoryBuilder, worker::workflow::ManagedWFFunc};
164
166
  use rustfsm::StateMachine;
165
167
  use temporal_sdk::{WfContext, WorkflowFunction};
166
- use temporal_sdk_core_protos::{
167
- coresdk::common::Payload, temporal::api::command::v1::command::Attributes,
168
+ use temporal_sdk_core_protos::temporal::api::{
169
+ command::v1::command::Attributes, common::v1::Payload,
168
170
  };
169
171
 
170
- use crate::{replay::TestHistoryBuilder, workflow::managed_wf::ManagedWFFunc};
171
-
172
- use super::{super::OnEventWrapper, *};
173
-
174
172
  #[tokio::test]
175
173
  async fn upsert_search_attrs_from_workflow() {
176
174
  let mut t = TestHistoryBuilder::default();
@@ -7,8 +7,8 @@ use std::{
7
7
  collections::{HashMap, HashSet},
8
8
  time::SystemTime,
9
9
  };
10
- use temporal_sdk_core_protos::{
11
- coresdk::common::WorkflowExecution, temporal::api::history::v1::HistoryEvent,
10
+ use temporal_sdk_core_protos::temporal::api::{
11
+ common::v1::WorkflowExecution, history::v1::HistoryEvent,
12
12
  };
13
13
 
14
14
  #[derive(Default)]
@@ -19,12 +19,12 @@ use crate::{
19
19
  protosext::{HistoryEventExt, ValidScheduleLA},
20
20
  telemetry::{metrics::MetricsContext, VecDisplayer},
21
21
  worker::{
22
+ workflow::{
23
+ CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, WFCommand, WorkflowFetcher,
24
+ WorkflowStartedInfo,
25
+ },
22
26
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
23
27
  },
24
- workflow::{
25
- CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, WFCommand, WorkflowFetcher,
26
- WorkflowStartedInfo,
27
- },
28
28
  };
29
29
  use siphasher::sip::SipHasher13;
30
30
  use slotmap::SlotMap;
@@ -318,6 +318,20 @@ impl WorkflowMachines {
318
318
  if event.is_final_wf_execution_event() {
319
319
  self.have_seen_terminal_event = true;
320
320
  }
321
+ if matches!(
322
+ event.event_type(),
323
+ EventType::WorkflowExecutionTerminated | EventType::WorkflowExecutionTimedOut
324
+ ) {
325
+ return if has_next_event {
326
+ Err(WFMachinesError::Fatal(
327
+ "Machines were fed a history which has an event after workflow execution was \
328
+ terminated!"
329
+ .to_string(),
330
+ ))
331
+ } else {
332
+ Ok(())
333
+ };
334
+ }
321
335
 
322
336
  if event.is_command_event() {
323
337
  self.handle_command_event(event)?;
@@ -525,7 +539,7 @@ impl WorkflowMachines {
525
539
  }
526
540
  _ => {
527
541
  return Err(WFMachinesError::Fatal(format!(
528
- "The event is non a non-stateful event, but we tried to handle it as one: {}",
542
+ "The event is not a non-stateful event, but we tried to handle it as one: {}",
529
543
  event
530
544
  )));
531
545
  }
@@ -564,6 +578,7 @@ impl WorkflowMachines {
564
578
  timestamp: self.current_wf_time.map(Into::into),
565
579
  is_replaying: self.replaying,
566
580
  run_id: self.run_id.clone(),
581
+ history_length: self.last_processed_event as u32,
567
582
  jobs,
568
583
  }
569
584
  }
@@ -598,11 +613,9 @@ impl WorkflowMachines {
598
613
  }
599
614
 
600
615
  /// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
601
- /// any events that need to be replayed until caught up to the newest WFT.
616
+ /// any events that need to be replayed until caught up to the newest WFT. May also fetch
617
+ /// history from server if needed.
602
618
  pub(crate) async fn apply_next_wft_from_history(&mut self) -> Result<usize> {
603
- // A much higher-up span (ex: poll) may want this field filled
604
- tracing::Span::current().record("run_id", &self.run_id.as_str());
605
-
606
619
  // If we have already seen the terminal event for the entire workflow in a previous WFT,
607
620
  // then we don't need to do anything here, and in fact we need to avoid re-applying the
608
621
  // final WFT.
@@ -697,8 +710,18 @@ impl WorkflowMachines {
697
710
  /// Transfer commands from `current_wf_task_commands` to `commands`, so they may be sent off
698
711
  /// to the server. While doing so, [TemporalStateMachine::handle_command] is called on the
699
712
  /// machine associated with the command.
700
- #[instrument(level = "debug", skip(self))]
701
713
  fn prepare_commands(&mut self) -> Result<()> {
714
+ // It's possible we might prepare commands more than once before completing a WFT. (Because
715
+ // of local activities, of course). Some commands might have since been cancelled that we
716
+ // already prepared. Rip them out of the outgoing command list if so.
717
+ self.commands.retain(|c| {
718
+ !self
719
+ .all_machines
720
+ .get(c.machine)
721
+ .expect("Machine must exist")
722
+ .was_cancelled_before_sent_to_server()
723
+ });
724
+
702
725
  while let Some(c) = self.current_wf_task_commands.pop_front() {
703
726
  if !self
704
727
  .machine(c.machine)
@@ -805,11 +828,7 @@ impl WorkflowMachines {
805
828
  self.add_cmd_to_wf_task(new_timer(attrs), Some(CommandID::Timer(seq)));
806
829
  }
807
830
  WFCommand::UpsertSearchAttributes(attrs) => {
808
- let seq = attrs.seq;
809
- self.add_cmd_to_wf_task(
810
- upsert_search_attrs(attrs),
811
- Some(CommandID::Timer(seq)),
812
- );
831
+ self.add_cmd_to_wf_task(upsert_search_attrs(attrs), None);
813
832
  }
814
833
  WFCommand::CancelTimer(attrs) => {
815
834
  jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?);
@@ -917,7 +936,12 @@ impl WorkflowMachines {
917
936
  Some(cancel_we::Target::WorkflowExecution(we)) => (we, false),
918
937
  };
919
938
  self.add_cmd_to_wf_task(
920
- new_external_cancel(attrs.seq, we, only_child),
939
+ new_external_cancel(
940
+ attrs.seq,
941
+ we,
942
+ only_child,
943
+ format!("Cancel requested by workflow with run id {}", self.run_id),
944
+ ),
921
945
  Some(CommandID::CancelExternal(attrs.seq)),
922
946
  );
923
947
  }
@@ -0,0 +1,198 @@
1
+ use super::*;
2
+ use crate::{
3
+ replay::TestHistoryBuilder,
4
+ test_help::TEST_Q,
5
+ worker::{
6
+ workflow::{
7
+ history_update::TestHBExt, machines::WorkflowMachines, WFCommand, WorkflowFetcher,
8
+ },
9
+ LocalActRequest, LocalActivityResolution,
10
+ },
11
+ };
12
+ use std::{convert::TryInto, time::Duration};
13
+ use temporal_sdk::{WorkflowFunction, WorkflowResult};
14
+ use temporal_sdk_core_protos::{
15
+ coresdk::{
16
+ activity_result::ActivityExecutionResult,
17
+ workflow_activation::{create_evict_activation, remove_from_cache::EvictionReason},
18
+ workflow_completion::{
19
+ workflow_activation_completion::Status, WorkflowActivationCompletion,
20
+ },
21
+ },
22
+ temporal::api::common::v1::Payload,
23
+ };
24
+ use tokio::{
25
+ sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
26
+ task::JoinHandle,
27
+ };
28
+
29
+ pub(crate) struct WFFutureDriver {
30
+ completions_rx: UnboundedReceiver<WorkflowActivationCompletion>,
31
+ }
32
+
33
+ #[async_trait::async_trait]
34
+ impl WorkflowFetcher for WFFutureDriver {
35
+ async fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
36
+ if let Some(completion) = self.completions_rx.recv().await {
37
+ debug!("Managed wf completion: {}", completion);
38
+ completion
39
+ .status
40
+ .map(|s| match s {
41
+ Status::Successful(s) => s
42
+ .commands
43
+ .into_iter()
44
+ .map(|cmd| cmd.try_into().unwrap())
45
+ .collect(),
46
+ Status::Failed(_) => panic!("Ahh failed"),
47
+ })
48
+ .unwrap_or_default()
49
+ } else {
50
+ // Sender went away so nothing to do here. End of wf/test.
51
+ vec![]
52
+ }
53
+ }
54
+ }
55
+
56
+ #[must_use]
57
+ pub struct ManagedWFFunc {
58
+ mgr: WorkflowManager,
59
+ activation_tx: UnboundedSender<WorkflowActivation>,
60
+ future_handle: Option<JoinHandle<WorkflowResult<()>>>,
61
+ was_shutdown: bool,
62
+ }
63
+
64
+ impl ManagedWFFunc {
65
+ pub fn new(hist: TestHistoryBuilder, func: WorkflowFunction, args: Vec<Payload>) -> Self {
66
+ Self::new_from_update(hist.as_history_update(), func, args)
67
+ }
68
+
69
+ pub fn new_from_update(
70
+ hist: HistoryUpdate,
71
+ func: WorkflowFunction,
72
+ args: Vec<Payload>,
73
+ ) -> Self {
74
+ let (completions_tx, completions_rx) = unbounded_channel();
75
+ let (wff, activations) = func.start_workflow(
76
+ "testnamespace".to_string(),
77
+ TEST_Q.to_string(),
78
+ args,
79
+ completions_tx,
80
+ );
81
+ let spawned = tokio::spawn(wff);
82
+ let driver = WFFutureDriver { completions_rx };
83
+ let state_machines = WorkflowMachines::new(
84
+ "test_namespace".to_string(),
85
+ "wfid".to_string(),
86
+ "wftype".to_string(),
87
+ "runid".to_string(),
88
+ hist,
89
+ Box::new(driver).into(),
90
+ Default::default(),
91
+ );
92
+ let mgr = WorkflowManager::new_from_machines(state_machines);
93
+ Self {
94
+ mgr,
95
+ activation_tx: activations,
96
+ future_handle: Some(spawned),
97
+ was_shutdown: false,
98
+ }
99
+ }
100
+
101
+ #[instrument(level = "debug", skip(self))]
102
+ pub(crate) async fn get_next_activation(&mut self) -> Result<WorkflowActivation> {
103
+ let res = self.mgr.get_next_activation().await?;
104
+ debug!("Managed wf next activation: {}", &res);
105
+ self.push_activation_to_wf(&res).await?;
106
+ Ok(res)
107
+ }
108
+
109
+ /// Return outgoing server commands as of the last iteration
110
+ pub(crate) fn get_server_commands(&mut self) -> OutgoingServerCommands {
111
+ self.mgr.get_server_commands()
112
+ }
113
+
114
+ pub(crate) fn drain_queued_local_activities(&mut self) -> Vec<LocalActRequest> {
115
+ self.mgr.drain_queued_local_activities()
116
+ }
117
+
118
+ /// Feed new history, as if received a new poll result. Returns new activation
119
+ #[instrument(level = "debug", skip(self, update))]
120
+ pub(crate) async fn new_history(
121
+ &mut self,
122
+ update: HistoryUpdate,
123
+ ) -> Result<WorkflowActivation> {
124
+ let res = self.mgr.feed_history_from_server(update).await?;
125
+ self.push_activation_to_wf(&res).await?;
126
+ Ok(res)
127
+ }
128
+
129
+ /// Say a local activity completed (they always take 1 second in these tests)
130
+ pub(crate) fn complete_local_activity(
131
+ &mut self,
132
+ seq_num: u32,
133
+ result: ActivityExecutionResult,
134
+ ) -> Result<bool> {
135
+ self.mgr
136
+ .notify_of_local_result(LocalResolution::LocalActivity(LocalActivityResolution {
137
+ seq: seq_num,
138
+ // We accept normal execution results and do this conversion because there
139
+ // are more helpers for constructing them.
140
+ result: result
141
+ .status
142
+ .expect("LA result must have a status")
143
+ .try_into()
144
+ .expect("LA execution result must be a valid LA result"),
145
+ runtime: Duration::from_secs(1),
146
+ attempt: 1,
147
+ backoff: None,
148
+ // Tests at this level don't use the LA dispatcher, so this is irrelevant
149
+ original_schedule_time: None,
150
+ }))
151
+ }
152
+
153
+ /// During testing it can be useful to run through all activations to simulate replay
154
+ /// easily. Returns the last produced activation with jobs in it, or an activation with no
155
+ /// jobs if the first call had no jobs.
156
+ pub(crate) async fn process_all_activations(&mut self) -> Result<WorkflowActivation> {
157
+ let mut last_act = self.get_next_activation().await?;
158
+ let mut next_act = self.get_next_activation().await?;
159
+ while !next_act.jobs.is_empty() {
160
+ last_act = next_act;
161
+ next_act = self.get_next_activation().await?;
162
+ }
163
+ Ok(last_act)
164
+ }
165
+
166
+ pub async fn shutdown(&mut self) -> WorkflowResult<()> {
167
+ self.was_shutdown = true;
168
+ // Send an eviction to ensure wf exits if it has not finished (ex: feeding partial hist)
169
+ let _ = self.activation_tx.send(create_evict_activation(
170
+ "not actually important".to_string(),
171
+ "force shutdown".to_string(),
172
+ EvictionReason::Unspecified,
173
+ ));
174
+ self.future_handle.take().unwrap().await.unwrap()
175
+ }
176
+
177
+ #[instrument(level = "debug", skip(self, res))]
178
+ async fn push_activation_to_wf(&mut self, res: &WorkflowActivation) -> Result<()> {
179
+ if res.jobs.is_empty() {
180
+ // Nothing to do here
181
+ return Ok(());
182
+ }
183
+ self.activation_tx
184
+ .send(res.clone())
185
+ .expect("Workflow should not be dropped if we are still sending activations");
186
+ self.mgr.machines.iterate_machines().await?;
187
+ Ok(())
188
+ }
189
+ }
190
+
191
+ impl Drop for ManagedWFFunc {
192
+ fn drop(&mut self) {
193
+ // Double panics cause a SIGILL
194
+ if !self.was_shutdown && !std::thread::panicking() {
195
+ panic!("You must call `shutdown` to properly use ManagedWFFunc in tests")
196
+ }
197
+ }
198
+ }