@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
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
use anyhow::anyhow;
|
|
2
|
+
use futures::future::join_all;
|
|
3
|
+
use std::time::Duration;
|
|
4
|
+
use temporal_sdk::{
|
|
5
|
+
act_cancelled, act_is_cancelled, ActivityCancelledError, CancellableFuture,
|
|
6
|
+
LocalActivityOptions, WfContext, WorkflowResult,
|
|
7
|
+
};
|
|
8
|
+
use temporal_sdk_core_protos::coresdk::{
|
|
9
|
+
common::RetryPolicy, workflow_commands::ActivityCancellationType, AsJsonPayloadExt,
|
|
10
|
+
};
|
|
11
|
+
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
12
|
+
use tokio_util::sync::CancellationToken;
|
|
13
|
+
|
|
14
|
+
pub async fn echo(e: String) -> anyhow::Result<String> {
|
|
15
|
+
Ok(e)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pub async fn one_local_activity_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
19
|
+
let initial_workflow_time = ctx.workflow_time().expect("Workflow time should be set");
|
|
20
|
+
ctx.local_activity(LocalActivityOptions {
|
|
21
|
+
activity_type: "echo_activity".to_string(),
|
|
22
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
23
|
+
..Default::default()
|
|
24
|
+
})
|
|
25
|
+
.await;
|
|
26
|
+
// Verify LA execution advances the clock
|
|
27
|
+
assert!(initial_workflow_time < ctx.workflow_time().unwrap());
|
|
28
|
+
Ok(().into())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[tokio::test]
|
|
32
|
+
async fn one_local_activity() {
|
|
33
|
+
let wf_name = "one_local_activity";
|
|
34
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
35
|
+
let mut worker = starter.worker().await;
|
|
36
|
+
worker.register_wf(wf_name.to_owned(), one_local_activity_wf);
|
|
37
|
+
worker.register_activity("echo_activity", echo);
|
|
38
|
+
|
|
39
|
+
worker
|
|
40
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
41
|
+
.await
|
|
42
|
+
.unwrap();
|
|
43
|
+
worker.run_until_done().await.unwrap();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub async fn local_act_concurrent_with_timer_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
47
|
+
let la = ctx.local_activity(LocalActivityOptions {
|
|
48
|
+
activity_type: "echo_activity".to_string(),
|
|
49
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
50
|
+
..Default::default()
|
|
51
|
+
});
|
|
52
|
+
let timer = ctx.timer(Duration::from_secs(1));
|
|
53
|
+
tokio::join!(la, timer);
|
|
54
|
+
Ok(().into())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[tokio::test]
|
|
58
|
+
async fn local_act_concurrent_with_timer() {
|
|
59
|
+
let wf_name = "local_act_concurrent_with_timer";
|
|
60
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
61
|
+
let mut worker = starter.worker().await;
|
|
62
|
+
worker.register_wf(wf_name.to_owned(), local_act_concurrent_with_timer_wf);
|
|
63
|
+
worker.register_activity("echo_activity", echo);
|
|
64
|
+
|
|
65
|
+
worker
|
|
66
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
67
|
+
.await
|
|
68
|
+
.unwrap();
|
|
69
|
+
worker.run_until_done().await.unwrap();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
pub async fn local_act_then_timer_then_wait(ctx: WfContext) -> WorkflowResult<()> {
|
|
73
|
+
let la = ctx.local_activity(LocalActivityOptions {
|
|
74
|
+
activity_type: "echo_activity".to_string(),
|
|
75
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
76
|
+
..Default::default()
|
|
77
|
+
});
|
|
78
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
79
|
+
let res = la.await;
|
|
80
|
+
assert!(res.completed_ok());
|
|
81
|
+
Ok(().into())
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[tokio::test]
|
|
85
|
+
async fn local_act_then_timer_then_wait_result() {
|
|
86
|
+
let wf_name = "local_act_then_timer_then_wait_result";
|
|
87
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
88
|
+
let mut worker = starter.worker().await;
|
|
89
|
+
worker.register_wf(wf_name.to_owned(), local_act_then_timer_then_wait);
|
|
90
|
+
worker.register_activity("echo_activity", echo);
|
|
91
|
+
|
|
92
|
+
worker
|
|
93
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
94
|
+
.await
|
|
95
|
+
.unwrap();
|
|
96
|
+
worker.run_until_done().await.unwrap();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#[tokio::test]
|
|
100
|
+
async fn long_running_local_act_with_timer() {
|
|
101
|
+
let wf_name = "long_running_local_act_with_timer";
|
|
102
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
103
|
+
starter.wft_timeout(Duration::from_secs(1));
|
|
104
|
+
let mut worker = starter.worker().await;
|
|
105
|
+
worker.register_wf(wf_name.to_owned(), local_act_then_timer_then_wait);
|
|
106
|
+
worker.register_activity("echo_activity", |str: String| async {
|
|
107
|
+
tokio::time::sleep(Duration::from_secs(4)).await;
|
|
108
|
+
Ok(str)
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
worker
|
|
112
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
113
|
+
.await
|
|
114
|
+
.unwrap();
|
|
115
|
+
worker.run_until_done().await.unwrap();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
119
|
+
let las: Vec<_> = (1..=50)
|
|
120
|
+
.map(|i| {
|
|
121
|
+
ctx.local_activity(LocalActivityOptions {
|
|
122
|
+
activity_type: "echo_activity".to_string(),
|
|
123
|
+
input: format!("Hi {}", i)
|
|
124
|
+
.as_json_payload()
|
|
125
|
+
.expect("serializes fine"),
|
|
126
|
+
..Default::default()
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
.collect();
|
|
130
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
131
|
+
join_all(las).await;
|
|
132
|
+
Ok(().into())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#[tokio::test]
|
|
136
|
+
async fn local_act_fanout() {
|
|
137
|
+
let wf_name = "local_act_fanout";
|
|
138
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
139
|
+
starter.max_local_at(1);
|
|
140
|
+
let mut worker = starter.worker().await;
|
|
141
|
+
worker.register_wf(wf_name.to_owned(), local_act_fanout_wf);
|
|
142
|
+
worker.register_activity("echo_activity", echo);
|
|
143
|
+
|
|
144
|
+
worker
|
|
145
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
146
|
+
.await
|
|
147
|
+
.unwrap();
|
|
148
|
+
worker.run_until_done().await.unwrap();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[tokio::test]
|
|
152
|
+
async fn local_act_retry_timer_backoff() {
|
|
153
|
+
let wf_name = "local_act_retry_timer_backoff";
|
|
154
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
155
|
+
let mut worker = starter.worker().await;
|
|
156
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
157
|
+
let res = ctx
|
|
158
|
+
.local_activity(LocalActivityOptions {
|
|
159
|
+
activity_type: "echo".to_string(),
|
|
160
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
161
|
+
retry_policy: RetryPolicy {
|
|
162
|
+
initial_interval: Some(Duration::from_micros(15).into()),
|
|
163
|
+
// We want two local backoffs that are short. Third backoff will use timer
|
|
164
|
+
backoff_coefficient: 1_000.,
|
|
165
|
+
maximum_interval: Some(Duration::from_millis(1500).into()),
|
|
166
|
+
maximum_attempts: 4,
|
|
167
|
+
non_retryable_error_types: vec![],
|
|
168
|
+
},
|
|
169
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
170
|
+
..Default::default()
|
|
171
|
+
})
|
|
172
|
+
.await;
|
|
173
|
+
assert!(res.failed());
|
|
174
|
+
Ok(().into())
|
|
175
|
+
});
|
|
176
|
+
worker.register_activity("echo", |_: String| async {
|
|
177
|
+
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
let run_id = worker
|
|
181
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
182
|
+
.await
|
|
183
|
+
.unwrap();
|
|
184
|
+
worker.run_until_done().await.unwrap();
|
|
185
|
+
starter
|
|
186
|
+
.fetch_history_and_replay(wf_name, run_id, &mut worker)
|
|
187
|
+
.await
|
|
188
|
+
.unwrap();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[rstest::rstest]
|
|
192
|
+
#[case::wait(ActivityCancellationType::WaitCancellationCompleted)]
|
|
193
|
+
#[case::try_cancel(ActivityCancellationType::TryCancel)]
|
|
194
|
+
#[case::abandon(ActivityCancellationType::Abandon)]
|
|
195
|
+
#[tokio::test]
|
|
196
|
+
async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
|
|
197
|
+
let wf_name = format!("cancel_immediate_{:?}", cancel_type);
|
|
198
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
199
|
+
let mut worker = starter.worker().await;
|
|
200
|
+
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
201
|
+
let la = ctx.local_activity(LocalActivityOptions {
|
|
202
|
+
activity_type: "echo".to_string(),
|
|
203
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
204
|
+
cancel_type,
|
|
205
|
+
..Default::default()
|
|
206
|
+
});
|
|
207
|
+
la.cancel(&ctx);
|
|
208
|
+
let resolution = la.await;
|
|
209
|
+
assert!(resolution.cancelled());
|
|
210
|
+
Ok(().into())
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
214
|
+
let manual_cancel = CancellationToken::new();
|
|
215
|
+
let manual_cancel_act = manual_cancel.clone();
|
|
216
|
+
|
|
217
|
+
worker.register_activity("echo", move |_: String| {
|
|
218
|
+
let manual_cancel_act = manual_cancel_act.clone();
|
|
219
|
+
async move {
|
|
220
|
+
tokio::select! {
|
|
221
|
+
_ = tokio::time::sleep(Duration::from_secs(10)) => {},
|
|
222
|
+
_ = act_cancelled() => {
|
|
223
|
+
return Err(anyhow!(ActivityCancelledError::default()))
|
|
224
|
+
}
|
|
225
|
+
_ = manual_cancel_act.cancelled() => {}
|
|
226
|
+
}
|
|
227
|
+
Ok(())
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
worker
|
|
232
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
233
|
+
.await
|
|
234
|
+
.unwrap();
|
|
235
|
+
worker
|
|
236
|
+
.run_until_done_shutdown_hook(|| manual_cancel.cancel())
|
|
237
|
+
.await
|
|
238
|
+
.unwrap();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#[rstest::rstest]
|
|
242
|
+
#[case::while_running(None)]
|
|
243
|
+
#[case::while_backing_off(Some(Duration::from_millis(1500)))]
|
|
244
|
+
#[case::while_backing_off_locally(Some(Duration::from_millis(150)))]
|
|
245
|
+
#[tokio::test]
|
|
246
|
+
async fn cancel_after_act_starts(
|
|
247
|
+
#[case] cancel_on_backoff: Option<Duration>,
|
|
248
|
+
#[values(
|
|
249
|
+
ActivityCancellationType::WaitCancellationCompleted,
|
|
250
|
+
ActivityCancellationType::TryCancel,
|
|
251
|
+
ActivityCancellationType::Abandon
|
|
252
|
+
)]
|
|
253
|
+
cancel_type: ActivityCancellationType,
|
|
254
|
+
) {
|
|
255
|
+
let wf_name = format!(
|
|
256
|
+
"cancel_after_act_starts_timer_{:?}_{:?}",
|
|
257
|
+
cancel_on_backoff, cancel_type
|
|
258
|
+
);
|
|
259
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
260
|
+
starter.wft_timeout(Duration::from_secs(1));
|
|
261
|
+
let mut worker = starter.worker().await;
|
|
262
|
+
let bo_dur = cancel_on_backoff.unwrap_or_else(|| Duration::from_secs(1));
|
|
263
|
+
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
264
|
+
let la = ctx.local_activity(LocalActivityOptions {
|
|
265
|
+
activity_type: "echo".to_string(),
|
|
266
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
267
|
+
retry_policy: RetryPolicy {
|
|
268
|
+
initial_interval: Some(bo_dur.into()),
|
|
269
|
+
backoff_coefficient: 1.,
|
|
270
|
+
maximum_interval: Some(bo_dur.into()),
|
|
271
|
+
// Retry forever until cancelled
|
|
272
|
+
..Default::default()
|
|
273
|
+
},
|
|
274
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
275
|
+
cancel_type,
|
|
276
|
+
..Default::default()
|
|
277
|
+
});
|
|
278
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
279
|
+
// Note that this cancel can't go through for *two* WF tasks, because we do a full heartbeat
|
|
280
|
+
// before the timer (LA hasn't resolved), and then the timer fired event won't appear in
|
|
281
|
+
// history until *after* the next WFT because we force generated it when we sent the timer
|
|
282
|
+
// command.
|
|
283
|
+
la.cancel(&ctx);
|
|
284
|
+
// This extra timer is here to ensure the presence of another WF task doesn't mess up
|
|
285
|
+
// resolving the LA with cancel on replay
|
|
286
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
287
|
+
let resolution = la.await;
|
|
288
|
+
assert!(resolution.cancelled());
|
|
289
|
+
Ok(().into())
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
293
|
+
let manual_cancel = CancellationToken::new();
|
|
294
|
+
let manual_cancel_act = manual_cancel.clone();
|
|
295
|
+
|
|
296
|
+
worker.register_activity("echo", move |_: String| {
|
|
297
|
+
let manual_cancel_act = manual_cancel_act.clone();
|
|
298
|
+
async move {
|
|
299
|
+
if cancel_on_backoff.is_some() {
|
|
300
|
+
if act_is_cancelled() {
|
|
301
|
+
return Err(anyhow!(ActivityCancelledError::default()));
|
|
302
|
+
}
|
|
303
|
+
// Just fail constantly so we get stuck on the backoff timer
|
|
304
|
+
return Err(anyhow!("Oh no I failed!"));
|
|
305
|
+
} else {
|
|
306
|
+
tokio::select! {
|
|
307
|
+
_ = tokio::time::sleep(Duration::from_secs(100)) => {},
|
|
308
|
+
_ = act_cancelled() => {
|
|
309
|
+
return Err(anyhow!(ActivityCancelledError::default()))
|
|
310
|
+
}
|
|
311
|
+
_ = manual_cancel_act.cancelled() => {
|
|
312
|
+
return Ok(())
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
Err(anyhow!("Oh no I failed!"))
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
worker
|
|
321
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
322
|
+
.await
|
|
323
|
+
.unwrap();
|
|
324
|
+
worker
|
|
325
|
+
.run_until_done_shutdown_hook(|| manual_cancel.cancel())
|
|
326
|
+
.await
|
|
327
|
+
.unwrap();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
#[rstest::rstest]
|
|
331
|
+
#[case::schedule(true)]
|
|
332
|
+
#[case::start(false)]
|
|
333
|
+
#[tokio::test]
|
|
334
|
+
async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
335
|
+
let wf_name = format!(
|
|
336
|
+
"{}_to_close_timeout",
|
|
337
|
+
if is_schedule { "schedule" } else { "start" }
|
|
338
|
+
);
|
|
339
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
340
|
+
let mut worker = starter.worker().await;
|
|
341
|
+
let (sched, start) = if is_schedule {
|
|
342
|
+
(Some(Duration::from_secs(2)), None)
|
|
343
|
+
} else {
|
|
344
|
+
(None, Some(Duration::from_secs(2)))
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
worker.register_wf(wf_name.to_owned(), move |ctx: WfContext| async move {
|
|
348
|
+
let res = ctx
|
|
349
|
+
.local_activity(LocalActivityOptions {
|
|
350
|
+
activity_type: "echo".to_string(),
|
|
351
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
352
|
+
retry_policy: RetryPolicy {
|
|
353
|
+
initial_interval: Some(Duration::from_micros(15).into()),
|
|
354
|
+
backoff_coefficient: 1_000.,
|
|
355
|
+
maximum_interval: Some(Duration::from_millis(1500).into()),
|
|
356
|
+
maximum_attempts: 4,
|
|
357
|
+
non_retryable_error_types: vec![],
|
|
358
|
+
},
|
|
359
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
360
|
+
schedule_to_close_timeout: sched,
|
|
361
|
+
start_to_close_timeout: start,
|
|
362
|
+
..Default::default()
|
|
363
|
+
})
|
|
364
|
+
.await;
|
|
365
|
+
assert!(res.timed_out());
|
|
366
|
+
Ok(().into())
|
|
367
|
+
});
|
|
368
|
+
worker.register_activity("echo", |_: String| async {
|
|
369
|
+
tokio::select! {
|
|
370
|
+
_ = tokio::time::sleep(Duration::from_secs(100)) => {},
|
|
371
|
+
_ = act_cancelled() => {
|
|
372
|
+
return Err(anyhow!(ActivityCancelledError::default()))
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
Ok(())
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
worker
|
|
379
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
380
|
+
.await
|
|
381
|
+
.unwrap();
|
|
382
|
+
worker.run_until_done().await.unwrap();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
#[rstest::rstest]
|
|
386
|
+
#[case::cached(true)]
|
|
387
|
+
#[case::not_cached(false)]
|
|
388
|
+
#[tokio::test]
|
|
389
|
+
async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
390
|
+
let wf_name = format!(
|
|
391
|
+
"schedule_to_close_timeout_across_timer_backoff_{}",
|
|
392
|
+
if cached { "cached" } else { "not_cached" }
|
|
393
|
+
);
|
|
394
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
395
|
+
if !cached {
|
|
396
|
+
starter.max_cached_workflows(0);
|
|
397
|
+
}
|
|
398
|
+
let mut worker = starter.worker().await;
|
|
399
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
400
|
+
let res = ctx
|
|
401
|
+
.local_activity(LocalActivityOptions {
|
|
402
|
+
activity_type: "echo".to_string(),
|
|
403
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
404
|
+
retry_policy: RetryPolicy {
|
|
405
|
+
initial_interval: Some(Duration::from_micros(15).into()),
|
|
406
|
+
backoff_coefficient: 1_000.,
|
|
407
|
+
maximum_interval: Some(Duration::from_millis(1500).into()),
|
|
408
|
+
maximum_attempts: 40,
|
|
409
|
+
non_retryable_error_types: vec![],
|
|
410
|
+
},
|
|
411
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
412
|
+
schedule_to_close_timeout: Some(Duration::from_secs(3)),
|
|
413
|
+
..Default::default()
|
|
414
|
+
})
|
|
415
|
+
.await;
|
|
416
|
+
assert!(res.timed_out());
|
|
417
|
+
Ok(().into())
|
|
418
|
+
});
|
|
419
|
+
worker.register_activity("echo", |_: String| async {
|
|
420
|
+
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
worker
|
|
424
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
425
|
+
.await
|
|
426
|
+
.unwrap();
|
|
427
|
+
worker.run_until_done().await.unwrap();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
#[tokio::test]
|
|
431
|
+
async fn eviction_wont_make_local_act_get_dropped() {
|
|
432
|
+
let wf_name = "eviction_wont_make_local_act_get_dropped";
|
|
433
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
434
|
+
starter.max_cached_workflows(0);
|
|
435
|
+
starter.wft_timeout(Duration::from_secs(1));
|
|
436
|
+
let mut worker = starter.worker().await;
|
|
437
|
+
worker.register_wf(wf_name.to_owned(), local_act_then_timer_then_wait);
|
|
438
|
+
worker.register_activity("echo_activity", |str: String| async {
|
|
439
|
+
tokio::time::sleep(Duration::from_secs(4)).await;
|
|
440
|
+
Ok(str)
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
worker
|
|
444
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
445
|
+
.await
|
|
446
|
+
.unwrap();
|
|
447
|
+
worker.run_until_done().await.unwrap();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
#[tokio::test]
|
|
451
|
+
async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
452
|
+
let wf_name = "timer_backoff_concurrent_with_non_timer_backoff";
|
|
453
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
454
|
+
let mut worker = starter.worker().await;
|
|
455
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
456
|
+
let r1 = ctx.local_activity(LocalActivityOptions {
|
|
457
|
+
activity_type: "echo".to_string(),
|
|
458
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
459
|
+
retry_policy: RetryPolicy {
|
|
460
|
+
initial_interval: Some(Duration::from_micros(15).into()),
|
|
461
|
+
backoff_coefficient: 1_000.,
|
|
462
|
+
maximum_interval: Some(Duration::from_millis(1500).into()),
|
|
463
|
+
maximum_attempts: 4,
|
|
464
|
+
non_retryable_error_types: vec![],
|
|
465
|
+
},
|
|
466
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
467
|
+
..Default::default()
|
|
468
|
+
});
|
|
469
|
+
let r2 = ctx.local_activity(LocalActivityOptions {
|
|
470
|
+
activity_type: "echo".to_string(),
|
|
471
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
472
|
+
retry_policy: RetryPolicy {
|
|
473
|
+
initial_interval: Some(Duration::from_millis(15).into()),
|
|
474
|
+
backoff_coefficient: 10.,
|
|
475
|
+
maximum_interval: Some(Duration::from_millis(1500).into()),
|
|
476
|
+
maximum_attempts: 4,
|
|
477
|
+
non_retryable_error_types: vec![],
|
|
478
|
+
},
|
|
479
|
+
timer_backoff_threshold: Some(Duration::from_secs(10)),
|
|
480
|
+
..Default::default()
|
|
481
|
+
});
|
|
482
|
+
let (r1, r2) = tokio::join!(r1, r2);
|
|
483
|
+
assert!(r1.failed());
|
|
484
|
+
assert!(r2.failed());
|
|
485
|
+
Ok(().into())
|
|
486
|
+
});
|
|
487
|
+
worker.register_activity("echo", |_: String| async {
|
|
488
|
+
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
worker
|
|
492
|
+
.submit_wf(wf_name.to_owned(), wf_name.to_owned(), vec![])
|
|
493
|
+
.await
|
|
494
|
+
.unwrap();
|
|
495
|
+
worker.run_until_done().await.unwrap();
|
|
496
|
+
}
|
|
@@ -2,12 +2,12 @@ use std::{
|
|
|
2
2
|
sync::atomic::{AtomicBool, Ordering},
|
|
3
3
|
time::Duration,
|
|
4
4
|
};
|
|
5
|
-
use
|
|
6
|
-
use
|
|
5
|
+
use temporal_sdk::{WfContext, WorkflowResult};
|
|
6
|
+
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
7
7
|
|
|
8
8
|
const MY_PATCH_ID: &str = "integ_test_change_name";
|
|
9
9
|
|
|
10
|
-
pub async fn changes_wf(
|
|
10
|
+
pub async fn changes_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
11
11
|
if ctx.patched(MY_PATCH_ID) {
|
|
12
12
|
ctx.timer(Duration::from_millis(100)).await;
|
|
13
13
|
} else {
|
|
@@ -34,13 +34,12 @@ async fn writes_change_markers() {
|
|
|
34
34
|
.await
|
|
35
35
|
.unwrap();
|
|
36
36
|
worker.run_until_done().await.unwrap();
|
|
37
|
-
starter.shutdown().await;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/// This one simulates a run as if the worker had the "old" code, then it fails at the end as
|
|
41
40
|
/// a cheapo way of being re-run, at which point it runs with change checks and the "new" code.
|
|
42
41
|
static DID_DIE: AtomicBool = AtomicBool::new(false);
|
|
43
|
-
pub async fn no_change_then_change_wf(
|
|
42
|
+
pub async fn no_change_then_change_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
44
43
|
if DID_DIE.load(Ordering::Acquire) {
|
|
45
44
|
assert!(!ctx.patched(MY_PATCH_ID));
|
|
46
45
|
}
|
|
@@ -70,11 +69,10 @@ async fn can_add_change_markers() {
|
|
|
70
69
|
.await
|
|
71
70
|
.unwrap();
|
|
72
71
|
worker.run_until_done().await.unwrap();
|
|
73
|
-
starter.shutdown().await;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
static DID_DIE_2: AtomicBool = AtomicBool::new(false);
|
|
77
|
-
pub async fn replay_with_change_marker_wf(
|
|
75
|
+
pub async fn replay_with_change_marker_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
78
76
|
assert!(ctx.patched(MY_PATCH_ID));
|
|
79
77
|
ctx.timer(Duration::from_millis(200)).await;
|
|
80
78
|
if !DID_DIE_2.load(Ordering::Acquire) {
|
|
@@ -96,5 +94,4 @@ async fn replaying_with_patch_marker() {
|
|
|
96
94
|
.await
|
|
97
95
|
.unwrap();
|
|
98
96
|
worker.run_until_done().await.unwrap();
|
|
99
|
-
starter.shutdown().await;
|
|
100
97
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
use assert_matches::assert_matches;
|
|
2
|
+
use std::time::Duration;
|
|
3
|
+
use temporal_sdk_core::{replay::mock_gateway_from_history, ServerGatewayApis};
|
|
4
|
+
use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
|
|
5
|
+
use temporal_sdk_core_protos::{
|
|
6
|
+
coresdk::{
|
|
7
|
+
workflow_activation::remove_from_cache::EvictionReason,
|
|
8
|
+
workflow_commands::{ScheduleActivity, StartTimer},
|
|
9
|
+
workflow_completion::WorkflowActivationCompletion,
|
|
10
|
+
},
|
|
11
|
+
temporal::api::workflowservice::v1::PollWorkflowTaskQueueResponse,
|
|
12
|
+
};
|
|
13
|
+
use temporal_sdk_core_test_utils::{
|
|
14
|
+
history_from_proto_binary, init_core_replay_preloaded, CoreTestHelpers,
|
|
15
|
+
};
|
|
16
|
+
use tokio::join;
|
|
17
|
+
|
|
18
|
+
#[tokio::test]
|
|
19
|
+
async fn timer_workflow_replay() {
|
|
20
|
+
let (core, task_q) = init_core_replay_preloaded(
|
|
21
|
+
"timer_workflow_replay",
|
|
22
|
+
&history_from_proto_binary("histories/timer_workflow_history.bin")
|
|
23
|
+
.await
|
|
24
|
+
.unwrap(),
|
|
25
|
+
);
|
|
26
|
+
let task = core.poll_workflow_activation(&task_q).await.unwrap();
|
|
27
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
28
|
+
&task_q,
|
|
29
|
+
task.run_id,
|
|
30
|
+
vec![StartTimer {
|
|
31
|
+
seq: 0,
|
|
32
|
+
start_to_fire_timeout: Some(Duration::from_secs(1).into()),
|
|
33
|
+
}
|
|
34
|
+
.into()],
|
|
35
|
+
))
|
|
36
|
+
.await
|
|
37
|
+
.unwrap();
|
|
38
|
+
let task = core.poll_workflow_activation(&task_q).await.unwrap();
|
|
39
|
+
// Verify that an in-progress poll is interrupted by completion finishing processing history
|
|
40
|
+
let act_poll_fut = async {
|
|
41
|
+
assert_matches!(
|
|
42
|
+
core.poll_activity_task(&task_q).await,
|
|
43
|
+
Err(PollActivityError::ShutDown)
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
let poll_fut = async {
|
|
47
|
+
assert_matches!(
|
|
48
|
+
core.poll_workflow_activation(&task_q).await,
|
|
49
|
+
Err(PollWfError::ShutDown)
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
let complete_fut = async {
|
|
53
|
+
core.complete_execution(&task_q, &task.run_id).await;
|
|
54
|
+
};
|
|
55
|
+
join!(act_poll_fut, poll_fut, complete_fut);
|
|
56
|
+
|
|
57
|
+
// Subsequent polls should still return shutdown
|
|
58
|
+
assert_matches!(
|
|
59
|
+
core.poll_workflow_activation(&task_q).await,
|
|
60
|
+
Err(PollWfError::ShutDown)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
core.shutdown().await;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Regression test to verify mock replayers don't interfere with each other
|
|
67
|
+
#[tokio::test]
|
|
68
|
+
async fn two_cores_replay() {
|
|
69
|
+
let hist = history_from_proto_binary("histories/fail_wf_task.bin")
|
|
70
|
+
.await
|
|
71
|
+
.unwrap();
|
|
72
|
+
|
|
73
|
+
let mock_1 = mock_gateway_from_history(&hist, "a");
|
|
74
|
+
let mock_2 = mock_gateway_from_history(&hist, "b");
|
|
75
|
+
assert_ne!(
|
|
76
|
+
mock_1
|
|
77
|
+
.poll_workflow_task("a".to_string(), false)
|
|
78
|
+
.await
|
|
79
|
+
.unwrap(),
|
|
80
|
+
PollWorkflowTaskQueueResponse::default()
|
|
81
|
+
);
|
|
82
|
+
assert_ne!(
|
|
83
|
+
mock_2
|
|
84
|
+
.poll_workflow_task("b".to_string(), false)
|
|
85
|
+
.await
|
|
86
|
+
.unwrap(),
|
|
87
|
+
PollWorkflowTaskQueueResponse::default()
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#[tokio::test]
|
|
92
|
+
async fn workflow_nondeterministic_replay() {
|
|
93
|
+
let (core, task_q) = init_core_replay_preloaded(
|
|
94
|
+
"timer_workflow_replay",
|
|
95
|
+
&history_from_proto_binary("histories/timer_workflow_history.bin")
|
|
96
|
+
.await
|
|
97
|
+
.unwrap(),
|
|
98
|
+
);
|
|
99
|
+
let task = core.poll_workflow_activation(&task_q).await.unwrap();
|
|
100
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
101
|
+
&task_q,
|
|
102
|
+
task.run_id,
|
|
103
|
+
vec![ScheduleActivity {
|
|
104
|
+
seq: 0,
|
|
105
|
+
activity_id: "0".to_string(),
|
|
106
|
+
activity_type: "fake_act".to_string(),
|
|
107
|
+
..Default::default()
|
|
108
|
+
}
|
|
109
|
+
.into()],
|
|
110
|
+
))
|
|
111
|
+
.await
|
|
112
|
+
.unwrap();
|
|
113
|
+
let task = core.poll_workflow_activation(&task_q).await.unwrap();
|
|
114
|
+
assert_eq!(task.eviction_reason(), Some(EvictionReason::Nondeterminism));
|
|
115
|
+
// Complete eviction
|
|
116
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::empty(&task_q, task.run_id))
|
|
117
|
+
.await
|
|
118
|
+
.unwrap();
|
|
119
|
+
// Call shutdown explicitly because we saw a nondeterminism eviction
|
|
120
|
+
core.shutdown().await;
|
|
121
|
+
assert_matches!(
|
|
122
|
+
core.poll_workflow_activation(&task_q).await,
|
|
123
|
+
Err(PollWfError::ShutDown)
|
|
124
|
+
);
|
|
125
|
+
}
|