@temporalio/core-bridge 0.14.0 → 0.16.4
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 +162 -38
- package/Cargo.toml +3 -3
- package/index.d.ts +14 -1
- package/index.node +0 -0
- package/package.json +8 -5
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/{x86_64-pc-windows-gnu → aarch64-unknown-linux-gnu}/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/scripts/build.js +77 -34
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/Cargo.toml +6 -5
- package/sdk-core/fsm/Cargo.toml +1 -1
- package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +2 -2
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +8 -9
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +13 -7
- package/sdk-core/fsm/rustfsm_trait/Cargo.toml +2 -2
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +1 -1
- package/sdk-core/protos/local/workflow_activation.proto +6 -3
- package/sdk-core/sdk-core-protos/Cargo.toml +4 -4
- package/sdk-core/sdk-core-protos/src/lib.rs +38 -50
- package/sdk-core/src/core_tests/activity_tasks.rs +5 -5
- package/sdk-core/src/core_tests/child_workflows.rs +55 -29
- package/sdk-core/src/core_tests/determinism.rs +19 -9
- package/sdk-core/src/core_tests/mod.rs +3 -3
- package/sdk-core/src/core_tests/retry.rs +14 -8
- package/sdk-core/src/core_tests/workers.rs +1 -1
- package/sdk-core/src/core_tests/workflow_tasks.rs +347 -4
- package/sdk-core/src/errors.rs +27 -44
- package/sdk-core/src/lib.rs +13 -3
- package/sdk-core/src/machines/activity_state_machine.rs +44 -5
- package/sdk-core/src/machines/child_workflow_state_machine.rs +31 -11
- package/sdk-core/src/machines/complete_workflow_state_machine.rs +1 -1
- package/sdk-core/src/machines/continue_as_new_workflow_state_machine.rs +1 -1
- package/sdk-core/src/machines/mod.rs +18 -23
- package/sdk-core/src/machines/patch_state_machine.rs +8 -8
- package/sdk-core/src/machines/signal_external_state_machine.rs +22 -1
- package/sdk-core/src/machines/timer_state_machine.rs +21 -3
- package/sdk-core/src/machines/transition_coverage.rs +3 -3
- package/sdk-core/src/machines/workflow_machines.rs +11 -11
- package/sdk-core/src/pending_activations.rs +27 -22
- package/sdk-core/src/pollers/gateway.rs +15 -7
- package/sdk-core/src/pollers/poll_buffer.rs +6 -5
- package/sdk-core/src/pollers/retry.rs +153 -120
- package/sdk-core/src/prototype_rust_sdk/workflow_context.rs +61 -46
- package/sdk-core/src/prototype_rust_sdk/workflow_future.rs +13 -12
- package/sdk-core/src/prototype_rust_sdk.rs +17 -23
- package/sdk-core/src/telemetry/metrics.rs +2 -4
- package/sdk-core/src/telemetry/mod.rs +6 -7
- package/sdk-core/src/test_help/canned_histories.rs +17 -93
- package/sdk-core/src/test_help/history_builder.rs +61 -2
- package/sdk-core/src/test_help/history_info.rs +21 -2
- package/sdk-core/src/test_help/mod.rs +26 -34
- package/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +246 -138
- package/sdk-core/src/worker/activities.rs +46 -45
- package/sdk-core/src/worker/config.rs +11 -0
- package/sdk-core/src/worker/dispatcher.rs +5 -5
- package/sdk-core/src/worker/mod.rs +86 -56
- package/sdk-core/src/workflow/driven_workflow.rs +3 -3
- package/sdk-core/src/workflow/history_update.rs +1 -1
- package/sdk-core/src/workflow/mod.rs +2 -1
- package/sdk-core/src/workflow/workflow_tasks/cache_manager.rs +13 -17
- package/sdk-core/src/workflow/workflow_tasks/concurrency_manager.rs +10 -18
- package/sdk-core/src/workflow/workflow_tasks/mod.rs +72 -57
- package/sdk-core/test_utils/Cargo.toml +1 -1
- package/sdk-core/test_utils/src/lib.rs +2 -2
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +61 -1
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +2 -2
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +49 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +2 -2
- package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
- package/src/conversions.rs +17 -0
- package/src/errors.rs +0 -7
- package/src/lib.rs +0 -20
|
@@ -128,7 +128,7 @@ impl ActivityMachine {
|
|
|
128
128
|
{
|
|
129
129
|
r.push(MachineResponse::PushWFJob(
|
|
130
130
|
self.create_cancelation_resolve(None).into(),
|
|
131
|
-
))
|
|
131
|
+
));
|
|
132
132
|
}
|
|
133
133
|
r
|
|
134
134
|
}
|
|
@@ -305,6 +305,20 @@ impl TryFrom<CommandType> for ActivityMachineEvents {
|
|
|
305
305
|
|
|
306
306
|
impl Cancellable for ActivityMachine {
|
|
307
307
|
fn cancel(&mut self) -> Result<Vec<MachineResponse>, MachineError<Self::Error>> {
|
|
308
|
+
if matches!(
|
|
309
|
+
self.state(),
|
|
310
|
+
ActivityMachineState::Completed(_)
|
|
311
|
+
| ActivityMachineState::Canceled(_)
|
|
312
|
+
| ActivityMachineState::Failed(_)
|
|
313
|
+
| ActivityMachineState::TimedOut(_)
|
|
314
|
+
) {
|
|
315
|
+
// Ignore attempted cancels in terminal states
|
|
316
|
+
debug!(
|
|
317
|
+
"Attempted to cancel already resolved activity (seq {})",
|
|
318
|
+
self.shared_state.attrs.seq
|
|
319
|
+
);
|
|
320
|
+
return Ok(vec![]);
|
|
321
|
+
}
|
|
308
322
|
let event = match self.shared_state.cancellation_type {
|
|
309
323
|
ActivityCancellationType::Abandon => ActivityMachineEvents::Abandon,
|
|
310
324
|
_ => ActivityMachineEvents::Cancel,
|
|
@@ -316,9 +330,15 @@ impl Cancellable for ActivityMachine {
|
|
|
316
330
|
ActivityMachineCommand::RequestCancellation(cmd) => {
|
|
317
331
|
self.machine_responses_from_cancel_request(cmd)
|
|
318
332
|
}
|
|
319
|
-
ActivityMachineCommand::Cancel(
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
ActivityMachineCommand::Cancel(details) => {
|
|
334
|
+
vec![self
|
|
335
|
+
.create_cancelation_resolve(
|
|
336
|
+
details
|
|
337
|
+
.map(TryInto::try_into)
|
|
338
|
+
.transpose()
|
|
339
|
+
.unwrap_or_default(),
|
|
340
|
+
)
|
|
341
|
+
.into()]
|
|
322
342
|
}
|
|
323
343
|
x => panic!("Invalid cancel event response {:?}", x),
|
|
324
344
|
})
|
|
@@ -665,7 +685,7 @@ fn notify_lang_activity_cancelled(
|
|
|
665
685
|
) -> ActivityMachineTransition<Canceled> {
|
|
666
686
|
ActivityMachineTransition::ok_shared(
|
|
667
687
|
vec![ActivityMachineCommand::Cancel(
|
|
668
|
-
canceled_event.
|
|
688
|
+
canceled_event.and_then(|e| e.details),
|
|
669
689
|
)],
|
|
670
690
|
Canceled::default(),
|
|
671
691
|
dat,
|
|
@@ -735,6 +755,7 @@ mod test {
|
|
|
735
755
|
workflow::managed_wf::ManagedWFFunc,
|
|
736
756
|
};
|
|
737
757
|
use rstest::{fixture, rstest};
|
|
758
|
+
use std::mem::discriminant;
|
|
738
759
|
use temporal_sdk_core_protos::coresdk::workflow_activation::{
|
|
739
760
|
wf_activation_job, WfActivationJob,
|
|
740
761
|
};
|
|
@@ -835,4 +856,22 @@ mod test {
|
|
|
835
856
|
);
|
|
836
857
|
wfm.shutdown().await.unwrap();
|
|
837
858
|
}
|
|
859
|
+
|
|
860
|
+
#[test]
|
|
861
|
+
fn cancels_ignored_terminal() {
|
|
862
|
+
for state in [
|
|
863
|
+
ActivityMachineState::Canceled(Canceled {}),
|
|
864
|
+
Failed {}.into(),
|
|
865
|
+
TimedOut {}.into(),
|
|
866
|
+
Completed {}.into(),
|
|
867
|
+
] {
|
|
868
|
+
let mut s = ActivityMachine {
|
|
869
|
+
state: state.clone(),
|
|
870
|
+
shared_state: Default::default(),
|
|
871
|
+
};
|
|
872
|
+
let cmds = s.cancel().unwrap();
|
|
873
|
+
assert_eq!(cmds.len(), 0);
|
|
874
|
+
assert_eq!(discriminant(&state), discriminant(&s.state));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
838
877
|
}
|
|
@@ -54,6 +54,12 @@ fsm! {
|
|
|
54
54
|
Started --(ChildWorkflowExecutionTimedOut(i32), shared on_child_workflow_execution_timed_out) --> TimedOut;
|
|
55
55
|
Started --(ChildWorkflowExecutionCancelled, shared on_child_workflow_execution_cancelled) --> Cancelled;
|
|
56
56
|
Started --(ChildWorkflowExecutionTerminated, shared on_child_workflow_execution_terminated) --> Terminated;
|
|
57
|
+
|
|
58
|
+
// Ignore any spurious cancellations after resolution
|
|
59
|
+
Cancelled --(Cancel) --> Cancelled;
|
|
60
|
+
Failed --(Cancel) --> Failed;
|
|
61
|
+
TimedOut --(Cancel) --> TimedOut;
|
|
62
|
+
Completed --(Cancel) --> Completed;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
pub struct ChildWorkflowExecutionStartedEvent {
|
|
@@ -288,7 +294,7 @@ pub(super) struct Terminated {}
|
|
|
288
294
|
#[derive(Default, Clone)]
|
|
289
295
|
pub(super) struct TimedOut {}
|
|
290
296
|
|
|
291
|
-
#[derive(Default, Clone)]
|
|
297
|
+
#[derive(Default, Clone, Debug)]
|
|
292
298
|
pub(super) struct SharedState {
|
|
293
299
|
initiated_event_id: i64,
|
|
294
300
|
started_event_id: i64,
|
|
@@ -630,6 +636,7 @@ mod test {
|
|
|
630
636
|
};
|
|
631
637
|
use anyhow::anyhow;
|
|
632
638
|
use rstest::{fixture, rstest};
|
|
639
|
+
use std::mem::discriminant;
|
|
633
640
|
use temporal_sdk_core_protos::coresdk::{
|
|
634
641
|
child_workflow::child_workflow_result,
|
|
635
642
|
workflow_activation::resolve_child_workflow_execution_start::Status as StartStatus,
|
|
@@ -643,11 +650,11 @@ mod test {
|
|
|
643
650
|
}
|
|
644
651
|
|
|
645
652
|
impl Expectation {
|
|
646
|
-
fn try_from_u8(x: u8) -> Option<Self> {
|
|
653
|
+
const fn try_from_u8(x: u8) -> Option<Self> {
|
|
647
654
|
Some(match x {
|
|
648
|
-
0 =>
|
|
649
|
-
1 =>
|
|
650
|
-
2 =>
|
|
655
|
+
0 => Self::Success,
|
|
656
|
+
1 => Self::Failure,
|
|
657
|
+
2 => Self::StartFailure,
|
|
651
658
|
_ => return None,
|
|
652
659
|
})
|
|
653
660
|
}
|
|
@@ -693,12 +700,7 @@ mod test {
|
|
|
693
700
|
};
|
|
694
701
|
match (
|
|
695
702
|
expectation,
|
|
696
|
-
start_res
|
|
697
|
-
.as_started()
|
|
698
|
-
.unwrap()
|
|
699
|
-
.result(&mut ctx)
|
|
700
|
-
.await
|
|
701
|
-
.status,
|
|
703
|
+
start_res.into_started().unwrap().result().await.status,
|
|
702
704
|
) {
|
|
703
705
|
(Expectation::Success, Some(child_workflow_result::Status::Completed(_))) => {
|
|
704
706
|
Ok(().into())
|
|
@@ -803,4 +805,22 @@ mod test {
|
|
|
803
805
|
);
|
|
804
806
|
wfm.shutdown().await.unwrap();
|
|
805
807
|
}
|
|
808
|
+
|
|
809
|
+
#[test]
|
|
810
|
+
fn cancels_ignored_terminal() {
|
|
811
|
+
for state in [
|
|
812
|
+
ChildWorkflowMachineState::Cancelled(Cancelled {}),
|
|
813
|
+
Failed {}.into(),
|
|
814
|
+
TimedOut {}.into(),
|
|
815
|
+
Completed {}.into(),
|
|
816
|
+
] {
|
|
817
|
+
let mut s = ChildWorkflowMachine {
|
|
818
|
+
state: state.clone(),
|
|
819
|
+
shared_state: Default::default(),
|
|
820
|
+
};
|
|
821
|
+
let cmds = s.cancel().unwrap();
|
|
822
|
+
assert_eq!(cmds.len(), 0);
|
|
823
|
+
assert_eq!(discriminant(&state), discriminant(&s.state));
|
|
824
|
+
}
|
|
825
|
+
}
|
|
806
826
|
}
|
|
@@ -117,7 +117,7 @@ pub(super) struct CompleteWorkflowCommandRecorded {}
|
|
|
117
117
|
|
|
118
118
|
impl From<CompleteWorkflowCommandCreated> for CompleteWorkflowCommandRecorded {
|
|
119
119
|
fn from(_: CompleteWorkflowCommandCreated) -> Self {
|
|
120
|
-
|
|
120
|
+
Self::default()
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -77,38 +77,34 @@ impl TryFrom<WorkflowCommand> for WFCommand {
|
|
|
77
77
|
|
|
78
78
|
fn try_from(c: WorkflowCommand) -> Result<Self, Self::Error> {
|
|
79
79
|
match c.variant.ok_or(EmptyWorkflowCommandErr)? {
|
|
80
|
-
workflow_command::Variant::StartTimer(s) => Ok(
|
|
81
|
-
workflow_command::Variant::CancelTimer(s) => Ok(
|
|
82
|
-
workflow_command::Variant::ScheduleActivity(s) => Ok(
|
|
80
|
+
workflow_command::Variant::StartTimer(s) => Ok(Self::AddTimer(s)),
|
|
81
|
+
workflow_command::Variant::CancelTimer(s) => Ok(Self::CancelTimer(s)),
|
|
82
|
+
workflow_command::Variant::ScheduleActivity(s) => Ok(Self::AddActivity(s)),
|
|
83
83
|
workflow_command::Variant::RequestCancelActivity(s) => {
|
|
84
|
-
Ok(
|
|
84
|
+
Ok(Self::RequestCancelActivity(s))
|
|
85
85
|
}
|
|
86
86
|
workflow_command::Variant::CompleteWorkflowExecution(c) => {
|
|
87
|
-
Ok(
|
|
87
|
+
Ok(Self::CompleteWorkflow(c))
|
|
88
88
|
}
|
|
89
|
-
workflow_command::Variant::FailWorkflowExecution(s) => Ok(
|
|
90
|
-
workflow_command::Variant::RespondToQuery(s) => Ok(
|
|
89
|
+
workflow_command::Variant::FailWorkflowExecution(s) => Ok(Self::FailWorkflow(s)),
|
|
90
|
+
workflow_command::Variant::RespondToQuery(s) => Ok(Self::QueryResponse(s)),
|
|
91
91
|
workflow_command::Variant::ContinueAsNewWorkflowExecution(s) => {
|
|
92
|
-
Ok(
|
|
92
|
+
Ok(Self::ContinueAsNew(s))
|
|
93
93
|
}
|
|
94
|
-
workflow_command::Variant::CancelWorkflowExecution(s) =>
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
workflow_command::Variant::SetPatchMarker(s) => Ok(WFCommand::SetPatchMarker(s)),
|
|
94
|
+
workflow_command::Variant::CancelWorkflowExecution(s) => Ok(Self::CancelWorkflow(s)),
|
|
95
|
+
workflow_command::Variant::SetPatchMarker(s) => Ok(Self::SetPatchMarker(s)),
|
|
98
96
|
workflow_command::Variant::StartChildWorkflowExecution(s) => {
|
|
99
|
-
Ok(
|
|
97
|
+
Ok(Self::AddChildWorkflow(s))
|
|
100
98
|
}
|
|
101
99
|
workflow_command::Variant::RequestCancelExternalWorkflowExecution(s) => {
|
|
102
|
-
Ok(
|
|
100
|
+
Ok(Self::RequestCancelExternalWorkflow(s))
|
|
103
101
|
}
|
|
104
102
|
workflow_command::Variant::SignalExternalWorkflowExecution(s) => {
|
|
105
|
-
Ok(
|
|
106
|
-
}
|
|
107
|
-
workflow_command::Variant::CancelSignalWorkflow(s) => {
|
|
108
|
-
Ok(WFCommand::CancelSignalWorkflow(s))
|
|
103
|
+
Ok(Self::SignalExternalWorkflow(s))
|
|
109
104
|
}
|
|
105
|
+
workflow_command::Variant::CancelSignalWorkflow(s) => Ok(Self::CancelSignalWorkflow(s)),
|
|
110
106
|
workflow_command::Variant::CancelUnstartedChildWorkflowExecution(s) => {
|
|
111
|
-
Ok(
|
|
107
|
+
Ok(Self::CancelUnstartedChild(s))
|
|
112
108
|
}
|
|
113
109
|
}
|
|
114
110
|
}
|
|
@@ -170,9 +166,7 @@ where
|
|
|
170
166
|
+ Clone
|
|
171
167
|
+ Send
|
|
172
168
|
+ 'static,
|
|
173
|
-
<SM as StateMachine>::Event: TryFrom<HistoryEvent
|
|
174
|
-
<SM as StateMachine>::Event: TryFrom<CommandType>,
|
|
175
|
-
<SM as StateMachine>::Event: Display,
|
|
169
|
+
<SM as StateMachine>::Event: TryFrom<HistoryEvent> + TryFrom<CommandType> + Display,
|
|
176
170
|
WFMachinesError: From<<<SM as StateMachine>::Event as TryFrom<HistoryEvent>>::Error>,
|
|
177
171
|
<SM as StateMachine>::Command: Debug + Display,
|
|
178
172
|
<SM as StateMachine>::State: Display,
|
|
@@ -248,7 +242,8 @@ where
|
|
|
248
242
|
let res = self.cancel();
|
|
249
243
|
res.map_err(|e| match e {
|
|
250
244
|
MachineError::InvalidTransition => WFMachinesError::Fatal(format!(
|
|
251
|
-
"Invalid transition while attempting to cancel in {}",
|
|
245
|
+
"Invalid transition while attempting to cancel {} in {}",
|
|
246
|
+
self.name(),
|
|
252
247
|
self.state(),
|
|
253
248
|
)),
|
|
254
249
|
MachineError::Underlying(e) => e.into(),
|
|
@@ -107,7 +107,7 @@ impl PatchMachine {
|
|
|
107
107
|
.into(),
|
|
108
108
|
),
|
|
109
109
|
};
|
|
110
|
-
let mut machine =
|
|
110
|
+
let mut machine = Self {
|
|
111
111
|
state: initial_state,
|
|
112
112
|
shared_state: state,
|
|
113
113
|
};
|
|
@@ -210,7 +210,7 @@ impl TryFrom<HistoryEvent> for PatchMachineEvents {
|
|
|
210
210
|
|
|
211
211
|
fn try_from(e: HistoryEvent) -> Result<Self, Self::Error> {
|
|
212
212
|
match e.get_changed_marker_details() {
|
|
213
|
-
Some((id, _)) => Ok(
|
|
213
|
+
Some((id, _)) => Ok(Self::MarkerRecorded(id)),
|
|
214
214
|
_ => Err(WFMachinesError::Nondeterminism(format!(
|
|
215
215
|
"Change machine cannot handle this event: {}",
|
|
216
216
|
e
|
|
@@ -422,7 +422,9 @@ mod tests {
|
|
|
422
422
|
commands[0].command_type,
|
|
423
423
|
CommandType::ScheduleActivityTask as i32
|
|
424
424
|
);
|
|
425
|
-
let act = if
|
|
425
|
+
let act = if replaying {
|
|
426
|
+
wfm.get_next_activation().await
|
|
427
|
+
} else {
|
|
426
428
|
// Feed more history
|
|
427
429
|
wfm.new_history(
|
|
428
430
|
patch_marker_single_activity(marker_type)
|
|
@@ -431,8 +433,6 @@ mod tests {
|
|
|
431
433
|
.into(),
|
|
432
434
|
)
|
|
433
435
|
.await
|
|
434
|
-
} else {
|
|
435
|
-
wfm.get_next_activation().await
|
|
436
436
|
};
|
|
437
437
|
|
|
438
438
|
if marker_type == MarkerType::Deprecated {
|
|
@@ -514,7 +514,9 @@ mod tests {
|
|
|
514
514
|
if activity_id == expected_activity_id
|
|
515
515
|
);
|
|
516
516
|
|
|
517
|
-
let act = if
|
|
517
|
+
let act = if replaying {
|
|
518
|
+
wfm.get_next_activation().await
|
|
519
|
+
} else {
|
|
518
520
|
// Feed more history. Since we are *not* replaying, we *always* "have" the change
|
|
519
521
|
// and the history should have the has-change timer. v3 of course always has the change
|
|
520
522
|
// regardless.
|
|
@@ -525,8 +527,6 @@ mod tests {
|
|
|
525
527
|
.into(),
|
|
526
528
|
)
|
|
527
529
|
.await
|
|
528
|
-
} else {
|
|
529
|
-
wfm.get_next_activation().await
|
|
530
530
|
};
|
|
531
531
|
|
|
532
532
|
let act = act.unwrap();
|
|
@@ -42,6 +42,10 @@ fsm! {
|
|
|
42
42
|
SignalExternalCommandRecorded
|
|
43
43
|
--(SignalExternalWorkflowExecutionFailed(SignalExternalWorkflowExecutionFailedCause),
|
|
44
44
|
on_signal_external_workflow_execution_failed) --> Failed;
|
|
45
|
+
|
|
46
|
+
// Ignore any spurious cancellations after resolution
|
|
47
|
+
Cancelled --(Cancel) --> Cancelled;
|
|
48
|
+
Signaled --(Cancel) --> Signaled;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
#[derive(Default, Clone)]
|
|
@@ -262,7 +266,7 @@ impl Cancellable for SignalExternalMachine {
|
|
|
262
266
|
..Default::default()
|
|
263
267
|
}),
|
|
264
268
|
}
|
|
265
|
-
.into()]
|
|
269
|
+
.into()];
|
|
266
270
|
}
|
|
267
271
|
Some(_) => panic!("Signal external machine cancel produced unexpected result"),
|
|
268
272
|
None => (),
|
|
@@ -285,6 +289,7 @@ mod tests {
|
|
|
285
289
|
test_help::TestHistoryBuilder,
|
|
286
290
|
workflow::managed_wf::ManagedWFFunc,
|
|
287
291
|
};
|
|
292
|
+
use std::mem::discriminant;
|
|
288
293
|
use temporal_sdk_core_protos::coresdk::workflow_activation::{
|
|
289
294
|
wf_activation_job, WfActivationJob,
|
|
290
295
|
};
|
|
@@ -382,4 +387,20 @@ mod tests {
|
|
|
382
387
|
);
|
|
383
388
|
wfm.shutdown().await.unwrap();
|
|
384
389
|
}
|
|
390
|
+
|
|
391
|
+
#[test]
|
|
392
|
+
fn cancels_ignored_terminal() {
|
|
393
|
+
for state in [
|
|
394
|
+
SignalExternalMachineState::Cancelled(Cancelled {}),
|
|
395
|
+
Signaled {}.into(),
|
|
396
|
+
] {
|
|
397
|
+
let mut s = SignalExternalMachine {
|
|
398
|
+
state: state.clone(),
|
|
399
|
+
shared_state: Default::default(),
|
|
400
|
+
};
|
|
401
|
+
let cmds = s.cancel().unwrap();
|
|
402
|
+
assert_eq!(cmds.len(), 0);
|
|
403
|
+
assert_eq!(discriminant(&state), discriminant(&s.state));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
385
406
|
}
|
|
@@ -39,6 +39,10 @@ fsm! {
|
|
|
39
39
|
--(CommandCancelTimer, on_command_cancel_timer) --> CancelTimerCommandSent;
|
|
40
40
|
|
|
41
41
|
CancelTimerCommandSent --(TimerCanceled) --> Canceled;
|
|
42
|
+
|
|
43
|
+
// Ignore any spurious cancellations after resolution
|
|
44
|
+
Canceled --(Cancel) --> Canceled;
|
|
45
|
+
Fired --(Cancel) --> Fired;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
#[derive(Debug, derive_more::Display)]
|
|
@@ -195,13 +199,13 @@ impl StartCommandRecorded {
|
|
|
195
199
|
dat: SharedState,
|
|
196
200
|
attrs: TimerFiredEventAttributes,
|
|
197
201
|
) -> TimerMachineTransition<Fired> {
|
|
198
|
-
if dat.attrs.seq.to_string()
|
|
202
|
+
if dat.attrs.seq.to_string() == attrs.timer_id {
|
|
203
|
+
TransitionResult::ok(vec![TimerMachineCommand::Complete], Fired::default())
|
|
204
|
+
} else {
|
|
199
205
|
TransitionResult::Err(WFMachinesError::Fatal(format!(
|
|
200
206
|
"Timer fired event did not have expected timer id {}, it was {}!",
|
|
201
207
|
dat.attrs.seq, attrs.timer_id
|
|
202
208
|
)))
|
|
203
|
-
} else {
|
|
204
|
-
TransitionResult::ok(vec![TimerMachineCommand::Complete], Fired::default())
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
@@ -275,6 +279,7 @@ mod test {
|
|
|
275
279
|
workflow::managed_wf::ManagedWFFunc,
|
|
276
280
|
};
|
|
277
281
|
use rstest::{fixture, rstest};
|
|
282
|
+
use std::mem::discriminant;
|
|
278
283
|
use std::time::Duration;
|
|
279
284
|
|
|
280
285
|
#[fixture]
|
|
@@ -420,4 +425,17 @@ mod test {
|
|
|
420
425
|
assert_eq!(commands.len(), 0);
|
|
421
426
|
wfm.shutdown().await.unwrap();
|
|
422
427
|
}
|
|
428
|
+
|
|
429
|
+
#[test]
|
|
430
|
+
fn cancels_ignored_terminal() {
|
|
431
|
+
for state in [TimerMachineState::Canceled(Canceled {}), Fired {}.into()] {
|
|
432
|
+
let mut s = TimerMachine {
|
|
433
|
+
state: state.clone(),
|
|
434
|
+
shared_state: Default::default(),
|
|
435
|
+
};
|
|
436
|
+
let cmds = s.cancel().unwrap();
|
|
437
|
+
assert_eq!(cmds.len(), 0);
|
|
438
|
+
assert_eq!(discriminant(&state), discriminant(&s.state));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
423
441
|
}
|
|
@@ -79,7 +79,7 @@ mod machine_coverage_report {
|
|
|
79
79
|
workflow_task_state_machine::WorkflowTaskMachine,
|
|
80
80
|
};
|
|
81
81
|
use rustfsm::StateMachine;
|
|
82
|
-
use std::{fs::File, io::Write
|
|
82
|
+
use std::{fs::File, io::Write};
|
|
83
83
|
|
|
84
84
|
// This "test" needs to exist so that we have a way to join the spawned thread. Otherwise
|
|
85
85
|
// it'll just get abandoned.
|
|
@@ -89,7 +89,7 @@ mod machine_coverage_report {
|
|
|
89
89
|
#[ignore]
|
|
90
90
|
fn reporter() {
|
|
91
91
|
// Make sure thread handle exists
|
|
92
|
-
let _ = COVERAGE_SENDER
|
|
92
|
+
let _ = &*COVERAGE_SENDER;
|
|
93
93
|
// Join it
|
|
94
94
|
THREAD_HANDLE
|
|
95
95
|
.lock()
|
|
@@ -124,7 +124,7 @@ mod machine_coverage_report {
|
|
|
124
124
|
m @ "WorkflowTaskMachine" => cover_transitions(m, &mut wf_task, coverage),
|
|
125
125
|
m @ "FailWorkflowMachine" => cover_transitions(m, &mut fail_wf, coverage),
|
|
126
126
|
m @ "ContinueAsNewWorkflowMachine" => {
|
|
127
|
-
cover_transitions(m, &mut cont_as_new, coverage)
|
|
127
|
+
cover_transitions(m, &mut cont_as_new, coverage);
|
|
128
128
|
}
|
|
129
129
|
m @ "CancelWorkflowMachine" => cover_transitions(m, &mut cancel_wf, coverage),
|
|
130
130
|
m @ "PatchMachine" => cover_transitions(m, &mut version, coverage),
|
|
@@ -150,7 +150,7 @@ where
|
|
|
150
150
|
T: Into<wf_activation_job::Variant>,
|
|
151
151
|
{
|
|
152
152
|
fn from(v: T) -> Self {
|
|
153
|
-
|
|
153
|
+
Self::PushWFJob(v.into())
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -171,7 +171,7 @@ pub(crate) enum WFMachinesError {
|
|
|
171
171
|
|
|
172
172
|
impl From<TimestampOutOfSystemRangeError> for WFMachinesError {
|
|
173
173
|
fn from(_: TimestampOutOfSystemRangeError) -> Self {
|
|
174
|
-
|
|
174
|
+
Self::Fatal("Could not decode timestamp".to_string())
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -210,7 +210,7 @@ impl WorkflowMachines {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/// Returns true if workflow has seen a terminal command
|
|
213
|
-
pub(crate) fn workflow_is_finished(&self) -> bool {
|
|
213
|
+
pub(crate) const fn workflow_is_finished(&self) -> bool {
|
|
214
214
|
self.workflow_end_time.is_some()
|
|
215
215
|
}
|
|
216
216
|
|
|
@@ -486,7 +486,7 @@ impl WorkflowMachines {
|
|
|
486
486
|
}
|
|
487
487
|
|
|
488
488
|
fn set_current_time(&mut self, time: SystemTime) -> SystemTime {
|
|
489
|
-
if self.current_wf_time.
|
|
489
|
+
if self.current_wf_time.map_or(true, |t| t < time) {
|
|
490
490
|
self.current_wf_time = Some(time);
|
|
491
491
|
}
|
|
492
492
|
self.current_wf_time
|
|
@@ -503,7 +503,7 @@ impl WorkflowMachines {
|
|
|
503
503
|
let results = self.drive_me.fetch_workflow_iteration_output().await;
|
|
504
504
|
let jobs = self.handle_driven_results(results)?;
|
|
505
505
|
let has_new_lang_jobs = !jobs.is_empty();
|
|
506
|
-
for job in jobs
|
|
506
|
+
for job in jobs {
|
|
507
507
|
self.drive_me.send_job(job);
|
|
508
508
|
}
|
|
509
509
|
self.prepare_commands()?;
|
|
@@ -626,15 +626,15 @@ impl WorkflowMachines {
|
|
|
626
626
|
.machine_mut(c.machine)
|
|
627
627
|
.handle_command(c.command.command_type())?;
|
|
628
628
|
self.process_machine_responses(c.machine, machine_responses)?;
|
|
629
|
+
self.commands.push_back(c);
|
|
629
630
|
}
|
|
630
|
-
self.commands.push_back(c);
|
|
631
631
|
}
|
|
632
632
|
debug!(commands = %self.commands.display(), "prepared commands");
|
|
633
633
|
Ok(())
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
/// After a machine handles either an event or a command, it produces [MachineResponses] which
|
|
637
|
-
/// this function uses to drive sending jobs to lang,
|
|
637
|
+
/// this function uses to drive sending jobs to lang, triggering new workflow tasks, etc.
|
|
638
638
|
fn process_machine_responses(
|
|
639
639
|
&mut self,
|
|
640
640
|
sm: MachineKey,
|
|
@@ -695,7 +695,7 @@ impl WorkflowMachines {
|
|
|
695
695
|
self.current_wf_task_commands.push_back(timer);
|
|
696
696
|
}
|
|
697
697
|
WFCommand::CancelTimer(attrs) => {
|
|
698
|
-
jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?)
|
|
698
|
+
jobs.extend(self.process_cancellation(CommandID::Timer(attrs.seq))?);
|
|
699
699
|
}
|
|
700
700
|
WFCommand::AddActivity(attrs) => {
|
|
701
701
|
let seq = attrs.seq;
|
|
@@ -705,7 +705,7 @@ impl WorkflowMachines {
|
|
|
705
705
|
self.current_wf_task_commands.push_back(activity);
|
|
706
706
|
}
|
|
707
707
|
WFCommand::RequestCancelActivity(attrs) => {
|
|
708
|
-
jobs.extend(self.process_cancellation(CommandID::Activity(attrs.seq))?)
|
|
708
|
+
jobs.extend(self.process_cancellation(CommandID::Activity(attrs.seq))?);
|
|
709
709
|
}
|
|
710
710
|
WFCommand::CompleteWorkflow(attrs) => {
|
|
711
711
|
self.metrics.wf_completed();
|
|
@@ -815,7 +815,7 @@ impl WorkflowMachines {
|
|
|
815
815
|
self.current_wf_task_commands.push_back(sigm);
|
|
816
816
|
}
|
|
817
817
|
WFCommand::CancelSignalWorkflow(attrs) => {
|
|
818
|
-
jobs.extend(self.process_cancellation(CommandID::SignalExternal(attrs.seq))?)
|
|
818
|
+
jobs.extend(self.process_cancellation(CommandID::SignalExternal(attrs.seq))?);
|
|
819
819
|
}
|
|
820
820
|
WFCommand::QueryResponse(_) => {
|
|
821
821
|
// Nothing to do here, queries are handled above the machine level
|
|
@@ -840,7 +840,7 @@ impl WorkflowMachines {
|
|
|
840
840
|
self.current_wf_task_commands.push_back(CommandAndMachine {
|
|
841
841
|
command: c,
|
|
842
842
|
machine: m_key,
|
|
843
|
-
})
|
|
843
|
+
});
|
|
844
844
|
}
|
|
845
845
|
MachineResponse::PushWFJob(j) => {
|
|
846
846
|
jobs.push(j);
|
|
@@ -68,15 +68,14 @@ impl PendingActivations {
|
|
|
68
68
|
let mut inner = self.inner.write();
|
|
69
69
|
let mut key_queue = inner.queue.iter().copied();
|
|
70
70
|
let maybe_key = key_queue.position(|k| {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
false
|
|
75
|
-
}
|
|
71
|
+
inner
|
|
72
|
+
.activations
|
|
73
|
+
.get(k)
|
|
74
|
+
.map_or(false, |activation| predicate(&activation.run_id))
|
|
76
75
|
});
|
|
77
76
|
|
|
78
77
|
let maybe_key = maybe_key.map(|pos| inner.queue.remove(pos).unwrap());
|
|
79
|
-
|
|
78
|
+
maybe_key.and_then(|key| {
|
|
80
79
|
if let Some(pa) = inner.activations.remove(key) {
|
|
81
80
|
inner.by_run_id.remove(&pa.run_id);
|
|
82
81
|
Some(pa)
|
|
@@ -87,9 +86,7 @@ impl PendingActivations {
|
|
|
87
86
|
drop(inner); // Will deadlock when we recurse w/o this
|
|
88
87
|
self.pop()
|
|
89
88
|
}
|
|
90
|
-
}
|
|
91
|
-
None
|
|
92
|
-
}
|
|
89
|
+
})
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
pub fn pop(&self) -> Option<WfActivation> {
|
|
@@ -119,17 +116,19 @@ fn merge_joblists(
|
|
|
119
116
|
.as_mut_slice()
|
|
120
117
|
.sort_by(evictions_always_last_compare);
|
|
121
118
|
// Drop any duplicate evictions
|
|
122
|
-
let truncate_len =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
let truncate_len = existing_list
|
|
120
|
+
.iter()
|
|
121
|
+
.rev()
|
|
122
|
+
.position(|j| {
|
|
123
|
+
!matches!(
|
|
124
|
+
j.variant,
|
|
125
|
+
Some(wf_activation_job::Variant::RemoveFromCache(_))
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
.map_or(1, |last_non_evict_job| {
|
|
129
|
+
existing_list.len() - last_non_evict_job + 1
|
|
130
|
+
});
|
|
131
|
+
existing_list.truncate(truncate_len);
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
fn evictions_always_last_compare(a: &WfActivationJob, b: &WfActivationJob) -> Ordering {
|
|
@@ -167,8 +166,14 @@ mod tests {
|
|
|
167
166
|
run_id: rid1.clone(),
|
|
168
167
|
..Default::default()
|
|
169
168
|
});
|
|
170
|
-
pas.push(create_evict_activation(
|
|
171
|
-
|
|
169
|
+
pas.push(create_evict_activation(
|
|
170
|
+
rid1.clone(),
|
|
171
|
+
"whatever".to_string(),
|
|
172
|
+
));
|
|
173
|
+
pas.push(create_evict_activation(
|
|
174
|
+
rid2.clone(),
|
|
175
|
+
"whatever".to_string(),
|
|
176
|
+
));
|
|
172
177
|
pas.push(WfActivation {
|
|
173
178
|
run_id: rid2.clone(),
|
|
174
179
|
..Default::default()
|