@temporalio/core-bridge 0.17.2 → 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.
- package/Cargo.lock +339 -226
- package/Cargo.toml +7 -3
- package/common.js +50 -0
- package/index.d.ts +7 -0
- package/index.js +12 -0
- package/package.json +7 -4
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/{index.node → releases/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/scripts/build.js +10 -50
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
- package/sdk-core/.buildkite/pipeline.yml +2 -0
- package/sdk-core/Cargo.toml +1 -88
- package/sdk-core/README.md +30 -6
- package/sdk-core/bridge-ffi/Cargo.toml +24 -0
- package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
- package/sdk-core/bridge-ffi/build.rs +25 -0
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
- package/sdk-core/bridge-ffi/src/lib.rs +829 -0
- package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
- package/sdk-core/client/Cargo.toml +32 -0
- package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
- package/sdk-core/client/src/metrics.rs +89 -0
- package/sdk-core/client/src/mocks.rs +167 -0
- package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
- package/sdk-core/core/Cargo.toml +96 -0
- package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
- package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
- package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
- package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
- package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
- package/sdk-core/{src → core/src}/core_tests/queries.rs +45 -52
- package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
- package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
- package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
- package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +264 -286
- package/sdk-core/core/src/lib.rs +374 -0
- package/sdk-core/{src → core/src}/log_export.rs +3 -27
- package/sdk-core/core/src/pending_activations.rs +162 -0
- package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
- package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
- package/sdk-core/core/src/protosext/mod.rs +396 -0
- package/sdk-core/core/src/replay/mod.rs +210 -0
- package/sdk-core/core/src/retry_logic.rs +144 -0
- package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
- package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
- package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
- package/sdk-core/{src → core/src}/test_help/mod.rs +34 -73
- package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
- package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
- package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
- package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
- package/sdk-core/{src → core/src}/worker/mod.rs +305 -195
- package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
- package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
- package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
- package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
- package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
- package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
- package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
- package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +19 -21
- package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
- package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
- package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
- package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
- package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
- package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
- package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
- package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
- package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
- package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
- package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
- package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +344 -160
- package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
- package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +297 -81
- package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
- package/sdk-core/{src → core-api/src}/errors.rs +42 -90
- package/sdk-core/core-api/src/lib.rs +158 -0
- package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
- package/sdk-core/etc/deps.svg +156 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
- package/sdk-core/histories/fail_wf_task.bin +0 -0
- package/sdk-core/histories/timer_workflow_history.bin +0 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
- package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
- package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
- package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
- package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
- package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
- package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
- package/sdk-core/sdk/Cargo.toml +32 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
- package/sdk-core/sdk/src/lib.rs +699 -0
- package/sdk-core/sdk/src/payload_converter.rs +11 -0
- package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
- package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
- package/sdk-core/sdk-core-protos/build.rs +28 -6
- package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
- package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
- package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +594 -168
- package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
- package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
- package/sdk-core/test-utils/Cargo.toml +32 -0
- package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
- package/sdk-core/test-utils/src/histfetch.rs +28 -0
- package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
- package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
- package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
- package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
- package/sdk-core/tests/load_tests.rs +6 -6
- package/sdk-core/tests/main.rs +2 -2
- package/src/conversions.rs +24 -21
- package/src/errors.rs +8 -0
- package/src/lib.rs +323 -211
- package/sdk-core/protos/local/activity_result.proto +0 -46
- package/sdk-core/protos/local/activity_task.proto +0 -66
- package/sdk-core/src/core_tests/retry.rs +0 -147
- package/sdk-core/src/lib.rs +0 -403
- package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
- package/sdk-core/src/pending_activations.rs +0 -249
- package/sdk-core/src/protosext/mod.rs +0 -160
- package/sdk-core/src/prototype_rust_sdk.rs +0 -412
- package/sdk-core/src/task_token.rs +0 -20
- package/sdk-core/src/test_help/history_info.rs +0 -158
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
NotifyHasPatch, StartWorkflow, UpdateRandomSeed,
|
|
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
|
|
|
@@ -64,13 +74,15 @@ pub(crate) struct WorkflowMachines {
|
|
|
64
74
|
/// The event id of the most recent event processed. It's possible in some situations (ex legacy
|
|
65
75
|
/// queries) to receive a history with no new workflow tasks. If the last history we processed
|
|
66
76
|
/// also had no new tasks, we need a way to know not to apply the same events over again.
|
|
67
|
-
last_processed_event: i64,
|
|
77
|
+
pub last_processed_event: i64,
|
|
68
78
|
/// True if the workflow is replaying from history
|
|
69
79
|
pub replaying: bool,
|
|
70
80
|
/// Namespace this workflow exists in
|
|
71
81
|
pub namespace: String,
|
|
72
82
|
/// Workflow identifier
|
|
73
83
|
pub workflow_id: String,
|
|
84
|
+
/// Workflow type identifier. (Function name, class, etc)
|
|
85
|
+
pub workflow_type: String,
|
|
74
86
|
/// Identifies the current run
|
|
75
87
|
pub run_id: String,
|
|
76
88
|
/// The time the workflow execution began, as told by the WEStarted event
|
|
@@ -78,17 +90,18 @@ pub(crate) struct WorkflowMachines {
|
|
|
78
90
|
/// The time the workflow execution finished, as determined by when the machines handled
|
|
79
91
|
/// a terminal workflow command. If this is `Some`, you know the workflow is ended.
|
|
80
92
|
workflow_end_time: Option<SystemTime>,
|
|
81
|
-
/// The
|
|
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
|
|
82
97
|
current_wf_time: Option<SystemTime>,
|
|
83
98
|
|
|
84
|
-
|
|
85
|
-
all_machines: SlotMap<MachineKey, Box<dyn TemporalStateMachine + 'static>>,
|
|
99
|
+
all_machines: SlotMap<MachineKey, Machines>,
|
|
86
100
|
|
|
87
101
|
/// A mapping for accessing machines associated to a particular event, where the key is the id
|
|
88
102
|
/// of the initiating event for that machine.
|
|
89
103
|
machines_by_event_id: HashMap<i64, MachineKey>,
|
|
90
104
|
|
|
91
|
-
// TODO: Nothing gets deleted from here
|
|
92
105
|
/// Maps command ids as created by workflow authors to their associated machines.
|
|
93
106
|
id_to_machine: HashMap<CommandID, MachineKey>,
|
|
94
107
|
|
|
@@ -97,19 +110,20 @@ pub(crate) struct WorkflowMachines {
|
|
|
97
110
|
commands: VecDeque<CommandAndMachine>,
|
|
98
111
|
/// Commands generated by the currently processing workflow task, which will eventually be
|
|
99
112
|
/// transferred to `commands` (and hence eventually sent to the server)
|
|
100
|
-
///
|
|
101
|
-
/// Old note: It is a queue as commands can be added (due to marker based commands) while
|
|
102
|
-
/// iterating over already added commands.
|
|
103
113
|
current_wf_task_commands: VecDeque<CommandAndMachine>,
|
|
114
|
+
|
|
104
115
|
/// Information about patch markers we have already seen while replaying history
|
|
105
116
|
encountered_change_markers: HashMap<String, ChangeInfo>,
|
|
106
117
|
|
|
118
|
+
/// Contains extra local-activity related data
|
|
119
|
+
local_activity_data: LocalActivityData,
|
|
120
|
+
|
|
107
121
|
/// The workflow that is being driven by this instance of the machines
|
|
108
122
|
drive_me: DrivenWorkflow,
|
|
109
123
|
|
|
110
124
|
/// Is set to true once we've seen the final event in workflow history, to avoid accidentally
|
|
111
125
|
/// re-applying the final workflow task.
|
|
112
|
-
have_seen_terminal_event: bool,
|
|
126
|
+
pub have_seen_terminal_event: bool,
|
|
113
127
|
|
|
114
128
|
/// Metrics context
|
|
115
129
|
pub metrics: MetricsContext,
|
|
@@ -118,10 +132,17 @@ pub(crate) struct WorkflowMachines {
|
|
|
118
132
|
#[derive(Debug, derive_more::Display)]
|
|
119
133
|
#[display(fmt = "Cmd&Machine({})", "command")]
|
|
120
134
|
struct CommandAndMachine {
|
|
121
|
-
command:
|
|
135
|
+
command: MachineAssociatedCommand,
|
|
122
136
|
machine: MachineKey,
|
|
123
137
|
}
|
|
124
138
|
|
|
139
|
+
#[derive(Debug, derive_more::Display)]
|
|
140
|
+
enum MachineAssociatedCommand {
|
|
141
|
+
Real(Box<ProtoCommand>),
|
|
142
|
+
#[display(fmt = "FakeLocalActivityMarker({})", "_0")]
|
|
143
|
+
FakeLocalActivityMarker(u32),
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
#[derive(Debug, Clone, Copy)]
|
|
126
147
|
struct ChangeInfo {
|
|
127
148
|
created_command: bool,
|
|
@@ -132,10 +153,12 @@ struct ChangeInfo {
|
|
|
132
153
|
#[must_use]
|
|
133
154
|
#[allow(clippy::large_enum_variant)]
|
|
134
155
|
pub enum MachineResponse {
|
|
135
|
-
#[display(fmt = "PushWFJob")]
|
|
136
|
-
PushWFJob(
|
|
156
|
+
#[display(fmt = "PushWFJob({})", "_0")]
|
|
157
|
+
PushWFJob(workflow_activation_job::Variant),
|
|
137
158
|
|
|
138
159
|
IssueNewCommand(ProtoCommand),
|
|
160
|
+
#[display(fmt = "IssueFakeLocalActivityMarker({})", "_0")]
|
|
161
|
+
IssueFakeLocalActivityMarker(u32),
|
|
139
162
|
#[display(fmt = "TriggerWFTaskStarted")]
|
|
140
163
|
TriggerWFTaskStarted {
|
|
141
164
|
task_started_event_id: i64,
|
|
@@ -145,43 +168,37 @@ pub enum MachineResponse {
|
|
|
145
168
|
UpdateRunIdOnWorkflowReset {
|
|
146
169
|
run_id: String,
|
|
147
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>),
|
|
148
186
|
}
|
|
149
187
|
|
|
150
|
-
// Must use `From` b/c ofZZ
|
|
151
188
|
impl<T> From<T> for MachineResponse
|
|
152
189
|
where
|
|
153
|
-
T: Into<
|
|
190
|
+
T: Into<workflow_activation_job::Variant>,
|
|
154
191
|
{
|
|
155
192
|
fn from(v: T) -> Self {
|
|
156
193
|
Self::PushWFJob(v.into())
|
|
157
194
|
}
|
|
158
195
|
}
|
|
159
196
|
|
|
160
|
-
#[derive(thiserror::Error, Debug)]
|
|
161
|
-
pub(crate) enum WFMachinesError {
|
|
162
|
-
#[error("Nondeterminism error: {0}")]
|
|
163
|
-
Nondeterminism(String),
|
|
164
|
-
#[error("Fatal error in workflow machines: {0}")]
|
|
165
|
-
Fatal(String),
|
|
166
|
-
|
|
167
|
-
#[error("Unrecoverable network error while fetching history: {0}")]
|
|
168
|
-
HistoryFetchingError(tonic::Status),
|
|
169
|
-
|
|
170
|
-
/// Should always be caught internally and turned into a workflow task failure
|
|
171
|
-
#[error("Unable to process partial event history because workflow is no longer cached.")]
|
|
172
|
-
CacheMiss,
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
impl From<TimestampOutOfSystemRangeError> for WFMachinesError {
|
|
176
|
-
fn from(_: TimestampOutOfSystemRangeError) -> Self {
|
|
177
|
-
Self::Fatal("Could not decode timestamp".to_string())
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
197
|
impl WorkflowMachines {
|
|
182
198
|
pub(crate) fn new(
|
|
183
199
|
namespace: String,
|
|
184
200
|
workflow_id: String,
|
|
201
|
+
workflow_type: String,
|
|
185
202
|
run_id: String,
|
|
186
203
|
history: HistoryUpdate,
|
|
187
204
|
driven_wf: DrivenWorkflow,
|
|
@@ -192,6 +209,7 @@ impl WorkflowMachines {
|
|
|
192
209
|
last_history_from_server: history,
|
|
193
210
|
namespace,
|
|
194
211
|
workflow_id,
|
|
212
|
+
workflow_type,
|
|
195
213
|
run_id,
|
|
196
214
|
drive_me: driven_wf,
|
|
197
215
|
replaying,
|
|
@@ -202,6 +220,7 @@ impl WorkflowMachines {
|
|
|
202
220
|
last_processed_event: 0,
|
|
203
221
|
workflow_start_time: None,
|
|
204
222
|
workflow_end_time: None,
|
|
223
|
+
wft_start_time: None,
|
|
205
224
|
current_wf_time: None,
|
|
206
225
|
all_machines: Default::default(),
|
|
207
226
|
machines_by_event_id: Default::default(),
|
|
@@ -209,6 +228,7 @@ impl WorkflowMachines {
|
|
|
209
228
|
commands: Default::default(),
|
|
210
229
|
current_wf_task_commands: Default::default(),
|
|
211
230
|
encountered_change_markers: Default::default(),
|
|
231
|
+
local_activity_data: LocalActivityData::default(),
|
|
212
232
|
have_seen_terminal_event: false,
|
|
213
233
|
}
|
|
214
234
|
}
|
|
@@ -233,16 +253,60 @@ impl WorkflowMachines {
|
|
|
233
253
|
Ok(())
|
|
234
254
|
}
|
|
235
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
|
+
|
|
236
304
|
/// Handle a single event from the workflow history. `has_next_event` should be false if `event`
|
|
237
305
|
/// is the last event in the history.
|
|
238
306
|
///
|
|
239
307
|
/// TODO: Describe what actually happens in here
|
|
240
308
|
#[instrument(level = "debug", skip(self, event), fields(event=%event))]
|
|
241
|
-
|
|
242
|
-
&mut self,
|
|
243
|
-
event: &HistoryEvent,
|
|
244
|
-
has_next_event: bool,
|
|
245
|
-
) -> Result<()> {
|
|
309
|
+
fn handle_event(&mut self, event: &HistoryEvent, has_next_event: bool) -> Result<()> {
|
|
246
310
|
if event.is_final_wf_execution_event() {
|
|
247
311
|
self.have_seen_terminal_event = true;
|
|
248
312
|
}
|
|
@@ -291,21 +355,24 @@ impl WorkflowMachines {
|
|
|
291
355
|
/// Called when a workflow task started event has triggered. Ensures we are tracking the ID
|
|
292
356
|
/// of the current started event as well as workflow time properly.
|
|
293
357
|
fn task_started(&mut self, task_started_event_id: i64, time: SystemTime) -> Result<()> {
|
|
294
|
-
let s = span!(Level::DEBUG, "Task started trigger");
|
|
295
|
-
let _enter = s.enter();
|
|
296
|
-
|
|
297
|
-
// TODO: Local activity machines
|
|
298
|
-
// // Give local activities a chance to recreate their requests if they were lost due
|
|
299
|
-
// // to the last workflow task failure. The loss could happen only the last workflow task
|
|
300
|
-
// // was forcibly created by setting forceCreate on RespondWorkflowTaskCompletedRequest.
|
|
301
|
-
// if (nonProcessedWorkflowTask) {
|
|
302
|
-
// for (LocalActivityStateMachine value : localActivityMap.values()) {
|
|
303
|
-
// value.nonReplayWorkflowTaskStarted();
|
|
304
|
-
// }
|
|
305
|
-
// }
|
|
306
|
-
|
|
307
358
|
self.current_started_event_id = task_started_event_id;
|
|
359
|
+
self.wft_start_time = Some(time);
|
|
308
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
|
+
}
|
|
309
376
|
Ok(())
|
|
310
377
|
}
|
|
311
378
|
|
|
@@ -319,10 +386,25 @@ impl WorkflowMachines {
|
|
|
319
386
|
/// with a state machine, which is then notified about the event and the command is removed from
|
|
320
387
|
/// the commands queue.
|
|
321
388
|
fn handle_command_event(&mut self, event: &HistoryEvent) -> Result<()> {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
+
}
|
|
326
408
|
|
|
327
409
|
let consumed_cmd = loop {
|
|
328
410
|
if let Some(peek_machine) = self.commands.front() {
|
|
@@ -347,19 +429,17 @@ impl WorkflowMachines {
|
|
|
347
429
|
)));
|
|
348
430
|
};
|
|
349
431
|
|
|
350
|
-
// Feed the machine the event
|
|
351
432
|
let canceled_before_sent = self
|
|
352
433
|
.machine(command.machine)
|
|
353
434
|
.was_cancelled_before_sent_to_server();
|
|
354
435
|
|
|
355
436
|
if !canceled_before_sent {
|
|
437
|
+
// Feed the machine the event
|
|
356
438
|
self.submachine_handle_event(command.machine, event, true)?;
|
|
357
439
|
break command;
|
|
358
440
|
}
|
|
359
441
|
};
|
|
360
442
|
|
|
361
|
-
// TODO: validate command
|
|
362
|
-
|
|
363
443
|
if !self.machine(consumed_cmd.machine).is_final_state() {
|
|
364
444
|
self.machines_by_event_id
|
|
365
445
|
.insert(event.event_id, consumed_cmd.machine);
|
|
@@ -421,7 +501,7 @@ impl WorkflowMachines {
|
|
|
421
501
|
}
|
|
422
502
|
Some(EventType::WorkflowTaskScheduled) => {
|
|
423
503
|
let wf_task_sm = WorkflowTaskMachine::new(self.next_started_event_id);
|
|
424
|
-
let key = self.all_machines.insert(
|
|
504
|
+
let key = self.all_machines.insert(wf_task_sm.into());
|
|
425
505
|
self.submachine_handle_event(key, event, has_next_event)?;
|
|
426
506
|
self.machines_by_event_id.insert(event.event_id, key);
|
|
427
507
|
}
|
|
@@ -465,7 +545,10 @@ impl WorkflowMachines {
|
|
|
465
545
|
.iter()
|
|
466
546
|
.filter_map(|c| {
|
|
467
547
|
if !self.machine(c.machine).is_final_state() {
|
|
468
|
-
|
|
548
|
+
match &c.command {
|
|
549
|
+
MachineAssociatedCommand::Real(cmd) => Some((**cmd).clone()),
|
|
550
|
+
MachineAssociatedCommand::FakeLocalActivityMarker(_) => None,
|
|
551
|
+
}
|
|
469
552
|
} else {
|
|
470
553
|
None
|
|
471
554
|
}
|
|
@@ -479,9 +562,9 @@ impl WorkflowMachines {
|
|
|
479
562
|
///
|
|
480
563
|
/// The job list may be empty, in which case it is expected the caller handles what to do in a
|
|
481
564
|
/// "no work" situation. Possibly, it may know about some work the machines don't, like queries.
|
|
482
|
-
pub(crate) fn get_wf_activation(&mut self) ->
|
|
565
|
+
pub(crate) fn get_wf_activation(&mut self) -> WorkflowActivation {
|
|
483
566
|
let jobs = self.drive_me.drain_jobs();
|
|
484
|
-
|
|
567
|
+
WorkflowActivation {
|
|
485
568
|
timestamp: self.current_wf_time.map(Into::into),
|
|
486
569
|
is_replaying: self.replaying,
|
|
487
570
|
run_id: self.run_id.clone(),
|
|
@@ -489,6 +572,10 @@ impl WorkflowMachines {
|
|
|
489
572
|
}
|
|
490
573
|
}
|
|
491
574
|
|
|
575
|
+
pub(crate) fn has_pending_jobs(&self) -> bool {
|
|
576
|
+
self.drive_me.has_pending_jobs()
|
|
577
|
+
}
|
|
578
|
+
|
|
492
579
|
fn set_current_time(&mut self, time: SystemTime) -> SystemTime {
|
|
493
580
|
if self.current_wf_time.map_or(true, |t| t < time) {
|
|
494
581
|
self.current_wf_time = Some(time);
|
|
@@ -499,14 +586,9 @@ impl WorkflowMachines {
|
|
|
499
586
|
|
|
500
587
|
/// Iterate the state machines, which consists of grabbing any pending outgoing commands from
|
|
501
588
|
/// the workflow code, handling them, and preparing them to be sent off to the server.
|
|
502
|
-
|
|
503
|
-
/// Returns a boolean flag which indicates whether or not new activations were produced by the
|
|
504
|
-
/// state machine. If true, pending activation should be created by the caller making jobs
|
|
505
|
-
/// available to the lang side.
|
|
506
|
-
pub(crate) async fn iterate_machines(&mut self) -> Result<bool> {
|
|
589
|
+
pub(crate) async fn iterate_machines(&mut self) -> Result<()> {
|
|
507
590
|
let results = self.drive_me.fetch_workflow_iteration_output().await;
|
|
508
591
|
let jobs = self.handle_driven_results(results)?;
|
|
509
|
-
let has_new_lang_jobs = !jobs.is_empty();
|
|
510
592
|
for job in jobs {
|
|
511
593
|
self.drive_me.send_job(job);
|
|
512
594
|
}
|
|
@@ -516,12 +598,12 @@ impl WorkflowMachines {
|
|
|
516
598
|
self.metrics.wf_e2e_latency(rt);
|
|
517
599
|
}
|
|
518
600
|
}
|
|
519
|
-
Ok(
|
|
601
|
+
Ok(())
|
|
520
602
|
}
|
|
521
603
|
|
|
522
604
|
/// Apply the next (unapplied) entire workflow task from history to these machines. Will replay
|
|
523
605
|
/// any events that need to be replayed until caught up to the newest WFT.
|
|
524
|
-
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> {
|
|
525
607
|
// A much higher-up span (ex: poll) may want this field filled
|
|
526
608
|
tracing::Span::current().record("run_id", &self.run_id.as_str());
|
|
527
609
|
|
|
@@ -529,7 +611,7 @@ impl WorkflowMachines {
|
|
|
529
611
|
// then we don't need to do anything here, and in fact we need to avoid re-applying the
|
|
530
612
|
// final WFT.
|
|
531
613
|
if self.have_seen_terminal_event {
|
|
532
|
-
return Ok(
|
|
614
|
+
return Ok(0);
|
|
533
615
|
}
|
|
534
616
|
|
|
535
617
|
let last_handled_wft_started_id = self.current_started_event_id;
|
|
@@ -543,6 +625,7 @@ impl WorkflowMachines {
|
|
|
543
625
|
evts.retain(|e| e.event_id > self.last_processed_event);
|
|
544
626
|
evts
|
|
545
627
|
};
|
|
628
|
+
let num_events_to_process = events.len();
|
|
546
629
|
|
|
547
630
|
// We're caught up on reply if there are no new events to process
|
|
548
631
|
// TODO: Probably this is unneeded if we evict whenever history is from non-sticky queue
|
|
@@ -575,12 +658,15 @@ impl WorkflowMachines {
|
|
|
575
658
|
let next_event = history.peek();
|
|
576
659
|
self.handle_event(event, next_event.is_some())?;
|
|
577
660
|
self.last_processed_event = event.event_id;
|
|
661
|
+
if event.event_type == EventType::WorkflowTaskStarted as i32 && next_event.is_none() {
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
578
664
|
}
|
|
579
665
|
|
|
580
666
|
// Scan through to the next WFT, searching for any patch markers, so that we can
|
|
581
667
|
// pre-resolve them.
|
|
582
668
|
for e in self.last_history_from_server.peek_next_wft_sequence() {
|
|
583
|
-
if let Some((patch_id, _)) = e.
|
|
669
|
+
if let Some((patch_id, _)) = e.get_patch_marker_details() {
|
|
584
670
|
self.encountered_change_markers.insert(
|
|
585
671
|
patch_id.clone(),
|
|
586
672
|
ChangeInfo {
|
|
@@ -589,9 +675,11 @@ impl WorkflowMachines {
|
|
|
589
675
|
);
|
|
590
676
|
// Found a patch marker
|
|
591
677
|
self.drive_me
|
|
592
|
-
.send_job(
|
|
593
|
-
patch_id,
|
|
594
|
-
|
|
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)?;
|
|
595
683
|
}
|
|
596
684
|
}
|
|
597
685
|
|
|
@@ -599,7 +687,7 @@ impl WorkflowMachines {
|
|
|
599
687
|
self.metrics.wf_task_replay_latency(replay_start.elapsed());
|
|
600
688
|
}
|
|
601
689
|
|
|
602
|
-
Ok(
|
|
690
|
+
Ok(num_events_to_process)
|
|
603
691
|
}
|
|
604
692
|
|
|
605
693
|
/// Wrapper for calling [TemporalStateMachine::handle_event] which appropriately takes action
|
|
@@ -625,10 +713,15 @@ impl WorkflowMachines {
|
|
|
625
713
|
.machine(c.machine)
|
|
626
714
|
.was_cancelled_before_sent_to_server()
|
|
627
715
|
{
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
+
}
|
|
632
725
|
self.commands.push_back(c);
|
|
633
726
|
}
|
|
634
727
|
}
|
|
@@ -640,10 +733,10 @@ impl WorkflowMachines {
|
|
|
640
733
|
/// this function uses to drive sending jobs to lang, triggering new workflow tasks, etc.
|
|
641
734
|
fn process_machine_responses(
|
|
642
735
|
&mut self,
|
|
643
|
-
|
|
736
|
+
smk: MachineKey,
|
|
644
737
|
machine_responses: Vec<MachineResponse>,
|
|
645
738
|
) -> Result<()> {
|
|
646
|
-
let sm = self.
|
|
739
|
+
let sm = self.machine(smk);
|
|
647
740
|
if !machine_responses.is_empty() {
|
|
648
741
|
debug!(responses = %machine_responses.display(), machine_name = %sm.kind(),
|
|
649
742
|
"Machine produced responses");
|
|
@@ -663,14 +756,40 @@ impl WorkflowMachines {
|
|
|
663
756
|
// TODO: Should this also update self.run_id? Should we track orig/current
|
|
664
757
|
// separately?
|
|
665
758
|
self.drive_me
|
|
666
|
-
.send_job(
|
|
759
|
+
.send_job(workflow_activation_job::Variant::UpdateRandomSeed(
|
|
667
760
|
UpdateRandomSeed {
|
|
668
761
|
randomness_seed: str_to_randomness_seed(&new_run_id),
|
|
669
762
|
},
|
|
670
763
|
));
|
|
671
764
|
}
|
|
672
|
-
MachineResponse::IssueNewCommand(
|
|
673
|
-
|
|
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
|
+
}
|
|
674
793
|
}
|
|
675
794
|
}
|
|
676
795
|
}
|
|
@@ -686,30 +805,53 @@ impl WorkflowMachines {
|
|
|
686
805
|
fn handle_driven_results(
|
|
687
806
|
&mut self,
|
|
688
807
|
results: Vec<WFCommand>,
|
|
689
|
-
) -> Result<Vec<
|
|
808
|
+
) -> Result<Vec<workflow_activation_job::Variant>> {
|
|
690
809
|
let mut jobs = vec![];
|
|
691
810
|
for cmd in results {
|
|
692
811
|
match cmd {
|
|
693
812
|
WFCommand::AddTimer(attrs) => {
|
|
694
813
|
let seq = attrs.seq;
|
|
695
|
-
|
|
696
|
-
self.id_to_machine
|
|
697
|
-
.insert(CommandID::Timer(seq), timer.machine);
|
|
698
|
-
self.current_wf_task_commands.push_back(timer);
|
|
814
|
+
self.add_cmd_to_wf_task(new_timer(attrs), Some(CommandID::Timer(seq)));
|
|
699
815
|
}
|
|
700
816
|
WFCommand::CancelTimer(attrs) => {
|
|
701
817
|
jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?);
|
|
702
818
|
}
|
|
703
819
|
WFCommand::AddActivity(attrs) => {
|
|
704
820
|
let seq = attrs.seq;
|
|
705
|
-
|
|
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());
|
|
706
845
|
self.id_to_machine
|
|
707
|
-
.insert(CommandID::
|
|
708
|
-
self.
|
|
846
|
+
.insert(CommandID::LocalActivity(seq), machkey);
|
|
847
|
+
self.process_machine_responses(machkey, mach_resp)?;
|
|
709
848
|
}
|
|
710
849
|
WFCommand::RequestCancelActivity(attrs) => {
|
|
711
850
|
jobs.extend(self.process_cancellation(CommandID::Activity(attrs.seq))?);
|
|
712
851
|
}
|
|
852
|
+
WFCommand::RequestCancelLocalActivity(attrs) => {
|
|
853
|
+
jobs.extend(self.process_cancellation(CommandID::LocalActivity(attrs.seq))?);
|
|
854
|
+
}
|
|
713
855
|
WFCommand::CompleteWorkflow(attrs) => {
|
|
714
856
|
self.metrics.wf_completed();
|
|
715
857
|
self.add_terminal_command(complete_workflow(attrs));
|
|
@@ -730,15 +872,12 @@ impl WorkflowMachines {
|
|
|
730
872
|
// Do not create commands for change IDs that we have already created commands
|
|
731
873
|
// for.
|
|
732
874
|
if !matches!(self.encountered_change_markers.get(&attrs.patch_id),
|
|
733
|
-
|
|
734
|
-
if *created_command)
|
|
875
|
+
Some(ChangeInfo {created_command}) if *created_command)
|
|
735
876
|
{
|
|
736
|
-
|
|
737
|
-
attrs.patch_id.clone(),
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
));
|
|
741
|
-
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
|
+
);
|
|
742
881
|
|
|
743
882
|
if let Some(ci) = self.encountered_change_markers.get_mut(&attrs.patch_id) {
|
|
744
883
|
ci.created_command = true;
|
|
@@ -754,10 +893,10 @@ impl WorkflowMachines {
|
|
|
754
893
|
}
|
|
755
894
|
WFCommand::AddChildWorkflow(attrs) => {
|
|
756
895
|
let seq = attrs.seq;
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
896
|
+
self.add_cmd_to_wf_task(
|
|
897
|
+
new_child_workflow(attrs),
|
|
898
|
+
Some(CommandID::ChildWorkflowStart(seq)),
|
|
899
|
+
);
|
|
761
900
|
}
|
|
762
901
|
WFCommand::CancelUnstartedChild(attrs) => jobs.extend(self.process_cancellation(
|
|
763
902
|
CommandID::ChildWorkflowStart(attrs.child_workflow_seq),
|
|
@@ -780,11 +919,10 @@ impl WorkflowMachines {
|
|
|
780
919
|
),
|
|
781
920
|
Some(cancel_we::Target::WorkflowExecution(we)) => (we, false),
|
|
782
921
|
};
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
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
|
+
);
|
|
788
926
|
}
|
|
789
927
|
WFCommand::SignalExternalWorkflow(attrs) => {
|
|
790
928
|
let (we, only_child) = match attrs.target {
|
|
@@ -805,16 +943,16 @@ impl WorkflowMachines {
|
|
|
805
943
|
Some(sig_we::Target::WorkflowExecution(we)) => (we, false),
|
|
806
944
|
};
|
|
807
945
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
+
);
|
|
818
956
|
}
|
|
819
957
|
WFCommand::CancelSignalWorkflow(attrs) => {
|
|
820
958
|
jobs.extend(self.process_cancellation(CommandID::SignalExternal(attrs.seq))?);
|
|
@@ -834,19 +972,62 @@ impl WorkflowMachines {
|
|
|
834
972
|
fn process_cancellation(&mut self, id: CommandID) -> Result<Vec<Variant>> {
|
|
835
973
|
let mut jobs = vec![];
|
|
836
974
|
let m_key = self.get_machine_key(id)?;
|
|
837
|
-
let
|
|
838
|
-
debug!(machine_responses = ?
|
|
839
|
-
for r in
|
|
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 {
|
|
840
978
|
match r {
|
|
841
979
|
MachineResponse::IssueNewCommand(c) => {
|
|
842
980
|
self.current_wf_task_commands.push_back(CommandAndMachine {
|
|
843
|
-
command: c,
|
|
981
|
+
command: MachineAssociatedCommand::Real(Box::new(c)),
|
|
844
982
|
machine: m_key,
|
|
845
983
|
});
|
|
846
984
|
}
|
|
847
985
|
MachineResponse::PushWFJob(j) => {
|
|
848
986
|
jobs.push(j);
|
|
849
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) => {}
|
|
850
1031
|
v => {
|
|
851
1032
|
return Err(WFMachinesError::Fatal(format!(
|
|
852
1033
|
"Unexpected machine response {:?} when cancelling {:?}",
|
|
@@ -864,34 +1045,37 @@ impl WorkflowMachines {
|
|
|
864
1045
|
})?)
|
|
865
1046
|
}
|
|
866
1047
|
|
|
867
|
-
fn add_terminal_command
|
|
868
|
-
&mut self,
|
|
869
|
-
machine: NewMachineWithCommand<T>,
|
|
870
|
-
) {
|
|
1048
|
+
fn add_terminal_command(&mut self, machine: NewMachineWithCommand) {
|
|
871
1049
|
let cwfm = self.add_new_command_machine(machine);
|
|
872
1050
|
self.workflow_end_time = Some(SystemTime::now());
|
|
873
1051
|
self.current_wf_task_commands.push_back(cwfm);
|
|
874
1052
|
}
|
|
875
1053
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
machine
|
|
879
|
-
|
|
880
|
-
|
|
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);
|
|
881
1065
|
CommandAndMachine {
|
|
882
|
-
command: machine.command,
|
|
1066
|
+
command: MachineAssociatedCommand::Real(Box::new(machine.command)),
|
|
883
1067
|
machine: k,
|
|
884
1068
|
}
|
|
885
1069
|
}
|
|
886
1070
|
|
|
887
|
-
fn machine(&self, m: MachineKey) -> &
|
|
1071
|
+
fn machine(&self, m: MachineKey) -> &Machines {
|
|
888
1072
|
self.all_machines
|
|
889
1073
|
.get(m)
|
|
890
1074
|
.expect("Machine must exist")
|
|
891
1075
|
.borrow()
|
|
892
1076
|
}
|
|
893
1077
|
|
|
894
|
-
fn machine_mut(&mut self, m: MachineKey) -> &mut
|
|
1078
|
+
fn machine_mut(&mut self, m: MachineKey) -> &mut Machines {
|
|
895
1079
|
self.all_machines
|
|
896
1080
|
.get_mut(m)
|
|
897
1081
|
.expect("Machine must exist")
|
|
@@ -919,23 +1103,23 @@ fn change_marker_handling(
|
|
|
919
1103
|
) -> Result<ChangeMarkerOutcome> {
|
|
920
1104
|
if !mach.matches_event(event) {
|
|
921
1105
|
// Version markers can be skipped in the event they are deprecated
|
|
922
|
-
if let Some(
|
|
1106
|
+
if let Some((patch_name, deprecated)) = event.get_patch_marker_details() {
|
|
923
1107
|
// Is deprecated. We can simply ignore this event, as deprecated change
|
|
924
1108
|
// markers are allowed without matching changed calls.
|
|
925
|
-
if
|
|
1109
|
+
if deprecated {
|
|
926
1110
|
debug!("Deprecated patch marker tried against wrong machine, skipping.");
|
|
927
1111
|
return Ok(ChangeMarkerOutcome::SkipEvent);
|
|
928
1112
|
}
|
|
929
1113
|
return Err(WFMachinesError::Nondeterminism(format!(
|
|
930
1114
|
"Non-deprecated patch marker encountered for change {}, \
|
|
931
1115
|
but there is no corresponding change command!",
|
|
932
|
-
|
|
1116
|
+
patch_name
|
|
933
1117
|
)));
|
|
934
1118
|
}
|
|
935
1119
|
// Version machines themselves may also not *have* matching markers, where non-deprecated
|
|
936
1120
|
// calls take the old path, and deprecated calls assume history is produced by a new-code
|
|
937
1121
|
// worker.
|
|
938
|
-
if mach.kind() == MachineKind::
|
|
1122
|
+
if mach.kind() == MachineKind::Patch {
|
|
939
1123
|
debug!("Skipping non-matching event against version machine");
|
|
940
1124
|
return Ok(ChangeMarkerOutcome::SkipCommand);
|
|
941
1125
|
}
|