@temporalio/core-bridge 0.19.2 → 0.20.2
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.
- package/Cargo.lock +90 -157
- package/Cargo.toml +1 -0
- package/index.d.ts +11 -27
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.cargo/config.toml +1 -0
- package/sdk-core/CODEOWNERS +1 -1
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
- package/sdk-core/bridge-ffi/src/lib.rs +311 -315
- package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
- package/sdk-core/client/Cargo.toml +13 -9
- package/sdk-core/client/LICENSE.txt +23 -0
- package/sdk-core/client/src/lib.rs +286 -174
- package/sdk-core/client/src/metrics.rs +86 -12
- package/sdk-core/client/src/raw.rs +566 -0
- package/sdk-core/client/src/retry.rs +137 -99
- package/sdk-core/core/Cargo.toml +15 -10
- package/sdk-core/core/LICENSE.txt +23 -0
- package/sdk-core/core/benches/workflow_replay.rs +79 -0
- package/sdk-core/core/src/abstractions.rs +38 -0
- package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
- package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
- package/sdk-core/core/src/core_tests/determinism.rs +24 -12
- package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
- package/sdk-core/core/src/core_tests/mod.rs +30 -43
- package/sdk-core/core/src/core_tests/queries.rs +82 -81
- package/sdk-core/core/src/core_tests/workers.rs +111 -296
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
- package/sdk-core/core/src/lib.rs +73 -318
- package/sdk-core/core/src/pollers/mod.rs +4 -6
- package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
- package/sdk-core/core/src/protosext/mod.rs +7 -10
- package/sdk-core/core/src/replay/mod.rs +11 -150
- package/sdk-core/core/src/telemetry/metrics.rs +35 -2
- package/sdk-core/core/src/telemetry/mod.rs +49 -16
- package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
- package/sdk-core/core/src/test_help/mod.rs +104 -170
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
- package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
- package/sdk-core/core/src/worker/activities.rs +23 -16
- package/sdk-core/core/src/worker/client/mocks.rs +86 -0
- package/sdk-core/core/src/worker/client.rs +209 -0
- package/sdk-core/core/src/worker/mod.rs +207 -108
- package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
- package/sdk-core/core/src/workflow/history_update.rs +107 -24
- package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
- package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
- package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
- package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
- package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
- package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
- package/sdk-core/core/src/workflow/mod.rs +13 -1
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
- package/sdk-core/core-api/Cargo.toml +9 -1
- package/sdk-core/core-api/LICENSE.txt +23 -0
- package/sdk-core/core-api/src/errors.rs +7 -38
- package/sdk-core/core-api/src/lib.rs +44 -52
- package/sdk-core/core-api/src/worker.rs +10 -2
- package/sdk-core/etc/deps.svg +127 -96
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
- package/sdk-core/sdk/Cargo.toml +16 -2
- package/sdk-core/sdk/LICENSE.txt +23 -0
- package/sdk-core/sdk/src/interceptors.rs +11 -0
- package/sdk-core/sdk/src/lib.rs +139 -151
- package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
- package/sdk-core/sdk/src/workflow_context.rs +36 -17
- package/sdk-core/sdk/src/workflow_future.rs +19 -25
- package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
- package/sdk-core/sdk-core-protos/build.rs +1 -0
- package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
- package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
- package/sdk-core/test-utils/Cargo.toml +3 -1
- package/sdk-core/test-utils/src/canned_histories.rs +27 -0
- package/sdk-core/test-utils/src/histfetch.rs +3 -3
- package/sdk-core/test-utils/src/lib.rs +223 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
- package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
- package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
- package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
- package/sdk-core/tests/load_tests.rs +9 -6
- package/sdk-core/tests/main.rs +43 -10
- package/src/conversions.rs +7 -12
- package/src/lib.rs +322 -357
- package/sdk-core/client/src/mocks.rs +0 -167
- package/sdk-core/core/src/worker/dispatcher.rs +0 -171
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
|
@@ -1,40 +1,248 @@
|
|
|
1
|
+
use super::{
|
|
2
|
+
workflow_machines::{MachineResponse, WFMachinesError},
|
|
3
|
+
NewMachineWithCommand,
|
|
4
|
+
};
|
|
5
|
+
use crate::workflow::machines::{Cancellable, EventInfo, MachineKind, WFMachinesAdapter};
|
|
1
6
|
use rustfsm::{fsm, TransitionResult};
|
|
7
|
+
use temporal_sdk_core_protos::{
|
|
8
|
+
coresdk::workflow_commands::UpsertWorkflowSearchAttributes,
|
|
9
|
+
temporal::api::{
|
|
10
|
+
command::v1::Command,
|
|
11
|
+
enums::v1::{CommandType, EventType},
|
|
12
|
+
history::v1::HistoryEvent,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
2
15
|
|
|
3
16
|
fsm! {
|
|
4
|
-
pub(super) name UpsertSearchAttributesMachine;
|
|
17
|
+
pub(super) name UpsertSearchAttributesMachine;
|
|
18
|
+
command UpsertSearchAttributesMachineCommand;
|
|
19
|
+
error WFMachinesError;
|
|
20
|
+
shared_state SharedState;
|
|
5
21
|
|
|
6
|
-
Created
|
|
22
|
+
// Machine is instantiated into the Created state and then transitions into the CommandIssued
|
|
23
|
+
// state when it receives the CommandScheduled event that is a result of looping back the
|
|
24
|
+
// Command initially packaged with NewMachineWithCommand (see upsert_search_attrs)
|
|
25
|
+
Created --(CommandScheduled) --> CommandIssued;
|
|
7
26
|
|
|
8
|
-
|
|
9
|
-
|
|
27
|
+
// Having sent the command to the server, the machine transitions into a terminal state (Done)
|
|
28
|
+
// upon observing a history event indicating that the command has been recorded. Note that this
|
|
29
|
+
// does not imply that the command has been _executed_, only that it _will be_ executed at some
|
|
30
|
+
// point in the future.
|
|
31
|
+
CommandIssued --(CommandRecorded) --> Done;
|
|
10
32
|
}
|
|
11
33
|
|
|
12
|
-
|
|
13
|
-
|
|
34
|
+
/// Instantiates an UpsertSearchAttributesMachine and packs it together with an initial command
|
|
35
|
+
/// to apply the provided search attribute update.
|
|
36
|
+
pub(super) fn upsert_search_attrs(
|
|
37
|
+
attribs: UpsertWorkflowSearchAttributes,
|
|
38
|
+
) -> NewMachineWithCommand {
|
|
39
|
+
let sm = UpsertSearchAttributesMachine::new();
|
|
40
|
+
let cmd = Command {
|
|
41
|
+
command_type: CommandType::UpsertWorkflowSearchAttributes as i32,
|
|
42
|
+
attributes: Some(attribs.into()),
|
|
43
|
+
};
|
|
44
|
+
NewMachineWithCommand {
|
|
45
|
+
command: cmd,
|
|
46
|
+
machine: sm.into(),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Unused but must exist
|
|
51
|
+
type SharedState = ();
|
|
14
52
|
|
|
15
|
-
|
|
53
|
+
/// The state-machine-specific set of commands that are the results of state transition in the
|
|
54
|
+
/// UpsertSearchAttributesMachine. There are none of these because this state machine emits the
|
|
55
|
+
/// UpsertSearchAttributes API command during construction and then does not emit any subsequent
|
|
56
|
+
/// state-machine specific commands.
|
|
57
|
+
#[derive(Debug, derive_more::Display)]
|
|
58
|
+
pub(super) enum UpsertSearchAttributesMachineCommand {}
|
|
16
59
|
|
|
17
|
-
|
|
60
|
+
/// The state of the UpsertSearchAttributesMachine at time zero (i.e. at instantiation)
|
|
61
|
+
#[derive(Debug, Default, Clone, derive_more::Display)]
|
|
18
62
|
pub(super) struct Created {}
|
|
19
63
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
64
|
+
/// Once the UpsertSearchAttributesMachine has been known to have issued the upsert command to
|
|
65
|
+
/// higher-level machinery, it transitions into this state.
|
|
66
|
+
#[derive(Debug, Default, Clone, derive_more::Display)]
|
|
67
|
+
pub(super) struct CommandIssued {}
|
|
68
|
+
|
|
69
|
+
/// Once the server has recorded its receipt of the search attribute update, the
|
|
70
|
+
/// UpsertSearchAttributesMachine transitions into this terminal state.
|
|
71
|
+
#[derive(Debug, Default, Clone, derive_more::Display)]
|
|
72
|
+
pub(super) struct Done {}
|
|
73
|
+
|
|
74
|
+
impl UpsertSearchAttributesMachine {
|
|
75
|
+
fn new() -> Self {
|
|
76
|
+
Self {
|
|
77
|
+
state: Created {}.into(),
|
|
78
|
+
shared_state: (),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
impl WFMachinesAdapter for UpsertSearchAttributesMachine {
|
|
84
|
+
/// Transforms an UpsertSearchAttributesMachine-specific command (i.e. an instance of the type
|
|
85
|
+
/// UpsertSearchAttributesMachineCommand) to a more generic form supported by the abstract
|
|
86
|
+
/// StateMachine type.
|
|
87
|
+
fn adapt_response(
|
|
88
|
+
&self,
|
|
89
|
+
_my_command: Self::Command,
|
|
90
|
+
_event_info: Option<EventInfo>,
|
|
91
|
+
) -> Result<Vec<MachineResponse>, Self::Error> {
|
|
92
|
+
// No implementation needed until this state machine emits state machine commands
|
|
93
|
+
Err(Self::Error::Nondeterminism(
|
|
94
|
+
"UpsertWorkflowSearchAttributesMachine does not use commands".to_string(),
|
|
95
|
+
))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Filters for EventType::UpsertWorkflowSearchAttributes
|
|
99
|
+
fn matches_event(&self, event: &HistoryEvent) -> bool {
|
|
100
|
+
matches!(
|
|
101
|
+
event.event_type(),
|
|
102
|
+
EventType::UpsertWorkflowSearchAttributes
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn kind(&self) -> MachineKind {
|
|
107
|
+
MachineKind::UpsertSearchAttributes
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
impl Cancellable for UpsertSearchAttributesMachine {}
|
|
112
|
+
|
|
113
|
+
// Converts the generic history event with type EventType::UpsertWorkflowSearchAttributes into the
|
|
114
|
+
// UpsertSearchAttributesMachine-specific event type
|
|
115
|
+
// UpsertSearchAttributesMachineEvents::CommandRecorded.
|
|
116
|
+
impl TryFrom<HistoryEvent> for UpsertSearchAttributesMachineEvents {
|
|
117
|
+
type Error = WFMachinesError;
|
|
118
|
+
|
|
119
|
+
fn try_from(e: HistoryEvent) -> Result<Self, Self::Error> {
|
|
120
|
+
match e.event_type() {
|
|
121
|
+
EventType::UpsertWorkflowSearchAttributes => {
|
|
122
|
+
Ok(UpsertSearchAttributesMachineEvents::CommandRecorded)
|
|
123
|
+
}
|
|
124
|
+
_ => Err(Self::Error::Nondeterminism(format!(
|
|
125
|
+
"UpsertWorkflowSearchAttributesMachine does not handle {e}"
|
|
126
|
+
))),
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Converts generic state machine command type CommandType::UpsertWorkflowSearchAttributes into
|
|
132
|
+
// the UpsertSearchAttributesMachine-specific event
|
|
133
|
+
impl TryFrom<CommandType> for UpsertSearchAttributesMachineEvents {
|
|
134
|
+
type Error = WFMachinesError;
|
|
135
|
+
|
|
136
|
+
fn try_from(c: CommandType) -> Result<Self, Self::Error> {
|
|
137
|
+
match c {
|
|
138
|
+
CommandType::UpsertWorkflowSearchAttributes => {
|
|
139
|
+
Ok(UpsertSearchAttributesMachineEvents::CommandScheduled)
|
|
140
|
+
}
|
|
141
|
+
_ => Err(Self::Error::Nondeterminism(format!(
|
|
142
|
+
"UpsertWorkflowSearchAttributesMachine does not handle command type {c:?}"
|
|
143
|
+
))),
|
|
144
|
+
}
|
|
25
145
|
}
|
|
26
146
|
}
|
|
27
147
|
|
|
28
|
-
|
|
29
|
-
|
|
148
|
+
// There is no Command/Response associated with this transition
|
|
149
|
+
impl From<CommandIssued> for Done {
|
|
150
|
+
fn from(_: CommandIssued) -> Self {
|
|
151
|
+
Self {}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
30
154
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
unimplemented!()
|
|
155
|
+
// There is no Command/Response associated with this transition
|
|
156
|
+
impl From<Created> for CommandIssued {
|
|
157
|
+
fn from(_: Created) -> Self {
|
|
158
|
+
Self {}
|
|
36
159
|
}
|
|
37
160
|
}
|
|
38
161
|
|
|
39
|
-
#[
|
|
40
|
-
|
|
162
|
+
#[cfg(test)]
|
|
163
|
+
mod tests {
|
|
164
|
+
use rustfsm::StateMachine;
|
|
165
|
+
use temporal_sdk::{WfContext, WorkflowFunction};
|
|
166
|
+
use temporal_sdk_core_protos::{
|
|
167
|
+
coresdk::common::Payload, temporal::api::command::v1::command::Attributes,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
use crate::{replay::TestHistoryBuilder, workflow::managed_wf::ManagedWFFunc};
|
|
171
|
+
|
|
172
|
+
use super::{super::OnEventWrapper, *};
|
|
173
|
+
|
|
174
|
+
#[tokio::test]
|
|
175
|
+
async fn upsert_search_attrs_from_workflow() {
|
|
176
|
+
let mut t = TestHistoryBuilder::default();
|
|
177
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
178
|
+
t.add_full_wf_task();
|
|
179
|
+
t.add_workflow_execution_completed();
|
|
180
|
+
|
|
181
|
+
let (k1, k2) = ("foo", "bar");
|
|
182
|
+
|
|
183
|
+
let wff = WorkflowFunction::new(move |ctx: WfContext| async move {
|
|
184
|
+
ctx.upsert_search_attributes([
|
|
185
|
+
(
|
|
186
|
+
String::from(k1),
|
|
187
|
+
Payload {
|
|
188
|
+
data: vec![0x01],
|
|
189
|
+
..Default::default()
|
|
190
|
+
},
|
|
191
|
+
),
|
|
192
|
+
(
|
|
193
|
+
String::from(k2),
|
|
194
|
+
Payload {
|
|
195
|
+
data: vec![0x02],
|
|
196
|
+
..Default::default()
|
|
197
|
+
},
|
|
198
|
+
),
|
|
199
|
+
]);
|
|
200
|
+
Ok(().into())
|
|
201
|
+
});
|
|
202
|
+
let mut wfm = ManagedWFFunc::new(t, wff, vec![]);
|
|
203
|
+
|
|
204
|
+
wfm.get_next_activation().await.unwrap();
|
|
205
|
+
let commands = wfm.get_server_commands().commands;
|
|
206
|
+
assert!(!commands.is_empty());
|
|
207
|
+
let cmd = commands[0].clone();
|
|
208
|
+
assert_eq!(
|
|
209
|
+
cmd.command_type,
|
|
210
|
+
CommandType::UpsertWorkflowSearchAttributes as i32
|
|
211
|
+
);
|
|
212
|
+
assert_matches!(
|
|
213
|
+
cmd.attributes.unwrap(),
|
|
214
|
+
Attributes::UpsertWorkflowSearchAttributesCommandAttributes(msg) => {
|
|
215
|
+
let fields = &msg.search_attributes.unwrap().indexed_fields;
|
|
216
|
+
let payload1 = fields.get(k1).unwrap();
|
|
217
|
+
let payload2 = fields.get(k2).unwrap();
|
|
218
|
+
assert_eq!(payload1.data[0], 0x01);
|
|
219
|
+
assert_eq!(payload2.data[0], 0x02);
|
|
220
|
+
assert_eq!(fields.len(), 2);
|
|
221
|
+
});
|
|
222
|
+
wfm.shutdown().await.unwrap();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#[tokio::test]
|
|
226
|
+
async fn upsert_search_attrs_sm() {
|
|
227
|
+
let mut sm = UpsertSearchAttributesMachine::new();
|
|
228
|
+
assert_eq!(Created {}.to_string(), sm.state().to_string());
|
|
229
|
+
|
|
230
|
+
let cmd_scheduled_sm_event = CommandType::UpsertWorkflowSearchAttributes
|
|
231
|
+
.try_into()
|
|
232
|
+
.unwrap();
|
|
233
|
+
let recorded_history_event = HistoryEvent {
|
|
234
|
+
event_type: EventType::UpsertWorkflowSearchAttributes as i32,
|
|
235
|
+
..Default::default()
|
|
236
|
+
};
|
|
237
|
+
assert!(sm.matches_event(&recorded_history_event));
|
|
238
|
+
let cmd_recorded_sm_event = recorded_history_event.try_into().unwrap();
|
|
239
|
+
|
|
240
|
+
OnEventWrapper::on_event_mut(&mut sm, cmd_scheduled_sm_event)
|
|
241
|
+
.expect("CommandScheduled should transition Created -> CommandIssued");
|
|
242
|
+
assert_eq!(CommandIssued {}.to_string(), sm.state().to_string());
|
|
243
|
+
|
|
244
|
+
OnEventWrapper::on_event_mut(&mut sm, cmd_recorded_sm_event)
|
|
245
|
+
.expect("CommandRecorded should transition CommandIssued -> Done");
|
|
246
|
+
assert_eq!(Done {}.to_string(), sm.state().to_string());
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -10,7 +10,8 @@ use super::{
|
|
|
10
10
|
continue_as_new_workflow_state_machine::continue_as_new,
|
|
11
11
|
fail_workflow_state_machine::fail_workflow, local_activity_state_machine::new_local_activity,
|
|
12
12
|
patch_state_machine::has_change, signal_external_state_machine::new_external_signal,
|
|
13
|
-
timer_state_machine::new_timer,
|
|
13
|
+
timer_state_machine::new_timer, upsert_search_attributes_state_machine::upsert_search_attrs,
|
|
14
|
+
workflow_machines::local_acts::LocalActivityData,
|
|
14
15
|
workflow_task_state_machine::WorkflowTaskMachine, MachineKind, Machines, NewMachineWithCommand,
|
|
15
16
|
TemporalStateMachine,
|
|
16
17
|
};
|
|
@@ -22,36 +23,32 @@ use crate::{
|
|
|
22
23
|
},
|
|
23
24
|
workflow::{
|
|
24
25
|
CommandID, DrivenWorkflow, HistoryUpdate, LocalResolution, WFCommand, WorkflowFetcher,
|
|
26
|
+
WorkflowStartedInfo,
|
|
25
27
|
},
|
|
26
28
|
};
|
|
29
|
+
use siphasher::sip::SipHasher13;
|
|
27
30
|
use slotmap::SlotMap;
|
|
28
31
|
use std::{
|
|
29
32
|
borrow::{Borrow, BorrowMut},
|
|
30
|
-
collections::{
|
|
33
|
+
collections::{HashMap, VecDeque},
|
|
31
34
|
convert::TryInto,
|
|
32
35
|
hash::{Hash, Hasher},
|
|
33
36
|
time::{Duration, Instant, SystemTime},
|
|
34
37
|
};
|
|
35
38
|
use temporal_sdk_core_protos::{
|
|
36
39
|
coresdk::{
|
|
37
|
-
common::
|
|
40
|
+
common::NamespacedWorkflowExecution,
|
|
38
41
|
workflow_activation::{
|
|
39
42
|
workflow_activation_job::{self, Variant},
|
|
40
|
-
NotifyHasPatch,
|
|
43
|
+
NotifyHasPatch, UpdateRandomSeed, WorkflowActivation,
|
|
41
44
|
},
|
|
42
|
-
workflow_commands::
|
|
43
|
-
request_cancel_external_workflow_execution as cancel_we,
|
|
44
|
-
signal_external_workflow_execution as sig_we,
|
|
45
|
-
},
|
|
46
|
-
FromPayloadsExt,
|
|
45
|
+
workflow_commands::request_cancel_external_workflow_execution as cancel_we,
|
|
47
46
|
},
|
|
48
47
|
temporal::api::{
|
|
49
48
|
command::v1::Command as ProtoCommand,
|
|
50
|
-
common::v1::Header,
|
|
51
49
|
enums::v1::EventType,
|
|
52
|
-
history::v1::{history_event, HistoryEvent
|
|
50
|
+
history::v1::{history_event, HistoryEvent},
|
|
53
51
|
},
|
|
54
|
-
utilities::TryIntoOrNone,
|
|
55
52
|
};
|
|
56
53
|
|
|
57
54
|
type Result<T, E = WFMachinesError> = std::result::Result<T, E>;
|
|
@@ -296,17 +293,21 @@ impl WorkflowMachines {
|
|
|
296
293
|
self.local_activity_data.outstanding_la_count()
|
|
297
294
|
}
|
|
298
295
|
|
|
299
|
-
/// Returns
|
|
300
|
-
pub(crate) fn
|
|
301
|
-
self.drive_me.
|
|
296
|
+
/// Returns start info for the workflow if it has started
|
|
297
|
+
pub(crate) fn get_started_info(&self) -> Option<&WorkflowStartedInfo> {
|
|
298
|
+
self.drive_me.get_started_info()
|
|
302
299
|
}
|
|
303
300
|
|
|
304
301
|
/// Handle a single event from the workflow history. `has_next_event` should be false if `event`
|
|
305
302
|
/// is the last event in the history.
|
|
306
303
|
///
|
|
307
|
-
///
|
|
304
|
+
/// This function will attempt to apply the event to the workflow state machines. If there is
|
|
305
|
+
/// not a matching machine for the event, a nondeterminism error is returned. Otherwise, the
|
|
306
|
+
/// event is applied to the machine, which may also return a nondeterminism error if the machine
|
|
307
|
+
/// does not match the expected type. A fatal error may be returned if the machine is in an
|
|
308
|
+
/// invalid state.
|
|
308
309
|
#[instrument(level = "debug", skip(self, event), fields(event=%event))]
|
|
309
|
-
fn handle_event(&mut self, event:
|
|
310
|
+
fn handle_event(&mut self, event: HistoryEvent, has_next_event: bool) -> Result<()> {
|
|
310
311
|
if event.is_final_wf_execution_event() {
|
|
311
312
|
self.have_seen_terminal_event = true;
|
|
312
313
|
}
|
|
@@ -329,20 +330,20 @@ impl WorkflowMachines {
|
|
|
329
330
|
// We remove the machine while we it handles events, then return it, to avoid
|
|
330
331
|
// borrowing from ourself mutably.
|
|
331
332
|
let maybe_machine = self.machines_by_event_id.remove(&initial_cmd_id);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
333
|
+
match maybe_machine {
|
|
334
|
+
Some(sm) => {
|
|
335
|
+
self.submachine_handle_event(sm, event, has_next_event)?;
|
|
336
|
+
// Restore machine if not in it's final state
|
|
337
|
+
if !self.machine(sm).is_final_state() {
|
|
338
|
+
self.machines_by_event_id.insert(initial_cmd_id, sm);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
None => {
|
|
342
|
+
return Err(WFMachinesError::Nondeterminism(format!(
|
|
343
|
+
"During event handling, this event had an initial command ID but we \
|
|
344
|
+
could not find a matching command for it: {:?}",
|
|
345
|
+
event
|
|
346
|
+
)));
|
|
346
347
|
}
|
|
347
348
|
}
|
|
348
349
|
}
|
|
@@ -385,7 +386,7 @@ impl WorkflowMachines {
|
|
|
385
386
|
/// The handling consists of verifying that the next command in the commands queue is associated
|
|
386
387
|
/// with a state machine, which is then notified about the event and the command is removed from
|
|
387
388
|
/// the commands queue.
|
|
388
|
-
fn handle_command_event(&mut self, event:
|
|
389
|
+
fn handle_command_event(&mut self, event: HistoryEvent) -> Result<()> {
|
|
389
390
|
if event.is_local_activity_marker() {
|
|
390
391
|
let deets = event.extract_local_activity_marker_data().ok_or_else(|| {
|
|
391
392
|
WFMachinesError::Fatal(format!("Local activity marker was unparsable: {:?}", event))
|
|
@@ -406,10 +407,12 @@ impl WorkflowMachines {
|
|
|
406
407
|
}
|
|
407
408
|
}
|
|
408
409
|
|
|
410
|
+
let event_id = event.event_id;
|
|
411
|
+
|
|
409
412
|
let consumed_cmd = loop {
|
|
410
413
|
if let Some(peek_machine) = self.commands.front() {
|
|
411
414
|
let mach = self.machine(peek_machine.machine);
|
|
412
|
-
match change_marker_handling(event, mach)? {
|
|
415
|
+
match change_marker_handling(&event, mach)? {
|
|
413
416
|
ChangeMarkerOutcome::SkipEvent => return Ok(()),
|
|
414
417
|
ChangeMarkerOutcome::SkipCommand => {
|
|
415
418
|
self.commands.pop_front();
|
|
@@ -442,7 +445,7 @@ impl WorkflowMachines {
|
|
|
442
445
|
|
|
443
446
|
if !self.machine(consumed_cmd.machine).is_final_state() {
|
|
444
447
|
self.machines_by_event_id
|
|
445
|
-
.insert(
|
|
448
|
+
.insert(event_id, consumed_cmd.machine);
|
|
446
449
|
}
|
|
447
450
|
|
|
448
451
|
Ok(())
|
|
@@ -450,48 +453,30 @@ impl WorkflowMachines {
|
|
|
450
453
|
|
|
451
454
|
fn handle_non_stateful_event(
|
|
452
455
|
&mut self,
|
|
453
|
-
event:
|
|
456
|
+
event: HistoryEvent,
|
|
454
457
|
has_next_event: bool,
|
|
455
458
|
) -> Result<()> {
|
|
456
459
|
debug!(
|
|
457
460
|
event = %event,
|
|
458
461
|
"handling non-stateful event"
|
|
459
462
|
);
|
|
463
|
+
let event_id = event.event_id;
|
|
460
464
|
match EventType::from_i32(event.event_type) {
|
|
461
465
|
Some(EventType::WorkflowExecutionStarted) => {
|
|
462
466
|
if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
|
|
463
467
|
attrs,
|
|
464
|
-
)) =
|
|
468
|
+
)) = event.attributes
|
|
465
469
|
{
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
let as_systime: SystemTime = st.clone().try_into()?;
|
|
470
|
+
if let Some(st) = event.event_time {
|
|
471
|
+
let as_systime: SystemTime = st.try_into()?;
|
|
469
472
|
self.workflow_start_time = Some(as_systime);
|
|
470
473
|
}
|
|
471
|
-
//
|
|
472
|
-
self.drive_me.
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.as_ref()
|
|
477
|
-
.map(|wt| wt.name.clone())
|
|
478
|
-
.unwrap_or_default(),
|
|
479
|
-
workflow_id: self.workflow_id.clone(),
|
|
480
|
-
arguments: Vec::from_payloads(attrs.input.clone()),
|
|
481
|
-
randomness_seed: str_to_randomness_seed(
|
|
482
|
-
&attrs.original_execution_run_id,
|
|
483
|
-
),
|
|
484
|
-
headers: match &attrs.header {
|
|
485
|
-
None => HashMap::new(),
|
|
486
|
-
Some(Header { fields }) => fields
|
|
487
|
-
.iter()
|
|
488
|
-
.map(|(k, v)| (k.clone(), Payload::from(v.clone())))
|
|
489
|
-
.collect(),
|
|
490
|
-
},
|
|
491
|
-
}
|
|
492
|
-
.into(),
|
|
474
|
+
// Notify the lang sdk that it's time to kick off a workflow
|
|
475
|
+
self.drive_me.start(
|
|
476
|
+
self.workflow_id.clone(),
|
|
477
|
+
str_to_randomness_seed(&attrs.original_execution_run_id),
|
|
478
|
+
attrs,
|
|
493
479
|
);
|
|
494
|
-
self.drive_me.start(attrs.clone());
|
|
495
480
|
} else {
|
|
496
481
|
return Err(WFMachinesError::Fatal(format!(
|
|
497
482
|
"WorkflowExecutionStarted event did not have appropriate attributes: {}",
|
|
@@ -503,14 +488,14 @@ impl WorkflowMachines {
|
|
|
503
488
|
let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
|
|
504
489
|
let key = self.all_machines.insert(wf_task_sm.into());
|
|
505
490
|
self.submachine_handle_event(key, event, has_next_event)?;
|
|
506
|
-
self.machines_by_event_id.insert(
|
|
491
|
+
self.machines_by_event_id.insert(event_id, key);
|
|
507
492
|
}
|
|
508
493
|
Some(EventType::WorkflowExecutionSignaled) => {
|
|
509
494
|
if let Some(history_event::Attributes::WorkflowExecutionSignaledEventAttributes(
|
|
510
495
|
attrs,
|
|
511
|
-
)) =
|
|
496
|
+
)) = event.attributes
|
|
512
497
|
{
|
|
513
|
-
self.drive_me.signal(attrs.
|
|
498
|
+
self.drive_me.signal(attrs.into());
|
|
514
499
|
} else {
|
|
515
500
|
// err
|
|
516
501
|
}
|
|
@@ -520,9 +505,9 @@ impl WorkflowMachines {
|
|
|
520
505
|
history_event::Attributes::WorkflowExecutionCancelRequestedEventAttributes(
|
|
521
506
|
attrs,
|
|
522
507
|
),
|
|
523
|
-
) =
|
|
508
|
+
) = event.attributes
|
|
524
509
|
{
|
|
525
|
-
self.drive_me.cancel(attrs.
|
|
510
|
+
self.drive_me.cancel(attrs.into());
|
|
526
511
|
} else {
|
|
527
512
|
// err
|
|
528
513
|
}
|
|
@@ -640,25 +625,20 @@ impl WorkflowMachines {
|
|
|
640
625
|
}
|
|
641
626
|
}
|
|
642
627
|
|
|
643
|
-
let
|
|
644
|
-
Some(event) => event.event_id,
|
|
645
|
-
None => 0,
|
|
646
|
-
};
|
|
647
|
-
// Workflow has been evicted, but we've received partial history from the server.
|
|
648
|
-
// Need to reset sticky and trigger another poll.
|
|
649
|
-
if self.current_started_event_id == 0 && first_event_id != 1 && !events.is_empty() {
|
|
650
|
-
debug!("Cache miss.");
|
|
651
|
-
self.metrics.sticky_cache_miss();
|
|
652
|
-
return Err(WFMachinesError::CacheMiss);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
let mut history = events.iter().peekable();
|
|
656
|
-
|
|
628
|
+
let mut history = events.into_iter().peekable();
|
|
657
629
|
while let Some(event) = history.next() {
|
|
630
|
+
if event.event_id != self.last_processed_event + 1 {
|
|
631
|
+
return Err(WFMachinesError::Fatal(format!(
|
|
632
|
+
"History is out of order. Last processed event: {}, event id: {}",
|
|
633
|
+
self.last_processed_event, event.event_id
|
|
634
|
+
)));
|
|
635
|
+
}
|
|
658
636
|
let next_event = history.peek();
|
|
637
|
+
let eid = event.event_id;
|
|
638
|
+
let etype = event.event_type;
|
|
659
639
|
self.handle_event(event, next_event.is_some())?;
|
|
660
|
-
self.last_processed_event =
|
|
661
|
-
if
|
|
640
|
+
self.last_processed_event = eid;
|
|
641
|
+
if etype == EventType::WorkflowTaskStarted as i32 && next_event.is_none() {
|
|
662
642
|
break;
|
|
663
643
|
}
|
|
664
644
|
}
|
|
@@ -695,7 +675,7 @@ impl WorkflowMachines {
|
|
|
695
675
|
fn submachine_handle_event(
|
|
696
676
|
&mut self,
|
|
697
677
|
sm: MachineKey,
|
|
698
|
-
event:
|
|
678
|
+
event: HistoryEvent,
|
|
699
679
|
has_next_event: bool,
|
|
700
680
|
) -> Result<()> {
|
|
701
681
|
let machine_responses = self.machine_mut(sm).handle_event(event, has_next_event)?;
|
|
@@ -813,6 +793,13 @@ impl WorkflowMachines {
|
|
|
813
793
|
let seq = attrs.seq;
|
|
814
794
|
self.add_cmd_to_wf_task(new_timer(attrs), Some(CommandID::Timer(seq)));
|
|
815
795
|
}
|
|
796
|
+
WFCommand::UpsertSearchAttributes(attrs) => {
|
|
797
|
+
let seq = attrs.seq;
|
|
798
|
+
self.add_cmd_to_wf_task(
|
|
799
|
+
upsert_search_attrs(attrs),
|
|
800
|
+
Some(CommandID::Timer(seq)),
|
|
801
|
+
);
|
|
802
|
+
}
|
|
816
803
|
WFCommand::CancelTimer(attrs) => {
|
|
817
804
|
jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?);
|
|
818
805
|
}
|
|
@@ -824,10 +811,9 @@ impl WorkflowMachines {
|
|
|
824
811
|
let seq = attrs.seq;
|
|
825
812
|
let attrs: ValidScheduleLA = ValidScheduleLA::from_schedule_la(
|
|
826
813
|
attrs,
|
|
827
|
-
self.
|
|
814
|
+
self.get_started_info()
|
|
828
815
|
.as_ref()
|
|
829
|
-
.
|
|
830
|
-
.flatten(),
|
|
816
|
+
.and_then(|x| x.workflow_execution_timeout),
|
|
831
817
|
)
|
|
832
818
|
.map_err(|e| {
|
|
833
819
|
WFMachinesError::Fatal(format!(
|
|
@@ -925,33 +911,10 @@ impl WorkflowMachines {
|
|
|
925
911
|
);
|
|
926
912
|
}
|
|
927
913
|
WFCommand::SignalExternalWorkflow(attrs) => {
|
|
928
|
-
let
|
|
929
|
-
None => {
|
|
930
|
-
return Err(WFMachinesError::Fatal(
|
|
931
|
-
"Signal external workflow command had empty target field"
|
|
932
|
-
.to_string(),
|
|
933
|
-
))
|
|
934
|
-
}
|
|
935
|
-
Some(sig_we::Target::ChildWorkflowId(wfid)) => (
|
|
936
|
-
NamespacedWorkflowExecution {
|
|
937
|
-
namespace: self.namespace.clone(),
|
|
938
|
-
workflow_id: wfid,
|
|
939
|
-
run_id: "".to_string(),
|
|
940
|
-
},
|
|
941
|
-
true,
|
|
942
|
-
),
|
|
943
|
-
Some(sig_we::Target::WorkflowExecution(we)) => (we, false),
|
|
944
|
-
};
|
|
945
|
-
|
|
914
|
+
let seq = attrs.seq;
|
|
946
915
|
self.add_cmd_to_wf_task(
|
|
947
|
-
new_external_signal(
|
|
948
|
-
|
|
949
|
-
we,
|
|
950
|
-
attrs.signal_name,
|
|
951
|
-
attrs.args,
|
|
952
|
-
only_child,
|
|
953
|
-
),
|
|
954
|
-
Some(CommandID::SignalExternal(attrs.seq)),
|
|
916
|
+
new_external_signal(attrs, &self.namespace)?,
|
|
917
|
+
Some(CommandID::SignalExternal(seq)),
|
|
955
918
|
);
|
|
956
919
|
}
|
|
957
920
|
WFCommand::CancelSignalWorkflow(attrs) => {
|
|
@@ -973,7 +936,8 @@ impl WorkflowMachines {
|
|
|
973
936
|
let mut jobs = vec![];
|
|
974
937
|
let m_key = self.get_machine_key(id)?;
|
|
975
938
|
let machine_resps = self.machine_mut(m_key).cancel()?;
|
|
976
|
-
debug!(machine_responses =
|
|
939
|
+
debug!(machine_responses = %machine_resps.display(), cmd_id = ?id,
|
|
940
|
+
"Cancel request responses");
|
|
977
941
|
for r in machine_resps {
|
|
978
942
|
match r {
|
|
979
943
|
MachineResponse::IssueNewCommand(c) => {
|
|
@@ -1084,7 +1048,9 @@ impl WorkflowMachines {
|
|
|
1084
1048
|
}
|
|
1085
1049
|
|
|
1086
1050
|
fn str_to_randomness_seed(run_id: &str) -> u64 {
|
|
1087
|
-
|
|
1051
|
+
// This was originally `DefaultHasher` but that is potentially unstable across Rust releases.
|
|
1052
|
+
// This must forever be `SipHasher13` now or we risk breaking history compat.
|
|
1053
|
+
let mut s = SipHasher13::new();
|
|
1088
1054
|
run_id.hash(&mut s);
|
|
1089
1055
|
s.finish()
|
|
1090
1056
|
}
|
|
@@ -64,8 +64,8 @@ impl WFMachinesAdapter for WorkflowTaskMachine {
|
|
|
64
64
|
task_started_event_id,
|
|
65
65
|
time,
|
|
66
66
|
} => {
|
|
67
|
-
let (
|
|
68
|
-
(ei.
|
|
67
|
+
let (event_id, event_type, has_next_event) = if let Some(ei) = event_info {
|
|
68
|
+
(ei.event_id, ei.event_type, ei.has_next_event)
|
|
69
69
|
} else {
|
|
70
70
|
return Err(WFMachinesError::Fatal(
|
|
71
71
|
"WF Task machine should never issue a task started trigger \
|
|
@@ -74,8 +74,8 @@ impl WFMachinesAdapter for WorkflowTaskMachine {
|
|
|
74
74
|
));
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
let cur_event_past_or_at_start =
|
|
78
|
-
if
|
|
77
|
+
let cur_event_past_or_at_start = event_id >= task_started_event_id;
|
|
78
|
+
if event_type == EventType::WorkflowTaskStarted
|
|
79
79
|
&& (!cur_event_past_or_at_start || has_next_event)
|
|
80
80
|
{
|
|
81
81
|
return Ok(vec![]);
|