@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
@@ -1,20 +1,29 @@
1
+ mod local_acts;
2
+
3
+ pub(crate) use temporal_sdk_core_api::errors::WFMachinesError;
4
+
5
+ use super::{
6
+ activity_state_machine::new_activity, cancel_external_state_machine::new_external_cancel,
7
+ cancel_workflow_state_machine::cancel_workflow,
8
+ child_workflow_state_machine::new_child_workflow,
9
+ complete_workflow_state_machine::complete_workflow,
10
+ continue_as_new_workflow_state_machine::continue_as_new,
11
+ fail_workflow_state_machine::fail_workflow, local_activity_state_machine::new_local_activity,
12
+ patch_state_machine::has_change, signal_external_state_machine::new_external_signal,
13
+ timer_state_machine::new_timer, workflow_machines::local_acts::LocalActivityData,
14
+ workflow_task_state_machine::WorkflowTaskMachine, MachineKind, Machines, NewMachineWithCommand,
15
+ TemporalStateMachine,
16
+ };
1
17
  use crate::{
2
- machines::{
3
- activity_state_machine::new_activity, cancel_external_state_machine::new_external_cancel,
4
- cancel_workflow_state_machine::cancel_workflow,
5
- child_workflow_state_machine::new_child_workflow,
6
- complete_workflow_state_machine::complete_workflow,
7
- continue_as_new_workflow_state_machine::continue_as_new,
8
- fail_workflow_state_machine::fail_workflow, patch_state_machine::has_change,
9
- signal_external_state_machine::new_external_signal, timer_state_machine::new_timer,
10
- workflow_task_state_machine::WorkflowTaskMachine, MachineKind, NewMachineWithCommand,
11
- ProtoCommand, TemporalStateMachine, WFCommand,
12
- },
13
- protosext::HistoryEventExt,
18
+ protosext::{HistoryEventExt, ValidScheduleLA},
14
19
  telemetry::{metrics::MetricsContext, VecDisplayer},
15
- workflow::{CommandID, DrivenWorkflow, HistoryUpdate, WorkflowFetcher},
20
+ worker::{
21
+ ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
22
+ },
23
+ workflow::{
24
+ CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, WFCommand, WorkflowFetcher,
25
+ },
16
26
  };
17
- use prost_types::TimestampOutOfSystemRangeError;
18
27
  use slotmap::SlotMap;
