@temporalio/core-bridge 1.15.0 → 1.16.1
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 +172 -70
- package/lib/native.d.ts +1 -1
- package/package.json +2 -2
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/per-pr.yml +6 -6
- package/sdk-core/AGENTS.md +41 -30
- package/sdk-core/Cargo.toml +3 -0
- package/sdk-core/README.md +15 -9
- package/sdk-core/crates/client/Cargo.toml +4 -0
- package/sdk-core/crates/client/README.md +139 -0
- package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
- package/sdk-core/crates/client/src/callback_based.rs +7 -0
- package/sdk-core/crates/client/src/errors.rs +294 -0
- package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
- package/sdk-core/crates/client/src/lib.rs +920 -1326
- package/sdk-core/crates/client/src/metrics.rs +24 -33
- package/sdk-core/crates/client/src/options_structs.rs +457 -0
- package/sdk-core/crates/client/src/replaceable.rs +5 -4
- package/sdk-core/crates/client/src/request_extensions.rs +8 -9
- package/sdk-core/crates/client/src/retry.rs +99 -54
- package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
- package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
- package/sdk-core/crates/common/Cargo.toml +61 -2
- package/sdk-core/crates/common/build.rs +742 -12
- package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
- package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
- package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
- package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
- package/sdk-core/crates/common/src/activity_definition.rs +20 -0
- package/sdk-core/crates/common/src/data_converters.rs +770 -0
- package/sdk-core/crates/common/src/envconfig.rs +5 -0
- package/sdk-core/crates/common/src/lib.rs +15 -211
- package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
- package/sdk-core/crates/common/src/priority.rs +110 -0
- package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
- package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
- package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
- package/sdk-core/crates/common/src/protos/mod.rs +122 -27
- package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
- package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
- package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
- package/sdk-core/crates/common/src/telemetry.rs +264 -4
- package/sdk-core/crates/common/src/worker.rs +68 -603
- package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
- package/sdk-core/crates/macros/Cargo.toml +5 -1
- package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
- package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
- package/sdk-core/crates/macros/src/lib.rs +138 -512
- package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
- package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
- package/sdk-core/crates/sdk/Cargo.toml +19 -6
- package/sdk-core/crates/sdk/README.md +415 -0
- package/sdk-core/crates/sdk/src/activities.rs +417 -0
- package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
- package/sdk-core/crates/sdk/src/lib.rs +757 -442
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
- package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
- package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
- package/sdk-core/crates/sdk/src/workflows.rs +711 -0
- package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
- package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
- package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
- package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
- package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
- package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
- package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
- package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
- package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
- package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
- package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
- package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
- package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
- package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
- package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
- package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
- package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
- package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
- package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
- package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
- package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
- package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
- package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
- package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
- package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
- package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
- package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
- package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
- package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
- package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
- package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
- package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
- package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
- package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
- package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
- package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
- package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
- package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
- package/sdk-core/rustfmt.toml +2 -1
- package/src/client.rs +205 -318
- package/src/metrics.rs +22 -30
- package/src/runtime.rs +4 -5
- package/src/worker.rs +16 -19
- package/ts/native.ts +1 -1
- package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
- package/sdk-core/crates/common/src/errors.rs +0 -85
- package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
- package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
- package/sdk-core/crates/sdk/src/app_data.rs +0 -37
- package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
|
@@ -1,59 +1,60 @@
|
|
|
1
1
|
use crate::common::{
|
|
2
|
-
ActivationAssertionsInterceptor, CoreWfStarter, WorkflowHandleExt,
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
ActivationAssertionsInterceptor, CoreWfStarter, WorkflowHandleExt,
|
|
3
|
+
activity_functions::StdActivities, build_fake_sdk, history_from_proto_binary,
|
|
4
|
+
init_core_replay_preloaded, mock_sdk, mock_sdk_cfg, replay_sdk_worker,
|
|
5
|
+
workflows::LaProblemWorkflow,
|
|
5
6
|
};
|
|
6
7
|
use anyhow::anyhow;
|
|
7
8
|
use crossbeam_queue::SegQueue;
|
|
8
|
-
use futures_util::
|
|
9
|
+
use futures_util::FutureExt;
|
|
9
10
|
use rstest::Context;
|
|
10
11
|
use std::{
|
|
11
12
|
collections::HashMap,
|
|
12
13
|
ops::Sub,
|
|
13
14
|
sync::{
|
|
14
15
|
Arc,
|
|
15
|
-
atomic::{
|
|
16
|
+
atomic::{AtomicI64, AtomicU8, AtomicUsize, Ordering},
|
|
16
17
|
},
|
|
17
18
|
time::{Duration, Instant, SystemTime},
|
|
18
19
|
};
|
|
19
|
-
use temporalio_client::{
|
|
20
|
+
use temporalio_client::{
|
|
21
|
+
NamespacedClient, WorkflowExecuteUpdateOptions, WorkflowExecutionInfo, WorkflowStartOptions,
|
|
22
|
+
};
|
|
20
23
|
use temporalio_common::{
|
|
21
|
-
|
|
22
|
-
errors::PollError,
|
|
24
|
+
data_converters::RawValue,
|
|
23
25
|
protos::{
|
|
24
26
|
DEFAULT_ACTIVITY_TYPE, canned_histories,
|
|
25
27
|
coresdk::{
|
|
26
|
-
ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
|
|
28
|
+
ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
|
|
27
29
|
activity_result::ActivityExecutionResult,
|
|
28
30
|
workflow_activation::{WorkflowActivationJob, workflow_activation_job},
|
|
29
31
|
workflow_commands::{
|
|
30
32
|
ActivityCancellationType, ScheduleLocalActivity, workflow_command::Variant,
|
|
31
33
|
},
|
|
32
|
-
workflow_completion
|
|
33
|
-
|
|
34
|
+
workflow_completion::{
|
|
35
|
+
self, WorkflowActivationCompletion, workflow_activation_completion,
|
|
36
|
+
},
|
|
34
37
|
},
|
|
35
38
|
temporal::api::{
|
|
36
39
|
command::v1::{RecordMarkerCommandAttributes, command},
|
|
37
40
|
common::v1::RetryPolicy,
|
|
38
|
-
enums::v1::{
|
|
39
|
-
CommandType, EventType, TimeoutType, UpdateWorkflowExecutionLifecycleStage,
|
|
40
|
-
WorkflowTaskFailedCause,
|
|
41
|
-
},
|
|
41
|
+
enums::v1::{CommandType, EventType, TimeoutType, WorkflowTaskFailedCause},
|
|
42
42
|
failure::v1::{Failure, failure::FailureInfo},
|
|
43
43
|
history::v1::history_event::Attributes::MarkerRecordedEventAttributes,
|
|
44
44
|
query::v1::WorkflowQuery,
|
|
45
|
-
update::v1::WaitPolicy,
|
|
46
45
|
},
|
|
47
46
|
test_utils::{query_ok, schedule_local_activity_cmd, start_timer_cmd},
|
|
48
47
|
},
|
|
49
48
|
};
|
|
49
|
+
use temporalio_macros::{activities, workflow, workflow_methods};
|
|
50
50
|
use temporalio_sdk::{
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
ActivityExecutionError, ActivityOptions, CancellableFuture, LocalActivityOptions,
|
|
52
|
+
WorkflowContext, WorkflowContextView, WorkflowResult, WorkflowTermination,
|
|
53
|
+
activities::{ActivityContext, ActivityError},
|
|
53
54
|
interceptors::{FailOnNondeterminismInterceptor, WorkerInterceptor},
|
|
54
55
|
};
|
|
55
56
|
use temporalio_sdk_core::{
|
|
56
|
-
prost_dur,
|
|
57
|
+
PollError, TunerHolder, prost_dur,
|
|
57
58
|
replay::{DEFAULT_WORKFLOW_TYPE, HistoryForReplay, TestHistoryBuilder, default_wes_attribs},
|
|
58
59
|
test_help::{
|
|
59
60
|
LEGACY_QUERY_ID, MockPollCfg, ResponseType, WorkerExt, WorkerTestHelpers,
|
|
@@ -64,28 +65,44 @@ use temporalio_sdk_core::{
|
|
|
64
65
|
use tokio::{join, select, sync::Barrier};
|
|
65
66
|
use tokio_util::sync::CancellationToken;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
#[workflow]
|
|
69
|
+
#[derive(Default)]
|
|
70
|
+
pub(crate) struct OneLocalActivityWf;
|
|
71
|
+
|
|
72
|
+
#[workflow_methods]
|
|
73
|
+
impl OneLocalActivityWf {
|
|
74
|
+
#[run]
|
|
75
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
76
|
+
let initial_workflow_time = ctx.workflow_time().expect("Workflow time should be set");
|
|
77
|
+
ctx.start_local_activity(
|
|
78
|
+
StdActivities::echo,
|
|
79
|
+
"hi!".to_string(),
|
|
80
|
+
LocalActivityOptions::default(),
|
|
81
|
+
)
|
|
82
|
+
.await
|
|
83
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
84
|
+
assert!(initial_workflow_time < ctx.workflow_time().unwrap());
|
|
85
|
+
Ok(())
|
|
86
|
+
}
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
#[tokio::test]
|
|
81
90
|
async fn one_local_activity() {
|
|
82
91
|
let wf_name = "one_local_activity";
|
|
83
92
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
93
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
84
94
|
let mut worker = starter.worker().await;
|
|
85
|
-
worker.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
let handle =
|
|
95
|
+
worker.register_workflow::<OneLocalActivityWf>();
|
|
96
|
+
|
|
97
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
98
|
+
let handle = worker
|
|
99
|
+
.submit_workflow(
|
|
100
|
+
OneLocalActivityWf::run,
|
|
101
|
+
(),
|
|
102
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
103
|
+
)
|
|
104
|
+
.await
|
|
105
|
+
.unwrap();
|
|
89
106
|
worker.run_until_done().await.unwrap();
|
|
90
107
|
handle
|
|
91
108
|
.fetch_history_and_replay(worker.inner_mut())
|
|
@@ -93,140 +110,215 @@ async fn one_local_activity() {
|
|
|
93
110
|
.unwrap();
|
|
94
111
|
}
|
|
95
112
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
#[workflow]
|
|
114
|
+
#[derive(Default)]
|
|
115
|
+
pub(crate) struct LocalActConcurrentWithTimerWf;
|
|
116
|
+
|
|
117
|
+
#[workflow_methods]
|
|
118
|
+
impl LocalActConcurrentWithTimerWf {
|
|
119
|
+
#[run]
|
|
120
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
121
|
+
let la = ctx.start_local_activity(
|
|
122
|
+
StdActivities::echo,
|
|
123
|
+
"hi!".to_string(),
|
|
124
|
+
LocalActivityOptions::default(),
|
|
125
|
+
);
|
|
126
|
+
let timer = ctx.timer(Duration::from_secs(1));
|
|
127
|
+
let _ = temporalio_sdk::workflows::join!(la, timer);
|
|
128
|
+
Ok(())
|
|
129
|
+
}
|
|
105
130
|
}
|
|
106
131
|
|
|
107
132
|
#[tokio::test]
|
|
108
133
|
async fn local_act_concurrent_with_timer() {
|
|
109
134
|
let wf_name = "local_act_concurrent_with_timer";
|
|
110
135
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
136
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
111
137
|
let mut worker = starter.worker().await;
|
|
112
|
-
worker.
|
|
113
|
-
worker.register_activity("echo_activity", echo);
|
|
138
|
+
worker.register_workflow::<LocalActConcurrentWithTimerWf>();
|
|
114
139
|
|
|
115
|
-
starter.
|
|
140
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
141
|
+
worker
|
|
142
|
+
.submit_workflow(
|
|
143
|
+
LocalActConcurrentWithTimerWf::run,
|
|
144
|
+
(),
|
|
145
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
146
|
+
)
|
|
147
|
+
.await
|
|
148
|
+
.unwrap();
|
|
116
149
|
worker.run_until_done().await.unwrap();
|
|
117
150
|
}
|
|
118
151
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
#[workflow]
|
|
153
|
+
#[derive(Default)]
|
|
154
|
+
struct LocalActThenTimerThenWaitResult;
|
|
155
|
+
|
|
156
|
+
#[workflow_methods]
|
|
157
|
+
impl LocalActThenTimerThenWaitResult {
|
|
158
|
+
#[run]
|
|
159
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
160
|
+
let la = ctx.start_local_activity(
|
|
161
|
+
StdActivities::echo,
|
|
162
|
+
"hi!".to_string(),
|
|
163
|
+
LocalActivityOptions::default(),
|
|
164
|
+
);
|
|
165
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
166
|
+
let res = la.await;
|
|
167
|
+
assert!(res.is_ok());
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
129
170
|
}
|
|
130
171
|
|
|
131
172
|
#[tokio::test]
|
|
132
173
|
async fn local_act_then_timer_then_wait_result() {
|
|
133
174
|
let wf_name = "local_act_then_timer_then_wait_result";
|
|
134
175
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
176
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
135
177
|
let mut worker = starter.worker().await;
|
|
136
|
-
worker.
|
|
137
|
-
worker.register_activity("echo_activity", echo);
|
|
178
|
+
worker.register_workflow::<LocalActThenTimerThenWaitResult>();
|
|
138
179
|
|
|
139
|
-
starter.
|
|
180
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
181
|
+
worker
|
|
182
|
+
.submit_workflow(
|
|
183
|
+
LocalActThenTimerThenWaitResult::run,
|
|
184
|
+
(),
|
|
185
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
186
|
+
)
|
|
187
|
+
.await
|
|
188
|
+
.unwrap();
|
|
140
189
|
worker.run_until_done().await.unwrap();
|
|
141
190
|
}
|
|
142
191
|
|
|
192
|
+
#[workflow]
|
|
193
|
+
#[derive(Default)]
|
|
194
|
+
pub(crate) struct LocalActThenTimerThenWait;
|
|
195
|
+
|
|
196
|
+
#[workflow_methods]
|
|
197
|
+
impl LocalActThenTimerThenWait {
|
|
198
|
+
#[run]
|
|
199
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
200
|
+
let la = ctx.start_local_activity(
|
|
201
|
+
StdActivities::delay,
|
|
202
|
+
Duration::from_secs(4),
|
|
203
|
+
LocalActivityOptions::default(),
|
|
204
|
+
);
|
|
205
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
206
|
+
let res = la.await;
|
|
207
|
+
assert!(res.is_ok());
|
|
208
|
+
Ok(())
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
143
212
|
#[tokio::test]
|
|
144
213
|
async fn long_running_local_act_with_timer() {
|
|
145
214
|
let wf_name = "long_running_local_act_with_timer";
|
|
146
215
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
147
216
|
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
217
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
148
218
|
let mut worker = starter.worker().await;
|
|
149
|
-
worker.
|
|
150
|
-
worker.register_activity("echo_activity", |_ctx: ActContext, str: String| async {
|
|
151
|
-
tokio::time::sleep(Duration::from_secs(4)).await;
|
|
152
|
-
Ok(str)
|
|
153
|
-
});
|
|
219
|
+
worker.register_workflow::<LocalActThenTimerThenWait>();
|
|
154
220
|
|
|
155
|
-
starter.
|
|
221
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
222
|
+
worker
|
|
223
|
+
.submit_workflow(
|
|
224
|
+
LocalActThenTimerThenWait::run,
|
|
225
|
+
(),
|
|
226
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
227
|
+
)
|
|
228
|
+
.await
|
|
229
|
+
.unwrap();
|
|
156
230
|
worker.run_until_done().await.unwrap();
|
|
157
231
|
}
|
|
158
232
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
233
|
+
#[workflow]
|
|
234
|
+
#[derive(Default)]
|
|
235
|
+
pub(crate) struct LocalActFanoutWf;
|
|
236
|
+
|
|
237
|
+
#[workflow_methods]
|
|
238
|
+
impl LocalActFanoutWf {
|
|
239
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
240
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
241
|
+
let las: Vec<_> = (1..=50)
|
|
242
|
+
.map(|i| {
|
|
243
|
+
ctx.start_local_activity(StdActivities::echo, format!("Hi {i}"), Default::default())
|
|
168
244
|
})
|
|
169
|
-
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
245
|
+
.collect();
|
|
246
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
247
|
+
temporalio_sdk::workflows::join_all(las).await;
|
|
248
|
+
Ok(())
|
|
249
|
+
}
|
|
174
250
|
}
|
|
175
251
|
|
|
176
252
|
#[tokio::test]
|
|
177
253
|
async fn local_act_fanout() {
|
|
178
254
|
let wf_name = "local_act_fanout";
|
|
179
255
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
180
|
-
starter.
|
|
256
|
+
starter.sdk_config.tuner = Arc::new(TunerHolder::fixed_size(5, 1, 1, 1));
|
|
257
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
181
258
|
let mut worker = starter.worker().await;
|
|
182
|
-
worker.
|
|
183
|
-
worker.register_activity("echo_activity", echo);
|
|
259
|
+
worker.register_workflow::<LocalActFanoutWf>();
|
|
184
260
|
|
|
185
|
-
starter.
|
|
261
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
262
|
+
worker
|
|
263
|
+
.submit_workflow(
|
|
264
|
+
LocalActFanoutWf::run,
|
|
265
|
+
(),
|
|
266
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
267
|
+
)
|
|
268
|
+
.await
|
|
269
|
+
.unwrap();
|
|
186
270
|
worker.run_until_done().await.unwrap();
|
|
187
271
|
}
|
|
188
272
|
|
|
273
|
+
#[workflow]
|
|
274
|
+
#[derive(Default)]
|
|
275
|
+
struct LocalActRetryTimerBackoff;
|
|
276
|
+
|
|
277
|
+
#[workflow_methods]
|
|
278
|
+
impl LocalActRetryTimerBackoff {
|
|
279
|
+
#[run]
|
|
280
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
281
|
+
let res = ctx
|
|
282
|
+
.start_local_activity(
|
|
283
|
+
StdActivities::always_fail,
|
|
284
|
+
(),
|
|
285
|
+
LocalActivityOptions {
|
|
286
|
+
retry_policy: RetryPolicy {
|
|
287
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
288
|
+
// We want two local backoffs that are short. Third backoff will use timer
|
|
289
|
+
backoff_coefficient: 1_000.,
|
|
290
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
291
|
+
maximum_attempts: 4,
|
|
292
|
+
non_retryable_error_types: vec![],
|
|
293
|
+
},
|
|
294
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
295
|
+
..Default::default()
|
|
296
|
+
},
|
|
297
|
+
)
|
|
298
|
+
.await;
|
|
299
|
+
assert!(matches!(res, Err(ActivityExecutionError::Failed(_))));
|
|
300
|
+
Ok(())
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
189
304
|
#[tokio::test]
|
|
190
305
|
async fn local_act_retry_timer_backoff() {
|
|
191
306
|
let wf_name = "local_act_retry_timer_backoff";
|
|
192
307
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
308
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
193
309
|
let mut worker = starter.worker().await;
|
|
194
|
-
worker.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
backoff_coefficient: 1_000.,
|
|
203
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
204
|
-
maximum_attempts: 4,
|
|
205
|
-
non_retryable_error_types: vec![],
|
|
206
|
-
},
|
|
207
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
208
|
-
..Default::default()
|
|
209
|
-
})
|
|
210
|
-
.await;
|
|
211
|
-
assert!(res.failed());
|
|
212
|
-
Ok(().into())
|
|
213
|
-
});
|
|
214
|
-
worker.register_activity("echo", |_: ActContext, _: String| async {
|
|
215
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
let run_id = worker
|
|
219
|
-
.submit_wf(
|
|
220
|
-
wf_name.to_owned(),
|
|
221
|
-
wf_name.to_owned(),
|
|
222
|
-
vec![],
|
|
223
|
-
WorkflowOptions::default(),
|
|
310
|
+
worker.register_workflow::<LocalActRetryTimerBackoff>();
|
|
311
|
+
|
|
312
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
313
|
+
let handle = worker
|
|
314
|
+
.submit_workflow(
|
|
315
|
+
LocalActRetryTimerBackoff::run,
|
|
316
|
+
(),
|
|
317
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
224
318
|
)
|
|
225
319
|
.await
|
|
226
320
|
.unwrap();
|
|
227
321
|
worker.run_until_done().await.unwrap();
|
|
228
|
-
let client = starter.get_client().await;
|
|
229
|
-
let handle = client.get_untyped_workflow_handle(wf_name, run_id);
|
|
230
322
|
handle
|
|
231
323
|
.fetch_history_and_replay(worker.inner_mut())
|
|
232
324
|
.await
|
|
@@ -239,41 +331,86 @@ async fn local_act_retry_timer_backoff() {
|
|
|
239
331
|
#[case::abandon(ActivityCancellationType::Abandon)]
|
|
240
332
|
#[tokio::test]
|
|
241
333
|
async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
|
|
242
|
-
|
|
243
|
-
let mut starter = CoreWfStarter::new(&wf_name);
|
|
244
|
-
let mut worker = starter.worker().await;
|
|
245
|
-
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
246
|
-
let la = ctx.local_activity(LocalActivityOptions {
|
|
247
|
-
activity_type: "echo".to_string(),
|
|
248
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
249
|
-
cancel_type,
|
|
250
|
-
..Default::default()
|
|
251
|
-
});
|
|
252
|
-
la.cancel(&ctx);
|
|
253
|
-
let resolution = la.await;
|
|
254
|
-
assert!(resolution.cancelled());
|
|
255
|
-
Ok(().into())
|
|
256
|
-
});
|
|
334
|
+
use temporalio_sdk::WorkflowContextView;
|
|
257
335
|
|
|
336
|
+
let wf_name = format!("cancel_immediate_{cancel_type:?}");
|
|
258
337
|
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
259
338
|
let manual_cancel = CancellationToken::new();
|
|
260
|
-
let
|
|
339
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
261
340
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
341
|
+
struct EchoWithManualCancel {
|
|
342
|
+
manual_cancel: CancellationToken,
|
|
343
|
+
}
|
|
344
|
+
#[activities]
|
|
345
|
+
impl EchoWithManualCancel {
|
|
346
|
+
#[activity]
|
|
347
|
+
async fn echo(
|
|
348
|
+
self: Arc<Self>,
|
|
349
|
+
ctx: ActivityContext,
|
|
350
|
+
_: String,
|
|
351
|
+
) -> Result<(), ActivityError> {
|
|
265
352
|
tokio::select! {
|
|
266
|
-
_ = tokio::time::sleep(Duration::from_secs(10)) => {}
|
|
353
|
+
_ = tokio::time::sleep(Duration::from_secs(10)) => {}
|
|
267
354
|
_ = ctx.cancelled() => {
|
|
268
355
|
return Err(ActivityError::cancelled())
|
|
269
356
|
}
|
|
270
|
-
_ =
|
|
357
|
+
_ = self.manual_cancel.cancelled() => {}
|
|
271
358
|
}
|
|
272
359
|
Ok(())
|
|
273
360
|
}
|
|
274
|
-
}
|
|
361
|
+
}
|
|
275
362
|
|
|
276
|
-
starter
|
|
363
|
+
starter
|
|
364
|
+
.sdk_config
|
|
365
|
+
.register_activities(EchoWithManualCancel {
|
|
366
|
+
manual_cancel: manual_cancel.clone(),
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
#[workflow]
|
|
370
|
+
struct CancelImmediate {
|
|
371
|
+
cancel_type: ActivityCancellationType,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
#[workflow_methods]
|
|
375
|
+
impl CancelImmediate {
|
|
376
|
+
#[init]
|
|
377
|
+
fn new(_ctx: &WorkflowContextView, cancel_type: ActivityCancellationType) -> Self {
|
|
378
|
+
Self { cancel_type }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#[run]
|
|
382
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
383
|
+
let cancel_type = ctx.state(|wf| wf.cancel_type);
|
|
384
|
+
let la = ctx.start_local_activity(
|
|
385
|
+
EchoWithManualCancel::echo,
|
|
386
|
+
"hi".to_string(),
|
|
387
|
+
LocalActivityOptions {
|
|
388
|
+
cancel_type,
|
|
389
|
+
..Default::default()
|
|
390
|
+
},
|
|
391
|
+
);
|
|
392
|
+
la.cancel();
|
|
393
|
+
let resolution = la.await;
|
|
394
|
+
assert!(matches!(
|
|
395
|
+
resolution,
|
|
396
|
+
Err(ActivityExecutionError::Cancelled(_))
|
|
397
|
+
));
|
|
398
|
+
Ok(())
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let mut worker = starter.worker().await;
|
|
403
|
+
worker.register_workflow::<CancelImmediate>();
|
|
404
|
+
|
|
405
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
406
|
+
worker
|
|
407
|
+
.submit_workflow(
|
|
408
|
+
CancelImmediate::run,
|
|
409
|
+
cancel_type,
|
|
410
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
411
|
+
)
|
|
412
|
+
.await
|
|
413
|
+
.unwrap();
|
|
277
414
|
worker
|
|
278
415
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
279
416
|
cancel_on_workflow_completed: false,
|
|
@@ -324,47 +461,24 @@ async fn cancel_after_act_starts(
|
|
|
324
461
|
cancel_type: ActivityCancellationType,
|
|
325
462
|
) {
|
|
326
463
|
let wf_name = format!("cancel_after_act_starts_{cancel_on_backoff:?}_{cancel_type:?}");
|
|
327
|
-
let mut starter = CoreWfStarter::new(&wf_name);
|
|
328
|
-
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
329
|
-
let mut worker = starter.worker().await;
|
|
330
|
-
let bo_dur = cancel_on_backoff.unwrap_or_else(|| Duration::from_secs(1));
|
|
331
|
-
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
332
|
-
let la = ctx.local_activity(LocalActivityOptions {
|
|
333
|
-
activity_type: "echo".to_string(),
|
|
334
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
335
|
-
retry_policy: RetryPolicy {
|
|
336
|
-
initial_interval: Some(bo_dur.try_into().unwrap()),
|
|
337
|
-
backoff_coefficient: 1.,
|
|
338
|
-
maximum_interval: Some(bo_dur.try_into().unwrap()),
|
|
339
|
-
// Retry forever until cancelled
|
|
340
|
-
..Default::default()
|
|
341
|
-
},
|
|
342
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
343
|
-
cancel_type,
|
|
344
|
-
..Default::default()
|
|
345
|
-
});
|
|
346
|
-
ctx.timer(Duration::from_secs(1)).await;
|
|
347
|
-
// Note that this cancel can't go through for *two* WF tasks, because we do a full heartbeat
|
|
348
|
-
// before the timer (LA hasn't resolved), and then the timer fired event won't appear in
|
|
349
|
-
// history until *after* the next WFT because we force generated it when we sent the timer
|
|
350
|
-
// command.
|
|
351
|
-
la.cancel(&ctx);
|
|
352
|
-
// This extra timer is here to ensure the presence of another WF task doesn't mess up
|
|
353
|
-
// resolving the LA with cancel on replay
|
|
354
|
-
ctx.timer(Duration::from_secs(1)).await;
|
|
355
|
-
let resolution = la.await;
|
|
356
|
-
assert!(resolution.cancelled());
|
|
357
|
-
Ok(().into())
|
|
358
|
-
});
|
|
359
|
-
|
|
360
464
|
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
361
465
|
let manual_cancel = CancellationToken::new();
|
|
362
|
-
let
|
|
466
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
467
|
+
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
363
468
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
469
|
+
struct EchoWithManualCancelAndBackoff {
|
|
470
|
+
manual_cancel: CancellationToken,
|
|
471
|
+
cancel_on_backoff: Option<CancellationToken>,
|
|
472
|
+
}
|
|
473
|
+
#[activities]
|
|
474
|
+
impl EchoWithManualCancelAndBackoff {
|
|
475
|
+
#[activity]
|
|
476
|
+
async fn echo(
|
|
477
|
+
self: Arc<Self>,
|
|
478
|
+
ctx: ActivityContext,
|
|
479
|
+
_: String,
|
|
480
|
+
) -> Result<(), ActivityError> {
|
|
481
|
+
if self.cancel_on_backoff.is_some() {
|
|
368
482
|
if ctx.is_cancelled() {
|
|
369
483
|
return Err(ActivityError::cancelled());
|
|
370
484
|
}
|
|
@@ -372,20 +486,87 @@ async fn cancel_after_act_starts(
|
|
|
372
486
|
return Err(anyhow!("Oh no I failed!").into());
|
|
373
487
|
} else {
|
|
374
488
|
tokio::select! {
|
|
375
|
-
_ = tokio::time::sleep(Duration::from_secs(100)) => {}
|
|
489
|
+
_ = tokio::time::sleep(Duration::from_secs(100)) => {}
|
|
376
490
|
_ = ctx.cancelled() => {
|
|
377
491
|
return Err(ActivityError::cancelled())
|
|
378
492
|
}
|
|
379
|
-
_ =
|
|
493
|
+
_ = self.manual_cancel.cancelled() => {
|
|
380
494
|
return Ok(())
|
|
381
495
|
}
|
|
382
496
|
}
|
|
383
497
|
}
|
|
384
498
|
Err(anyhow!("Oh no I failed!").into())
|
|
385
499
|
}
|
|
386
|
-
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
starter
|
|
503
|
+
.sdk_config
|
|
504
|
+
.register_activities(EchoWithManualCancelAndBackoff {
|
|
505
|
+
manual_cancel: manual_cancel.clone(),
|
|
506
|
+
cancel_on_backoff: if cancel_on_backoff.is_some() {
|
|
507
|
+
Some(CancellationToken::new())
|
|
508
|
+
} else {
|
|
509
|
+
None
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
#[workflow]
|
|
513
|
+
#[derive(Default)]
|
|
514
|
+
struct CancelAfterActStartsWf;
|
|
515
|
+
|
|
516
|
+
#[workflow_methods]
|
|
517
|
+
impl CancelAfterActStartsWf {
|
|
518
|
+
#[run]
|
|
519
|
+
async fn run(
|
|
520
|
+
ctx: &mut WorkflowContext<Self>,
|
|
521
|
+
(bo_dur, cancel_type): (Duration, ActivityCancellationType),
|
|
522
|
+
) -> WorkflowResult<()> {
|
|
523
|
+
let la = ctx.start_local_activity(
|
|
524
|
+
EchoWithManualCancelAndBackoff::echo,
|
|
525
|
+
"hi".to_string(),
|
|
526
|
+
LocalActivityOptions {
|
|
527
|
+
retry_policy: RetryPolicy {
|
|
528
|
+
initial_interval: Some(bo_dur.try_into().unwrap()),
|
|
529
|
+
backoff_coefficient: 1.,
|
|
530
|
+
maximum_interval: Some(bo_dur.try_into().unwrap()),
|
|
531
|
+
// Retry forever until cancelled
|
|
532
|
+
..Default::default()
|
|
533
|
+
},
|
|
534
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
535
|
+
cancel_type,
|
|
536
|
+
..Default::default()
|
|
537
|
+
},
|
|
538
|
+
);
|
|
539
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
540
|
+
// Note that this cancel can't go through for *two* WF tasks, because we do a full heartbeat
|
|
541
|
+
// before the timer (LA hasn't resolved), and then the timer fired event won't appear in
|
|
542
|
+
// history until *after* the next WFT because we force generated it when we sent the timer
|
|
543
|
+
// command.
|
|
544
|
+
la.cancel();
|
|
545
|
+
// This extra timer is here to ensure the presence of another WF task doesn't mess up
|
|
546
|
+
// resolving the LA with cancel on replay
|
|
547
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
548
|
+
let resolution = la.await;
|
|
549
|
+
assert!(matches!(
|
|
550
|
+
resolution,
|
|
551
|
+
Err(ActivityExecutionError::Cancelled(_))
|
|
552
|
+
));
|
|
553
|
+
Ok(())
|
|
554
|
+
}
|
|
555
|
+
}
|
|
387
556
|
|
|
388
|
-
|
|
557
|
+
let mut worker = starter.worker().await;
|
|
558
|
+
let bo_dur = cancel_on_backoff.unwrap_or_else(|| Duration::from_secs(1));
|
|
559
|
+
worker.register_workflow::<CancelAfterActStartsWf>();
|
|
560
|
+
|
|
561
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
562
|
+
worker
|
|
563
|
+
.submit_workflow(
|
|
564
|
+
CancelAfterActStartsWf::run,
|
|
565
|
+
(bo_dur, cancel_type),
|
|
566
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
567
|
+
)
|
|
568
|
+
.await
|
|
569
|
+
.unwrap();
|
|
389
570
|
worker
|
|
390
571
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
391
572
|
token: manual_cancel,
|
|
@@ -408,6 +589,68 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
|
408
589
|
if is_schedule { "schedule" } else { "start" }
|
|
409
590
|
);
|
|
410
591
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
592
|
+
|
|
593
|
+
struct LongRunningWithCancellation;
|
|
594
|
+
#[activities]
|
|
595
|
+
impl LongRunningWithCancellation {
|
|
596
|
+
#[activity]
|
|
597
|
+
async fn go(ctx: ActivityContext) -> Result<(), ActivityError> {
|
|
598
|
+
tokio::select! {
|
|
599
|
+
_ = tokio::time::sleep(Duration::from_secs(100)) => {}
|
|
600
|
+
_ = ctx.cancelled() => {
|
|
601
|
+
return Err(ActivityError::cancelled())
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
Ok(())
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
starter
|
|
609
|
+
.sdk_config
|
|
610
|
+
.register_activities(LongRunningWithCancellation);
|
|
611
|
+
#[workflow]
|
|
612
|
+
#[derive(Default)]
|
|
613
|
+
struct XToCloseTimeoutWf;
|
|
614
|
+
|
|
615
|
+
#[workflow_methods]
|
|
616
|
+
impl XToCloseTimeoutWf {
|
|
617
|
+
#[run]
|
|
618
|
+
async fn run(
|
|
619
|
+
ctx: &mut WorkflowContext<Self>,
|
|
620
|
+
(sched, start, timeout_type): (Option<Duration>, Option<Duration>, i32),
|
|
621
|
+
) -> WorkflowResult<()> {
|
|
622
|
+
let res = ctx
|
|
623
|
+
.start_local_activity(
|
|
624
|
+
LongRunningWithCancellation::go,
|
|
625
|
+
(),
|
|
626
|
+
LocalActivityOptions {
|
|
627
|
+
retry_policy: RetryPolicy {
|
|
628
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
629
|
+
backoff_coefficient: 1_000.,
|
|
630
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
631
|
+
maximum_attempts: 4,
|
|
632
|
+
non_retryable_error_types: vec![],
|
|
633
|
+
},
|
|
634
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
635
|
+
schedule_to_close_timeout: sched,
|
|
636
|
+
start_to_close_timeout: start,
|
|
637
|
+
..Default::default()
|
|
638
|
+
},
|
|
639
|
+
)
|
|
640
|
+
.await;
|
|
641
|
+
let err = res.unwrap_err();
|
|
642
|
+
if let ActivityExecutionError::Failed(f) = &err {
|
|
643
|
+
assert_eq!(
|
|
644
|
+
f.is_timeout(),
|
|
645
|
+
Some(TimeoutType::try_from(timeout_type).unwrap())
|
|
646
|
+
);
|
|
647
|
+
} else {
|
|
648
|
+
return Err(anyhow!("expected Failed, got {err:?}").into());
|
|
649
|
+
}
|
|
650
|
+
Ok(())
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
411
654
|
let mut worker = starter.worker().await;
|
|
412
655
|
let (sched, start) = if is_schedule {
|
|
413
656
|
(Some(Duration::from_secs(2)), None)
|
|
@@ -419,39 +662,17 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
|
419
662
|
} else {
|
|
420
663
|
TimeoutType::StartToClose
|
|
421
664
|
};
|
|
665
|
+
worker.register_workflow::<XToCloseTimeoutWf>();
|
|
422
666
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
maximum_attempts: 4,
|
|
433
|
-
non_retryable_error_types: vec![],
|
|
434
|
-
},
|
|
435
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
436
|
-
schedule_to_close_timeout: sched,
|
|
437
|
-
start_to_close_timeout: start,
|
|
438
|
-
..Default::default()
|
|
439
|
-
})
|
|
440
|
-
.await;
|
|
441
|
-
assert_eq!(res.timed_out(), Some(timeout_type));
|
|
442
|
-
Ok(().into())
|
|
443
|
-
});
|
|
444
|
-
worker.register_activity("echo", |ctx: ActContext, _: String| async move {
|
|
445
|
-
tokio::select! {
|
|
446
|
-
_ = tokio::time::sleep(Duration::from_secs(100)) => {},
|
|
447
|
-
_ = ctx.cancelled() => {
|
|
448
|
-
return Err(ActivityError::cancelled())
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
Ok(())
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
starter.start_with_worker(wf_name, &mut worker).await;
|
|
667
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
668
|
+
worker
|
|
669
|
+
.submit_workflow(
|
|
670
|
+
XToCloseTimeoutWf::run,
|
|
671
|
+
(sched, start, timeout_type as i32),
|
|
672
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
673
|
+
)
|
|
674
|
+
.await
|
|
675
|
+
.unwrap();
|
|
455
676
|
worker.run_until_done().await.unwrap();
|
|
456
677
|
}
|
|
457
678
|
|
|
@@ -466,36 +687,75 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
|
466
687
|
);
|
|
467
688
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
468
689
|
if !cached {
|
|
469
|
-
starter.
|
|
690
|
+
starter.sdk_config.max_cached_workflows = 0_usize;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
#[workflow]
|
|
694
|
+
#[derive(Default)]
|
|
695
|
+
struct ScheduleToCloseTimeoutAcrossTimerBackoff;
|
|
696
|
+
|
|
697
|
+
#[workflow_methods]
|
|
698
|
+
impl ScheduleToCloseTimeoutAcrossTimerBackoff {
|
|
699
|
+
#[run]
|
|
700
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
701
|
+
let res = ctx
|
|
702
|
+
.start_local_activity(
|
|
703
|
+
FailWithAtomicCounter::go,
|
|
704
|
+
"hi".to_string(),
|
|
705
|
+
LocalActivityOptions {
|
|
706
|
+
retry_policy: RetryPolicy {
|
|
707
|
+
initial_interval: Some(prost_dur!(from_millis(15))),
|
|
708
|
+
backoff_coefficient: 1_000.,
|
|
709
|
+
maximum_interval: Some(prost_dur!(from_millis(1000))),
|
|
710
|
+
maximum_attempts: 40,
|
|
711
|
+
non_retryable_error_types: vec![],
|
|
712
|
+
},
|
|
713
|
+
timer_backoff_threshold: Some(Duration::from_millis(500)),
|
|
714
|
+
schedule_to_close_timeout: Some(Duration::from_secs(2)),
|
|
715
|
+
..Default::default()
|
|
716
|
+
},
|
|
717
|
+
)
|
|
718
|
+
.await;
|
|
719
|
+
let err = res.unwrap_err();
|
|
720
|
+
if let ActivityExecutionError::Failed(f) = &err {
|
|
721
|
+
assert_eq!(f.is_timeout(), Some(TimeoutType::ScheduleToClose));
|
|
722
|
+
} else {
|
|
723
|
+
panic!("expected Failed, got {err:?}");
|
|
724
|
+
}
|
|
725
|
+
Ok(())
|
|
726
|
+
}
|
|
470
727
|
}
|
|
728
|
+
|
|
471
729
|
let mut worker = starter.worker().await;
|
|
472
|
-
worker.
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
Ok(().into())
|
|
491
|
-
});
|
|
492
|
-
let num_attempts: &'static _ = Box::leak(Box::new(AtomicU8::new(0)));
|
|
493
|
-
worker.register_activity("echo", move |_: ActContext, _: String| async {
|
|
494
|
-
num_attempts.fetch_add(1, Ordering::Relaxed);
|
|
495
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
730
|
+
worker.register_workflow::<ScheduleToCloseTimeoutAcrossTimerBackoff>();
|
|
731
|
+
|
|
732
|
+
let num_attempts = Arc::new(AtomicU8::new(0));
|
|
733
|
+
|
|
734
|
+
struct FailWithAtomicCounter {
|
|
735
|
+
counter: Arc<AtomicU8>,
|
|
736
|
+
}
|
|
737
|
+
#[activities]
|
|
738
|
+
impl FailWithAtomicCounter {
|
|
739
|
+
#[activity]
|
|
740
|
+
async fn go(self: Arc<Self>, _: ActivityContext, _: String) -> Result<(), ActivityError> {
|
|
741
|
+
self.counter.fetch_add(1, Ordering::Relaxed);
|
|
742
|
+
Err(anyhow!("Oh no I failed!").into())
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
worker.register_activities(FailWithAtomicCounter {
|
|
747
|
+
counter: num_attempts.clone(),
|
|
496
748
|
});
|
|
497
749
|
|
|
498
|
-
starter.
|
|
750
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
751
|
+
worker
|
|
752
|
+
.submit_workflow(
|
|
753
|
+
ScheduleToCloseTimeoutAcrossTimerBackoff::run,
|
|
754
|
+
(),
|
|
755
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
756
|
+
)
|
|
757
|
+
.await
|
|
758
|
+
.unwrap();
|
|
499
759
|
worker.run_until_done().await.unwrap();
|
|
500
760
|
// 3 attempts b/c first backoff is very small, then the next 2 attempts take at least 2 seconds
|
|
501
761
|
// b/c of timer backoff.
|
|
@@ -507,24 +767,21 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
|
507
767
|
async fn eviction_wont_make_local_act_get_dropped(#[values(true, false)] short_wft_timeout: bool) {
|
|
508
768
|
let wf_name = format!("eviction_wont_make_local_act_get_dropped_{short_wft_timeout}");
|
|
509
769
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
510
|
-
starter.
|
|
770
|
+
starter.sdk_config.max_cached_workflows = 0_usize;
|
|
771
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
511
772
|
let mut worker = starter.worker().await;
|
|
512
|
-
worker.
|
|
513
|
-
worker.register_activity("echo_activity", |_ctx: ActContext, str: String| async {
|
|
514
|
-
tokio::time::sleep(Duration::from_secs(4)).await;
|
|
515
|
-
Ok(str)
|
|
516
|
-
});
|
|
773
|
+
worker.register_workflow::<LocalActThenTimerThenWait>();
|
|
517
774
|
|
|
775
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
518
776
|
let opts = if short_wft_timeout {
|
|
519
|
-
|
|
520
|
-
task_timeout
|
|
521
|
-
|
|
522
|
-
}
|
|
777
|
+
WorkflowStartOptions::new(task_queue, wf_name.clone())
|
|
778
|
+
.task_timeout(Duration::from_secs(1))
|
|
779
|
+
.build()
|
|
523
780
|
} else {
|
|
524
|
-
|
|
781
|
+
WorkflowStartOptions::new(task_queue, wf_name.clone()).build()
|
|
525
782
|
};
|
|
526
783
|
worker
|
|
527
|
-
.
|
|
784
|
+
.submit_workflow(LocalActThenTimerThenWait::run, (), opts)
|
|
528
785
|
.await
|
|
529
786
|
.unwrap();
|
|
530
787
|
worker.run_until_done().await.unwrap();
|
|
@@ -534,44 +791,65 @@ async fn eviction_wont_make_local_act_get_dropped(#[values(true, false)] short_w
|
|
|
534
791
|
async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
535
792
|
let wf_name = "timer_backoff_concurrent_with_non_timer_backoff";
|
|
536
793
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
794
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
795
|
+
|
|
796
|
+
#[workflow]
|
|
797
|
+
#[derive(Default)]
|
|
798
|
+
struct TimerBackoffConcurrentWf;
|
|
799
|
+
|
|
800
|
+
#[workflow_methods]
|
|
801
|
+
impl TimerBackoffConcurrentWf {
|
|
802
|
+
#[run]
|
|
803
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
804
|
+
let r1 = ctx.start_local_activity(
|
|
805
|
+
StdActivities::always_fail,
|
|
806
|
+
(),
|
|
807
|
+
LocalActivityOptions {
|
|
808
|
+
retry_policy: RetryPolicy {
|
|
809
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
810
|
+
backoff_coefficient: 1_000.,
|
|
811
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
812
|
+
maximum_attempts: 4,
|
|
813
|
+
non_retryable_error_types: vec![],
|
|
814
|
+
},
|
|
815
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
816
|
+
..Default::default()
|
|
817
|
+
},
|
|
818
|
+
);
|
|
819
|
+
let r2 = ctx.start_local_activity(
|
|
820
|
+
StdActivities::always_fail,
|
|
821
|
+
(),
|
|
822
|
+
LocalActivityOptions {
|
|
823
|
+
retry_policy: RetryPolicy {
|
|
824
|
+
initial_interval: Some(prost_dur!(from_millis(15))),
|
|
825
|
+
backoff_coefficient: 10.,
|
|
826
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
827
|
+
maximum_attempts: 4,
|
|
828
|
+
non_retryable_error_types: vec![],
|
|
829
|
+
},
|
|
830
|
+
timer_backoff_threshold: Some(Duration::from_secs(10)),
|
|
831
|
+
..Default::default()
|
|
832
|
+
},
|
|
833
|
+
);
|
|
834
|
+
let (r1, r2) = temporalio_sdk::workflows::join!(r1, r2);
|
|
835
|
+
assert!(matches!(r1, Err(ActivityExecutionError::Failed(_))));
|
|
836
|
+
assert!(matches!(r2, Err(ActivityExecutionError::Failed(_))));
|
|
837
|
+
Ok(())
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
537
841
|
let mut worker = starter.worker().await;
|
|
538
|
-
worker.
|
|
539
|
-
let r1 = ctx.local_activity(LocalActivityOptions {
|
|
540
|
-
activity_type: "echo".to_string(),
|
|
541
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
542
|
-
retry_policy: RetryPolicy {
|
|
543
|
-
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
544
|
-
backoff_coefficient: 1_000.,
|
|
545
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
546
|
-
maximum_attempts: 4,
|
|
547
|
-
non_retryable_error_types: vec![],
|
|
548
|
-
},
|
|
549
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
550
|
-
..Default::default()
|
|
551
|
-
});
|
|
552
|
-
let r2 = ctx.local_activity(LocalActivityOptions {
|
|
553
|
-
activity_type: "echo".to_string(),
|
|
554
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
555
|
-
retry_policy: RetryPolicy {
|
|
556
|
-
initial_interval: Some(prost_dur!(from_millis(15))),
|
|
557
|
-
backoff_coefficient: 10.,
|
|
558
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
559
|
-
maximum_attempts: 4,
|
|
560
|
-
non_retryable_error_types: vec![],
|
|
561
|
-
},
|
|
562
|
-
timer_backoff_threshold: Some(Duration::from_secs(10)),
|
|
563
|
-
..Default::default()
|
|
564
|
-
});
|
|
565
|
-
let (r1, r2) = tokio::join!(r1, r2);
|
|
566
|
-
assert!(r1.failed());
|
|
567
|
-
assert!(r2.failed());
|
|
568
|
-
Ok(().into())
|
|
569
|
-
});
|
|
570
|
-
worker.register_activity("echo", |_: ActContext, _: String| async {
|
|
571
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
572
|
-
});
|
|
842
|
+
worker.register_workflow::<TimerBackoffConcurrentWf>();
|
|
573
843
|
|
|
574
|
-
starter.
|
|
844
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
845
|
+
worker
|
|
846
|
+
.submit_workflow(
|
|
847
|
+
TimerBackoffConcurrentWf::run,
|
|
848
|
+
(),
|
|
849
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
850
|
+
)
|
|
851
|
+
.await
|
|
852
|
+
.unwrap();
|
|
575
853
|
worker.run_until_done().await.unwrap();
|
|
576
854
|
}
|
|
577
855
|
|
|
@@ -579,50 +857,64 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
|
579
857
|
async fn repro_nondeterminism_with_timer_bug() {
|
|
580
858
|
let wf_name = "repro_nondeterminism_with_timer_bug";
|
|
581
859
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
860
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
861
|
+
|
|
862
|
+
#[workflow]
|
|
863
|
+
#[derive(Default)]
|
|
864
|
+
struct ReproNondeterminismWithTimerBugWf;
|
|
865
|
+
|
|
866
|
+
#[workflow_methods]
|
|
867
|
+
impl ReproNondeterminismWithTimerBugWf {
|
|
868
|
+
#[run]
|
|
869
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
870
|
+
let mut t1 = ctx.timer(Duration::from_secs(30));
|
|
871
|
+
let mut r1 = ctx.start_local_activity(
|
|
872
|
+
StdActivities::delay,
|
|
873
|
+
Duration::from_secs(2),
|
|
874
|
+
LocalActivityOptions {
|
|
875
|
+
retry_policy: RetryPolicy {
|
|
876
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
877
|
+
backoff_coefficient: 1_000.,
|
|
878
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
879
|
+
maximum_attempts: 4,
|
|
880
|
+
non_retryable_error_types: vec![],
|
|
881
|
+
},
|
|
882
|
+
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
883
|
+
..Default::default()
|
|
884
|
+
},
|
|
885
|
+
);
|
|
886
|
+
temporalio_sdk::workflows::select! {
|
|
887
|
+
_ = t1 => {},
|
|
888
|
+
_ = r1 => {
|
|
889
|
+
t1.cancel();
|
|
890
|
+
},
|
|
891
|
+
}
|
|
892
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
893
|
+
Ok(())
|
|
605
894
|
}
|
|
606
|
-
|
|
607
|
-
Ok(().into())
|
|
608
|
-
});
|
|
609
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
610
|
-
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
611
|
-
Ok(())
|
|
612
|
-
});
|
|
895
|
+
}
|
|
613
896
|
|
|
614
|
-
let
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
897
|
+
let mut worker = starter.worker().await;
|
|
898
|
+
worker.register_workflow::<ReproNondeterminismWithTimerBugWf>();
|
|
899
|
+
|
|
900
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
901
|
+
let handle = worker
|
|
902
|
+
.submit_workflow(
|
|
903
|
+
ReproNondeterminismWithTimerBugWf::run,
|
|
904
|
+
(),
|
|
905
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
620
906
|
)
|
|
621
907
|
.await
|
|
622
908
|
.unwrap();
|
|
623
909
|
worker.run_until_done().await.unwrap();
|
|
624
910
|
let client = starter.get_client().await;
|
|
625
|
-
let handle =
|
|
911
|
+
let handle = WorkflowExecutionInfo {
|
|
912
|
+
namespace: client.namespace(),
|
|
913
|
+
workflow_id: wf_name.into(),
|
|
914
|
+
run_id: Some(handle.run_id().unwrap().to_string()),
|
|
915
|
+
first_execution_run_id: None,
|
|
916
|
+
}
|
|
917
|
+
.bind_untyped(client.clone());
|
|
626
918
|
handle
|
|
627
919
|
.fetch_history_and_replay(worker.inner_mut())
|
|
628
920
|
.await
|
|
@@ -646,14 +938,8 @@ async fn weird_la_nondeterminism_repro(#[values(true, false)] fix_hist: bool) {
|
|
|
646
938
|
}
|
|
647
939
|
|
|
648
940
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
649
|
-
worker.
|
|
650
|
-
|
|
651
|
-
la_problem_workflow,
|
|
652
|
-
);
|
|
653
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
654
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
655
|
-
Ok(())
|
|
656
|
-
});
|
|
941
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
942
|
+
worker.register_activities(StdActivities);
|
|
657
943
|
worker.run().await.unwrap();
|
|
658
944
|
}
|
|
659
945
|
|
|
@@ -670,14 +956,8 @@ async fn second_weird_la_nondeterminism_repro() {
|
|
|
670
956
|
hist = thb.get_full_history_info().unwrap().into();
|
|
671
957
|
|
|
672
958
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
673
|
-
worker.
|
|
674
|
-
|
|
675
|
-
la_problem_workflow,
|
|
676
|
-
);
|
|
677
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
678
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
679
|
-
Ok(())
|
|
680
|
-
});
|
|
959
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
960
|
+
worker.register_activities(StdActivities);
|
|
681
961
|
worker.run().await.unwrap();
|
|
682
962
|
}
|
|
683
963
|
|
|
@@ -692,14 +972,8 @@ async fn third_weird_la_nondeterminism_repro() {
|
|
|
692
972
|
hist = thb.get_full_history_info().unwrap().into();
|
|
693
973
|
|
|
694
974
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
695
|
-
worker.
|
|
696
|
-
|
|
697
|
-
la_problem_workflow,
|
|
698
|
-
);
|
|
699
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
700
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
701
|
-
Ok(())
|
|
702
|
-
});
|
|
975
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
976
|
+
worker.register_activities(StdActivities);
|
|
703
977
|
worker.run().await.unwrap();
|
|
704
978
|
}
|
|
705
979
|
|
|
@@ -719,58 +993,87 @@ async fn third_weird_la_nondeterminism_repro() {
|
|
|
719
993
|
async fn la_resolve_same_time_as_other_cancel() {
|
|
720
994
|
let wf_name = "la_resolve_same_time_as_other_cancel";
|
|
721
995
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
722
|
-
// The activity won't get a chance to receive the cancel so make sure we still exit fast
|
|
723
|
-
starter.worker_config.graceful_shutdown_period = Some(Duration::from_millis(100));
|
|
724
|
-
let mut worker = starter.worker().await;
|
|
725
|
-
|
|
726
|
-
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
727
|
-
let normal_act = ctx.activity(ActivityOptions {
|
|
728
|
-
activity_type: "delay".to_string(),
|
|
729
|
-
input: 9000.as_json_payload().expect("serializes fine"),
|
|
730
|
-
cancellation_type: ActivityCancellationType::TryCancel,
|
|
731
|
-
start_to_close_timeout: Some(Duration::from_secs(9000)),
|
|
732
|
-
..Default::default()
|
|
733
|
-
});
|
|
734
|
-
// Make new task
|
|
735
|
-
ctx.timer(Duration::from_millis(1)).await;
|
|
736
996
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
_ = normal_act => {},
|
|
748
|
-
_ = local_act => {
|
|
749
|
-
ctx.timer(Duration::from_millis(1)).await;
|
|
750
|
-
},
|
|
997
|
+
struct DelayWithCancellation;
|
|
998
|
+
#[activities]
|
|
999
|
+
impl DelayWithCancellation {
|
|
1000
|
+
#[activity]
|
|
1001
|
+
async fn delay(ctx: ActivityContext, dur: Duration) -> Result<(), ActivityError> {
|
|
1002
|
+
tokio::select! {
|
|
1003
|
+
_ = tokio::time::sleep(dur) => {}
|
|
1004
|
+
_ = ctx.cancelled() => {}
|
|
1005
|
+
}
|
|
1006
|
+
Ok(())
|
|
751
1007
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
starter
|
|
1011
|
+
.sdk_config
|
|
1012
|
+
.register_activities(DelayWithCancellation);
|
|
1013
|
+
// The activity won't get a chance to receive the cancel so make sure we still exit fast
|
|
1014
|
+
starter.sdk_config.graceful_shutdown_period = Some(Duration::from_millis(100));
|
|
1015
|
+
|
|
1016
|
+
#[workflow]
|
|
1017
|
+
#[derive(Default)]
|
|
1018
|
+
struct LaResolveSameTimeAsOtherCancelWf;
|
|
1019
|
+
|
|
1020
|
+
#[workflow_methods]
|
|
1021
|
+
impl LaResolveSameTimeAsOtherCancelWf {
|
|
1022
|
+
#[run]
|
|
1023
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1024
|
+
let mut normal_act = ctx.start_activity(
|
|
1025
|
+
DelayWithCancellation::delay,
|
|
1026
|
+
Duration::from_secs(9),
|
|
1027
|
+
ActivityOptions {
|
|
1028
|
+
cancellation_type: ActivityCancellationType::TryCancel,
|
|
1029
|
+
start_to_close_timeout: Some(Duration::from_secs(9000)),
|
|
1030
|
+
..Default::default()
|
|
1031
|
+
},
|
|
1032
|
+
);
|
|
1033
|
+
// Make new task
|
|
1034
|
+
ctx.timer(Duration::from_millis(1)).await;
|
|
1035
|
+
|
|
1036
|
+
// Start LA and cancel the activity at the same time
|
|
1037
|
+
let mut local_act = ctx.start_local_activity(
|
|
1038
|
+
DelayWithCancellation::delay,
|
|
1039
|
+
Duration::from_millis(100),
|
|
1040
|
+
LocalActivityOptions {
|
|
1041
|
+
..Default::default()
|
|
1042
|
+
},
|
|
1043
|
+
);
|
|
1044
|
+
normal_act.cancel();
|
|
1045
|
+
// Race them, starting a timer if LA completes first
|
|
1046
|
+
temporalio_sdk::workflows::select! {
|
|
1047
|
+
_ = normal_act => {},
|
|
1048
|
+
_ = local_act => {
|
|
1049
|
+
ctx.timer(Duration::from_millis(1)).await;
|
|
1050
|
+
},
|
|
1051
|
+
}
|
|
1052
|
+
Ok(())
|
|
758
1053
|
}
|
|
759
|
-
|
|
760
|
-
});
|
|
1054
|
+
}
|
|
761
1055
|
|
|
762
|
-
let
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1056
|
+
let mut worker = starter.worker().await;
|
|
1057
|
+
worker.register_workflow::<LaResolveSameTimeAsOtherCancelWf>();
|
|
1058
|
+
|
|
1059
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
1060
|
+
let handle = worker
|
|
1061
|
+
.submit_workflow(
|
|
1062
|
+
LaResolveSameTimeAsOtherCancelWf::run,
|
|
1063
|
+
(),
|
|
1064
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
768
1065
|
)
|
|
769
1066
|
.await
|
|
770
1067
|
.unwrap();
|
|
771
1068
|
worker.run_until_done().await.unwrap();
|
|
772
1069
|
let client = starter.get_client().await;
|
|
773
|
-
let handle =
|
|
1070
|
+
let handle = WorkflowExecutionInfo {
|
|
1071
|
+
namespace: client.namespace(),
|
|
1072
|
+
workflow_id: wf_name.into(),
|
|
1073
|
+
run_id: Some(handle.run_id().unwrap().to_string()),
|
|
1074
|
+
first_execution_run_id: None,
|
|
1075
|
+
}
|
|
1076
|
+
.bind_untyped(client.clone());
|
|
774
1077
|
handle
|
|
775
1078
|
.fetch_history_and_replay(worker.inner_mut())
|
|
776
1079
|
.await
|
|
@@ -791,59 +1094,68 @@ async fn long_local_activity_with_update(
|
|
|
791
1094
|
let wf_name = format!("{}-{}", ctx.name, ctx.case.unwrap());
|
|
792
1095
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
793
1096
|
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
794
|
-
|
|
795
|
-
let client = starter.get_client().await;
|
|
1097
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
796
1098
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
move |u: UpdateContext, _: ()| {
|
|
804
|
-
let uc = uc.clone();
|
|
805
|
-
async move {
|
|
806
|
-
if update_inner_timer != 0 {
|
|
807
|
-
u.wf_ctx
|
|
808
|
-
.timer(Duration::from_millis(update_inner_timer))
|
|
809
|
-
.await;
|
|
810
|
-
}
|
|
811
|
-
uc.fetch_add(1, Ordering::Relaxed);
|
|
812
|
-
Ok(())
|
|
813
|
-
}
|
|
814
|
-
},
|
|
815
|
-
);
|
|
816
|
-
ctx.local_activity(LocalActivityOptions {
|
|
817
|
-
activity_type: "delay".to_string(),
|
|
818
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
819
|
-
..Default::default()
|
|
820
|
-
})
|
|
821
|
-
.await;
|
|
822
|
-
update_counter.load(Ordering::Relaxed);
|
|
823
|
-
Ok(().into())
|
|
824
|
-
});
|
|
825
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
826
|
-
tokio::time::sleep(Duration::from_secs(6)).await;
|
|
827
|
-
Ok(())
|
|
828
|
-
});
|
|
1099
|
+
#[workflow]
|
|
1100
|
+
#[derive(Default)]
|
|
1101
|
+
struct LongLocalActivityWithUpdateWf {
|
|
1102
|
+
update_counter: usize,
|
|
1103
|
+
update_inner_timer: u64,
|
|
1104
|
+
}
|
|
829
1105
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1106
|
+
#[workflow_methods]
|
|
1107
|
+
impl LongLocalActivityWithUpdateWf {
|
|
1108
|
+
#[init]
|
|
1109
|
+
fn init(_ctx: &WorkflowContextView, update_inner_timer: u64) -> Self {
|
|
1110
|
+
Self {
|
|
1111
|
+
update_counter: 1,
|
|
1112
|
+
update_inner_timer,
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
#[run]
|
|
1117
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<usize> {
|
|
1118
|
+
ctx.start_local_activity(
|
|
1119
|
+
StdActivities::delay,
|
|
1120
|
+
Duration::from_secs(6),
|
|
1121
|
+
LocalActivityOptions::default(),
|
|
1122
|
+
)
|
|
1123
|
+
.await
|
|
1124
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1125
|
+
Ok(ctx.state(|wf| wf.update_counter))
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
#[update]
|
|
1129
|
+
async fn do_update(ctx: &mut WorkflowContext<Self>, _: ()) {
|
|
1130
|
+
let update_inner_timer = ctx.state(|s| s.update_inner_timer);
|
|
1131
|
+
if update_inner_timer != 0 {
|
|
1132
|
+
ctx.timer(Duration::from_millis(update_inner_timer)).await;
|
|
1133
|
+
}
|
|
1134
|
+
ctx.state_mut(|s| s.update_counter += 1);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
let mut worker = starter.worker().await;
|
|
1139
|
+
worker.register_workflow::<LongLocalActivityWithUpdateWf>();
|
|
1140
|
+
|
|
1141
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
1142
|
+
let handle = worker
|
|
1143
|
+
.submit_workflow(
|
|
1144
|
+
LongLocalActivityWithUpdateWf::run,
|
|
1145
|
+
update_inner_timer,
|
|
1146
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
1147
|
+
)
|
|
1148
|
+
.await
|
|
1149
|
+
.unwrap();
|
|
833
1150
|
|
|
834
|
-
let wf_id = starter.get_task_queue().to_string();
|
|
835
1151
|
let update = async {
|
|
836
1152
|
loop {
|
|
837
1153
|
tokio::time::sleep(Duration::from_millis(update_interval_ms)).await;
|
|
838
|
-
let _ =
|
|
839
|
-
.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
WaitPolicy {
|
|
844
|
-
lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
|
|
845
|
-
},
|
|
846
|
-
[().as_json_payload().unwrap()].into_payloads(),
|
|
1154
|
+
let _ = handle
|
|
1155
|
+
.execute_update(
|
|
1156
|
+
LongLocalActivityWithUpdateWf::do_update,
|
|
1157
|
+
(),
|
|
1158
|
+
WorkflowExecuteUpdateOptions::default(),
|
|
847
1159
|
)
|
|
848
1160
|
.await;
|
|
849
1161
|
}
|
|
@@ -852,16 +1164,12 @@ async fn long_local_activity_with_update(
|
|
|
852
1164
|
worker.run_until_done().await.unwrap();
|
|
853
1165
|
};
|
|
854
1166
|
tokio::select!(_ = update => {}, _ = runner => {});
|
|
855
|
-
let res = handle
|
|
856
|
-
.get_workflow_result(Default::default())
|
|
857
|
-
.await
|
|
858
|
-
.unwrap()
|
|
859
|
-
.unwrap_success();
|
|
1167
|
+
let res = handle.get_result(Default::default()).await.unwrap();
|
|
860
1168
|
let replay_res = handle
|
|
861
1169
|
.fetch_history_and_replay(worker.inner_mut())
|
|
862
1170
|
.await
|
|
863
1171
|
.unwrap();
|
|
864
|
-
assert_eq!(res
|
|
1172
|
+
assert_eq!(res, usize::from_json_payload(&replay_res.unwrap()).unwrap());
|
|
865
1173
|
|
|
866
1174
|
// Load histories from pre-fix version and ensure compat
|
|
867
1175
|
let replay_worker = init_core_replay_preloaded(
|
|
@@ -874,7 +1182,7 @@ async fn long_local_activity_with_update(
|
|
|
874
1182
|
)],
|
|
875
1183
|
);
|
|
876
1184
|
let inner_worker = worker.inner_mut();
|
|
877
|
-
inner_worker.with_new_core_worker(replay_worker);
|
|
1185
|
+
inner_worker.with_new_core_worker(Arc::new(replay_worker));
|
|
878
1186
|
inner_worker.set_worker_interceptor(FailOnNondeterminismInterceptor {});
|
|
879
1187
|
inner_worker.run().await.unwrap();
|
|
880
1188
|
}
|
|
@@ -884,68 +1192,92 @@ async fn local_activity_with_heartbeat_only_causes_one_wakeup() {
|
|
|
884
1192
|
let wf_name = "local_activity_with_heartbeat_only_causes_one_wakeup";
|
|
885
1193
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
886
1194
|
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
887
|
-
|
|
1195
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
888
1196
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1197
|
+
#[workflow]
|
|
1198
|
+
#[derive(Default)]
|
|
1199
|
+
struct LocalActivityWithHeartbeatOnlyCausesOneWakeupWf {
|
|
1200
|
+
la_resolved: bool,
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
#[workflow_methods]
|
|
1204
|
+
impl LocalActivityWithHeartbeatOnlyCausesOneWakeupWf {
|
|
1205
|
+
#[run]
|
|
1206
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<usize> {
|
|
1207
|
+
let mut wakeup_counter = 0;
|
|
1208
|
+
// Interestingly LA munst come first because if the condition is polled first, we won't
|
|
1209
|
+
// see that resolved is true.
|
|
1210
|
+
// TODO [rust-sdk-branch] - See if we can fix this and know that we should re-poll.
|
|
1211
|
+
temporalio_sdk::workflows::join!(
|
|
1212
|
+
async {
|
|
1213
|
+
ctx.start_local_activity(
|
|
1214
|
+
StdActivities::delay,
|
|
1215
|
+
Duration::from_secs(6),
|
|
1216
|
+
LocalActivityOptions::default(),
|
|
1217
|
+
)
|
|
1218
|
+
.await
|
|
1219
|
+
.ok();
|
|
1220
|
+
ctx.state_mut(|s| s.la_resolved = true);
|
|
1221
|
+
},
|
|
1222
|
+
ctx.wait_condition(|_| {
|
|
904
1223
|
wakeup_counter += 1;
|
|
905
|
-
|
|
1224
|
+
ctx.state(|s| s.la_resolved)
|
|
906
1225
|
})
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
});
|
|
912
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
913
|
-
tokio::time::sleep(Duration::from_secs(6)).await;
|
|
914
|
-
Ok(())
|
|
915
|
-
});
|
|
1226
|
+
);
|
|
1227
|
+
Ok(wakeup_counter as usize)
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
916
1230
|
|
|
917
|
-
let
|
|
918
|
-
worker.
|
|
919
|
-
|
|
920
|
-
|
|
1231
|
+
let mut worker = starter.worker().await;
|
|
1232
|
+
worker.register_workflow::<LocalActivityWithHeartbeatOnlyCausesOneWakeupWf>();
|
|
1233
|
+
|
|
1234
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
1235
|
+
let handle = worker
|
|
1236
|
+
.submit_workflow(
|
|
1237
|
+
LocalActivityWithHeartbeatOnlyCausesOneWakeupWf::run,
|
|
1238
|
+
(),
|
|
1239
|
+
WorkflowStartOptions::new(task_queue, wf_name.to_owned()).build(),
|
|
1240
|
+
)
|
|
921
1241
|
.await
|
|
922
|
-
.unwrap()
|
|
923
|
-
|
|
924
|
-
let
|
|
1242
|
+
.unwrap();
|
|
1243
|
+
worker.run_until_done().await.unwrap();
|
|
1244
|
+
let r = handle.get_result(Default::default()).await.unwrap();
|
|
1245
|
+
assert_eq!(r, 2);
|
|
1246
|
+
handle
|
|
925
1247
|
.fetch_history_and_replay(worker.inner_mut())
|
|
926
1248
|
.await
|
|
927
1249
|
.unwrap();
|
|
928
|
-
assert_eq!(res[0], replay_res.unwrap());
|
|
929
1250
|
}
|
|
930
1251
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1252
|
+
#[workflow]
|
|
1253
|
+
#[derive(Default)]
|
|
1254
|
+
pub(crate) struct LocalActivityWithSummaryWf;
|
|
1255
|
+
|
|
1256
|
+
#[workflow_methods]
|
|
1257
|
+
impl LocalActivityWithSummaryWf {
|
|
1258
|
+
#[run(name = "local_activity_with_summary")]
|
|
1259
|
+
pub(crate) async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1260
|
+
ctx.start_local_activity(
|
|
1261
|
+
StdActivities::echo,
|
|
1262
|
+
"hi".to_string(),
|
|
1263
|
+
LocalActivityOptions {
|
|
1264
|
+
summary: Some("Echo summary".to_string()),
|
|
1265
|
+
..Default::default()
|
|
1266
|
+
},
|
|
1267
|
+
)
|
|
1268
|
+
.await
|
|
1269
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1270
|
+
Ok(())
|
|
1271
|
+
}
|
|
940
1272
|
}
|
|
941
1273
|
|
|
942
1274
|
#[tokio::test]
|
|
943
1275
|
async fn local_activity_with_summary() {
|
|
944
1276
|
let wf_name = "local_activity_with_summary";
|
|
945
1277
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
1278
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
946
1279
|
let mut worker = starter.worker().await;
|
|
947
|
-
worker.
|
|
948
|
-
worker.register_activity("echo_activity", echo);
|
|
1280
|
+
worker.register_workflow::<LocalActivityWithSummaryWf>();
|
|
949
1281
|
|
|
950
1282
|
let handle = starter.start_with_worker(wf_name, &mut worker).await;
|
|
951
1283
|
worker.run_until_done().await.unwrap();
|
|
@@ -978,10 +1310,6 @@ async fn local_activity_with_summary() {
|
|
|
978
1310
|
);
|
|
979
1311
|
}
|
|
980
1312
|
|
|
981
|
-
async fn echo(_ctx: ActContext, e: String) -> Result<String, ActivityError> {
|
|
982
|
-
Ok(e)
|
|
983
|
-
}
|
|
984
|
-
|
|
985
1313
|
/// This test verifies that when replaying we are able to resolve local activities whose data we
|
|
986
1314
|
/// don't see until after the workflow issues the command
|
|
987
1315
|
#[rstest::rstest]
|
|
@@ -1015,29 +1343,23 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
1015
1343
|
}
|
|
1016
1344
|
});
|
|
1017
1345
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1346
|
+
#[workflow]
|
|
1347
|
+
#[derive(Default)]
|
|
1348
|
+
struct LocalActTwoWftsBeforeMarkerWf;
|
|
1349
|
+
|
|
1350
|
+
#[workflow_methods]
|
|
1351
|
+
impl LocalActTwoWftsBeforeMarkerWf {
|
|
1352
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1353
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1354
|
+
let la = ctx.start_local_activity(StdActivities::default, (), Default::default());
|
|
1026
1355
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1027
|
-
la.await;
|
|
1028
|
-
Ok(()
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
worker
|
|
1033
|
-
|
|
1034
|
-
wf_id.to_owned(),
|
|
1035
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1036
|
-
vec![],
|
|
1037
|
-
WorkflowOptions::default(),
|
|
1038
|
-
)
|
|
1039
|
-
.await
|
|
1040
|
-
.unwrap();
|
|
1356
|
+
let _ = la.await;
|
|
1357
|
+
Ok(())
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
worker.register_workflow::<LocalActTwoWftsBeforeMarkerWf>();
|
|
1362
|
+
worker.register_activities(StdActivities);
|
|
1041
1363
|
worker.run_until_done().await.unwrap();
|
|
1042
1364
|
}
|
|
1043
1365
|
|
|
@@ -1060,17 +1382,8 @@ async fn local_act_many_concurrent() {
|
|
|
1060
1382
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
1061
1383
|
let mut worker = mock_sdk(mh);
|
|
1062
1384
|
|
|
1063
|
-
worker.
|
|
1064
|
-
worker.
|
|
1065
|
-
worker
|
|
1066
|
-
.submit_wf(
|
|
1067
|
-
wf_id.to_owned(),
|
|
1068
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1069
|
-
vec![],
|
|
1070
|
-
WorkflowOptions::default(),
|
|
1071
|
-
)
|
|
1072
|
-
.await
|
|
1073
|
-
.unwrap();
|
|
1385
|
+
worker.register_workflow::<LocalActFanoutWf>();
|
|
1386
|
+
worker.register_activities(StdActivities);
|
|
1074
1387
|
worker.run_until_done().await.unwrap();
|
|
1075
1388
|
}
|
|
1076
1389
|
|
|
@@ -1101,43 +1414,67 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
1101
1414
|
wc.max_cached_workflows = 1;
|
|
1102
1415
|
wc.max_outstanding_workflow_tasks = Some(1);
|
|
1103
1416
|
});
|
|
1104
|
-
let core = worker.core_worker
|
|
1417
|
+
let core = worker.core_worker();
|
|
1418
|
+
|
|
1419
|
+
let shutdown_barr = Arc::new(Barrier::new(2));
|
|
1420
|
+
|
|
1421
|
+
#[workflow]
|
|
1422
|
+
#[derive(Default)]
|
|
1423
|
+
struct LocalActHeartbeatWf;
|
|
1424
|
+
|
|
1425
|
+
#[workflow_methods]
|
|
1426
|
+
impl LocalActHeartbeatWf {
|
|
1427
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1428
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1429
|
+
dbg!("dafuq");
|
|
1430
|
+
ctx.start_local_activity(
|
|
1431
|
+
EchoWithConditionalBarrier::echo,
|
|
1432
|
+
"hi".to_string(),
|
|
1433
|
+
LocalActivityOptions::default(),
|
|
1434
|
+
)
|
|
1435
|
+
.await
|
|
1436
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1437
|
+
Ok(())
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1105
1440
|
|
|
1106
|
-
|
|
1441
|
+
worker.register_workflow::<LocalActHeartbeatWf>();
|
|
1107
1442
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1443
|
+
struct EchoWithConditionalBarrier {
|
|
1444
|
+
shutdown_barr: Option<Arc<Barrier>>,
|
|
1445
|
+
wft_timeout: Duration,
|
|
1446
|
+
}
|
|
1447
|
+
#[activities]
|
|
1448
|
+
impl EchoWithConditionalBarrier {
|
|
1449
|
+
#[activity]
|
|
1450
|
+
async fn echo(
|
|
1451
|
+
self: Arc<Self>,
|
|
1452
|
+
_: ActivityContext,
|
|
1453
|
+
str: String,
|
|
1454
|
+
) -> Result<String, ActivityError> {
|
|
1455
|
+
dbg!("Running activity");
|
|
1456
|
+
if let Some(barr) = &self.shutdown_barr {
|
|
1457
|
+
barr.wait().await;
|
|
1458
|
+
}
|
|
1459
|
+
// Take slightly more than two workflow tasks
|
|
1460
|
+
tokio::time::sleep(self.wft_timeout.mul_f32(2.2)).await;
|
|
1461
|
+
Ok(str)
|
|
1123
1462
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
worker.register_activities(EchoWithConditionalBarrier {
|
|
1466
|
+
shutdown_barr: if shutdown_middle {
|
|
1467
|
+
Some(shutdown_barr.clone())
|
|
1468
|
+
} else {
|
|
1469
|
+
None
|
|
1470
|
+
},
|
|
1471
|
+
wft_timeout,
|
|
1127
1472
|
});
|
|
1128
|
-
worker
|
|
1129
|
-
.submit_wf(
|
|
1130
|
-
wf_id.to_owned(),
|
|
1131
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1132
|
-
vec![],
|
|
1133
|
-
WorkflowOptions::default(),
|
|
1134
|
-
)
|
|
1135
|
-
.await
|
|
1136
|
-
.unwrap();
|
|
1137
1473
|
let (_, runres) = tokio::join!(
|
|
1138
1474
|
async {
|
|
1139
1475
|
if shutdown_middle {
|
|
1140
1476
|
shutdown_barr.wait().await;
|
|
1477
|
+
dbg!("Past barrier");
|
|
1141
1478
|
core.shutdown().await;
|
|
1142
1479
|
}
|
|
1143
1480
|
},
|
|
@@ -1153,6 +1490,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
1153
1490
|
async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
1154
1491
|
let mut t = TestHistoryBuilder::default();
|
|
1155
1492
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
1493
|
+
t.set_wf_input(eventually_pass.as_json_payload().unwrap());
|
|
1156
1494
|
t.add_workflow_task_scheduled_and_started();
|
|
1157
1495
|
|
|
1158
1496
|
let wf_id = "fakeid";
|
|
@@ -1160,49 +1498,63 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
1160
1498
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
1161
1499
|
let mut worker = mock_sdk(mh);
|
|
1162
1500
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1501
|
+
#[workflow]
|
|
1502
|
+
#[derive(Default)]
|
|
1503
|
+
struct LocalActFailAndRetryWf;
|
|
1504
|
+
|
|
1505
|
+
#[workflow_methods]
|
|
1506
|
+
impl LocalActFailAndRetryWf {
|
|
1507
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1508
|
+
async fn run(ctx: &mut WorkflowContext<Self>, eventually_pass: bool) -> WorkflowResult<()> {
|
|
1166
1509
|
let la_res = ctx
|
|
1167
|
-
.
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1510
|
+
.start_local_activity(
|
|
1511
|
+
EventuallyPassingActivity::echo,
|
|
1512
|
+
"hi".to_string(),
|
|
1513
|
+
LocalActivityOptions {
|
|
1514
|
+
retry_policy: RetryPolicy {
|
|
1515
|
+
initial_interval: Some(prost_dur!(from_millis(50))),
|
|
1516
|
+
backoff_coefficient: 1.2,
|
|
1517
|
+
maximum_interval: None,
|
|
1518
|
+
maximum_attempts: 5,
|
|
1519
|
+
non_retryable_error_types: vec![],
|
|
1520
|
+
},
|
|
1521
|
+
..Default::default()
|
|
1176
1522
|
},
|
|
1177
|
-
|
|
1178
|
-
})
|
|
1523
|
+
)
|
|
1179
1524
|
.await;
|
|
1180
1525
|
if eventually_pass {
|
|
1181
|
-
assert!(la_res.
|
|
1526
|
+
assert!(la_res.is_ok())
|
|
1182
1527
|
} else {
|
|
1183
|
-
assert!(la_res
|
|
1528
|
+
assert!(matches!(la_res, Err(ActivityExecutionError::Failed(_))))
|
|
1184
1529
|
}
|
|
1185
|
-
Ok(().into())
|
|
1186
|
-
},
|
|
1187
|
-
);
|
|
1188
|
-
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
1189
|
-
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
1190
|
-
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
1191
|
-
if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
|
|
1192
1530
|
Ok(())
|
|
1193
|
-
} else {
|
|
1194
|
-
Err(anyhow!("Oh no I failed!").into())
|
|
1195
1531
|
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
worker.register_workflow::<LocalActFailAndRetryWf>();
|
|
1535
|
+
let attempts = Arc::new(AtomicUsize::new(0));
|
|
1536
|
+
|
|
1537
|
+
struct EventuallyPassingActivity {
|
|
1538
|
+
attempts: Arc<AtomicUsize>,
|
|
1539
|
+
eventually_pass: bool,
|
|
1540
|
+
}
|
|
1541
|
+
#[activities]
|
|
1542
|
+
impl EventuallyPassingActivity {
|
|
1543
|
+
#[activity]
|
|
1544
|
+
async fn echo(self: Arc<Self>, _: ActivityContext, _: String) -> Result<(), ActivityError> {
|
|
1545
|
+
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
1546
|
+
if 2 == self.attempts.fetch_add(1, Ordering::Relaxed) && self.eventually_pass {
|
|
1547
|
+
Ok(())
|
|
1548
|
+
} else {
|
|
1549
|
+
Err(anyhow!("Oh no I failed!").into())
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
worker.register_activities(EventuallyPassingActivity {
|
|
1555
|
+
attempts: attempts.clone(),
|
|
1556
|
+
eventually_pass,
|
|
1196
1557
|
});
|
|
1197
|
-
worker
|
|
1198
|
-
.submit_wf(
|
|
1199
|
-
wf_id.to_owned(),
|
|
1200
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1201
|
-
vec![],
|
|
1202
|
-
WorkflowOptions::default(),
|
|
1203
|
-
)
|
|
1204
|
-
.await
|
|
1205
|
-
.unwrap();
|
|
1206
1558
|
worker.run_until_done().await.unwrap();
|
|
1207
1559
|
let expected_attempts = if eventually_pass { 3 } else { 5 };
|
|
1208
1560
|
assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
|
|
@@ -1213,18 +1565,22 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
1213
1565
|
let mut t = TestHistoryBuilder::default();
|
|
1214
1566
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
1215
1567
|
t.add_full_wf_task();
|
|
1216
|
-
t.
|
|
1568
|
+
t.add_local_activity_marker(
|
|
1217
1569
|
1,
|
|
1218
1570
|
"1",
|
|
1219
|
-
|
|
1571
|
+
None,
|
|
1572
|
+
Some(Failure::application_failure("la failed".to_string(), false)),
|
|
1573
|
+
|m| m.activity_type = StdActivities::always_fail.name().to_owned(),
|
|
1220
1574
|
);
|
|
1221
1575
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
1222
1576
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
1223
1577
|
t.add_full_wf_task();
|
|
1224
|
-
t.
|
|
1578
|
+
t.add_local_activity_marker(
|
|
1225
1579
|
2,
|
|
1226
1580
|
"2",
|
|
1227
|
-
|
|
1581
|
+
None,
|
|
1582
|
+
Some(Failure::application_failure("la failed".to_string(), false)),
|
|
1583
|
+
|m| m.activity_type = StdActivities::always_fail.name().to_owned(),
|
|
1228
1584
|
);
|
|
1229
1585
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
1230
1586
|
t.add_timer_fired(timer_started_event_id, "2".to_string());
|
|
@@ -1241,45 +1597,38 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
1241
1597
|
);
|
|
1242
1598
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1243
1599
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1600
|
+
#[workflow]
|
|
1601
|
+
#[derive(Default)]
|
|
1602
|
+
struct LocalActRetryLongBackoffUsesTimerWf;
|
|
1603
|
+
|
|
1604
|
+
#[workflow_methods]
|
|
1605
|
+
impl LocalActRetryLongBackoffUsesTimerWf {
|
|
1606
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1607
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1247
1608
|
let la_res = ctx
|
|
1248
|
-
.
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1609
|
+
.start_local_activity(
|
|
1610
|
+
StdActivities::always_fail,
|
|
1611
|
+
(),
|
|
1612
|
+
LocalActivityOptions {
|
|
1613
|
+
retry_policy: RetryPolicy {
|
|
1614
|
+
initial_interval: Some(prost_dur!(from_millis(65))),
|
|
1615
|
+
backoff_coefficient: 1_000.,
|
|
1616
|
+
maximum_interval: Some(prost_dur!(from_secs(600))),
|
|
1617
|
+
maximum_attempts: 3,
|
|
1618
|
+
non_retryable_error_types: vec![],
|
|
1619
|
+
},
|
|
1620
|
+
..Default::default()
|
|
1258
1621
|
},
|
|
1259
|
-
|
|
1260
|
-
})
|
|
1622
|
+
)
|
|
1261
1623
|
.await;
|
|
1262
|
-
assert!(la_res
|
|
1263
|
-
// Extra timer just to have an extra workflow task which we can return full history for
|
|
1624
|
+
assert!(matches!(la_res, Err(ActivityExecutionError::Failed(_))));
|
|
1264
1625
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1265
|
-
Ok(()
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
1272
|
-
},
|
|
1273
|
-
);
|
|
1274
|
-
worker
|
|
1275
|
-
.submit_wf(
|
|
1276
|
-
wf_id.to_owned(),
|
|
1277
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1278
|
-
vec![],
|
|
1279
|
-
WorkflowOptions::default(),
|
|
1280
|
-
)
|
|
1281
|
-
.await
|
|
1282
|
-
.unwrap();
|
|
1626
|
+
Ok(())
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
worker.register_workflow::<LocalActRetryLongBackoffUsesTimerWf>();
|
|
1631
|
+
worker.register_activities(StdActivities);
|
|
1283
1632
|
worker.run_until_done().await.unwrap();
|
|
1284
1633
|
}
|
|
1285
1634
|
|
|
@@ -1296,28 +1645,23 @@ async fn local_act_null_result() {
|
|
|
1296
1645
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
|
|
1297
1646
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1298
1647
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
.
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
vec![],
|
|
1317
|
-
WorkflowOptions::default(),
|
|
1318
|
-
)
|
|
1319
|
-
.await
|
|
1320
|
-
.unwrap();
|
|
1648
|
+
#[workflow]
|
|
1649
|
+
#[derive(Default)]
|
|
1650
|
+
struct LocalActNullResultWf;
|
|
1651
|
+
|
|
1652
|
+
#[workflow_methods]
|
|
1653
|
+
impl LocalActNullResultWf {
|
|
1654
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1655
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1656
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
1657
|
+
.await
|
|
1658
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1659
|
+
Ok(())
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
worker.register_workflow::<LocalActNullResultWf>();
|
|
1664
|
+
worker.register_activities(StdActivities);
|
|
1321
1665
|
worker.run_until_done().await.unwrap();
|
|
1322
1666
|
}
|
|
1323
1667
|
|
|
@@ -1335,33 +1679,27 @@ async fn local_act_command_immediately_follows_la_marker() {
|
|
|
1335
1679
|
|
|
1336
1680
|
let wf_id = "fakeid";
|
|
1337
1681
|
let mock = mock_worker_client();
|
|
1338
|
-
// Bug only repros when seeing history up to third wft
|
|
1339
1682
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [3], mock);
|
|
1340
1683
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 0);
|
|
1341
1684
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
.
|
|
1685
|
+
#[workflow]
|
|
1686
|
+
#[derive(Default)]
|
|
1687
|
+
struct LocalActCommandImmediatelyFollowsLaMarkerWf;
|
|
1688
|
+
|
|
1689
|
+
#[workflow_methods]
|
|
1690
|
+
impl LocalActCommandImmediatelyFollowsLaMarkerWf {
|
|
1691
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1692
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1693
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
1694
|
+
.await
|
|
1695
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1351
1696
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1352
|
-
Ok(()
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
worker
|
|
1357
|
-
|
|
1358
|
-
wf_id.to_owned(),
|
|
1359
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1360
|
-
vec![],
|
|
1361
|
-
WorkflowOptions::default(),
|
|
1362
|
-
)
|
|
1363
|
-
.await
|
|
1364
|
-
.unwrap();
|
|
1697
|
+
Ok(())
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
worker.register_workflow::<LocalActCommandImmediatelyFollowsLaMarkerWf>();
|
|
1702
|
+
worker.register_activities(StdActivities);
|
|
1365
1703
|
worker.run_until_done().await.unwrap();
|
|
1366
1704
|
}
|
|
1367
1705
|
|
|
@@ -1632,44 +1970,39 @@ async fn test_schedule_to_start_timeout() {
|
|
|
1632
1970
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::ToTaskNum(1)], mock);
|
|
1633
1971
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1634
1972
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1973
|
+
#[workflow]
|
|
1974
|
+
#[derive(Default)]
|
|
1975
|
+
struct TestScheduleToStartTimeoutWf;
|
|
1976
|
+
|
|
1977
|
+
#[workflow_methods]
|
|
1978
|
+
impl TestScheduleToStartTimeoutWf {
|
|
1979
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
1980
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
1638
1981
|
let la_res = ctx
|
|
1639
|
-
.
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1982
|
+
.start_local_activity(
|
|
1983
|
+
StdActivities::echo,
|
|
1984
|
+
"hi".to_string(),
|
|
1985
|
+
LocalActivityOptions {
|
|
1986
|
+
schedule_to_start_timeout: prost_dur!(from_nanos(1)),
|
|
1987
|
+
..Default::default()
|
|
1988
|
+
},
|
|
1989
|
+
)
|
|
1646
1990
|
.await;
|
|
1647
|
-
|
|
1648
|
-
let
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
)
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
worker.
|
|
1661
|
-
|
|
1662
|
-
move |_ctx: ActContext, _: String| async move { Ok(()) },
|
|
1663
|
-
);
|
|
1664
|
-
worker
|
|
1665
|
-
.submit_wf(
|
|
1666
|
-
wf_id.to_owned(),
|
|
1667
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1668
|
-
vec![],
|
|
1669
|
-
WorkflowOptions::default(),
|
|
1670
|
-
)
|
|
1671
|
-
.await
|
|
1672
|
-
.unwrap();
|
|
1991
|
+
assert!(la_res.is_err());
|
|
1992
|
+
if let Err(ActivityExecutionError::Failed(ref fail)) = la_res {
|
|
1993
|
+
assert_eq!(fail.is_timeout(), Some(TimeoutType::ScheduleToStart));
|
|
1994
|
+
assert_matches!(fail.failure_info, Some(FailureInfo::ActivityFailureInfo(_)));
|
|
1995
|
+
assert_matches!(
|
|
1996
|
+
fail.cause.as_ref().unwrap().failure_info,
|
|
1997
|
+
Some(FailureInfo::TimeoutFailureInfo(_))
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
Ok(())
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
worker.register_workflow::<TestScheduleToStartTimeoutWf>();
|
|
2005
|
+
worker.register_activities(StdActivities);
|
|
1673
2006
|
worker.run_until_done().await.unwrap();
|
|
1674
2007
|
}
|
|
1675
2008
|
|
|
@@ -1686,8 +2019,19 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1686
2019
|
// * we don't time out on s-t-s timeouts because of that, when the param is true.
|
|
1687
2020
|
// * we do properly time out on s-t-c timeouts when the param is false
|
|
1688
2021
|
|
|
2022
|
+
let schedule_to_close_timeout = Some(if is_sched_to_start {
|
|
2023
|
+
Duration::from_secs(60 * 60)
|
|
2024
|
+
} else {
|
|
2025
|
+
Duration::from_secs(10 * 60)
|
|
2026
|
+
});
|
|
2027
|
+
|
|
1689
2028
|
let mut t = TestHistoryBuilder::default();
|
|
1690
2029
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
2030
|
+
t.set_wf_input(
|
|
2031
|
+
(is_sched_to_start, schedule_to_close_timeout)
|
|
2032
|
+
.as_json_payload()
|
|
2033
|
+
.unwrap(),
|
|
2034
|
+
);
|
|
1691
2035
|
t.add_full_wf_task();
|
|
1692
2036
|
let orig_sched = SystemTime::now().sub(Duration::from_secs(60 * 20));
|
|
1693
2037
|
t.add_local_activity_marker(
|
|
@@ -1711,55 +2055,47 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1711
2055
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
|
|
1712
2056
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1713
2057
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2058
|
+
#[workflow]
|
|
2059
|
+
#[derive(Default)]
|
|
2060
|
+
struct TestScheduleToStartTimeoutNotBasedOnOriginalTimeWf;
|
|
2061
|
+
|
|
2062
|
+
#[workflow_methods]
|
|
2063
|
+
impl TestScheduleToStartTimeoutNotBasedOnOriginalTimeWf {
|
|
2064
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2065
|
+
async fn run(
|
|
2066
|
+
ctx: &mut WorkflowContext<Self>,
|
|
2067
|
+
input: (bool, Option<Duration>),
|
|
2068
|
+
) -> WorkflowResult<()> {
|
|
2069
|
+
let (is_sched_to_start, schedule_to_close_timeout) = input;
|
|
1726
2070
|
let la_res = ctx
|
|
1727
|
-
.
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2071
|
+
.start_local_activity(
|
|
2072
|
+
StdActivities::echo,
|
|
2073
|
+
"hi".to_string(),
|
|
2074
|
+
LocalActivityOptions {
|
|
2075
|
+
retry_policy: RetryPolicy {
|
|
2076
|
+
initial_interval: Some(prost_dur!(from_millis(50))),
|
|
2077
|
+
backoff_coefficient: 1.2,
|
|
2078
|
+
maximum_interval: None,
|
|
2079
|
+
maximum_attempts: 5,
|
|
2080
|
+
non_retryable_error_types: vec![],
|
|
2081
|
+
},
|
|
2082
|
+
schedule_to_start_timeout: Some(Duration::from_secs(60)),
|
|
2083
|
+
schedule_to_close_timeout,
|
|
2084
|
+
..Default::default()
|
|
1736
2085
|
},
|
|
1737
|
-
|
|
1738
|
-
schedule_to_close_timeout,
|
|
1739
|
-
..Default::default()
|
|
1740
|
-
})
|
|
2086
|
+
)
|
|
1741
2087
|
.await;
|
|
1742
2088
|
if is_sched_to_start {
|
|
1743
|
-
assert!(la_res.
|
|
1744
|
-
} else {
|
|
1745
|
-
assert_eq!(
|
|
2089
|
+
assert!(la_res.is_ok());
|
|
2090
|
+
} else if let Err(ActivityExecutionError::Failed(ref fail)) = la_res {
|
|
2091
|
+
assert_eq!(fail.is_timeout(), Some(TimeoutType::ScheduleToClose));
|
|
1746
2092
|
}
|
|
1747
|
-
Ok(()
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
);
|
|
1754
|
-
worker
|
|
1755
|
-
.submit_wf(
|
|
1756
|
-
wf_id.to_owned(),
|
|
1757
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1758
|
-
vec![],
|
|
1759
|
-
WorkflowOptions::default(),
|
|
1760
|
-
)
|
|
1761
|
-
.await
|
|
1762
|
-
.unwrap();
|
|
2093
|
+
Ok(())
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
worker.register_workflow::<TestScheduleToStartTimeoutNotBasedOnOriginalTimeWf>();
|
|
2098
|
+
worker.register_activities(StdActivities);
|
|
1763
2099
|
worker.run_until_done().await.unwrap();
|
|
1764
2100
|
}
|
|
1765
2101
|
|
|
@@ -1768,6 +2104,7 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1768
2104
|
async fn start_to_close_timeout_allows_retries(#[values(true, false)] la_completes: bool) {
|
|
1769
2105
|
let mut t = TestHistoryBuilder::default();
|
|
1770
2106
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
2107
|
+
t.set_wf_input(la_completes.as_json_payload().unwrap());
|
|
1771
2108
|
t.add_full_wf_task();
|
|
1772
2109
|
if la_completes {
|
|
1773
2110
|
t.add_local_activity_marker(1, "1", Some("hi".into()), None, |_| {});
|
|
@@ -1793,61 +2130,73 @@ async fn start_to_close_timeout_allows_retries(#[values(true, false)] la_complet
|
|
|
1793
2130
|
);
|
|
1794
2131
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1795
2132
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2133
|
+
#[workflow]
|
|
2134
|
+
#[derive(Default)]
|
|
2135
|
+
struct StartToCloseTimeoutAllowsRetriesWf;
|
|
2136
|
+
|
|
2137
|
+
#[workflow_methods]
|
|
2138
|
+
impl StartToCloseTimeoutAllowsRetriesWf {
|
|
2139
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2140
|
+
async fn run(ctx: &mut WorkflowContext<Self>, la_completes: bool) -> WorkflowResult<()> {
|
|
1799
2141
|
let la_res = ctx
|
|
1800
|
-
.
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
2142
|
+
.start_local_activity(
|
|
2143
|
+
ActivityWithRetriesAndCancellation::go,
|
|
2144
|
+
(),
|
|
2145
|
+
LocalActivityOptions {
|
|
2146
|
+
retry_policy: RetryPolicy {
|
|
2147
|
+
initial_interval: Some(prost_dur!(from_millis(20))),
|
|
2148
|
+
backoff_coefficient: 1.0,
|
|
2149
|
+
maximum_interval: None,
|
|
2150
|
+
maximum_attempts: 5,
|
|
2151
|
+
non_retryable_error_types: vec![],
|
|
2152
|
+
},
|
|
2153
|
+
start_to_close_timeout: Some(prost_dur!(from_millis(25))),
|
|
2154
|
+
..Default::default()
|
|
1809
2155
|
},
|
|
1810
|
-
|
|
1811
|
-
..Default::default()
|
|
1812
|
-
})
|
|
2156
|
+
)
|
|
1813
2157
|
.await;
|
|
1814
2158
|
if la_completes {
|
|
1815
|
-
assert!(la_res.
|
|
1816
|
-
} else {
|
|
1817
|
-
assert_eq!(
|
|
2159
|
+
assert!(la_res.is_ok(), "Result should be ok was {la_res:?}");
|
|
2160
|
+
} else if let Err(ActivityExecutionError::Failed(ref fail)) = la_res {
|
|
2161
|
+
assert_eq!(fail.is_timeout(), Some(TimeoutType::StartToClose));
|
|
1818
2162
|
}
|
|
1819
|
-
Ok(()
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2163
|
+
Ok(())
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
worker.register_workflow::<StartToCloseTimeoutAllowsRetriesWf>();
|
|
2168
|
+
let attempts = Arc::new(AtomicUsize::new(0));
|
|
2169
|
+
let cancels = Arc::new(AtomicUsize::new(0));
|
|
2170
|
+
|
|
2171
|
+
struct ActivityWithRetriesAndCancellation {
|
|
2172
|
+
attempts: Arc<AtomicUsize>,
|
|
2173
|
+
cancels: Arc<AtomicUsize>,
|
|
2174
|
+
la_completes: bool,
|
|
2175
|
+
}
|
|
2176
|
+
#[activities]
|
|
2177
|
+
impl ActivityWithRetriesAndCancellation {
|
|
2178
|
+
#[activity(name = DEFAULT_ACTIVITY_TYPE)]
|
|
2179
|
+
async fn go(self: Arc<Self>, ctx: ActivityContext) -> Result<RawValue, ActivityError> {
|
|
1827
2180
|
// Timeout the first 4 attempts, or all of them if we intend to fail
|
|
1828
|
-
if attempts.fetch_add(1, Ordering::AcqRel) < 4 || !la_completes {
|
|
2181
|
+
if self.attempts.fetch_add(1, Ordering::AcqRel) < 4 || !self.la_completes {
|
|
1829
2182
|
select! {
|
|
1830
2183
|
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
|
1831
2184
|
_ = ctx.cancelled() => {
|
|
1832
|
-
cancels.fetch_add(1, Ordering::AcqRel);
|
|
2185
|
+
self.cancels.fetch_add(1, Ordering::AcqRel);
|
|
1833
2186
|
return Err(ActivityError::cancelled());
|
|
1834
2187
|
}
|
|
1835
2188
|
}
|
|
1836
2189
|
}
|
|
1837
|
-
Ok(())
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
)
|
|
1847
|
-
.await
|
|
1848
|
-
.unwrap();
|
|
2190
|
+
Ok(RawValue::empty())
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
worker.register_activities(ActivityWithRetriesAndCancellation {
|
|
2195
|
+
attempts: attempts.clone(),
|
|
2196
|
+
cancels: cancels.clone(),
|
|
2197
|
+
la_completes,
|
|
2198
|
+
});
|
|
1849
2199
|
worker.run_until_done().await.unwrap();
|
|
1850
|
-
// Activity should have been attempted all 5 times
|
|
1851
2200
|
assert_eq!(attempts.load(Ordering::Acquire), 5);
|
|
1852
2201
|
let num_cancels = if la_completes { 4 } else { 5 };
|
|
1853
2202
|
assert_eq!(cancels.load(Ordering::Acquire), num_cancels);
|
|
@@ -1868,43 +2217,46 @@ async fn wft_failure_cancels_running_las() {
|
|
|
1868
2217
|
mh.num_expected_fails = 1;
|
|
1869
2218
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1870
2219
|
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2220
|
+
#[workflow]
|
|
2221
|
+
#[derive(Default)]
|
|
2222
|
+
struct WftFailureCancelsRunningLasWf;
|
|
2223
|
+
|
|
2224
|
+
#[workflow_methods]
|
|
2225
|
+
impl WftFailureCancelsRunningLasWf {
|
|
2226
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2227
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2228
|
+
let la_handle = ctx.start_local_activity(
|
|
2229
|
+
ActivityThatExpectsCancellation::go,
|
|
2230
|
+
(),
|
|
2231
|
+
Default::default(),
|
|
2232
|
+
);
|
|
2233
|
+
temporalio_sdk::workflows::join!(
|
|
1880
2234
|
async {
|
|
1881
2235
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1882
2236
|
panic!("ahhh I'm failing wft")
|
|
1883
2237
|
},
|
|
1884
2238
|
la_handle
|
|
1885
2239
|
);
|
|
1886
|
-
Ok(()
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2240
|
+
Ok(())
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
worker.register_workflow::<WftFailureCancelsRunningLasWf>();
|
|
2245
|
+
|
|
2246
|
+
struct ActivityThatExpectsCancellation;
|
|
2247
|
+
#[activities]
|
|
2248
|
+
impl ActivityThatExpectsCancellation {
|
|
2249
|
+
#[activity]
|
|
2250
|
+
async fn go(ctx: ActivityContext) -> Result<(), ActivityError> {
|
|
1892
2251
|
let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
|
|
1893
2252
|
if res.is_err() {
|
|
1894
2253
|
panic!("Activity must be cancelled!!!!");
|
|
1895
2254
|
}
|
|
1896
|
-
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
wf_id.to_owned(),
|
|
1902
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1903
|
-
vec![],
|
|
1904
|
-
WorkflowOptions::default(),
|
|
1905
|
-
)
|
|
1906
|
-
.await
|
|
1907
|
-
.unwrap();
|
|
2255
|
+
Err(ActivityError::cancelled())
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
worker.register_activities(ActivityThatExpectsCancellation);
|
|
1908
2260
|
worker.run_until_done().await.unwrap();
|
|
1909
2261
|
}
|
|
1910
2262
|
|
|
@@ -1934,32 +2286,30 @@ async fn resolved_las_not_recorded_if_wft_fails_many_times() {
|
|
|
1934
2286
|
mh.num_expected_completions = Some(0.into());
|
|
1935
2287
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1936
2288
|
|
|
1937
|
-
#[
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
.
|
|
2289
|
+
#[workflow]
|
|
2290
|
+
#[derive(Default)]
|
|
2291
|
+
struct ResolvedLasNotRecordedIfWftFailsManyTimesWf;
|
|
2292
|
+
|
|
2293
|
+
#[workflow_methods]
|
|
2294
|
+
impl ResolvedLasNotRecordedIfWftFailsManyTimesWf {
|
|
2295
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2296
|
+
#[allow(unreachable_code)]
|
|
2297
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2298
|
+
ctx.start_local_activity(
|
|
2299
|
+
StdActivities::echo,
|
|
2300
|
+
"hi".to_string(),
|
|
2301
|
+
LocalActivityOptions {
|
|
2302
|
+
..Default::default()
|
|
2303
|
+
},
|
|
2304
|
+
)
|
|
2305
|
+
.await
|
|
2306
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
1947
2307
|
panic!()
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
);
|
|
1954
|
-
worker
|
|
1955
|
-
.submit_wf(
|
|
1956
|
-
wf_id.to_owned(),
|
|
1957
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1958
|
-
vec![],
|
|
1959
|
-
WorkflowOptions::default(),
|
|
1960
|
-
)
|
|
1961
|
-
.await
|
|
1962
|
-
.unwrap();
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
worker.register_workflow::<ResolvedLasNotRecordedIfWftFailsManyTimesWf>();
|
|
2312
|
+
worker.register_activities(StdActivities);
|
|
1963
2313
|
worker.run_until_done().await.unwrap();
|
|
1964
2314
|
}
|
|
1965
2315
|
|
|
@@ -1990,39 +2340,37 @@ async fn local_act_records_nonfirst_attempts_ok() {
|
|
|
1990
2340
|
wc.max_outstanding_workflow_tasks = Some(1);
|
|
1991
2341
|
});
|
|
1992
2342
|
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2343
|
+
#[workflow]
|
|
2344
|
+
#[derive(Default)]
|
|
2345
|
+
struct LocalActRecordsNonfirstAttemptsOkWf;
|
|
2346
|
+
|
|
2347
|
+
#[workflow_methods]
|
|
2348
|
+
impl LocalActRecordsNonfirstAttemptsOkWf {
|
|
2349
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2350
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2351
|
+
ctx.start_local_activity(
|
|
2352
|
+
StdActivities::always_fail,
|
|
2353
|
+
(),
|
|
2354
|
+
LocalActivityOptions {
|
|
2355
|
+
retry_policy: RetryPolicy {
|
|
2356
|
+
initial_interval: Some(prost_dur!(from_millis(10))),
|
|
2357
|
+
backoff_coefficient: 1.0,
|
|
2358
|
+
maximum_interval: None,
|
|
2359
|
+
maximum_attempts: 0,
|
|
2360
|
+
non_retryable_error_types: vec![],
|
|
2361
|
+
},
|
|
2362
|
+
..Default::default()
|
|
2005
2363
|
},
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
.
|
|
2009
|
-
Ok(()
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
worker
|
|
2016
|
-
.submit_wf(
|
|
2017
|
-
wf_id.to_owned(),
|
|
2018
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
2019
|
-
vec![],
|
|
2020
|
-
WorkflowOptions::default(),
|
|
2021
|
-
)
|
|
2022
|
-
.await
|
|
2023
|
-
.unwrap();
|
|
2364
|
+
)
|
|
2365
|
+
.await
|
|
2366
|
+
.ok();
|
|
2367
|
+
Ok(())
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
worker.register_workflow::<LocalActRecordsNonfirstAttemptsOkWf>();
|
|
2372
|
+
worker.register_activities(StdActivities);
|
|
2024
2373
|
worker.run_until_done().await.unwrap();
|
|
2025
|
-
// 3 workflow tasks
|
|
2026
2374
|
assert_eq!(nonfirst_counts.len(), 3);
|
|
2027
2375
|
// First task's non-first count should, of course, be 0
|
|
2028
2376
|
assert_eq!(nonfirst_counts.pop().unwrap(), 0);
|
|
@@ -2308,70 +2656,92 @@ async fn local_act_retry_explicit_delay() {
|
|
|
2308
2656
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
2309
2657
|
let mut worker = mock_sdk(mh);
|
|
2310
2658
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2659
|
+
#[workflow]
|
|
2660
|
+
#[derive(Default)]
|
|
2661
|
+
struct LocalActRetryExplicitDelayWf;
|
|
2662
|
+
|
|
2663
|
+
#[workflow_methods]
|
|
2664
|
+
impl LocalActRetryExplicitDelayWf {
|
|
2665
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2666
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2314
2667
|
let la_res = ctx
|
|
2315
|
-
.
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2668
|
+
.start_local_activity(
|
|
2669
|
+
ActivityWithExplicitBackoff::go,
|
|
2670
|
+
(),
|
|
2671
|
+
LocalActivityOptions {
|
|
2672
|
+
retry_policy: RetryPolicy {
|
|
2673
|
+
initial_interval: Some(prost_dur!(from_millis(50))),
|
|
2674
|
+
backoff_coefficient: 1.0,
|
|
2675
|
+
maximum_attempts: 5,
|
|
2676
|
+
..Default::default()
|
|
2677
|
+
},
|
|
2322
2678
|
..Default::default()
|
|
2323
2679
|
},
|
|
2324
|
-
|
|
2325
|
-
})
|
|
2680
|
+
)
|
|
2326
2681
|
.await;
|
|
2327
|
-
assert!(la_res.
|
|
2328
|
-
Ok(().into())
|
|
2329
|
-
},
|
|
2330
|
-
);
|
|
2331
|
-
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
2332
|
-
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
2333
|
-
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
2334
|
-
let last_attempt = attempts.fetch_add(1, Ordering::Relaxed);
|
|
2335
|
-
if 0 == last_attempt {
|
|
2336
|
-
Err(ActivityError::Retryable {
|
|
2337
|
-
source: anyhow!("Explicit backoff error"),
|
|
2338
|
-
explicit_delay: Some(Duration::from_millis(300)),
|
|
2339
|
-
})
|
|
2340
|
-
} else if 2 == last_attempt {
|
|
2682
|
+
assert!(la_res.is_ok());
|
|
2341
2683
|
Ok(())
|
|
2342
|
-
} else {
|
|
2343
|
-
Err(anyhow!("Oh no I failed!").into())
|
|
2344
2684
|
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
worker.register_workflow::<LocalActRetryExplicitDelayWf>();
|
|
2688
|
+
let attempts = Arc::new(AtomicUsize::new(0));
|
|
2689
|
+
|
|
2690
|
+
struct ActivityWithExplicitBackoff {
|
|
2691
|
+
attempts: Arc<AtomicUsize>,
|
|
2692
|
+
}
|
|
2693
|
+
#[activities]
|
|
2694
|
+
impl ActivityWithExplicitBackoff {
|
|
2695
|
+
#[activity]
|
|
2696
|
+
async fn go(self: Arc<Self>, _: ActivityContext) -> Result<(), ActivityError> {
|
|
2697
|
+
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
2698
|
+
let last_attempt = self.attempts.fetch_add(1, Ordering::Relaxed);
|
|
2699
|
+
if 0 == last_attempt {
|
|
2700
|
+
Err(ActivityError::Retryable {
|
|
2701
|
+
source: anyhow!("Explicit backoff error").into_boxed_dyn_error(),
|
|
2702
|
+
explicit_delay: Some(Duration::from_millis(300)),
|
|
2703
|
+
})
|
|
2704
|
+
} else if 2 == last_attempt {
|
|
2705
|
+
Ok(())
|
|
2706
|
+
} else {
|
|
2707
|
+
Err(anyhow!("Oh no I failed!").into())
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
worker.register_activities(ActivityWithExplicitBackoff {
|
|
2713
|
+
attempts: attempts.clone(),
|
|
2345
2714
|
});
|
|
2346
|
-
worker
|
|
2347
|
-
.submit_wf(
|
|
2348
|
-
wf_id.to_owned(),
|
|
2349
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
2350
|
-
vec![],
|
|
2351
|
-
WorkflowOptions::default(),
|
|
2352
|
-
)
|
|
2353
|
-
.await
|
|
2354
|
-
.unwrap();
|
|
2355
2715
|
let start = Instant::now();
|
|
2356
2716
|
worker.run_until_done().await.unwrap();
|
|
2357
2717
|
let expected_attempts = 3;
|
|
2358
2718
|
assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
|
|
2359
|
-
// There will be one 300ms backoff and one 50s backoff, so things should take at least that long
|
|
2360
2719
|
assert!(start.elapsed() > Duration::from_millis(350));
|
|
2361
2720
|
}
|
|
2362
2721
|
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2722
|
+
#[workflow]
|
|
2723
|
+
#[derive(Default)]
|
|
2724
|
+
struct LaWf;
|
|
2725
|
+
|
|
2726
|
+
#[workflow_methods]
|
|
2727
|
+
impl LaWf {
|
|
2728
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2729
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2730
|
+
let _ = ctx
|
|
2731
|
+
.start_local_activity(
|
|
2732
|
+
StdActivities::default,
|
|
2733
|
+
(),
|
|
2734
|
+
LocalActivityOptions {
|
|
2735
|
+
retry_policy: RetryPolicy {
|
|
2736
|
+
maximum_attempts: 1,
|
|
2737
|
+
..Default::default()
|
|
2738
|
+
},
|
|
2739
|
+
..Default::default()
|
|
2740
|
+
},
|
|
2741
|
+
)
|
|
2742
|
+
.await;
|
|
2743
|
+
Ok(())
|
|
2744
|
+
}
|
|
2375
2745
|
}
|
|
2376
2746
|
|
|
2377
2747
|
#[rstest]
|
|
@@ -2440,53 +2810,81 @@ async fn one_la_success(#[case] replay: bool, #[case] completes_ok: bool) {
|
|
|
2440
2810
|
});
|
|
2441
2811
|
|
|
2442
2812
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2443
|
-
worker.
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2813
|
+
worker.register_workflow::<LaWf>();
|
|
2814
|
+
|
|
2815
|
+
struct ActivityWithReplayCheck {
|
|
2816
|
+
replay: bool,
|
|
2817
|
+
completes_ok: bool,
|
|
2818
|
+
}
|
|
2819
|
+
#[activities]
|
|
2820
|
+
impl ActivityWithReplayCheck {
|
|
2821
|
+
#[activity(name = DEFAULT_ACTIVITY_TYPE)]
|
|
2822
|
+
#[allow(unused)]
|
|
2823
|
+
async fn echo(
|
|
2824
|
+
self: Arc<Self>,
|
|
2825
|
+
_: ActivityContext,
|
|
2826
|
+
_: RawValue,
|
|
2827
|
+
) -> Result<String, ActivityError> {
|
|
2828
|
+
if self.replay {
|
|
2448
2829
|
panic!("Should not be invoked on replay");
|
|
2449
2830
|
}
|
|
2450
|
-
if completes_ok {
|
|
2451
|
-
Ok("hi")
|
|
2831
|
+
if self.completes_ok {
|
|
2832
|
+
Ok("hi".to_string())
|
|
2452
2833
|
} else {
|
|
2453
2834
|
Err(anyhow!("Oh no I failed!").into())
|
|
2454
2835
|
}
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
worker.register_activities(ActivityWithReplayCheck {
|
|
2840
|
+
replay,
|
|
2841
|
+
completes_ok,
|
|
2842
|
+
});
|
|
2457
2843
|
worker.run().await.unwrap();
|
|
2458
2844
|
}
|
|
2459
2845
|
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
ctx
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2846
|
+
#[workflow]
|
|
2847
|
+
#[derive(Default)]
|
|
2848
|
+
struct TwoLaWf;
|
|
2849
|
+
|
|
2850
|
+
#[workflow_methods]
|
|
2851
|
+
impl TwoLaWf {
|
|
2852
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2853
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2854
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
2855
|
+
.await
|
|
2856
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
2857
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
2858
|
+
.await
|
|
2859
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
2860
|
+
Ok(())
|
|
2861
|
+
}
|
|
2474
2862
|
}
|
|
2475
2863
|
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2864
|
+
#[workflow]
|
|
2865
|
+
#[derive(Default)]
|
|
2866
|
+
struct TwoLaWfParallel;
|
|
2867
|
+
|
|
2868
|
+
#[workflow_methods]
|
|
2869
|
+
impl TwoLaWfParallel {
|
|
2870
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2871
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2872
|
+
let _ = temporalio_sdk::workflows::join!(
|
|
2873
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default()),
|
|
2874
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
2875
|
+
);
|
|
2876
|
+
Ok(())
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
struct ResolvedActivity;
|
|
2881
|
+
#[activities]
|
|
2882
|
+
impl ResolvedActivity {
|
|
2883
|
+
#[allow(unused)]
|
|
2884
|
+
#[activity(name = DEFAULT_ACTIVITY_TYPE)]
|
|
2885
|
+
async fn echo(_: ActivityContext, _: ()) -> Result<String, ActivityError> {
|
|
2886
|
+
Ok("Resolved".to_string())
|
|
2887
|
+
}
|
|
2490
2888
|
}
|
|
2491
2889
|
|
|
2492
2890
|
#[rstest]
|
|
@@ -2574,32 +2972,31 @@ async fn two_sequential_las(
|
|
|
2574
2972
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2575
2973
|
worker.set_worker_interceptor(aai);
|
|
2576
2974
|
if parallel {
|
|
2577
|
-
worker.
|
|
2975
|
+
worker.register_workflow::<TwoLaWfParallel>();
|
|
2578
2976
|
} else {
|
|
2579
|
-
worker.
|
|
2977
|
+
worker.register_workflow::<TwoLaWf>();
|
|
2580
2978
|
}
|
|
2581
|
-
worker.
|
|
2582
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2583
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2584
|
-
);
|
|
2979
|
+
worker.register_activities(ResolvedActivity);
|
|
2585
2980
|
worker.run().await.unwrap();
|
|
2586
2981
|
}
|
|
2587
2982
|
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
ctx
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2983
|
+
#[workflow]
|
|
2984
|
+
#[derive(Default)]
|
|
2985
|
+
struct LaTimerLaWf;
|
|
2986
|
+
|
|
2987
|
+
#[workflow_methods]
|
|
2988
|
+
impl LaTimerLaWf {
|
|
2989
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
2990
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<()> {
|
|
2991
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
2992
|
+
.await
|
|
2993
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
2994
|
+
ctx.timer(Duration::from_secs(5)).await;
|
|
2995
|
+
ctx.start_local_activity(StdActivities::default, (), LocalActivityOptions::default())
|
|
2996
|
+
.await
|
|
2997
|
+
.map_err(|e| WorkflowTermination::from(anyhow::Error::from(e)))?;
|
|
2998
|
+
Ok(())
|
|
2999
|
+
}
|
|
2603
3000
|
}
|
|
2604
3001
|
|
|
2605
3002
|
#[rstest]
|
|
@@ -2672,11 +3069,8 @@ async fn las_separated_by_timer(#[case] replay: bool) {
|
|
|
2672
3069
|
|
|
2673
3070
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2674
3071
|
worker.set_worker_interceptor(aai);
|
|
2675
|
-
worker.
|
|
2676
|
-
worker.
|
|
2677
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2678
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2679
|
-
);
|
|
3072
|
+
worker.register_workflow::<LaTimerLaWf>();
|
|
3073
|
+
worker.register_activities(ResolvedActivity);
|
|
2680
3074
|
worker.run().await.unwrap();
|
|
2681
3075
|
}
|
|
2682
3076
|
|
|
@@ -2707,11 +3101,8 @@ async fn one_la_heartbeating_wft_failure_still_executes() {
|
|
|
2707
3101
|
});
|
|
2708
3102
|
|
|
2709
3103
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2710
|
-
worker.
|
|
2711
|
-
worker.
|
|
2712
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2713
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2714
|
-
);
|
|
3104
|
+
worker.register_workflow::<LaWf>();
|
|
3105
|
+
worker.register_activities(ResolvedActivity);
|
|
2715
3106
|
worker.run().await.unwrap();
|
|
2716
3107
|
}
|
|
2717
3108
|
|
|
@@ -2727,6 +3118,7 @@ async fn immediate_cancel(
|
|
|
2727
3118
|
) {
|
|
2728
3119
|
let mut t = TestHistoryBuilder::default();
|
|
2729
3120
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
3121
|
+
t.set_wf_input(cancel_type.as_json_payload().unwrap());
|
|
2730
3122
|
t.add_full_wf_task();
|
|
2731
3123
|
t.add_workflow_execution_completed();
|
|
2732
3124
|
|
|
@@ -2744,16 +3136,33 @@ async fn immediate_cancel(
|
|
|
2744
3136
|
});
|
|
2745
3137
|
|
|
2746
3138
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3139
|
+
|
|
3140
|
+
#[workflow]
|
|
3141
|
+
#[derive(Default)]
|
|
3142
|
+
struct CancelBeforeActStartsWf;
|
|
3143
|
+
|
|
3144
|
+
#[workflow_methods]
|
|
3145
|
+
impl CancelBeforeActStartsWf {
|
|
3146
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
3147
|
+
async fn run(
|
|
3148
|
+
ctx: &mut WorkflowContext<Self>,
|
|
3149
|
+
cancel_type: ActivityCancellationType,
|
|
3150
|
+
) -> WorkflowResult<()> {
|
|
3151
|
+
let la = ctx.start_local_activity(
|
|
3152
|
+
StdActivities::default,
|
|
3153
|
+
(),
|
|
3154
|
+
LocalActivityOptions {
|
|
3155
|
+
cancel_type,
|
|
3156
|
+
..Default::default()
|
|
3157
|
+
},
|
|
3158
|
+
);
|
|
3159
|
+
la.cancel();
|
|
3160
|
+
la.await.ok();
|
|
3161
|
+
Ok(())
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
worker.register_workflow::<CancelBeforeActStartsWf>();
|
|
2757
3166
|
worker.run().await.unwrap();
|
|
2758
3167
|
}
|
|
2759
3168
|
|
|
@@ -2772,6 +3181,7 @@ async fn cancel_after_act_starts_canned(
|
|
|
2772
3181
|
) {
|
|
2773
3182
|
let mut t = TestHistoryBuilder::default();
|
|
2774
3183
|
t.add_wfe_started_with_wft_timeout(Duration::from_millis(100));
|
|
3184
|
+
t.set_wf_input(cancel_type.as_json_payload().unwrap());
|
|
2775
3185
|
t.add_full_wf_task();
|
|
2776
3186
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
2777
3187
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
@@ -2841,40 +3251,69 @@ async fn cancel_after_act_starts_canned(
|
|
|
2841
3251
|
}
|
|
2842
3252
|
|
|
2843
3253
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
if
|
|
3254
|
+
|
|
3255
|
+
#[workflow]
|
|
3256
|
+
#[derive(Default)]
|
|
3257
|
+
struct CancelAfterActStartsCannedWf;
|
|
3258
|
+
|
|
3259
|
+
#[workflow_methods]
|
|
3260
|
+
impl CancelAfterActStartsCannedWf {
|
|
3261
|
+
#[run(name = DEFAULT_WORKFLOW_TYPE)]
|
|
3262
|
+
async fn run(
|
|
3263
|
+
ctx: &mut WorkflowContext<Self>,
|
|
3264
|
+
cancel_type: ActivityCancellationType,
|
|
3265
|
+
) -> WorkflowResult<()> {
|
|
3266
|
+
let la = ctx.start_local_activity(
|
|
3267
|
+
ActivityWithConditionalCancelWait::echo,
|
|
3268
|
+
(),
|
|
3269
|
+
LocalActivityOptions {
|
|
3270
|
+
cancel_type,
|
|
3271
|
+
..Default::default()
|
|
3272
|
+
},
|
|
3273
|
+
);
|
|
3274
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
3275
|
+
la.cancel();
|
|
3276
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
3277
|
+
let resolution = la.await;
|
|
3278
|
+
assert!(matches!(
|
|
3279
|
+
resolution,
|
|
3280
|
+
Err(ActivityExecutionError::Cancelled(_))
|
|
3281
|
+
));
|
|
3282
|
+
if let Err(ActivityExecutionError::Cancelled(rfail)) = resolution {
|
|
3283
|
+
assert_matches!(
|
|
3284
|
+
rfail.failure_info,
|
|
3285
|
+
Some(FailureInfo::ActivityFailureInfo(_))
|
|
3286
|
+
);
|
|
3287
|
+
assert_matches!(
|
|
3288
|
+
rfail.cause.unwrap().failure_info,
|
|
3289
|
+
Some(FailureInfo::CanceledFailureInfo(_))
|
|
3290
|
+
);
|
|
3291
|
+
}
|
|
3292
|
+
Ok(())
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
worker.register_workflow::<CancelAfterActStartsCannedWf>();
|
|
3297
|
+
|
|
3298
|
+
struct ActivityWithConditionalCancelWait {
|
|
3299
|
+
cancel_type: ActivityCancellationType,
|
|
3300
|
+
allow_cancel_barr: CancellationToken,
|
|
3301
|
+
}
|
|
3302
|
+
#[activities]
|
|
3303
|
+
impl ActivityWithConditionalCancelWait {
|
|
3304
|
+
#[activity(name = DEFAULT_ACTIVITY_TYPE)]
|
|
3305
|
+
async fn echo(self: Arc<Self>, ctx: ActivityContext, _: ()) -> Result<(), ActivityError> {
|
|
3306
|
+
if self.cancel_type == ActivityCancellationType::WaitCancellationCompleted {
|
|
2873
3307
|
ctx.cancelled().await;
|
|
2874
3308
|
}
|
|
2875
|
-
|
|
2876
|
-
|
|
3309
|
+
self.allow_cancel_barr.cancelled().await;
|
|
3310
|
+
Err(ActivityError::cancelled())
|
|
2877
3311
|
}
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
worker.register_activities(ActivityWithConditionalCancelWait {
|
|
3315
|
+
cancel_type,
|
|
3316
|
+
allow_cancel_barr: allow_cancel_barr_clone,
|
|
2878
3317
|
});
|
|
2879
3318
|
worker.run().await.unwrap();
|
|
2880
3319
|
}
|