@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.
- 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 +54 -54
- 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 +280 -292
- 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 +35 -83
- 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 +347 -221
- 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 +20 -31
- 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 +357 -171
- 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 +317 -103
- package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
- package/sdk-core/{src → core-api/src}/errors.rs +42 -92
- 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 +601 -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 -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
|
-
|
|
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
|
|
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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(
|
|
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
|
-
|
|
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) ->
|
|
565
|
+
pub(crate) fn get_wf_activation(&mut self) -> WorkflowActivation {
|
|
479
566
|
let jobs = self.drive_me.drain_jobs();
|
|
480
|
-
|
|
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(
|
|
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 =
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
736
|
+
smk: MachineKey,
|
|
641
737
|
machine_responses: Vec<MachineResponse>,
|
|
642
738
|
) -> Result<()> {
|
|
643
|
-
let sm = self.
|
|
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(
|
|
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
|
-
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
705
|
-
self.
|
|
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
|
-
|
|
731
|
-
if *created_command)
|
|
875
|
+
Some(ChangeInfo {created_command}) if *created_command)
|
|
732
876
|
{
|
|
733
|
-
|
|
734
|
-
attrs.patch_id.clone(),
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
|
836
|
-
debug!(machine_responses = ?
|
|
837
|
-
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 {
|
|
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
|
|
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
|
-
|
|
875
|
-
|
|
876
|
-
machine
|
|
877
|
-
|
|
878
|
-
|
|
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) -> &
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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::
|
|
1122
|
+
if mach.kind() == MachineKind::Patch {
|
|
937
1123
|
debug!("Skipping non-matching event against version machine");
|
|
938
1124
|
return Ok(ChangeMarkerOutcome::SkipCommand);
|
|
939
1125
|
}
|