19
28
  use std::{
20
29
  borrow::{Borrow, BorrowMut},
@@ -27,8 +36,8 @@ use temporal_sdk_core_protos::{
27
36
  coresdk::{
28
37
  common::{NamespacedWorkflowExecution, Payload},
29
38
  workflow_activation::{
30
- wf_activation_job::{self, Variant},
31
- NotifyHasPatch, StartWorkflow, UpdateRandomSeed, WfActivation,
39
+ workflow_activation_job::{self, Variant},
40
+ NotifyHasPatch, StartWorkflow, UpdateRandomSeed, WorkflowActivation,
32
41
  },
33
42
  workflow_commands::{
34
43
  request_cancel_external_workflow_execution as cancel_we,
@@ -37,12 +46,13 @@ use temporal_sdk_core_protos::{
37
46
  FromPayloadsExt,
38
47
  },
39
48
  temporal::api::{
49
+ command::v1::Command as ProtoCommand,
40
50
  common::v1::Header,
41
51
  enums::v1::EventType,
42
- history::v1::{history_event, HistoryEvent},
52
+ history::v1::{history_event, HistoryEvent, WorkflowExecutionStartedEventAttributes},
43
53
  },
54
+ utilities::TryIntoOrNone,
44
55
  };
45
- use tracing::Level;
46
56
 
47
57
  type Result<T, E = WFMachinesError> = std::result::Result<T, E>;
48
58
 
@@ -61,12 +71,18 @@ pub(crate) struct WorkflowMachines {
61
71
  /// Eventually, this number should reach the started id in the latest history update, but
62
72
  /// we must incrementally apply the history while communicating with lang.
63
73
  next_started_event_id: i64,
74
+ /// The event id of the most recent event processed. It's possible in some situations (ex legacy
75
+ /// queries) to receive a history with no new workflow tasks. If the last history we processed
76
+ /// also had no new tasks, we need a way to know not to apply the same events over again.
77
+ pub last_processed_event: i64,
64
78
  /// True if the workflow is replaying from history
65
79
  pub replaying: bool,
66
80
  /// Namespace this workflow exists in
67
81
  pub namespace: String,
68
82
  /// Workflow identifier
69
83
  pub workflow_id: String,
84
+ /// Workflow type identifier. (Function name, class, etc)
85
+ pub workflow_type: String,
70
86
  /// Identifies the current run
71
87
  pub run_id: String,
72
88
  /// The time the workflow execution began, as told by the WEStarted event
@@ -74,17 +90,18 @@ pub(crate) struct WorkflowMachines {
74
90
  /// The time the workflow execution finished, as determined by when the machines handled
75
91
  /// a terminal workflow command. If this is `Some`, you know the workflow is ended.
76
92
  workflow_end_time: Option<SystemTime>,
77
- /// The current workflow time if it has been established
93
+ /// The WFT start time if it has been established
94
+ wft_start_time: Option<SystemTime>,
95
+ /// The current workflow time if it has been established. This may differ from the WFT start
96
+ /// time since local activities may advance the clock
78
97
  current_wf_time: Option<SystemTime>,
79
98
 
80
- // TODO: Nothing gets deleted from here
81
- all_machines: SlotMap<MachineKey, Box<dyn TemporalStateMachine + 'static>>,
99
+ all_machines: SlotMap<MachineKey, Machines>,
82
100
 
83
101
  /// A mapping for accessing machines associated to a particular event, where the key is the id
84
102
  /// of the initiating event for that machine.
85
103
  machines_by_event_id: HashMap<i64, MachineKey>,
86
104
 
87
- // TODO: Nothing gets deleted from here
88
105
  /// Maps command ids as created by workflow authors to their associated machines.
89
106
  id_to_machine: HashMap<CommandID, MachineKey>,
90
107
 
@@ -93,19 +110,20 @@ pub(crate) struct WorkflowMachines {
93
110
  commands: VecDeque<CommandAndMachine>,
94
111
  /// Commands generated by the currently processing workflow task, which will eventually be
95
112
  /// transferred to `commands` (and hence eventually sent to the server)
96
- ///
97
- /// Old note: It is a queue as commands can be added (due to marker based commands) while
98
- /// iterating over already added commands.
99
113
  current_wf_task_commands: VecDeque<CommandAndMachine>,
114
+
100
115
  /// Information about patch markers we have already seen while replaying history
101
116
  encountered_change_markers: HashMap<String, ChangeInfo>,
102
117
 
118
+ /// Contains extra local-activity related data
119
+ local_activity_data: LocalActivityData,
120
+
103
121
  /// The workflow that is being driven by this instance of the machines
104
122
  drive_me: DrivenWorkflow,
105
123
 
106
124
  /// Is set to true once we've seen the final event in workflow history, to avoid accidentally
107
125
  /// re-applying the final workflow task.
108
- have_seen_terminal_event: bool,
126
+ pub have_seen_terminal_event: bool,
109
127
 
110
128
  /// Metrics context
111
129
  pub metrics: MetricsContext,
@@ -114,13 +132,19 @@ pub(crate) struct WorkflowMachines {
114
132
  #[derive(Debug, derive_more::Display)]
115
133
  #[display(fmt = "Cmd&Machine({})", "command")]
116
134
  struct CommandAndMachine {
117
- command: ProtoCommand,
135
+ command: MachineAssociatedCommand,
118
136
  machine: MachineKey,
119
137
  }
120
138
 
139
+ #[derive(Debug, derive_more::Display)]
140
+ enum MachineAssociatedCommand {
141
+ Real(Box<ProtoCommand>),
142
+ #[display(fmt = "FakeLocalActivityMarker({})", "_0")]
143
+ FakeLocalActivityMarker(u32),
144
+ }
145
+
121
146
  #[derive(Debug, Clone, Copy)]
122
147
  struct ChangeInfo {
123
- deprecated: bool,
124
148
  created_command: bool,
125
149
  }
126
150
 
@@ -129,10 +153,12 @@ struct ChangeInfo {
129
153
  #[must_use]
130
154
  #[allow(clippy::large_enum_variant)]
131
155
  pub enum MachineResponse {
132
- #[display(fmt = "PushWFJob")]
133
- PushWFJob(wf_activation_job::Variant),
156
+ #[display(fmt = "PushWFJob({})", "_0")]
157
+ PushWFJob(workflow_activation_job::Variant),
134
158
 
135
159
  IssueNewCommand(ProtoCommand),
160
+ #[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
161
+ IssueFakeLocalActivityMarker(u32),
136
162
  #[display(fmt = "TriggerWFTaskStarted")]
137
163
  TriggerWFTaskStarted {
138
164
  task_started_event_id: i64,
@@ -142,43 +168,37 @@ pub enum MachineResponse {
142
168
  UpdateRunIdOnWorkflowReset {
143
169
  run_id: String,
144
170
  },
171
+
172
+ /// Queue a local activity to be processed by the worker
173
+ #[display(fmt = "QueueLocalActivity")]
174
+ QueueLocalActivity(ValidScheduleLA),
175
+ /// Request cancellation of an executing local activity
176
+ #[display(fmt = "RequestCancelLocalActivity({})", "_0")]
177
+ RequestCancelLocalActivity(u32),
178
+ /// Indicates we are abandoning the indicated LA, so we can remove it from "outstanding" LAs
179
+ /// and we will not try to WFT heartbeat because of it.
180
+ #[display(fmt = "AbandonLocalActivity({:?})", "_0")]
181
+ AbandonLocalActivity(u32),
182
+
183
+ /// Set the workflow time to the provided time
184
+ #[display(fmt = "UpdateWFTime({:?})", "_0")]
185
+ UpdateWFTime(Option<SystemTime>),
145
186
  }
146
187
 
147
- // Must use `From` b/c ofZZ
148
188
  impl<T> From<T> for MachineResponse
149
189
  where
150
- T: Into<wf_activation_job::Variant>,
190
+ T: Into<workflow_activation_job::Variant>,
151
191
  {
152
192
  fn from(v: T) -> Self {
153
193
  Self::PushWFJob(v.into())
154
194
  }
155
195
  }
156
196
 
157
- #[derive(thiserror::Error, Debug)]
158
- pub(crate) enum WFMachinesError {
159
- #[error("Nondeterminism error: {0}")]
160
- Nondeterminism(String),
161
- #[error("Fatal error in workflow machines: {0}")]
162
- Fatal(String),
163
-
164
- #[error("Unrecoverable network error while fetching history: {0}")]
165
- HistoryFetchingError(tonic::Status),
166
-
167
- /// Should always be caught internally and turned into a workflow task failure
168
- #[error("Unable to process partial event history because workflow is no longer cached.")]
169
- CacheMiss,
170
- }
171
-
172
- impl From<TimestampOutOfSystemRangeError> for WFMachinesError {
173
- fn from(_: TimestampOutOfSystemRangeError) -> Self {
174
- Self::Fatal("Could not decode timestamp".to_string())
175
- }
176
- }
177
-
178
197
  impl WorkflowMachines {
179
198
  pub(crate) fn new(
180
199
  namespace: String,
181
200
  workflow_id: String,
201
+ workflow_type: String,
182
202
  run_id: String,
183
203
  history: HistoryUpdate,
184
204
  driven_wf: DrivenWorkflow,
@@ -189,6 +209,7 @@ impl WorkflowMachines {
189
209
  last_history_from_server: history,
190
210
  namespace,
191
211
  workflow_id,
212
+ workflow_type,
192
213
  run_id,
193
214
  drive_me: driven_wf,
194
215
  replaying,
@@ -196,8 +217,10 @@ impl WorkflowMachines {
196
217
  // In an ideal world one could say ..Default::default() here and it'd still work.
197
218
  current_started_event_id: 0,
198
219
  next_started_event_id: 0,
220
+ last_processed_event: 0,
199
221
  workflow_start_time: None,
200
222
  workflow_end_time: None,
223
+ wft_start_time: None,
201
224
  current_wf_time: None,
202
225
  all_machines: Default::default(),
203
226
  machines_by_event_id: Default::default(),
@@ -205,6 +228,7 @@ impl WorkflowMachines {
205
228
  commands: Default::default(),
206
229
  current_wf_task_commands: Default::default(),
207
230
  encountered_change_markers: Default::default(),
231
+ local_activity_data: LocalActivityData::default(),
208
232
  have_seen_terminal_event: false,
209
233
  }
210
234
  }
@@ -229,16 +253,60 @@ impl WorkflowMachines {
229
253
  Ok(())
230
254
  }
231
255
 
256
+ /// Let this workflow know that something we've been waiting locally on has resolved, like a
257
+ /// local activity or side effect
258
+ pub(crate) fn local_resolution(&mut self, resolution: LocalResolution) -> Result<()> {
259
+ match resolution {
260
+ LocalResolution::LocalActivity(LocalActivityResolution {
261
+ seq,
262
+ result,
263
+ runtime,
264
+ attempt,
265
+ backoff,
266
+ original_schedule_time,
267
+ }) => {
268
+ let act_id = CommandID::LocalActivity(seq);
269
+ let mk = self.get_machine_key(act_id)?;
270
+ let mach = self.machine_mut(mk);
271
+ if let Machines::LocalActivityMachine(ref mut lam) = *mach {
272
+ let resps =
273
+ lam.try_resolve(result, runtime, attempt, backoff, original_schedule_time)?;
274
+ self.process_machine_responses(mk, resps)?;
275
+ } else {
276
+ return Err(WFMachinesError::Nondeterminism(format!(
277
+ "Command matching activity with seq num {} existed but was not a \
278
+ local activity!",
279
+ seq
280
+ )));
281
+ }
282
+ self.local_activity_data.done_executing(seq);
283
+ }
284
+ }
285
+ Ok(())
286
+ }
287
+
288
+ /// Drain all queued local activities that need executing or cancellation
289
+ pub(crate) fn drain_queued_local_activities(&mut self) -> Vec<LocalActRequest> {
290
+ self.local_activity_data
291
+ .take_all_reqs(&self.workflow_type, &self.workflow_id, &self.run_id)
292
+ }
293
+
294
+ /// Returns the number of local activities we know we need to execute but have not yet finished
295
+ pub(crate) fn outstanding_local_activity_count(&self) -> usize {
296
+ self.local_activity_data.outstanding_la_count()
297
+ }
298
+
299
+ /// Returns the start attributes for the workflow if it has started
300
+ pub(crate) fn started_attrs(&self) -> Option<&WorkflowExecutionStartedEventAttributes> {
301
+ self.drive_me.get_started_attrs()
302
+ }
303
+
232
304
  /// Handle a single event from the workflow history. `has_next_event` should be false if `event`
233
305
  /// is the last event in the history.
234
306
  ///
235
307
  /// TODO: Describe what actually happens in here
236
308
  #[instrument(level = "debug", skip(self, event), fields(event=%event))]
237
- pub(crate) fn handle_event(
238
- &mut self,
239
- event: &HistoryEvent,
240
- has_next_event: bool,
241
- ) -> Result<()> {
309
+ fn handle_event(&mut self, event: &HistoryEvent, has_next_event: bool) -> Result<()> {
242
310
  if event.is_final_wf_execution_event() {
243
311
  self.have_seen_terminal_event = true;
244
312
  }
@@ -287,21 +355,24 @@ impl WorkflowMachines {
287
355
  /// Called when a workflow task started event has triggered. Ensures we are tracking the ID
288
356
  /// of the current started event as well as workflow time properly.
289
357
  fn task_started(&mut self, task_started_event_id: i64, time: SystemTime) -> Result<()> {
290
- let s = span!(Level::DEBUG, "Task started trigger");
291
- let _enter = s.enter();
292
-
293
- // TODO: Local activity machines
294
- // // Give local activities a chance to recreate their requests if they were lost due
295
- // // to the last workflow task failure. The loss could happen only the last workflow task
296
- // // was forcibly created by setting forceCreate on RespondWorkflowTaskCompletedRequest.
297
- // if (nonProcessedWorkflowTask) {
298
- // for (LocalActivityStateMachine value : localActivityMap.values()) {
299
- // value.nonReplayWorkflowTaskStarted();
300
- // }
301
- // }
302
-
303
358
  self.current_started_event_id = task_started_event_id;
359
+ self.wft_start_time = Some(time);
304
360
  self.set_current_time(time);
361
+
362
+ // Notify local activity machines that we started a non-replay WFT, which will allow any
363
+ // which were waiting for a marker to instead decide to execute the LA since it clearly
364
+ // will not be resolved via marker.
365
+ if !self.replaying {
366
+ let mut resps = vec![];
367
+ for (k, mach) in self.all_machines.iter_mut() {
368
+ if let Machines::LocalActivityMachine(lam) = mach {
369
+ resps.push((k, lam.encountered_non_replay_wft()?));
370
+ }
371
+ }
372
+ for (mkey, resp_set) in resps {
373
+ self.process_machine_responses(mkey, resp_set)?;
374
+ }
375
+ }
305
376
  Ok(())
306
377
  }
307
378
 
@@ -315,10 +386,25 @@ impl WorkflowMachines {
315
386
  /// with a state machine, which is then notified about the event and the command is removed from
316
387
  /// the commands queue.
317
388
  fn handle_command_event(&mut self, event: &HistoryEvent) -> Result<()> {
318
- // TODO: Local activity handling stuff
319
- // if (handleLocalActivityMarker(event)) {
320
- // return;
321
- // }
389
+ if event.is_local_activity_marker() {
390
+ let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
391
+ WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
392
+ })?;
393
+ let cmdid = CommandID::LocalActivity(deets.seq);
394
+ let mkey = self.get_machine_key(cmdid)?;
395
+ if let Machines::LocalActivityMachine(lam) = self.machine(mkey) {
396
+ if lam.marker_should_get_special_handling()? {
397
+ self.submachine_handle_event(mkey, event, false)?;
398
+ return Ok(());
399
+ }
400
+ } else {
401
+ return Err(WFMachinesError::Fatal(format!(
402
+ "Encountered local activity marker but the associated machine was of the \
403
+ wrong type! {:?}",
404
+ event
405
+ )));
406
+ }
407
+ }
322
408
 
323
409
  let consumed_cmd = loop {
324
410
  if let Some(peek_machine) = self.commands.front() {
@@ -343,19 +429,17 @@ impl WorkflowMachines {
343
429
  )));
344
430
  };
345
431
 
346
- // Feed the machine the event
347
432
  let canceled_before_sent = self
348
433
  .machine(command.machine)
349
434
  .was_cancelled_before_sent_to_server();
350
435
 
351
436
  if !canceled_before_sent {
437
+ // Feed the machine the event
352
438
  self.submachine_handle_event(command.machine, event, true)?;
353
439
  break command;
354
440
  }
355
441
  };
356
442
 
357
- // TODO: validate command
358
-
359
443
  if !self.machine(consumed_cmd.machine).is_final_state() {
360
444
  self.machines_by_event_id
361
445
  .insert(event.event_id, consumed_cmd.machine);
@@ -417,7 +501,7 @@ impl WorkflowMachines {
417
501
  }
418
502
  Some(EventType::WorkflowTaskScheduled) => {
419
503
  let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
420
- let key = self.all_machines.insert(Box::new(wf_task_sm));
504
+ let key = self.all_machines.insert(wf_task_sm.into());
421
505
  self.submachine_handle_event(key, event, has_next_event)?;
422
506
  self.machines_by_event_id.insert(event.event_id, key);
423
507
  }
@@ -461,7 +545,10 @@ impl WorkflowMachines {
461
545
  .iter()
462
546
  .filter_map(|c| {
463
547
  if !self.machine(c.machine).is_final_state() {
464
- Some(c.command.clone())
548
+ match &c.command {
549
+ MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
550
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
551
+ }
465
552
  } else {
466
553
  None
467
554
  }
@@ -475,9 +562,9 @@ impl WorkflowMachines {
475
562
  ///
476
563
  /// The job list may be empty, in which case it is expected the caller handles what to do in a
477
564
  /// "no work" situation. Possibly, it may know about some work the machines don't, like queries.
478
- pub(crate) fn get_wf_activation(&mut self) -> WfActivation {
565
+ pub(crate) fn get_wf_activation(&mut self) -> WorkflowActivation {
479
566
  let jobs = self.drive_me.drain_jobs();
480
- WfActivation {
567
+ WorkflowActivation {
481
568
  timestamp: self.current_wf_time.map(Into::into),
482
569
  is_replaying: self.replaying,
483
570
  run_id: self.run_id.clone(),
@@ -485,6 +572,10 @@ impl WorkflowMachines {
485
572
  }
486
573
  }
487
574
 
575
+ pub(crate) fn has_pending_jobs(&self) -> bool {
576
+ self.drive_me.has_pending_jobs()
577
+ }
578
+
488
579
  fn set_current_time(&mut self, time: SystemTime) -> SystemTime {
489
580
  if self.current_wf_time.map_or(true, |t| t < time) {
490
581
  self.current_wf_time = Some(time);
@@ -495,14 +586,9 @@ impl WorkflowMachines {
495
586
 
496
587
  /// Iterate the state machines, which consists of grabbing any pending outgoing commands from
497
588
  /// the workflow code, handling them, and preparing them to be sent off to the server.
498
- ///
499
- /// Returns a boolean flag which indicates whether or not new activations were produced by the
500
- /// state machine. If true, pending activation should be created by the caller making jobs
501
- /// available to the lang side.
502
- pub(crate) async fn iterate_machines(&mut self) -> Result<bool> {
589
+ pub(crate) async fn iterate_machines(&mut self) -> Result<()> {
503
590
  let results = self.drive_me.fetch_workflow_iteration_output().await;
504
591
  let jobs = self.handle_driven_results(results)?;
505
- let has_new_lang_jobs = !jobs.is_empty();
506
592
  for job in jobs {
507
593
  self.drive_me.send_job(job);
508
594
  }
@@ -512,12 +598,12 @@ impl WorkflowMachines {
512
598
  self.metrics.wf_e2e_latency(rt);
513
599
  }
514
600
  }
515
- Ok(has_new_lang_jobs)
601
+ Ok(())
516
602
  }
517
603
 
518
604
  /// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
519
605
  /// any events that need to be replayed until caught up to the newest WFT.
520
- pub(crate) async fn apply_next_wft_from_history(&mut self) -> Result<()> {
606
+ pub(crate) async fn apply_next_wft_from_history(&mut self) -> Result<usize> {
521
607
  // A much higher-up span (ex: poll) may want this field filled
522
608
  tracing::Span::current().record("run_id", &self.run_id.as_str());
523
609
 
@@ -525,15 +611,21 @@ impl WorkflowMachines {
525
611
  // then we don't need to do anything here, and in fact we need to avoid re-applying the
526
612
  // final WFT.
527
613
  if self.have_seen_terminal_event {
528
- return Ok(());
614
+ return Ok(0);
529
615
  }
530
616
 
531
617
  let last_handled_wft_started_id = self.current_started_event_id;
532
- let events = self
533
- .last_history_from_server
534
- .take_next_wft_sequence(last_handled_wft_started_id)
535
- .await
536
- .map_err(WFMachinesError::HistoryFetchingError)?;
618
+ let events = {
619
+ let mut evts = self
620
+ .last_history_from_server
621
+ .take_next_wft_sequence(last_handled_wft_started_id)
622
+ .await
623
+ .map_err(WFMachinesError::HistoryFetchingError)?;
624
+ // Do not re-process events we have already processed
625
+ evts.retain(|e| e.event_id > self.last_processed_event);
626
+ evts
627
+ };
628
+ let num_events_to_process = events.len();
537
629
 
538
630
  // We're caught up on reply if there are no new events to process
539
631
  // TODO: Probably this is unneeded if we evict whenever history is from non-sticky queue
@@ -564,31 +656,30 @@ impl WorkflowMachines {
564
656
 
565
657
  while let Some(event) = history.next() {
566
658
  let next_event = history.peek();
567
-
659
+ self.handle_event(event, next_event.is_some())?;
660
+ self.last_processed_event = event.event_id;
568
661
  if event.event_type == EventType::WorkflowTaskStarted as i32 && next_event.is_none() {
569
- self.handle_event(event, false)?;
570
662
  break;
571
663
  }
572
-
573
- self.handle_event(event, next_event.is_some())?;
574
664
  }
575
665
 
576
666
  // Scan through to the next WFT, searching for any patch markers, so that we can
577
667
  // pre-resolve them.
578
668
  for e in self.last_history_from_server.peek_next_wft_sequence() {
579
- if let Some((patch_id, deprecated)) = e.get_changed_marker_details() {
669
+ if let Some((patch_id, _)) = e.get_patch_marker_details() {
580
670
  self.encountered_change_markers.insert(
581
671
  patch_id.clone(),
582
672
  ChangeInfo {
583
- deprecated,
584
673
  created_command: false,
585
674
  },
586
675
  );
587
676
  // Found a patch marker
588
677
  self.drive_me
589
- .send_job(wf_activation_job::Variant::NotifyHasPatch(NotifyHasPatch {
590
- patch_id,
591
- }));
678
+ .send_job(workflow_activation_job::Variant::NotifyHasPatch(
679
+ NotifyHasPatch { patch_id },
680
+ ));
681
+ } else if e.is_local_activity_marker() {
682
+ self.local_activity_data.process_peekahead_marker(e)?;
592
683
  }
593
684
  }
594
685
 
@@ -596,7 +687,7 @@ impl WorkflowMachines {
596
687
  self.metrics.wf_task_replay_latency(replay_start.elapsed());
597
688
  }
598
689
 
599
- Ok(())
690
+ Ok(num_events_to_process)
600
691
  }
601
692
 
602
693
  /// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
@@ -622,10 +713,15 @@ impl WorkflowMachines {
622
713
  .machine(c.machine)
623
714
  .was_cancelled_before_sent_to_server()
624
715
  {
625
- let machine_responses = self
626
- .machine_mut(c.machine)
627
- .handle_command(c.command.command_type())?;
628
- self.process_machine_responses(c.machine, machine_responses)?;
716
+ match &c.command {
717
+ MachineAssociatedCommand::Real(cmd) => {
718
+ let machine_responses = self
719
+ .machine_mut(c.machine)
720
+ .handle_command(cmd.command_type())?;
721
+ self.process_machine_responses(c.machine, machine_responses)?;
722
+ }
723
+ MachineAssociatedCommand::FakeLocalActivityMarker(_) => {}
724
+ }
629
725
  self.commands.push_back(c);
630
726
  }
631
727
  }
@@ -637,10 +733,10 @@ impl WorkflowMachines {
637
733
  /// this function uses to drive sending jobs to lang, triggering new workflow tasks, etc.
638
734
  fn process_machine_responses(
639
735
  &mut self,
640
- sm: MachineKey,
736
+ smk: MachineKey,
641
737
  machine_responses: Vec<MachineResponse>,
642
738
  ) -> Result<()> {
643
- let sm = self.machine_mut(sm);
739
+ let sm = self.machine(smk);
644
740
  if !machine_responses.is_empty() {
645
741
  debug!(responses = %machine_responses.display(), machine_name = %sm.kind(),
646
742
  "Machine produced responses");
@@ -660,14 +756,40 @@ impl WorkflowMachines {
660
756
  // TODO: Should this also update self.run_id? Should we track orig/current
661
757
  // separately?
662
758
  self.drive_me
663
- .send_job(wf_activation_job::Variant::UpdateRandomSeed(
759
+ .send_job(workflow_activation_job::Variant::UpdateRandomSeed(
664
760
  UpdateRandomSeed {
665
761
  randomness_seed: str_to_randomness_seed(&new_run_id),
666
762
  },
667
763
  ));
668
764
  }
669
- MachineResponse::IssueNewCommand(_) => {
670
- panic!("Issue new command machine response not expected here")
765
+ MachineResponse::IssueNewCommand(c) => {
766
+ self.current_wf_task_commands.push_back(CommandAndMachine {
767
+ command: MachineAssociatedCommand::Real(Box::new(c)),
768
+ machine: smk,
769
+ })
770
+ }
771
+ MachineResponse::IssueFakeLocalActivityMarker(seq) => {
772
+ self.current_wf_task_commands.push_back(CommandAndMachine {
773
+ command: MachineAssociatedCommand::FakeLocalActivityMarker(seq),
774
+ machine: smk,
775
+ });
776
+ }
777
+ MachineResponse::QueueLocalActivity(act) => {
778
+ self.local_activity_data.enqueue(act);
779
+ }
780
+ MachineResponse::RequestCancelLocalActivity(_) => {
781
+ panic!(
782
+ "Request cancel local activity should not be returned from \
783
+ anything other than explicit cancellation"
784
+ )
785
+ }
786
+ MachineResponse::AbandonLocalActivity(seq) => {
787
+ self.local_activity_data.done_executing(seq);
788
+ }
789
+ MachineResponse::UpdateWFTime(t) => {
790
+ if let Some(t) = t {
791
+ self.set_current_time(t);
792
+ }
671
793
  }
672
794
  }
673
795
  }
@@ -683,30 +805,53 @@ impl WorkflowMachines {
683
805
  fn handle_driven_results(
684
806
  &mut self,
685
807
  results: Vec<WFCommand>,
686
- ) -> Result<Vec<wf_activation_job::Variant>> {
808
+ ) -> Result<Vec<workflow_activation_job::Variant>> {
687
809
  let mut jobs = vec![];
688
810
  for cmd in results {
689
811
  match cmd {
690
812
  WFCommand::AddTimer(attrs) => {
691
813
  let seq = attrs.seq;
692
- let timer = self.add_new_command_machine(new_timer(attrs));
693
- self.id_to_machine
694
- .insert(CommandID::Timer(seq), timer.machine);
695
- self.current_wf_task_commands.push_back(timer);
814
+ self.add_cmd_to_wf_task(new_timer(attrs), Some(CommandID::Timer(seq)));
696
815
  }
697
816
  WFCommand::CancelTimer(attrs) => {
698
817
  jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?);
699
818
  }
700
819
  WFCommand::AddActivity(attrs) => {
701
820
  let seq = attrs.seq;
702
- let activity = self.add_new_command_machine(new_activity(attrs));
821
+ self.add_cmd_to_wf_task(new_activity(attrs), Some(CommandID::Activity(seq)));
822
+ }
823
+ WFCommand::AddLocalActivity(attrs) => {
824
+ let seq = attrs.seq;
825
+ let attrs: ValidScheduleLA = ValidScheduleLA::from_schedule_la(
826
+ attrs,
827
+ self.started_attrs()
828
+ .as_ref()
829
+ .map(|x| x.workflow_execution_timeout.clone().try_into_or_none())
830
+ .flatten(),
831
+ )
832
+ .map_err(|e| {
833
+ WFMachinesError::Fatal(format!(
834
+ "Invalid schedule local activity request (seq {}): {}",
835
+ seq, e
836
+ ))
837
+ })?;
838
+ let (la, mach_resp) = new_local_activity(
839
+ attrs,
840
+ self.replaying,
841
+ self.local_activity_data.take_preresolution(seq),
842
+ self.current_wf_time,
843
+ )?;
844
+ let machkey = self.all_machines.insert(la.into());
703
845
  self.id_to_machine
704
- .insert(CommandID::Activity(seq), activity.machine);
705
- self.current_wf_task_commands.push_back(activity);
846
+ .insert(CommandID::LocalActivity(seq), machkey);
847
+ self.process_machine_responses(machkey, mach_resp)?;
706
848
  }
707
849
  WFCommand::RequestCancelActivity(attrs) => {
708
850
  jobs.extend(self.process_cancellation(CommandID::Activity(attrs.seq))?);
709
851
  }
852
+ WFCommand::RequestCancelLocalActivity(attrs) => {
853
+ jobs.extend(self.process_cancellation(CommandID::LocalActivity(attrs.seq))?);
854
+ }
710
855
  WFCommand::CompleteWorkflow(attrs) => {
711
856
  self.metrics.wf_completed();
712
857
  self.add_terminal_command(complete_workflow(attrs));
@@ -727,15 +872,12 @@ impl WorkflowMachines {
727
872
  // Do not create commands for change IDs that we have already created commands
728
873
  // for.
729
874
  if !matches!(self.encountered_change_markers.get(&attrs.patch_id),
730
- Some(ChangeInfo {created_command, ..})
731
- if *created_command)
875
+ Some(ChangeInfo {created_command}) if *created_command)
732
876
  {
733
- let verm = self.add_new_command_machine(has_change(
734
- attrs.patch_id.clone(),
735
- self.replaying,
736
- attrs.deprecated,
737
- ));
738
- self.current_wf_task_commands.push_back(verm);
877
+ self.add_cmd_to_wf_task(
878
+ has_change(attrs.patch_id.clone(), self.replaying, attrs.deprecated),
879
+ None,
880
+ );
739
881
 
740
882
  if let Some(ci) = self.encountered_change_markers.get_mut(&attrs.patch_id) {
741
883
  ci.created_command = true;
@@ -743,7 +885,6 @@ impl WorkflowMachines {
743
885
  self.encountered_change_markers.insert(
744
886
  attrs.patch_id,
745
887
  ChangeInfo {
746
- deprecated: attrs.deprecated,
747
888
  created_command: true,
748
889
  },
749
890
  );
@@ -752,10 +893,10 @@ impl WorkflowMachines {
752
893
  }
753
894
  WFCommand::AddChildWorkflow(attrs) => {
754
895
  let seq = attrs.seq;
755
- let child_workflow = self.add_new_command_machine(new_child_workflow(attrs));
756
- self.id_to_machine
757
- .insert(CommandID::ChildWorkflowStart(seq), child_workflow.machine);
758
- self.current_wf_task_commands.push_back(child_workflow);
896
+ self.add_cmd_to_wf_task(
897
+ new_child_workflow(attrs),
898
+ Some(CommandID::ChildWorkflowStart(seq)),
899
+ );
759
900
  }
760
901
  WFCommand::CancelUnstartedChild(attrs) => jobs.extend(self.process_cancellation(
761
902
  CommandID::ChildWorkflowStart(attrs.child_workflow_seq),
@@ -778,11 +919,10 @@ impl WorkflowMachines {
778
919
  ),
779
920
  Some(cancel_we::Target::WorkflowExecution(we)) => (we, false),
780
921
  };
781
- let mach = self
782
- .add_new_command_machine(new_external_cancel(attrs.seq, we, only_child));
783
- self.id_to_machine
784
- .insert(CommandID::CancelExternal(attrs.seq), mach.machine);
785
- self.current_wf_task_commands.push_back(mach);
922
+ self.add_cmd_to_wf_task(
923
+ new_external_cancel(attrs.seq, we, only_child),
924
+ Some(CommandID::CancelExternal(attrs.seq)),
925
+ );
786
926
  }
787
927
  WFCommand::SignalExternalWorkflow(attrs) => {
788
928
  let (we, only_child) = match attrs.target {
@@ -803,16 +943,16 @@ impl WorkflowMachines {
803
943
  Some(sig_we::Target::WorkflowExecution(we)) => (we, false),
804
944
  };
805
945
 
806
- let sigm = self.add_new_command_machine(new_external_signal(
807
- attrs.seq,
808
- we,
809
- attrs.signal_name,
810
- attrs.args,
811
- only_child,
812
- ));
813
- self.id_to_machine
814
- .insert(CommandID::SignalExternal(attrs.seq), sigm.machine);
815
- self.current_wf_task_commands.push_back(sigm);
946
+ self.add_cmd_to_wf_task(
947
+ new_external_signal(
948
+ attrs.seq,
949
+ we,
950
+ attrs.signal_name,
951
+ attrs.args,
952
+ only_child,
953
+ ),
954
+ Some(CommandID::SignalExternal(attrs.seq)),
955
+ );
816
956
  }
817
957
  WFCommand::CancelSignalWorkflow(attrs) => {
818
958
  jobs.extend(self.process_cancellation(CommandID::SignalExternal(attrs.seq))?);
@@ -832,19 +972,62 @@ impl WorkflowMachines {
832
972
  fn process_cancellation(&mut self, id: CommandID) -> Result<Vec<Variant>> {
833
973
  let mut jobs = vec![];
834
974
  let m_key = self.get_machine_key(id)?;
835
- let res = self.machine_mut(m_key).cancel()?;
836
- debug!(machine_responses = ?res, cmd_id = ?id, "Cancel request responses");
837
- for r in res {
975
+ let machine_resps = self.machine_mut(m_key).cancel()?;
976
+ debug!(machine_responses = ?machine_resps, cmd_id = ?id, "Cancel request responses");
977
+ for r in machine_resps {
838
978
  match r {
839
979
  MachineResponse::IssueNewCommand(c) => {
840
980
  self.current_wf_task_commands.push_back(CommandAndMachine {
841
- command: c,
981
+ command: MachineAssociatedCommand::Real(Box::new(c)),
842
982
  machine: m_key,
843
983
  });
844
984
  }
845
985
  MachineResponse::PushWFJob(j) => {
846
986
  jobs.push(j);
847
987
  }
988
+ MachineResponse::RequestCancelLocalActivity(seq) => {
989
+ // We might already know about the status from a pre-resolution. Apply it if so.
990
+ // We need to do this because otherwise we might need to perform additional
991
+ // activations during replay that didn't happen during execution, just like
992
+ // we sometimes pre-resolve activities when first requested.
993
+ if let Some(preres) = self.local_activity_data.take_preresolution(seq) {
994
+ if let Machines::LocalActivityMachine(lam) = self.machine_mut(m_key) {
995
+ let more_responses = lam.try_resolve_with_dat(preres)?;
996
+ self.process_machine_responses(m_key, more_responses)?;
997
+ } else {
998
+ panic!("A non local-activity machine returned a request cancel LA response");
999
+ }
1000
+ }
1001
+ // If it's in the request queue, just rip it out.
1002
+ else if let Some(removed_act) =
1003
+ self.local_activity_data.remove_from_queue(seq)
1004
+ {
1005
+ // We removed it. Notify the machine that the activity cancelled.
1006
+ if let Machines::LocalActivityMachine(lam) = self.machine_mut(m_key) {
1007
+ let more_responses = lam.try_resolve(
1008
+ LocalActivityExecutionResult::empty_cancel(),
1009
+ Duration::from_secs(0),
1010
+ removed_act.attempt,
1011
+ None,
1012
+ None,
1013
+ )?;
1014
+ self.process_machine_responses(m_key, more_responses)?;
1015
+ } else {
1016
+ panic!("A non local-activity machine returned a request cancel LA response");
1017
+ }
1018
+ } else {
1019
+ // Finally, if we know about the LA at all, it's currently running, so
1020
+ // queue the cancel request to be given to the LA manager.
1021
+ self.local_activity_data.enqueue_cancel(ExecutingLAId {
1022
+ run_id: self.run_id.clone(),
1023
+ seq_num: seq,
1024
+ });
1025
+ }
1026
+ }
1027
+ MachineResponse::AbandonLocalActivity(seq) => {
1028
+ self.local_activity_data.done_executing(seq);
1029
+ }
1030
+ MachineResponse::UpdateWFTime(None) => {}
848
1031
  v => {
849
1032
  return Err(WFMachinesError::Fatal(format!(
850
1033
  "Unexpected machine response {:?} when cancelling {:?}",
@@ -862,34 +1045,37 @@ impl WorkflowMachines {
862
1045
  })?)
863
1046
  }
864
1047
 
865
- fn add_terminal_command<T: TemporalStateMachine + 'static>(
866
- &mut self,
867
- machine: NewMachineWithCommand<T>,
868
- ) {
1048
+ fn add_terminal_command(&mut self, machine: NewMachineWithCommand) {
869
1049
  let cwfm = self.add_new_command_machine(machine);
870
1050
  self.workflow_end_time = Some(SystemTime::now());
871
1051
  self.current_wf_task_commands.push_back(cwfm);
872
1052
  }
873
1053
 
874
- fn add_new_command_machine<T: TemporalStateMachine + 'static>(
875
- &mut self,
876
- machine: NewMachineWithCommand<T>,
877
- ) -> CommandAndMachine {
878
- let k = self.all_machines.insert(Box::new(machine.machine));
1054
+ /// Add a new command/machines for that command to the current workflow task
1055
+ fn add_cmd_to_wf_task(&mut self, machine: NewMachineWithCommand, id: Option<CommandID>) {
1056
+ let mach = self.add_new_command_machine(machine);
1057
+ if let Some(id) = id {
1058
+ self.id_to_machine.insert(id, mach.machine);
1059
+ }
1060
+ self.current_wf_task_commands.push_back(mach);
1061
+ }
1062
+
1063
+ fn add_new_command_machine(&mut self, machine: NewMachineWithCommand) -> CommandAndMachine {
1064
+ let k = self.all_machines.insert(machine.machine);
879
1065
  CommandAndMachine {
880
- command: machine.command,
1066
+ command: MachineAssociatedCommand::Real(Box::new(machine.command)),
881
1067
  machine: k,
882
1068
  }
883
1069
  }
884
1070
 
885
- fn machine(&self, m: MachineKey) -> &dyn TemporalStateMachine {
1071
+ fn machine(&self, m: MachineKey) -> &Machines {
886
1072
  self.all_machines
887
1073
  .get(m)
888
1074
  .expect("Machine must exist")
889
1075
  .borrow()
890
1076
  }
891
1077
 
892
- fn machine_mut(&mut self, m: MachineKey) -> &mut (dyn TemporalStateMachine + 'static) {
1078
+ fn machine_mut(&mut self, m: MachineKey) -> &mut Machines {
893
1079
  self.all_machines
894
1080
  .get_mut(m)
895
1081
  .expect("Machine must exist")
@@ -917,23 +1103,23 @@ fn change_marker_handling(
917
1103
  ) -> Result<ChangeMarkerOutcome> {
918
1104
  if !mach.matches_event(event) {
919
1105
  // Version markers can be skipped in the event they are deprecated
920
- if let Some(changed_info) = event.get_changed_marker_details() {
1106
+ if let Some((patch_name, deprecated)) = event.get_patch_marker_details() {
921
1107
  // Is deprecated. We can simply ignore this event, as deprecated change
922
1108
  // markers are allowed without matching changed calls.
923
- if changed_info.1 {
1109
+ if deprecated {
924
1110
  debug!("Deprecated patch marker tried against wrong machine, skipping.");
925
1111
  return Ok(ChangeMarkerOutcome::SkipEvent);
926
1112
  }
927
1113
  return Err(WFMachinesError::Nondeterminism(format!(
928
1114
  "Non-deprecated patch marker encountered for change {}, \
929
1115
  but there is no corresponding change command!",
930
- changed_info.0
1116
+ patch_name
931
1117
  )));
932
1118
  }
933
1119
  // Version machines themselves may also not *have* matching markers, where non-deprecated
934
1120
  // calls take the old path, and deprecated calls assume history is produced by a new-code
935
1121
  // worker.
936
- if mach.kind() == MachineKind::Version {
1122
+ if mach.kind() == MachineKind::Patch {
937
1123
  debug!("Skipping non-matching event against version machine");
938
1124
  return Ok(ChangeMarkerOutcome::SkipCommand);
939
1125
  }