@temporalio/core-bridge 1.14.2-canary-release-testing.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +794 -650
- package/bridge-macros/src/derive_tryintojs.rs +40 -0
- package/lib/native.d.ts +24 -3
- package/package.json +4 -4
- 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 +42 -31
- package/sdk-core/Cargo.toml +4 -1
- package/sdk-core/README.md +19 -13
- 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} +370 -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} +104 -29
- package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
- package/sdk-core/crates/common/Cargo.toml +62 -3
- 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/.github/workflows/create-release.yml +0 -5
- package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
- package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
- 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 +3329 -2647
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +2734 -708
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +27 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +63 -15
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
- 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 +111 -17
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +21 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +20 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +347 -23
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +242 -43
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +9 -2
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +8 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +22 -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 +19 -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 +134 -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 +11 -16
- package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +272 -225
- 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 +278 -19
- package/sdk-core/crates/common/src/worker.rs +68 -636
- 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 +759 -442
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +64 -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 +59 -65
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +45 -54
- 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 +22 -21
- 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 +647 -27
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +46 -41
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +13 -16
- package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
- package/sdk-core/crates/sdk-core/src/lib.rs +60 -123
- package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +411 -32
- 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 +183 -198
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -281
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +35 -16
- package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
- package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
- 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 +11 -5
- package/sdk-core/crates/sdk-core/src/worker/client.rs +104 -86
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +10 -14
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +1175 -241
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +150 -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 +25 -27
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +64 -44
- package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
- 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 +28 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +20 -41
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +50 -9
- 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 +281 -236
- package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +9 -14
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -66
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +306 -268
- 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 +37 -38
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +49 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +447 -300
- 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 +157 -157
- 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 -463
- 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 +389 -265
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +250 -185
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -49
- 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 +437 -327
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -58
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -30
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -251
- 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 +110 -46
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -149
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -32
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1040
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -43
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +402 -245
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +343 -207
- 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 +155 -140
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -113
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -44
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -48
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -56
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +365 -242
- package/sdk-core/crates/sdk-core/tests/main.rs +22 -16
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +233 -187
- package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +73 -27
- 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 +8 -1
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +37 -26
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +180 -87
- package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +10 -16
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -67
- package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +57 -22
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +108 -12
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +9 -52
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +74 -91
- package/sdk-core/rustfmt.toml +2 -1
- package/src/client.rs +206 -289
- package/src/helpers/try_into_js.rs +88 -2
- package/src/metrics.rs +277 -35
- package/src/runtime.rs +94 -45
- package/src/testing.rs +9 -16
- package/src/worker.rs +86 -68
- package/ts/native.ts +39 -3
- 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/macros/LICENSE.txt +0 -21
- 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,142 +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
|
|
181
|
-
|
|
182
|
-
.max_outstanding_local_activities(1_usize);
|
|
256
|
+
starter.sdk_config.tuner = Arc::new(TunerHolder::fixed_size(5, 1, 1, 1));
|
|
257
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
183
258
|
let mut worker = starter.worker().await;
|
|
184
|
-
worker.
|
|
185
|
-
worker.register_activity("echo_activity", echo);
|
|
259
|
+
worker.register_workflow::<LocalActFanoutWf>();
|
|
186
260
|
|
|
187
|
-
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();
|
|
188
270
|
worker.run_until_done().await.unwrap();
|
|
189
271
|
}
|
|
190
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
|
+
|
|
191
304
|
#[tokio::test]
|
|
192
305
|
async fn local_act_retry_timer_backoff() {
|
|
193
306
|
let wf_name = "local_act_retry_timer_backoff";
|
|
194
307
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
308
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
195
309
|
let mut worker = starter.worker().await;
|
|
196
|
-
worker.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
backoff_coefficient: 1_000.,
|
|
205
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
206
|
-
maximum_attempts: 4,
|
|
207
|
-
non_retryable_error_types: vec![],
|
|
208
|
-
},
|
|
209
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
210
|
-
..Default::default()
|
|
211
|
-
})
|
|
212
|
-
.await;
|
|
213
|
-
assert!(res.failed());
|
|
214
|
-
Ok(().into())
|
|
215
|
-
});
|
|
216
|
-
worker.register_activity("echo", |_: ActContext, _: String| async {
|
|
217
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
let run_id = worker
|
|
221
|
-
.submit_wf(
|
|
222
|
-
wf_name.to_owned(),
|
|
223
|
-
wf_name.to_owned(),
|
|
224
|
-
vec![],
|
|
225
|
-
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(),
|
|
226
318
|
)
|
|
227
319
|
.await
|
|
228
320
|
.unwrap();
|
|
229
321
|
worker.run_until_done().await.unwrap();
|
|
230
|
-
let client = starter.get_client().await;
|
|
231
|
-
let handle = client.get_untyped_workflow_handle(wf_name, run_id);
|
|
232
322
|
handle
|
|
233
323
|
.fetch_history_and_replay(worker.inner_mut())
|
|
234
324
|
.await
|
|
@@ -241,41 +331,86 @@ async fn local_act_retry_timer_backoff() {
|
|
|
241
331
|
#[case::abandon(ActivityCancellationType::Abandon)]
|
|
242
332
|
#[tokio::test]
|
|
243
333
|
async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
|
|
244
|
-
|
|
245
|
-
let mut starter = CoreWfStarter::new(&wf_name);
|
|
246
|
-
let mut worker = starter.worker().await;
|
|
247
|
-
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
248
|
-
let la = ctx.local_activity(LocalActivityOptions {
|
|
249
|
-
activity_type: "echo".to_string(),
|
|
250
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
251
|
-
cancel_type,
|
|
252
|
-
..Default::default()
|
|
253
|
-
});
|
|
254
|
-
la.cancel(&ctx);
|
|
255
|
-
let resolution = la.await;
|
|
256
|
-
assert!(resolution.cancelled());
|
|
257
|
-
Ok(().into())
|
|
258
|
-
});
|
|
334
|
+
use temporalio_sdk::WorkflowContextView;
|
|
259
335
|
|
|
336
|
+
let wf_name = format!("cancel_immediate_{cancel_type:?}");
|
|
260
337
|
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
261
338
|
let manual_cancel = CancellationToken::new();
|
|
262
|
-
let
|
|
339
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
263
340
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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> {
|
|
267
352
|
tokio::select! {
|
|
268
|
-
_ = tokio::time::sleep(Duration::from_secs(10)) => {}
|
|
353
|
+
_ = tokio::time::sleep(Duration::from_secs(10)) => {}
|
|
269
354
|
_ = ctx.cancelled() => {
|
|
270
355
|
return Err(ActivityError::cancelled())
|
|
271
356
|
}
|
|
272
|
-
_ =
|
|
357
|
+
_ = self.manual_cancel.cancelled() => {}
|
|
273
358
|
}
|
|
274
359
|
Ok(())
|
|
275
360
|
}
|
|
276
|
-
}
|
|
361
|
+
}
|
|
362
|
+
|
|
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
|
+
}
|
|
277
380
|
|
|
278
|
-
|
|
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();
|
|
279
414
|
worker
|
|
280
415
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
281
416
|
cancel_on_workflow_completed: false,
|
|
@@ -326,47 +461,24 @@ async fn cancel_after_act_starts(
|
|
|
326
461
|
cancel_type: ActivityCancellationType,
|
|
327
462
|
) {
|
|
328
463
|
let wf_name = format!("cancel_after_act_starts_{cancel_on_backoff:?}_{cancel_type:?}");
|
|
329
|
-
let mut starter = CoreWfStarter::new(&wf_name);
|
|
330
|
-
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
331
|
-
let mut worker = starter.worker().await;
|
|
332
|
-
let bo_dur = cancel_on_backoff.unwrap_or_else(|| Duration::from_secs(1));
|
|
333
|
-
worker.register_wf(&wf_name, move |ctx: WfContext| async move {
|
|
334
|
-
let la = ctx.local_activity(LocalActivityOptions {
|
|
335
|
-
activity_type: "echo".to_string(),
|
|
336
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
337
|
-
retry_policy: RetryPolicy {
|
|
338
|
-
initial_interval: Some(bo_dur.try_into().unwrap()),
|
|
339
|
-
backoff_coefficient: 1.,
|
|
340
|
-
maximum_interval: Some(bo_dur.try_into().unwrap()),
|
|
341
|
-
// Retry forever until cancelled
|
|
342
|
-
..Default::default()
|
|
343
|
-
},
|
|
344
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
345
|
-
cancel_type,
|
|
346
|
-
..Default::default()
|
|
347
|
-
});
|
|
348
|
-
ctx.timer(Duration::from_secs(1)).await;
|
|
349
|
-
// Note that this cancel can't go through for *two* WF tasks, because we do a full heartbeat
|
|
350
|
-
// before the timer (LA hasn't resolved), and then the timer fired event won't appear in
|
|
351
|
-
// history until *after* the next WFT because we force generated it when we sent the timer
|
|
352
|
-
// command.
|
|
353
|
-
la.cancel(&ctx);
|
|
354
|
-
// This extra timer is here to ensure the presence of another WF task doesn't mess up
|
|
355
|
-
// resolving the LA with cancel on replay
|
|
356
|
-
ctx.timer(Duration::from_secs(1)).await;
|
|
357
|
-
let resolution = la.await;
|
|
358
|
-
assert!(resolution.cancelled());
|
|
359
|
-
Ok(().into())
|
|
360
|
-
});
|
|
361
|
-
|
|
362
464
|
// If we don't use this, we'd hang on shutdown for abandon cancel modes.
|
|
363
465
|
let manual_cancel = CancellationToken::new();
|
|
364
|
-
let
|
|
466
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
467
|
+
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
365
468
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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() {
|
|
370
482
|
if ctx.is_cancelled() {
|
|
371
483
|
return Err(ActivityError::cancelled());
|
|
372
484
|
}
|
|
@@ -374,20 +486,87 @@ async fn cancel_after_act_starts(
|
|
|
374
486
|
return Err(anyhow!("Oh no I failed!").into());
|
|
375
487
|
} else {
|
|
376
488
|
tokio::select! {
|
|
377
|
-
_ = tokio::time::sleep(Duration::from_secs(100)) => {}
|
|
489
|
+
_ = tokio::time::sleep(Duration::from_secs(100)) => {}
|
|
378
490
|
_ = ctx.cancelled() => {
|
|
379
491
|
return Err(ActivityError::cancelled())
|
|
380
492
|
}
|
|
381
|
-
_ =
|
|
493
|
+
_ = self.manual_cancel.cancelled() => {
|
|
382
494
|
return Ok(())
|
|
383
495
|
}
|
|
384
496
|
}
|
|
385
497
|
}
|
|
386
498
|
Err(anyhow!("Oh no I failed!").into())
|
|
387
499
|
}
|
|
388
|
-
}
|
|
500
|
+
}
|
|
389
501
|
|
|
390
|
-
starter
|
|
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
|
+
}
|
|
556
|
+
|
|
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();
|
|
391
570
|
worker
|
|
392
571
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
393
572
|
token: manual_cancel,
|
|
@@ -410,6 +589,68 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
|
410
589
|
if is_schedule { "schedule" } else { "start" }
|
|
411
590
|
);
|
|
412
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
|
+
|
|
413
654
|
let mut worker = starter.worker().await;
|
|
414
655
|
let (sched, start) = if is_schedule {
|
|
415
656
|
(Some(Duration::from_secs(2)), None)
|
|
@@ -421,39 +662,17 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
|
421
662
|
} else {
|
|
422
663
|
TimeoutType::StartToClose
|
|
423
664
|
};
|
|
665
|
+
worker.register_workflow::<XToCloseTimeoutWf>();
|
|
424
666
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
maximum_attempts: 4,
|
|
435
|
-
non_retryable_error_types: vec![],
|
|
436
|
-
},
|
|
437
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
438
|
-
schedule_to_close_timeout: sched,
|
|
439
|
-
start_to_close_timeout: start,
|
|
440
|
-
..Default::default()
|
|
441
|
-
})
|
|
442
|
-
.await;
|
|
443
|
-
assert_eq!(res.timed_out(), Some(timeout_type));
|
|
444
|
-
Ok(().into())
|
|
445
|
-
});
|
|
446
|
-
worker.register_activity("echo", |ctx: ActContext, _: String| async move {
|
|
447
|
-
tokio::select! {
|
|
448
|
-
_ = tokio::time::sleep(Duration::from_secs(100)) => {},
|
|
449
|
-
_ = ctx.cancelled() => {
|
|
450
|
-
return Err(ActivityError::cancelled())
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
Ok(())
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
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();
|
|
457
676
|
worker.run_until_done().await.unwrap();
|
|
458
677
|
}
|
|
459
678
|
|
|
@@ -468,36 +687,75 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
|
468
687
|
);
|
|
469
688
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
470
689
|
if !cached {
|
|
471
|
-
starter.
|
|
690
|
+
starter.sdk_config.max_cached_workflows = 0_usize;
|
|
472
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
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
473
729
|
let mut worker = starter.worker().await;
|
|
474
|
-
worker.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
Ok(().into())
|
|
493
|
-
});
|
|
494
|
-
let num_attempts: &'static _ = Box::leak(Box::new(AtomicU8::new(0)));
|
|
495
|
-
worker.register_activity("echo", move |_: ActContext, _: String| async {
|
|
496
|
-
num_attempts.fetch_add(1, Ordering::Relaxed);
|
|
497
|
-
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(),
|
|
498
748
|
});
|
|
499
749
|
|
|
500
|
-
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();
|
|
501
759
|
worker.run_until_done().await.unwrap();
|
|
502
760
|
// 3 attempts b/c first backoff is very small, then the next 2 attempts take at least 2 seconds
|
|
503
761
|
// b/c of timer backoff.
|
|
@@ -509,24 +767,21 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
|
509
767
|
async fn eviction_wont_make_local_act_get_dropped(#[values(true, false)] short_wft_timeout: bool) {
|
|
510
768
|
let wf_name = format!("eviction_wont_make_local_act_get_dropped_{short_wft_timeout}");
|
|
511
769
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
512
|
-
starter.
|
|
770
|
+
starter.sdk_config.max_cached_workflows = 0_usize;
|
|
771
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
513
772
|
let mut worker = starter.worker().await;
|
|
514
|
-
worker.
|
|
515
|
-
worker.register_activity("echo_activity", |_ctx: ActContext, str: String| async {
|
|
516
|
-
tokio::time::sleep(Duration::from_secs(4)).await;
|
|
517
|
-
Ok(str)
|
|
518
|
-
});
|
|
773
|
+
worker.register_workflow::<LocalActThenTimerThenWait>();
|
|
519
774
|
|
|
775
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
520
776
|
let opts = if short_wft_timeout {
|
|
521
|
-
|
|
522
|
-
task_timeout
|
|
523
|
-
|
|
524
|
-
}
|
|
777
|
+
WorkflowStartOptions::new(task_queue, wf_name.clone())
|
|
778
|
+
.task_timeout(Duration::from_secs(1))
|
|
779
|
+
.build()
|
|
525
780
|
} else {
|
|
526
|
-
|
|
781
|
+
WorkflowStartOptions::new(task_queue, wf_name.clone()).build()
|
|
527
782
|
};
|
|
528
783
|
worker
|
|
529
|
-
.
|
|
784
|
+
.submit_workflow(LocalActThenTimerThenWait::run, (), opts)
|
|
530
785
|
.await
|
|
531
786
|
.unwrap();
|
|
532
787
|
worker.run_until_done().await.unwrap();
|
|
@@ -536,44 +791,65 @@ async fn eviction_wont_make_local_act_get_dropped(#[values(true, false)] short_w
|
|
|
536
791
|
async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
537
792
|
let wf_name = "timer_backoff_concurrent_with_non_timer_backoff";
|
|
538
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
|
+
|
|
539
841
|
let mut worker = starter.worker().await;
|
|
540
|
-
worker.
|
|
541
|
-
let r1 = ctx.local_activity(LocalActivityOptions {
|
|
542
|
-
activity_type: "echo".to_string(),
|
|
543
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
544
|
-
retry_policy: RetryPolicy {
|
|
545
|
-
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
546
|
-
backoff_coefficient: 1_000.,
|
|
547
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
548
|
-
maximum_attempts: 4,
|
|
549
|
-
non_retryable_error_types: vec![],
|
|
550
|
-
},
|
|
551
|
-
timer_backoff_threshold: Some(Duration::from_secs(1)),
|
|
552
|
-
..Default::default()
|
|
553
|
-
});
|
|
554
|
-
let r2 = ctx.local_activity(LocalActivityOptions {
|
|
555
|
-
activity_type: "echo".to_string(),
|
|
556
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
557
|
-
retry_policy: RetryPolicy {
|
|
558
|
-
initial_interval: Some(prost_dur!(from_millis(15))),
|
|
559
|
-
backoff_coefficient: 10.,
|
|
560
|
-
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
561
|
-
maximum_attempts: 4,
|
|
562
|
-
non_retryable_error_types: vec![],
|
|
563
|
-
},
|
|
564
|
-
timer_backoff_threshold: Some(Duration::from_secs(10)),
|
|
565
|
-
..Default::default()
|
|
566
|
-
});
|
|
567
|
-
let (r1, r2) = tokio::join!(r1, r2);
|
|
568
|
-
assert!(r1.failed());
|
|
569
|
-
assert!(r2.failed());
|
|
570
|
-
Ok(().into())
|
|
571
|
-
});
|
|
572
|
-
worker.register_activity("echo", |_: ActContext, _: String| async {
|
|
573
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
574
|
-
});
|
|
842
|
+
worker.register_workflow::<TimerBackoffConcurrentWf>();
|
|
575
843
|
|
|
576
|
-
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();
|
|
577
853
|
worker.run_until_done().await.unwrap();
|
|
578
854
|
}
|
|
579
855
|
|
|
@@ -581,50 +857,64 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
|
581
857
|
async fn repro_nondeterminism_with_timer_bug() {
|
|
582
858
|
let wf_name = "repro_nondeterminism_with_timer_bug";
|
|
583
859
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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(())
|
|
607
894
|
}
|
|
608
|
-
|
|
609
|
-
Ok(().into())
|
|
610
|
-
});
|
|
611
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
612
|
-
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
613
|
-
Ok(())
|
|
614
|
-
});
|
|
895
|
+
}
|
|
615
896
|
|
|
616
|
-
let
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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(),
|
|
622
906
|
)
|
|
623
907
|
.await
|
|
624
908
|
.unwrap();
|
|
625
909
|
worker.run_until_done().await.unwrap();
|
|
626
910
|
let client = starter.get_client().await;
|
|
627
|
-
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());
|
|
628
918
|
handle
|
|
629
919
|
.fetch_history_and_replay(worker.inner_mut())
|
|
630
920
|
.await
|
|
@@ -648,14 +938,8 @@ async fn weird_la_nondeterminism_repro(#[values(true, false)] fix_hist: bool) {
|
|
|
648
938
|
}
|
|
649
939
|
|
|
650
940
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
651
|
-
worker.
|
|
652
|
-
|
|
653
|
-
la_problem_workflow,
|
|
654
|
-
);
|
|
655
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
656
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
657
|
-
Ok(())
|
|
658
|
-
});
|
|
941
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
942
|
+
worker.register_activities(StdActivities);
|
|
659
943
|
worker.run().await.unwrap();
|
|
660
944
|
}
|
|
661
945
|
|
|
@@ -672,14 +956,8 @@ async fn second_weird_la_nondeterminism_repro() {
|
|
|
672
956
|
hist = thb.get_full_history_info().unwrap().into();
|
|
673
957
|
|
|
674
958
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
675
|
-
worker.
|
|
676
|
-
|
|
677
|
-
la_problem_workflow,
|
|
678
|
-
);
|
|
679
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
680
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
681
|
-
Ok(())
|
|
682
|
-
});
|
|
959
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
960
|
+
worker.register_activities(StdActivities);
|
|
683
961
|
worker.run().await.unwrap();
|
|
684
962
|
}
|
|
685
963
|
|
|
@@ -694,14 +972,8 @@ async fn third_weird_la_nondeterminism_repro() {
|
|
|
694
972
|
hist = thb.get_full_history_info().unwrap().into();
|
|
695
973
|
|
|
696
974
|
let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
|
|
697
|
-
worker.
|
|
698
|
-
|
|
699
|
-
la_problem_workflow,
|
|
700
|
-
);
|
|
701
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
702
|
-
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
703
|
-
Ok(())
|
|
704
|
-
});
|
|
975
|
+
worker.register_workflow::<LaProblemWorkflow>();
|
|
976
|
+
worker.register_activities(StdActivities);
|
|
705
977
|
worker.run().await.unwrap();
|
|
706
978
|
}
|
|
707
979
|
|
|
@@ -721,60 +993,87 @@ async fn third_weird_la_nondeterminism_repro() {
|
|
|
721
993
|
async fn la_resolve_same_time_as_other_cancel() {
|
|
722
994
|
let wf_name = "la_resolve_same_time_as_other_cancel";
|
|
723
995
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
724
|
-
// The activity won't get a chance to receive the cancel so make sure we still exit fast
|
|
725
|
-
starter
|
|
726
|
-
.worker_config
|
|
727
|
-
.graceful_shutdown_period(Duration::from_millis(100));
|
|
728
|
-
let mut worker = starter.worker().await;
|
|
729
|
-
|
|
730
|
-
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
731
|
-
let normal_act = ctx.activity(ActivityOptions {
|
|
732
|
-
activity_type: "delay".to_string(),
|
|
733
|
-
input: 9000.as_json_payload().expect("serializes fine"),
|
|
734
|
-
cancellation_type: ActivityCancellationType::TryCancel,
|
|
735
|
-
start_to_close_timeout: Some(Duration::from_secs(9000)),
|
|
736
|
-
..Default::default()
|
|
737
|
-
});
|
|
738
|
-
// Make new task
|
|
739
|
-
ctx.timer(Duration::from_millis(1)).await;
|
|
740
996
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
_ = normal_act => {},
|
|
752
|
-
_ = local_act => {
|
|
753
|
-
ctx.timer(Duration::from_millis(1)).await;
|
|
754
|
-
},
|
|
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(())
|
|
755
1007
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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(())
|
|
762
1053
|
}
|
|
763
|
-
|
|
764
|
-
});
|
|
1054
|
+
}
|
|
765
1055
|
|
|
766
|
-
let
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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(),
|
|
772
1065
|
)
|
|
773
1066
|
.await
|
|
774
1067
|
.unwrap();
|
|
775
1068
|
worker.run_until_done().await.unwrap();
|
|
776
1069
|
let client = starter.get_client().await;
|
|
777
|
-
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());
|
|
778
1077
|
handle
|
|
779
1078
|
.fetch_history_and_replay(worker.inner_mut())
|
|
780
1079
|
.await
|
|
@@ -795,59 +1094,68 @@ async fn long_local_activity_with_update(
|
|
|
795
1094
|
let wf_name = format!("{}-{}", ctx.name, ctx.case.unwrap());
|
|
796
1095
|
let mut starter = CoreWfStarter::new(&wf_name);
|
|
797
1096
|
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
798
|
-
|
|
799
|
-
let client = starter.get_client().await;
|
|
1097
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
800
1098
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
move |u: UpdateContext, _: ()| {
|
|
808
|
-
let uc = uc.clone();
|
|
809
|
-
async move {
|
|
810
|
-
if update_inner_timer != 0 {
|
|
811
|
-
u.wf_ctx
|
|
812
|
-
.timer(Duration::from_millis(update_inner_timer))
|
|
813
|
-
.await;
|
|
814
|
-
}
|
|
815
|
-
uc.fetch_add(1, Ordering::Relaxed);
|
|
816
|
-
Ok(())
|
|
817
|
-
}
|
|
818
|
-
},
|
|
819
|
-
);
|
|
820
|
-
ctx.local_activity(LocalActivityOptions {
|
|
821
|
-
activity_type: "delay".to_string(),
|
|
822
|
-
input: "hi".as_json_payload().expect("serializes fine"),
|
|
823
|
-
..Default::default()
|
|
824
|
-
})
|
|
825
|
-
.await;
|
|
826
|
-
update_counter.load(Ordering::Relaxed);
|
|
827
|
-
Ok(().into())
|
|
828
|
-
});
|
|
829
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
830
|
-
tokio::time::sleep(Duration::from_secs(6)).await;
|
|
831
|
-
Ok(())
|
|
832
|
-
});
|
|
1099
|
+
#[workflow]
|
|
1100
|
+
#[derive(Default)]
|
|
1101
|
+
struct LongLocalActivityWithUpdateWf {
|
|
1102
|
+
update_counter: usize,
|
|
1103
|
+
update_inner_timer: u64,
|
|
1104
|
+
}
|
|
833
1105
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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();
|
|
837
1150
|
|
|
838
|
-
let wf_id = starter.get_task_queue().to_string();
|
|
839
1151
|
let update = async {
|
|
840
1152
|
loop {
|
|
841
1153
|
tokio::time::sleep(Duration::from_millis(update_interval_ms)).await;
|
|
842
|
-
let _ =
|
|
843
|
-
.
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
WaitPolicy {
|
|
848
|
-
lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
|
|
849
|
-
},
|
|
850
|
-
[().as_json_payload().unwrap()].into_payloads(),
|
|
1154
|
+
let _ = handle
|
|
1155
|
+
.execute_update(
|
|
1156
|
+
LongLocalActivityWithUpdateWf::do_update,
|
|
1157
|
+
(),
|
|
1158
|
+
WorkflowExecuteUpdateOptions::default(),
|
|
851
1159
|
)
|
|
852
1160
|
.await;
|
|
853
1161
|
}
|
|
@@ -856,16 +1164,12 @@ async fn long_local_activity_with_update(
|
|
|
856
1164
|
worker.run_until_done().await.unwrap();
|
|
857
1165
|
};
|
|
858
1166
|
tokio::select!(_ = update => {}, _ = runner => {});
|
|
859
|
-
let res = handle
|
|
860
|
-
.get_workflow_result(Default::default())
|
|
861
|
-
.await
|
|
862
|
-
.unwrap()
|
|
863
|
-
.unwrap_success();
|
|
1167
|
+
let res = handle.get_result(Default::default()).await.unwrap();
|
|
864
1168
|
let replay_res = handle
|
|
865
1169
|
.fetch_history_and_replay(worker.inner_mut())
|
|
866
1170
|
.await
|
|
867
1171
|
.unwrap();
|
|
868
|
-
assert_eq!(res
|
|
1172
|
+
assert_eq!(res, usize::from_json_payload(&replay_res.unwrap()).unwrap());
|
|
869
1173
|
|
|
870
1174
|
// Load histories from pre-fix version and ensure compat
|
|
871
1175
|
let replay_worker = init_core_replay_preloaded(
|
|
@@ -878,7 +1182,7 @@ async fn long_local_activity_with_update(
|
|
|
878
1182
|
)],
|
|
879
1183
|
);
|
|
880
1184
|
let inner_worker = worker.inner_mut();
|
|
881
|
-
inner_worker.with_new_core_worker(replay_worker);
|
|
1185
|
+
inner_worker.with_new_core_worker(Arc::new(replay_worker));
|
|
882
1186
|
inner_worker.set_worker_interceptor(FailOnNondeterminismInterceptor {});
|
|
883
1187
|
inner_worker.run().await.unwrap();
|
|
884
1188
|
}
|
|
@@ -888,68 +1192,92 @@ async fn local_activity_with_heartbeat_only_causes_one_wakeup() {
|
|
|
888
1192
|
let wf_name = "local_activity_with_heartbeat_only_causes_one_wakeup";
|
|
889
1193
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
890
1194
|
starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
|
|
891
|
-
|
|
1195
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
892
1196
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
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(|_| {
|
|
908
1223
|
wakeup_counter += 1;
|
|
909
|
-
|
|
1224
|
+
ctx.state(|s| s.la_resolved)
|
|
910
1225
|
})
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
});
|
|
916
|
-
worker.register_activity("delay", |_: ActContext, _: String| async {
|
|
917
|
-
tokio::time::sleep(Duration::from_secs(6)).await;
|
|
918
|
-
Ok(())
|
|
919
|
-
});
|
|
1226
|
+
);
|
|
1227
|
+
Ok(wakeup_counter as usize)
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
920
1230
|
|
|
921
|
-
let
|
|
922
|
-
worker.
|
|
923
|
-
|
|
924
|
-
|
|
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
|
+
)
|
|
925
1241
|
.await
|
|
926
|
-
.unwrap()
|
|
927
|
-
|
|
928
|
-
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
|
|
929
1247
|
.fetch_history_and_replay(worker.inner_mut())
|
|
930
1248
|
.await
|
|
931
1249
|
.unwrap();
|
|
932
|
-
assert_eq!(res[0], replay_res.unwrap());
|
|
933
1250
|
}
|
|
934
1251
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
+
}
|
|
944
1272
|
}
|
|
945
1273
|
|
|
946
1274
|
#[tokio::test]
|
|
947
1275
|
async fn local_activity_with_summary() {
|
|
948
1276
|
let wf_name = "local_activity_with_summary";
|
|
949
1277
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
1278
|
+
starter.sdk_config.register_activities(StdActivities);
|
|
950
1279
|
let mut worker = starter.worker().await;
|
|
951
|
-
worker.
|
|
952
|
-
worker.register_activity("echo_activity", echo);
|
|
1280
|
+
worker.register_workflow::<LocalActivityWithSummaryWf>();
|
|
953
1281
|
|
|
954
1282
|
let handle = starter.start_with_worker(wf_name, &mut worker).await;
|
|
955
1283
|
worker.run_until_done().await.unwrap();
|
|
@@ -982,10 +1310,6 @@ async fn local_activity_with_summary() {
|
|
|
982
1310
|
);
|
|
983
1311
|
}
|
|
984
1312
|
|
|
985
|
-
async fn echo(_ctx: ActContext, e: String) -> Result<String, ActivityError> {
|
|
986
|
-
Ok(e)
|
|
987
|
-
}
|
|
988
|
-
|
|
989
1313
|
/// This test verifies that when replaying we are able to resolve local activities whose data we
|
|
990
1314
|
/// don't see until after the workflow issues the command
|
|
991
1315
|
#[rstest::rstest]
|
|
@@ -1019,29 +1343,23 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
1019
1343
|
}
|
|
1020
1344
|
});
|
|
1021
1345
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
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());
|
|
1030
1355
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1031
|
-
la.await;
|
|
1032
|
-
Ok(()
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
worker
|
|
1037
|
-
|
|
1038
|
-
wf_id.to_owned(),
|
|
1039
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1040
|
-
vec![],
|
|
1041
|
-
WorkflowOptions::default(),
|
|
1042
|
-
)
|
|
1043
|
-
.await
|
|
1044
|
-
.unwrap();
|
|
1356
|
+
let _ = la.await;
|
|
1357
|
+
Ok(())
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
worker.register_workflow::<LocalActTwoWftsBeforeMarkerWf>();
|
|
1362
|
+
worker.register_activities(StdActivities);
|
|
1045
1363
|
worker.run_until_done().await.unwrap();
|
|
1046
1364
|
}
|
|
1047
1365
|
|
|
@@ -1064,17 +1382,8 @@ async fn local_act_many_concurrent() {
|
|
|
1064
1382
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
1065
1383
|
let mut worker = mock_sdk(mh);
|
|
1066
1384
|
|
|
1067
|
-
worker.
|
|
1068
|
-
worker.
|
|
1069
|
-
worker
|
|
1070
|
-
.submit_wf(
|
|
1071
|
-
wf_id.to_owned(),
|
|
1072
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1073
|
-
vec![],
|
|
1074
|
-
WorkflowOptions::default(),
|
|
1075
|
-
)
|
|
1076
|
-
.await
|
|
1077
|
-
.unwrap();
|
|
1385
|
+
worker.register_workflow::<LocalActFanoutWf>();
|
|
1386
|
+
worker.register_activities(StdActivities);
|
|
1078
1387
|
worker.run_until_done().await.unwrap();
|
|
1079
1388
|
}
|
|
1080
1389
|
|
|
@@ -1105,43 +1414,67 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
1105
1414
|
wc.max_cached_workflows = 1;
|
|
1106
1415
|
wc.max_outstanding_workflow_tasks = Some(1);
|
|
1107
1416
|
});
|
|
1108
|
-
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
|
+
}
|
|
1109
1440
|
|
|
1110
|
-
|
|
1441
|
+
worker.register_workflow::<LocalActHeartbeatWf>();
|
|
1111
1442
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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)
|
|
1127
1462
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
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,
|
|
1131
1472
|
});
|
|
1132
|
-
worker
|
|
1133
|
-
.submit_wf(
|
|
1134
|
-
wf_id.to_owned(),
|
|
1135
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1136
|
-
vec![],
|
|
1137
|
-
WorkflowOptions::default(),
|
|
1138
|
-
)
|
|
1139
|
-
.await
|
|
1140
|
-
.unwrap();
|
|
1141
1473
|
let (_, runres) = tokio::join!(
|
|
1142
1474
|
async {
|
|
1143
1475
|
if shutdown_middle {
|
|
1144
1476
|
shutdown_barr.wait().await;
|
|
1477
|
+
dbg!("Past barrier");
|
|
1145
1478
|
core.shutdown().await;
|
|
1146
1479
|
}
|
|
1147
1480
|
},
|
|
@@ -1157,6 +1490,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
1157
1490
|
async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
1158
1491
|
let mut t = TestHistoryBuilder::default();
|
|
1159
1492
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
1493
|
+
t.set_wf_input(eventually_pass.as_json_payload().unwrap());
|
|
1160
1494
|
t.add_workflow_task_scheduled_and_started();
|
|
1161
1495
|
|
|
1162
1496
|
let wf_id = "fakeid";
|
|
@@ -1164,49 +1498,63 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
1164
1498
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
1165
1499
|
let mut worker = mock_sdk(mh);
|
|
1166
1500
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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<()> {
|
|
1170
1509
|
let la_res = ctx
|
|
1171
|
-
.
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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()
|
|
1180
1522
|
},
|
|
1181
|
-
|
|
1182
|
-
})
|
|
1523
|
+
)
|
|
1183
1524
|
.await;
|
|
1184
1525
|
if eventually_pass {
|
|
1185
|
-
assert!(la_res.
|
|
1526
|
+
assert!(la_res.is_ok())
|
|
1186
1527
|
} else {
|
|
1187
|
-
assert!(la_res
|
|
1528
|
+
assert!(matches!(la_res, Err(ActivityExecutionError::Failed(_))))
|
|
1188
1529
|
}
|
|
1189
|
-
Ok(().into())
|
|
1190
|
-
},
|
|
1191
|
-
);
|
|
1192
|
-
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
1193
|
-
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
1194
|
-
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
1195
|
-
if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
|
|
1196
1530
|
Ok(())
|
|
1197
|
-
} else {
|
|
1198
|
-
Err(anyhow!("Oh no I failed!").into())
|
|
1199
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,
|
|
1200
1557
|
});
|
|
1201
|
-
worker
|
|
1202
|
-
.submit_wf(
|
|
1203
|
-
wf_id.to_owned(),
|
|
1204
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1205
|
-
vec![],
|
|
1206
|
-
WorkflowOptions::default(),
|
|
1207
|
-
)
|
|
1208
|
-
.await
|
|
1209
|
-
.unwrap();
|
|
1210
1558
|
worker.run_until_done().await.unwrap();
|
|
1211
1559
|
let expected_attempts = if eventually_pass { 3 } else { 5 };
|
|
1212
1560
|
assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
|
|
@@ -1217,18 +1565,22 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
1217
1565
|
let mut t = TestHistoryBuilder::default();
|
|
1218
1566
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
1219
1567
|
t.add_full_wf_task();
|
|
1220
|
-
t.
|
|
1568
|
+
t.add_local_activity_marker(
|
|
1221
1569
|
1,
|
|
1222
1570
|
"1",
|
|
1223
|
-
|
|
1571
|
+
None,
|
|
1572
|
+
Some(Failure::application_failure("la failed".to_string(), false)),
|
|
1573
|
+
|m| m.activity_type = StdActivities::always_fail.name().to_owned(),
|
|
1224
1574
|
);
|
|
1225
1575
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
1226
1576
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
1227
1577
|
t.add_full_wf_task();
|
|
1228
|
-
t.
|
|
1578
|
+
t.add_local_activity_marker(
|
|
1229
1579
|
2,
|
|
1230
1580
|
"2",
|
|
1231
|
-
|
|
1581
|
+
None,
|
|
1582
|
+
Some(Failure::application_failure("la failed".to_string(), false)),
|
|
1583
|
+
|m| m.activity_type = StdActivities::always_fail.name().to_owned(),
|
|
1232
1584
|
);
|
|
1233
1585
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
1234
1586
|
t.add_timer_fired(timer_started_event_id, "2".to_string());
|
|
@@ -1245,45 +1597,38 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
1245
1597
|
);
|
|
1246
1598
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1247
1599
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
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<()> {
|
|
1251
1608
|
let la_res = ctx
|
|
1252
|
-
.
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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()
|
|
1262
1621
|
},
|
|
1263
|
-
|
|
1264
|
-
})
|
|
1622
|
+
)
|
|
1265
1623
|
.await;
|
|
1266
|
-
assert!(la_res
|
|
1267
|
-
// Extra timer just to have an extra workflow task which we can return full history for
|
|
1624
|
+
assert!(matches!(la_res, Err(ActivityExecutionError::Failed(_))));
|
|
1268
1625
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1269
|
-
Ok(()
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
1276
|
-
},
|
|
1277
|
-
);
|
|
1278
|
-
worker
|
|
1279
|
-
.submit_wf(
|
|
1280
|
-
wf_id.to_owned(),
|
|
1281
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1282
|
-
vec![],
|
|
1283
|
-
WorkflowOptions::default(),
|
|
1284
|
-
)
|
|
1285
|
-
.await
|
|
1286
|
-
.unwrap();
|
|
1626
|
+
Ok(())
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
worker.register_workflow::<LocalActRetryLongBackoffUsesTimerWf>();
|
|
1631
|
+
worker.register_activities(StdActivities);
|
|
1287
1632
|
worker.run_until_done().await.unwrap();
|
|
1288
1633
|
}
|
|
1289
1634
|
|
|
@@ -1300,28 +1645,23 @@ async fn local_act_null_result() {
|
|
|
1300
1645
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
|
|
1301
1646
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1302
1647
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
.
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
vec![],
|
|
1321
|
-
WorkflowOptions::default(),
|
|
1322
|
-
)
|
|
1323
|
-
.await
|
|
1324
|
-
.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);
|
|
1325
1665
|
worker.run_until_done().await.unwrap();
|
|
1326
1666
|
}
|
|
1327
1667
|
|
|
@@ -1339,33 +1679,27 @@ async fn local_act_command_immediately_follows_la_marker() {
|
|
|
1339
1679
|
|
|
1340
1680
|
let wf_id = "fakeid";
|
|
1341
1681
|
let mock = mock_worker_client();
|
|
1342
|
-
// Bug only repros when seeing history up to third wft
|
|
1343
1682
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [3], mock);
|
|
1344
1683
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 0);
|
|
1345
1684
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
.
|
|
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)))?;
|
|
1355
1696
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1356
|
-
Ok(()
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
worker
|
|
1361
|
-
|
|
1362
|
-
wf_id.to_owned(),
|
|
1363
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1364
|
-
vec![],
|
|
1365
|
-
WorkflowOptions::default(),
|
|
1366
|
-
)
|
|
1367
|
-
.await
|
|
1368
|
-
.unwrap();
|
|
1697
|
+
Ok(())
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
worker.register_workflow::<LocalActCommandImmediatelyFollowsLaMarkerWf>();
|
|
1702
|
+
worker.register_activities(StdActivities);
|
|
1369
1703
|
worker.run_until_done().await.unwrap();
|
|
1370
1704
|
}
|
|
1371
1705
|
|
|
@@ -1636,44 +1970,39 @@ async fn test_schedule_to_start_timeout() {
|
|
|
1636
1970
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::ToTaskNum(1)], mock);
|
|
1637
1971
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1638
1972
|
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
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<()> {
|
|
1642
1981
|
let la_res = ctx
|
|
1643
|
-
.
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
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
|
+
)
|
|
1650
1990
|
.await;
|
|
1651
|
-
|
|
1652
|
-
let
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
)
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
worker.
|
|
1665
|
-
|
|
1666
|
-
move |_ctx: ActContext, _: String| async move { Ok(()) },
|
|
1667
|
-
);
|
|
1668
|
-
worker
|
|
1669
|
-
.submit_wf(
|
|
1670
|
-
wf_id.to_owned(),
|
|
1671
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1672
|
-
vec![],
|
|
1673
|
-
WorkflowOptions::default(),
|
|
1674
|
-
)
|
|
1675
|
-
.await
|
|
1676
|
-
.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);
|
|
1677
2006
|
worker.run_until_done().await.unwrap();
|
|
1678
2007
|
}
|
|
1679
2008
|
|
|
@@ -1690,8 +2019,19 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1690
2019
|
// * we don't time out on s-t-s timeouts because of that, when the param is true.
|
|
1691
2020
|
// * we do properly time out on s-t-c timeouts when the param is false
|
|
1692
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
|
+
|
|
1693
2028
|
let mut t = TestHistoryBuilder::default();
|
|
1694
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
|
+
);
|
|
1695
2035
|
t.add_full_wf_task();
|
|
1696
2036
|
let orig_sched = SystemTime::now().sub(Duration::from_secs(60 * 20));
|
|
1697
2037
|
t.add_local_activity_marker(
|
|
@@ -1715,55 +2055,47 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1715
2055
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
|
|
1716
2056
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1717
2057
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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;
|
|
1730
2070
|
let la_res = ctx
|
|
1731
|
-
.
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
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()
|
|
1740
2085
|
},
|
|
1741
|
-
|
|
1742
|
-
schedule_to_close_timeout,
|
|
1743
|
-
..Default::default()
|
|
1744
|
-
})
|
|
2086
|
+
)
|
|
1745
2087
|
.await;
|
|
1746
2088
|
if is_sched_to_start {
|
|
1747
|
-
assert!(la_res.
|
|
1748
|
-
} else {
|
|
1749
|
-
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));
|
|
1750
2092
|
}
|
|
1751
|
-
Ok(()
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
);
|
|
1758
|
-
worker
|
|
1759
|
-
.submit_wf(
|
|
1760
|
-
wf_id.to_owned(),
|
|
1761
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1762
|
-
vec![],
|
|
1763
|
-
WorkflowOptions::default(),
|
|
1764
|
-
)
|
|
1765
|
-
.await
|
|
1766
|
-
.unwrap();
|
|
2093
|
+
Ok(())
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
worker.register_workflow::<TestScheduleToStartTimeoutNotBasedOnOriginalTimeWf>();
|
|
2098
|
+
worker.register_activities(StdActivities);
|
|
1767
2099
|
worker.run_until_done().await.unwrap();
|
|
1768
2100
|
}
|
|
1769
2101
|
|
|
@@ -1772,6 +2104,7 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time(
|
|
|
1772
2104
|
async fn start_to_close_timeout_allows_retries(#[values(true, false)] la_completes: bool) {
|
|
1773
2105
|
let mut t = TestHistoryBuilder::default();
|
|
1774
2106
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
2107
|
+
t.set_wf_input(la_completes.as_json_payload().unwrap());
|
|
1775
2108
|
t.add_full_wf_task();
|
|
1776
2109
|
if la_completes {
|
|
1777
2110
|
t.add_local_activity_marker(1, "1", Some("hi".into()), None, |_| {});
|
|
@@ -1797,61 +2130,73 @@ async fn start_to_close_timeout_allows_retries(#[values(true, false)] la_complet
|
|
|
1797
2130
|
);
|
|
1798
2131
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1799
2132
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
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<()> {
|
|
1803
2141
|
let la_res = ctx
|
|
1804
|
-
.
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
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()
|
|
1813
2155
|
},
|
|
1814
|
-
|
|
1815
|
-
..Default::default()
|
|
1816
|
-
})
|
|
2156
|
+
)
|
|
1817
2157
|
.await;
|
|
1818
2158
|
if la_completes {
|
|
1819
|
-
assert!(la_res.
|
|
1820
|
-
} else {
|
|
1821
|
-
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));
|
|
1822
2162
|
}
|
|
1823
|
-
Ok(()
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
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> {
|
|
1831
2180
|
// Timeout the first 4 attempts, or all of them if we intend to fail
|
|
1832
|
-
if attempts.fetch_add(1, Ordering::AcqRel) < 4 || !la_completes {
|
|
2181
|
+
if self.attempts.fetch_add(1, Ordering::AcqRel) < 4 || !self.la_completes {
|
|
1833
2182
|
select! {
|
|
1834
2183
|
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
|
1835
2184
|
_ = ctx.cancelled() => {
|
|
1836
|
-
cancels.fetch_add(1, Ordering::AcqRel);
|
|
2185
|
+
self.cancels.fetch_add(1, Ordering::AcqRel);
|
|
1837
2186
|
return Err(ActivityError::cancelled());
|
|
1838
2187
|
}
|
|
1839
2188
|
}
|
|
1840
2189
|
}
|
|
1841
|
-
Ok(())
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
)
|
|
1851
|
-
.await
|
|
1852
|
-
.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
|
+
});
|
|
1853
2199
|
worker.run_until_done().await.unwrap();
|
|
1854
|
-
// Activity should have been attempted all 5 times
|
|
1855
2200
|
assert_eq!(attempts.load(Ordering::Acquire), 5);
|
|
1856
2201
|
let num_cancels = if la_completes { 4 } else { 5 };
|
|
1857
2202
|
assert_eq!(cancels.load(Ordering::Acquire), num_cancels);
|
|
@@ -1872,43 +2217,46 @@ async fn wft_failure_cancels_running_las() {
|
|
|
1872
2217
|
mh.num_expected_fails = 1;
|
|
1873
2218
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1874
2219
|
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
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!(
|
|
1884
2234
|
async {
|
|
1885
2235
|
ctx.timer(Duration::from_secs(1)).await;
|
|
1886
2236
|
panic!("ahhh I'm failing wft")
|
|
1887
2237
|
},
|
|
1888
2238
|
la_handle
|
|
1889
2239
|
);
|
|
1890
|
-
Ok(()
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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> {
|
|
1896
2251
|
let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
|
|
1897
2252
|
if res.is_err() {
|
|
1898
2253
|
panic!("Activity must be cancelled!!!!");
|
|
1899
2254
|
}
|
|
1900
|
-
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
wf_id.to_owned(),
|
|
1906
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1907
|
-
vec![],
|
|
1908
|
-
WorkflowOptions::default(),
|
|
1909
|
-
)
|
|
1910
|
-
.await
|
|
1911
|
-
.unwrap();
|
|
2255
|
+
Err(ActivityError::cancelled())
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
worker.register_activities(ActivityThatExpectsCancellation);
|
|
1912
2260
|
worker.run_until_done().await.unwrap();
|
|
1913
2261
|
}
|
|
1914
2262
|
|
|
@@ -1938,32 +2286,30 @@ async fn resolved_las_not_recorded_if_wft_fails_many_times() {
|
|
|
1938
2286
|
mh.num_expected_completions = Some(0.into());
|
|
1939
2287
|
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
1940
2288
|
|
|
1941
|
-
#[
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
.
|
|
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)))?;
|
|
1951
2307
|
panic!()
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
);
|
|
1958
|
-
worker
|
|
1959
|
-
.submit_wf(
|
|
1960
|
-
wf_id.to_owned(),
|
|
1961
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1962
|
-
vec![],
|
|
1963
|
-
WorkflowOptions::default(),
|
|
1964
|
-
)
|
|
1965
|
-
.await
|
|
1966
|
-
.unwrap();
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
worker.register_workflow::<ResolvedLasNotRecordedIfWftFailsManyTimesWf>();
|
|
2312
|
+
worker.register_activities(StdActivities);
|
|
1967
2313
|
worker.run_until_done().await.unwrap();
|
|
1968
2314
|
}
|
|
1969
2315
|
|
|
@@ -1994,39 +2340,37 @@ async fn local_act_records_nonfirst_attempts_ok() {
|
|
|
1994
2340
|
wc.max_outstanding_workflow_tasks = Some(1);
|
|
1995
2341
|
});
|
|
1996
2342
|
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
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()
|
|
2009
2363
|
},
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
.
|
|
2013
|
-
Ok(()
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
worker
|
|
2020
|
-
.submit_wf(
|
|
2021
|
-
wf_id.to_owned(),
|
|
2022
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
2023
|
-
vec![],
|
|
2024
|
-
WorkflowOptions::default(),
|
|
2025
|
-
)
|
|
2026
|
-
.await
|
|
2027
|
-
.unwrap();
|
|
2364
|
+
)
|
|
2365
|
+
.await
|
|
2366
|
+
.ok();
|
|
2367
|
+
Ok(())
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
worker.register_workflow::<LocalActRecordsNonfirstAttemptsOkWf>();
|
|
2372
|
+
worker.register_activities(StdActivities);
|
|
2028
2373
|
worker.run_until_done().await.unwrap();
|
|
2029
|
-
// 3 workflow tasks
|
|
2030
2374
|
assert_eq!(nonfirst_counts.len(), 3);
|
|
2031
2375
|
// First task's non-first count should, of course, be 0
|
|
2032
2376
|
assert_eq!(nonfirst_counts.pop().unwrap(), 0);
|
|
@@ -2312,70 +2656,92 @@ async fn local_act_retry_explicit_delay() {
|
|
|
2312
2656
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
2313
2657
|
let mut worker = mock_sdk(mh);
|
|
2314
2658
|
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
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<()> {
|
|
2318
2667
|
let la_res = ctx
|
|
2319
|
-
.
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
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
|
+
},
|
|
2326
2678
|
..Default::default()
|
|
2327
2679
|
},
|
|
2328
|
-
|
|
2329
|
-
})
|
|
2680
|
+
)
|
|
2330
2681
|
.await;
|
|
2331
|
-
assert!(la_res.
|
|
2332
|
-
Ok(().into())
|
|
2333
|
-
},
|
|
2334
|
-
);
|
|
2335
|
-
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
2336
|
-
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
2337
|
-
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
2338
|
-
let last_attempt = attempts.fetch_add(1, Ordering::Relaxed);
|
|
2339
|
-
if 0 == last_attempt {
|
|
2340
|
-
Err(ActivityError::Retryable {
|
|
2341
|
-
source: anyhow!("Explicit backoff error"),
|
|
2342
|
-
explicit_delay: Some(Duration::from_millis(300)),
|
|
2343
|
-
})
|
|
2344
|
-
} else if 2 == last_attempt {
|
|
2682
|
+
assert!(la_res.is_ok());
|
|
2345
2683
|
Ok(())
|
|
2346
|
-
} else {
|
|
2347
|
-
Err(anyhow!("Oh no I failed!").into())
|
|
2348
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(),
|
|
2349
2714
|
});
|
|
2350
|
-
worker
|
|
2351
|
-
.submit_wf(
|
|
2352
|
-
wf_id.to_owned(),
|
|
2353
|
-
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
2354
|
-
vec![],
|
|
2355
|
-
WorkflowOptions::default(),
|
|
2356
|
-
)
|
|
2357
|
-
.await
|
|
2358
|
-
.unwrap();
|
|
2359
2715
|
let start = Instant::now();
|
|
2360
2716
|
worker.run_until_done().await.unwrap();
|
|
2361
2717
|
let expected_attempts = 3;
|
|
2362
2718
|
assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
|
|
2363
|
-
// There will be one 300ms backoff and one 50s backoff, so things should take at least that long
|
|
2364
2719
|
assert!(start.elapsed() > Duration::from_millis(350));
|
|
2365
2720
|
}
|
|
2366
2721
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
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
|
+
}
|
|
2379
2745
|
}
|
|
2380
2746
|
|
|
2381
2747
|
#[rstest]
|
|
@@ -2444,53 +2810,81 @@ async fn one_la_success(#[case] replay: bool, #[case] completes_ok: bool) {
|
|
|
2444
2810
|
});
|
|
2445
2811
|
|
|
2446
2812
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2447
|
-
worker.
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
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 {
|
|
2452
2829
|
panic!("Should not be invoked on replay");
|
|
2453
2830
|
}
|
|
2454
|
-
if completes_ok {
|
|
2455
|
-
Ok("hi")
|
|
2831
|
+
if self.completes_ok {
|
|
2832
|
+
Ok("hi".to_string())
|
|
2456
2833
|
} else {
|
|
2457
2834
|
Err(anyhow!("Oh no I failed!").into())
|
|
2458
2835
|
}
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
worker.register_activities(ActivityWithReplayCheck {
|
|
2840
|
+
replay,
|
|
2841
|
+
completes_ok,
|
|
2842
|
+
});
|
|
2461
2843
|
worker.run().await.unwrap();
|
|
2462
2844
|
}
|
|
2463
2845
|
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
ctx
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
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
|
+
}
|
|
2478
2862
|
}
|
|
2479
2863
|
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
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
|
+
}
|
|
2494
2888
|
}
|
|
2495
2889
|
|
|
2496
2890
|
#[rstest]
|
|
@@ -2578,32 +2972,31 @@ async fn two_sequential_las(
|
|
|
2578
2972
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2579
2973
|
worker.set_worker_interceptor(aai);
|
|
2580
2974
|
if parallel {
|
|
2581
|
-
worker.
|
|
2975
|
+
worker.register_workflow::<TwoLaWfParallel>();
|
|
2582
2976
|
} else {
|
|
2583
|
-
worker.
|
|
2977
|
+
worker.register_workflow::<TwoLaWf>();
|
|
2584
2978
|
}
|
|
2585
|
-
worker.
|
|
2586
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2587
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2588
|
-
);
|
|
2979
|
+
worker.register_activities(ResolvedActivity);
|
|
2589
2980
|
worker.run().await.unwrap();
|
|
2590
2981
|
}
|
|
2591
2982
|
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
ctx
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
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
|
+
}
|
|
2607
3000
|
}
|
|
2608
3001
|
|
|
2609
3002
|
#[rstest]
|
|
@@ -2676,11 +3069,8 @@ async fn las_separated_by_timer(#[case] replay: bool) {
|
|
|
2676
3069
|
|
|
2677
3070
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2678
3071
|
worker.set_worker_interceptor(aai);
|
|
2679
|
-
worker.
|
|
2680
|
-
worker.
|
|
2681
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2682
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2683
|
-
);
|
|
3072
|
+
worker.register_workflow::<LaTimerLaWf>();
|
|
3073
|
+
worker.register_activities(ResolvedActivity);
|
|
2684
3074
|
worker.run().await.unwrap();
|
|
2685
3075
|
}
|
|
2686
3076
|
|
|
@@ -2711,11 +3101,8 @@ async fn one_la_heartbeating_wft_failure_still_executes() {
|
|
|
2711
3101
|
});
|
|
2712
3102
|
|
|
2713
3103
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2714
|
-
worker.
|
|
2715
|
-
worker.
|
|
2716
|
-
DEFAULT_ACTIVITY_TYPE,
|
|
2717
|
-
move |_ctx: ActContext, _: ()| async move { Ok("Resolved") },
|
|
2718
|
-
);
|
|
3104
|
+
worker.register_workflow::<LaWf>();
|
|
3105
|
+
worker.register_activities(ResolvedActivity);
|
|
2719
3106
|
worker.run().await.unwrap();
|
|
2720
3107
|
}
|
|
2721
3108
|
|
|
@@ -2731,6 +3118,7 @@ async fn immediate_cancel(
|
|
|
2731
3118
|
) {
|
|
2732
3119
|
let mut t = TestHistoryBuilder::default();
|
|
2733
3120
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
3121
|
+
t.set_wf_input(cancel_type.as_json_payload().unwrap());
|
|
2734
3122
|
t.add_full_wf_task();
|
|
2735
3123
|
t.add_workflow_execution_completed();
|
|
2736
3124
|
|
|
@@ -2748,16 +3136,33 @@ async fn immediate_cancel(
|
|
|
2748
3136
|
});
|
|
2749
3137
|
|
|
2750
3138
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
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>();
|
|
2761
3166
|
worker.run().await.unwrap();
|
|
2762
3167
|
}
|
|
2763
3168
|
|
|
@@ -2776,6 +3181,7 @@ async fn cancel_after_act_starts_canned(
|
|
|
2776
3181
|
) {
|
|
2777
3182
|
let mut t = TestHistoryBuilder::default();
|
|
2778
3183
|
t.add_wfe_started_with_wft_timeout(Duration::from_millis(100));
|
|
3184
|
+
t.set_wf_input(cancel_type.as_json_payload().unwrap());
|
|
2779
3185
|
t.add_full_wf_task();
|
|
2780
3186
|
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
2781
3187
|
t.add_timer_fired(timer_started_event_id, "1".to_string());
|
|
@@ -2845,40 +3251,69 @@ async fn cancel_after_act_starts_canned(
|
|
|
2845
3251
|
}
|
|
2846
3252
|
|
|
2847
3253
|
let mut worker = build_fake_sdk(mock_cfg);
|
|
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
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
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 {
|
|
2877
3307
|
ctx.cancelled().await;
|
|
2878
3308
|
}
|
|
2879
|
-
|
|
2880
|
-
|
|
3309
|
+
self.allow_cancel_barr.cancelled().await;
|
|
3310
|
+
Err(ActivityError::cancelled())
|
|
2881
3311
|
}
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
worker.register_activities(ActivityWithConditionalCancelWait {
|
|
3315
|
+
cancel_type,
|
|
3316
|
+
allow_cancel_barr: allow_cancel_barr_clone,
|
|
2882
3317
|
});
|
|
2883
3318
|
worker.run().await.unwrap();
|
|
2884
3319
|
}
|