@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
|
@@ -8,38 +8,59 @@
|
|
|
8
8
|
//!
|
|
9
9
|
//! An example of running an activity worker:
|
|
10
10
|
//! ```no_run
|
|
11
|
-
//! use std::
|
|
12
|
-
//! use
|
|
13
|
-
//! use temporalio_sdk_core::{init_worker, Url, CoreRuntime, RuntimeOptionsBuilder};
|
|
11
|
+
//! use std::str::FromStr;
|
|
12
|
+
//! use temporalio_client::{Client, ClientOptions, Connection, ConnectionOptions};
|
|
14
13
|
//! use temporalio_common::{
|
|
15
|
-
//!
|
|
16
|
-
//!
|
|
14
|
+
//! telemetry::TelemetryOptions,
|
|
15
|
+
//! worker::{WorkerDeploymentOptions, WorkerDeploymentVersion, WorkerTaskTypes},
|
|
17
16
|
//! };
|
|
17
|
+
//! use temporalio_macros::activities;
|
|
18
|
+
//! use temporalio_sdk::{
|
|
19
|
+
//! Worker, WorkerOptions,
|
|
20
|
+
//! activities::{ActivityContext, ActivityError},
|
|
21
|
+
//! };
|
|
22
|
+
//! use temporalio_sdk_core::{CoreRuntime, RuntimeOptions, Url};
|
|
18
23
|
//!
|
|
19
|
-
//!
|
|
20
|
-
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
21
|
-
//! let server_options = sdk_client_options(Url::from_str("http://localhost:7233")?).build();
|
|
24
|
+
//! struct MyActivities;
|
|
22
25
|
//!
|
|
23
|
-
//!
|
|
24
|
-
//!
|
|
25
|
-
//!
|
|
26
|
-
//!
|
|
27
|
-
//!
|
|
26
|
+
//! #[activities]
|
|
27
|
+
//! impl MyActivities {
|
|
28
|
+
//! #[activity]
|
|
29
|
+
//! pub(crate) async fn echo(
|
|
30
|
+
//! _ctx: ActivityContext,
|
|
31
|
+
//! e: String,
|
|
32
|
+
//! ) -> Result<String, ActivityError> {
|
|
33
|
+
//! Ok(e)
|
|
34
|
+
//! }
|
|
35
|
+
//! }
|
|
28
36
|
//!
|
|
29
|
-
//!
|
|
30
|
-
//!
|
|
31
|
-
//!
|
|
32
|
-
//!
|
|
37
|
+
//! #[tokio::main]
|
|
38
|
+
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
39
|
+
//! let connection_options =
|
|
40
|
+
//! ConnectionOptions::new(Url::from_str("http://localhost:7233")?).build();
|
|
41
|
+
//! let telemetry_options = TelemetryOptions::builder().build();
|
|
42
|
+
//! let runtime_options = RuntimeOptions::builder()
|
|
43
|
+
//! .telemetry_options(telemetry_options)
|
|
33
44
|
//! .build()?;
|
|
45
|
+
//! let runtime = CoreRuntime::new_assume_tokio(runtime_options)?;
|
|
34
46
|
//!
|
|
35
|
-
//! let
|
|
47
|
+
//! let connection = Connection::connect(connection_options).await?;
|
|
48
|
+
//! let client = Client::new(connection, ClientOptions::new("my_namespace").build())?;
|
|
36
49
|
//!
|
|
37
|
-
//! let
|
|
38
|
-
//!
|
|
39
|
-
//!
|
|
40
|
-
//!
|
|
41
|
-
//!
|
|
50
|
+
//! let worker_options = WorkerOptions::new("task_queue")
|
|
51
|
+
//! .task_types(WorkerTaskTypes::activity_only())
|
|
52
|
+
//! .deployment_options(WorkerDeploymentOptions {
|
|
53
|
+
//! version: WorkerDeploymentVersion {
|
|
54
|
+
//! deployment_name: "my_deployment".to_owned(),
|
|
55
|
+
//! build_id: "my_build_id".to_owned(),
|
|
56
|
+
//! },
|
|
57
|
+
//! use_worker_versioning: false,
|
|
58
|
+
//! default_versioning_behavior: None,
|
|
59
|
+
//! })
|
|
60
|
+
//! .register_activities(MyActivities)
|
|
61
|
+
//! .build();
|
|
42
62
|
//!
|
|
63
|
+
//! let mut worker = Worker::new(&runtime, client, worker_options)?;
|
|
43
64
|
//! worker.run().await?;
|
|
44
65
|
//!
|
|
45
66
|
//! Ok(())
|
|
@@ -48,48 +69,70 @@
|
|
|
48
69
|
|
|
49
70
|
#[macro_use]
|
|
50
71
|
extern crate tracing;
|
|
72
|
+
extern crate self as temporalio_sdk;
|
|
51
73
|
|
|
52
|
-
mod
|
|
53
|
-
mod app_data;
|
|
74
|
+
pub mod activities;
|
|
54
75
|
pub mod interceptors;
|
|
55
76
|
mod workflow_context;
|
|
56
77
|
mod workflow_future;
|
|
78
|
+
pub mod workflows;
|
|
79
|
+
|
|
80
|
+
#[macro_export]
|
|
81
|
+
#[doc(hidden)]
|
|
82
|
+
macro_rules! __temporal_select {
|
|
83
|
+
($($tokens:tt)*) => {
|
|
84
|
+
::futures_util::select_biased! { $($tokens)* }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[macro_export]
|
|
89
|
+
#[doc(hidden)]
|
|
90
|
+
macro_rules! __temporal_join {
|
|
91
|
+
($($tokens:tt)*) => {
|
|
92
|
+
::futures_util::join!($($tokens)*)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
use workflow_future::WorkflowFunction;
|
|
57
97
|
|
|
58
|
-
pub use activity_context::ActContext;
|
|
59
98
|
pub use temporalio_client::Namespace;
|
|
60
|
-
use tracing::{Instrument, Span, field};
|
|
61
99
|
pub use workflow_context::{
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
100
|
+
ActivityExecutionError, ActivityOptions, BaseWorkflowContext, CancellableFuture, ChildWorkflow,
|
|
101
|
+
ChildWorkflowOptions, LocalActivityOptions, NexusOperationOptions, ParentWorkflowInfo,
|
|
102
|
+
PendingChildWorkflow, RootWorkflowInfo, Signal, SignalData, SignalWorkflowOptions,
|
|
103
|
+
StartedChildWorkflow, SyncWorkflowContext, TimerOptions, WorkflowContext, WorkflowContextView,
|
|
65
104
|
};
|
|
66
105
|
|
|
67
106
|
use crate::{
|
|
107
|
+
activities::{
|
|
108
|
+
ActivityContext, ActivityDefinitions, ActivityError, ActivityImplementer,
|
|
109
|
+
ExecutableActivity,
|
|
110
|
+
},
|
|
68
111
|
interceptors::WorkerInterceptor,
|
|
69
112
|
workflow_context::{ChildWfCommon, NexusUnblockData, StartedNexusOperation},
|
|
113
|
+
workflows::{WorkflowDefinitions, WorkflowImplementation, WorkflowImplementer},
|
|
70
114
|
};
|
|
71
115
|
use anyhow::{Context, anyhow, bail};
|
|
72
|
-
use
|
|
73
|
-
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::BoxFuture};
|
|
74
|
-
use serde::Serialize;
|
|
116
|
+
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
|
75
117
|
use std::{
|
|
76
118
|
any::{Any, TypeId},
|
|
77
119
|
cell::RefCell,
|
|
78
|
-
collections::HashMap,
|
|
120
|
+
collections::{HashMap, HashSet},
|
|
79
121
|
fmt::{Debug, Display, Formatter},
|
|
80
122
|
future::Future,
|
|
81
123
|
panic::AssertUnwindSafe,
|
|
82
124
|
sync::Arc,
|
|
83
125
|
time::Duration,
|
|
84
126
|
};
|
|
85
|
-
use temporalio_client::{
|
|
127
|
+
use temporalio_client::{Client, NamespacedClient};
|
|
86
128
|
use temporalio_common::{
|
|
87
|
-
|
|
88
|
-
|
|
129
|
+
ActivityDefinition, WorkflowDefinition,
|
|
130
|
+
data_converters::{DataConverter, SerializationContextData},
|
|
131
|
+
payload_visitor::{decode_payloads, encode_payloads},
|
|
89
132
|
protos::{
|
|
90
133
|
TaskToken,
|
|
91
134
|
coresdk::{
|
|
92
|
-
ActivityTaskCompletion, AsJsonPayloadExt,
|
|
135
|
+
ActivityTaskCompletion, AsJsonPayloadExt,
|
|
93
136
|
activity_result::{ActivityExecutionResult, ActivityResolution},
|
|
94
137
|
activity_task::{ActivityTask, activity_task},
|
|
95
138
|
child_workflow::ChildWorkflowResult,
|
|
@@ -111,8 +154,12 @@ use temporalio_common::{
|
|
|
111
154
|
failure::v1::{Failure, failure},
|
|
112
155
|
},
|
|
113
156
|
},
|
|
157
|
+
worker::{WorkerDeploymentOptions, WorkerTaskTypes, build_id_from_current_exe},
|
|
158
|
+
};
|
|
159
|
+
use temporalio_sdk_core::{
|
|
160
|
+
CoreRuntime, PollError, PollerBehavior, TunerBuilder, Worker as CoreWorker, WorkerConfig,
|
|
161
|
+
WorkerTuner, WorkerVersioningStrategy, WorkflowErrorType, init_worker,
|
|
114
162
|
};
|
|
115
|
-
use temporalio_sdk_core::Url;
|
|
116
163
|
use tokio::{
|
|
117
164
|
sync::{
|
|
118
165
|
Notify,
|
|
@@ -123,40 +170,271 @@ use tokio::{
|
|
|
123
170
|
};
|
|
124
171
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
125
172
|
use tokio_util::sync::CancellationToken;
|
|
173
|
+
use tracing::{Instrument, Span, field};
|
|
174
|
+
use uuid::Uuid;
|
|
175
|
+
|
|
176
|
+
/// Contains options for configuring a worker.
|
|
177
|
+
#[derive(bon::Builder, Clone)]
|
|
178
|
+
#[builder(start_fn = new, on(String, into), state_mod(vis = "pub"))]
|
|
179
|
+
#[non_exhaustive]
|
|
180
|
+
pub struct WorkerOptions {
|
|
181
|
+
/// What task queue will this worker poll from? This task queue name will be used for both
|
|
182
|
+
/// workflow and activity polling.
|
|
183
|
+
#[builder(start_fn)]
|
|
184
|
+
pub task_queue: String,
|
|
185
|
+
|
|
186
|
+
#[builder(field)]
|
|
187
|
+
activities: ActivityDefinitions,
|
|
188
|
+
|
|
189
|
+
#[builder(field)]
|
|
190
|
+
workflows: WorkflowDefinitions,
|
|
191
|
+
|
|
192
|
+
/// Set the deployment options for this worker. Defaults to a hash of the currently running
|
|
193
|
+
/// executable.
|
|
194
|
+
#[builder(default = def_build_id())]
|
|
195
|
+
pub deployment_options: WorkerDeploymentOptions,
|
|
196
|
+
/// A human-readable string that can identify this worker. If set, overrides the identity on
|
|
197
|
+
/// the client used by this worker. If unset and the client has no identity, defaults to
|
|
198
|
+
/// `{pid}@{hostname}`.
|
|
199
|
+
pub client_identity_override: Option<String>,
|
|
200
|
+
/// If set nonzero, workflows will be cached and sticky task queues will be used, meaning that
|
|
201
|
+
/// history updates are applied incrementally to suspended instances of workflow execution.
|
|
202
|
+
/// Workflows are evicted according to a least-recently-used policy once the cache maximum is
|
|
203
|
+
/// reached. Workflows may also be explicitly evicted at any time, or as a result of errors
|
|
204
|
+
/// or failures.
|
|
205
|
+
#[builder(default = 1000)]
|
|
206
|
+
pub max_cached_workflows: usize,
|
|
207
|
+
/// Set a [crate::WorkerTuner] for this worker, which controls how many slots are available for
|
|
208
|
+
/// the different kinds of tasks.
|
|
209
|
+
#[builder(default = Arc::new(TunerBuilder::default().build()))]
|
|
210
|
+
pub tuner: Arc<dyn WorkerTuner + Send + Sync>,
|
|
211
|
+
/// Controls how polling for Workflow tasks will happen on this worker's task queue. See also
|
|
212
|
+
/// [WorkerConfig::nonsticky_to_sticky_poll_ratio]. If using SimpleMaximum, Must be at least 2
|
|
213
|
+
/// when `max_cached_workflows` > 0, or is an error.
|
|
214
|
+
#[builder(default = PollerBehavior::SimpleMaximum(5))]
|
|
215
|
+
pub workflow_task_poller_behavior: PollerBehavior,
|
|
216
|
+
/// Only applies when using [PollerBehavior::SimpleMaximum]
|
|
217
|
+
///
|
|
218
|
+
/// (max workflow task polls * this number) = the number of max pollers that will be allowed for
|
|
219
|
+
/// the nonsticky queue when sticky tasks are enabled. If both defaults are used, the sticky
|
|
220
|
+
/// queue will allow 4 max pollers while the nonsticky queue will allow one. The minimum for
|
|
221
|
+
/// either poller is 1, so if the maximum allowed is 1 and sticky queues are enabled, there will
|
|
222
|
+
/// be 2 concurrent polls.
|
|
223
|
+
#[builder(default = 0.2)]
|
|
224
|
+
pub nonsticky_to_sticky_poll_ratio: f32,
|
|
225
|
+
/// Controls how polling for Activity tasks will happen on this worker's task queue.
|
|
226
|
+
#[builder(default = PollerBehavior::SimpleMaximum(5))]
|
|
227
|
+
pub activity_task_poller_behavior: PollerBehavior,
|
|
228
|
+
/// Controls how polling for Nexus tasks will happen on this worker's task queue.
|
|
229
|
+
#[builder(default = PollerBehavior::SimpleMaximum(5))]
|
|
230
|
+
pub nexus_task_poller_behavior: PollerBehavior,
|
|
231
|
+
// TODO [rust-sdk-branch]: Will go away once workflow registration can only happen in here.
|
|
232
|
+
// Then it can be auto-determined.
|
|
233
|
+
/// Specifies which task types this worker will poll for.
|
|
234
|
+
///
|
|
235
|
+
/// Note: At least one task type must be specified or the worker will fail validation.
|
|
236
|
+
#[builder(default = WorkerTaskTypes::all())]
|
|
237
|
+
pub task_types: WorkerTaskTypes,
|
|
238
|
+
/// How long a workflow task is allowed to sit on the sticky queue before it is timed out
|
|
239
|
+
/// and moved to the non-sticky queue where it may be picked up by any worker.
|
|
240
|
+
#[builder(default = Duration::from_secs(10))]
|
|
241
|
+
pub sticky_queue_schedule_to_start_timeout: Duration,
|
|
242
|
+
/// Longest interval for throttling activity heartbeats
|
|
243
|
+
#[builder(default = Duration::from_secs(60))]
|
|
244
|
+
pub max_heartbeat_throttle_interval: Duration,
|
|
245
|
+
/// Default interval for throttling activity heartbeats in case
|
|
246
|
+
/// `ActivityOptions.heartbeat_timeout` is unset.
|
|
247
|
+
/// When the timeout *is* set in the `ActivityOptions`, throttling is set to
|
|
248
|
+
/// `heartbeat_timeout * 0.8`.
|
|
249
|
+
#[builder(default = Duration::from_secs(30))]
|
|
250
|
+
pub default_heartbeat_throttle_interval: Duration,
|
|
251
|
+
/// Sets the maximum number of activities per second the task queue will dispatch, controlled
|
|
252
|
+
/// server-side. Note that this only takes effect upon an activity poll request. If multiple
|
|
253
|
+
/// workers on the same queue have different values set, they will thrash with the last poller
|
|
254
|
+
/// winning.
|
|
255
|
+
///
|
|
256
|
+
/// Setting this to a nonzero value will also disable eager activity execution.
|
|
257
|
+
pub max_task_queue_activities_per_second: Option<f64>,
|
|
258
|
+
/// Limits the number of activities per second that this worker will process. The worker will
|
|
259
|
+
/// not poll for new activities if by doing so it might receive and execute an activity which
|
|
260
|
+
/// would cause it to exceed this limit. Negative, zero, or NaN values will cause building
|
|
261
|
+
/// the options to fail.
|
|
262
|
+
pub max_worker_activities_per_second: Option<f64>,
|
|
263
|
+
/// Any error types listed here will cause any workflow being processed by this worker to fail,
|
|
264
|
+
/// rather than simply failing the workflow task.
|
|
265
|
+
#[builder(default)]
|
|
266
|
+
pub workflow_failure_errors: HashSet<WorkflowErrorType>,
|
|
267
|
+
/// Like [WorkerConfig::workflow_failure_errors], but specific to certain workflow types (the
|
|
268
|
+
/// map key).
|
|
269
|
+
#[builder(default)]
|
|
270
|
+
pub workflow_types_to_failure_errors: HashMap<String, HashSet<WorkflowErrorType>>,
|
|
271
|
+
/// If set, the worker will issue cancels for all outstanding activities and nexus operations after
|
|
272
|
+
/// shutdown has been initiated and this amount of time has elapsed.
|
|
273
|
+
pub graceful_shutdown_period: Option<Duration>,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
impl<S: worker_options_builder::State> WorkerOptionsBuilder<S> {
|
|
277
|
+
/// Registers all activities on an activity implementer.
|
|
278
|
+
pub fn register_activities<AI: ActivityImplementer>(mut self, instance: AI) -> Self {
|
|
279
|
+
self.activities.register_activities::<AI>(instance);
|
|
280
|
+
self
|
|
281
|
+
}
|
|
282
|
+
/// Registers a specific activitiy.
|
|
283
|
+
pub fn register_activity<AD>(mut self, instance: Arc<AD::Implementer>) -> Self
|
|
284
|
+
where
|
|
285
|
+
AD: ActivityDefinition + ExecutableActivity,
|
|
286
|
+
AD::Output: Send + Sync,
|
|
287
|
+
{
|
|
288
|
+
self.activities.register_activity::<AD>(instance);
|
|
289
|
+
self
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// Registers all workflows on a workflow implementer.
|
|
293
|
+
pub fn register_workflow<WI: WorkflowImplementer>(mut self) -> Self {
|
|
294
|
+
self.workflows.register_workflow::<WI>();
|
|
295
|
+
self
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/// Register a workflow with a custom factory for instance creation.
|
|
299
|
+
///
|
|
300
|
+
/// # Warning: Advanced Usage
|
|
301
|
+
///
|
|
302
|
+
/// This method is intended for scenarios requiring injection of un-serializable
|
|
303
|
+
/// state into workflows.
|
|
304
|
+
///
|
|
305
|
+
/// **This can easily cause nondeterminism**
|
|
306
|
+
///
|
|
307
|
+
/// Only use when you understand the implications and have a specific need that cannot be met
|
|
308
|
+
/// otherwise.
|
|
309
|
+
///
|
|
310
|
+
/// # Panics
|
|
311
|
+
///
|
|
312
|
+
/// Panics if the workflow type defines an `#[init]` method. Workflows using
|
|
313
|
+
/// factory registration must not have `#[init]` to avoid ambiguity about
|
|
314
|
+
/// instance creation.
|
|
315
|
+
pub fn register_workflow_with_factory<W, F>(mut self, factory: F) -> Self
|
|
316
|
+
where
|
|
317
|
+
W: WorkflowImplementation,
|
|
318
|
+
<W::Run as WorkflowDefinition>::Input: Send,
|
|
319
|
+
F: Fn() -> W + Send + Sync + 'static,
|
|
320
|
+
{
|
|
321
|
+
self.workflows
|
|
322
|
+
.register_workflow_run_with_factory::<W, F>(factory);
|
|
323
|
+
self
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Needs to exist to avoid https://github.com/elastio/bon/issues/359
|
|
328
|
+
fn def_build_id() -> WorkerDeploymentOptions {
|
|
329
|
+
WorkerDeploymentOptions::from_build_id(build_id_from_current_exe().to_owned())
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
impl WorkerOptions {
|
|
333
|
+
/// Registers all activities on an activity implementer.
|
|
334
|
+
pub fn register_activities<AI: ActivityImplementer>(&mut self, instance: AI) -> &mut Self {
|
|
335
|
+
self.activities.register_activities::<AI>(instance);
|
|
336
|
+
self
|
|
337
|
+
}
|
|
338
|
+
/// Registers a specific activitiy.
|
|
339
|
+
pub fn register_activity<AD>(&mut self, instance: Arc<AD::Implementer>) -> &mut Self
|
|
340
|
+
where
|
|
341
|
+
AD: ActivityDefinition + ExecutableActivity,
|
|
342
|
+
AD::Output: Send + Sync,
|
|
343
|
+
{
|
|
344
|
+
self.activities.register_activity::<AD>(instance);
|
|
345
|
+
self
|
|
346
|
+
}
|
|
347
|
+
/// Returns all the registered activities by cloning the current set.
|
|
348
|
+
pub fn activities(&self) -> ActivityDefinitions {
|
|
349
|
+
self.activities.clone()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/// Registers all workflows on a workflow implementer.
|
|
353
|
+
pub fn register_workflow<WI: WorkflowImplementer>(&mut self) -> &mut Self {
|
|
354
|
+
self.workflows.register_workflow::<WI>();
|
|
355
|
+
self
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// Register a workflow with a custom factory for instance creation.
|
|
359
|
+
///
|
|
360
|
+
/// # Warning: Advanced Usage
|
|
361
|
+
/// See [WorkerOptionsBuilder::register_workflow_with_factory] for more.
|
|
362
|
+
pub fn register_workflow_with_factory<W, F>(&mut self, factory: F) -> &mut Self
|
|
363
|
+
where
|
|
364
|
+
W: WorkflowImplementation,
|
|
365
|
+
<W::Run as WorkflowDefinition>::Input: Send,
|
|
366
|
+
F: Fn() -> W + Send + Sync + 'static,
|
|
367
|
+
{
|
|
368
|
+
self.workflows
|
|
369
|
+
.register_workflow_run_with_factory::<W, F>(factory);
|
|
370
|
+
self
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/// Returns all the registered workflows by cloning the current set.
|
|
374
|
+
pub fn workflows(&self) -> WorkflowDefinitions {
|
|
375
|
+
self.workflows.clone()
|
|
376
|
+
}
|
|
126
377
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
378
|
+
#[doc(hidden)]
|
|
379
|
+
pub fn to_core_options(
|
|
380
|
+
&self,
|
|
381
|
+
namespace: String,
|
|
382
|
+
connection_identity: String,
|
|
383
|
+
) -> Result<WorkerConfig, String> {
|
|
384
|
+
WorkerConfig::builder()
|
|
385
|
+
.namespace(namespace)
|
|
386
|
+
.task_queue(self.task_queue.clone())
|
|
387
|
+
.maybe_client_identity_override(self.client_identity_override.clone().or_else(|| {
|
|
388
|
+
connection_identity.is_empty().then(|| {
|
|
389
|
+
format!(
|
|
390
|
+
"{}@{}",
|
|
391
|
+
std::process::id(),
|
|
392
|
+
gethostname::gethostname().to_string_lossy()
|
|
393
|
+
)
|
|
394
|
+
})
|
|
395
|
+
}))
|
|
396
|
+
.max_cached_workflows(self.max_cached_workflows)
|
|
397
|
+
.tuner(self.tuner.clone())
|
|
398
|
+
.workflow_task_poller_behavior(self.workflow_task_poller_behavior)
|
|
399
|
+
.activity_task_poller_behavior(self.activity_task_poller_behavior)
|
|
400
|
+
.nexus_task_poller_behavior(self.nexus_task_poller_behavior)
|
|
401
|
+
.task_types(self.task_types)
|
|
402
|
+
.sticky_queue_schedule_to_start_timeout(self.sticky_queue_schedule_to_start_timeout)
|
|
403
|
+
.max_heartbeat_throttle_interval(self.max_heartbeat_throttle_interval)
|
|
404
|
+
.default_heartbeat_throttle_interval(self.default_heartbeat_throttle_interval)
|
|
405
|
+
.maybe_max_task_queue_activities_per_second(self.max_task_queue_activities_per_second)
|
|
406
|
+
.maybe_max_worker_activities_per_second(self.max_worker_activities_per_second)
|
|
407
|
+
.maybe_graceful_shutdown_period(self.graceful_shutdown_period)
|
|
408
|
+
.versioning_strategy(WorkerVersioningStrategy::WorkerDeploymentBased(
|
|
409
|
+
self.deployment_options.clone(),
|
|
410
|
+
))
|
|
411
|
+
.workflow_failure_errors(self.workflow_failure_errors.clone())
|
|
412
|
+
.workflow_types_to_failure_errors(self.workflow_types_to_failure_errors.clone())
|
|
413
|
+
.build()
|
|
414
|
+
}
|
|
138
415
|
}
|
|
139
416
|
|
|
140
|
-
/// A worker that can poll for and respond to workflow tasks by using
|
|
141
|
-
/// and activity tasks by using
|
|
417
|
+
/// A worker that can poll for and respond to workflow tasks by using
|
|
418
|
+
/// [temporalio_macros::workflow], and activity tasks by using activities defined with
|
|
419
|
+
/// [temporalio_macros::activities].
|
|
142
420
|
pub struct Worker {
|
|
143
421
|
common: CommonWorker,
|
|
144
422
|
workflow_half: WorkflowHalf,
|
|
145
423
|
activity_half: ActivityHalf,
|
|
146
|
-
app_data: Option<AppData>,
|
|
147
424
|
}
|
|
148
425
|
|
|
149
426
|
struct CommonWorker {
|
|
150
|
-
worker: Arc<
|
|
427
|
+
worker: Arc<CoreWorker>,
|
|
151
428
|
task_queue: String,
|
|
152
429
|
worker_interceptor: Option<Box<dyn WorkerInterceptor>>,
|
|
430
|
+
data_converter: DataConverter,
|
|
153
431
|
}
|
|
154
432
|
|
|
433
|
+
#[derive(Default)]
|
|
155
434
|
struct WorkflowHalf {
|
|
156
435
|
/// Maps run id to cached workflow state
|
|
157
436
|
workflows: RefCell<HashMap<String, WorkflowData>>,
|
|
158
|
-
|
|
159
|
-
workflow_fns: RefCell<HashMap<String, WorkflowFunction>>,
|
|
437
|
+
workflow_definitions: WorkflowDefinitions,
|
|
160
438
|
workflow_removed_from_map: Notify,
|
|
161
439
|
}
|
|
162
440
|
struct WorkflowData {
|
|
@@ -169,31 +447,71 @@ struct WorkflowFutureHandle<F: Future<Output = Result<WorkflowResult<Payload>, J
|
|
|
169
447
|
run_id: String,
|
|
170
448
|
}
|
|
171
449
|
|
|
450
|
+
#[derive(Default)]
|
|
172
451
|
struct ActivityHalf {
|
|
173
452
|
/// Maps activity type to the function for executing activities of that type
|
|
174
|
-
|
|
453
|
+
activities: ActivityDefinitions,
|
|
175
454
|
task_tokens_to_cancels: HashMap<TaskToken, CancellationToken>,
|
|
176
455
|
}
|
|
177
456
|
|
|
178
457
|
impl Worker {
|
|
179
|
-
/// Create a new
|
|
180
|
-
pub fn
|
|
458
|
+
/// Create a new worker from an existing connection, and options.
|
|
459
|
+
pub fn new(
|
|
460
|
+
runtime: &CoreRuntime,
|
|
461
|
+
client: Client,
|
|
462
|
+
mut options: WorkerOptions,
|
|
463
|
+
) -> Result<Self, Box<dyn std::error::Error>> {
|
|
464
|
+
let acts = std::mem::take(&mut options.activities);
|
|
465
|
+
let wfs = std::mem::take(&mut options.workflows);
|
|
466
|
+
let wc = options
|
|
467
|
+
.to_core_options(client.namespace(), client.identity())
|
|
468
|
+
.map_err(|s| anyhow::anyhow!("{s}"))?;
|
|
469
|
+
let core = init_worker(runtime, wc, client.connection().clone())?;
|
|
470
|
+
let mut me = Self::new_from_core_definitions(
|
|
471
|
+
Arc::new(core),
|
|
472
|
+
client.data_converter().clone(),
|
|
473
|
+
Default::default(),
|
|
474
|
+
Default::default(),
|
|
475
|
+
);
|
|
476
|
+
me.activity_half.activities = acts;
|
|
477
|
+
me.workflow_half.workflow_definitions = wfs;
|
|
478
|
+
Ok(me)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// TODO [rust-sdk-branch]: Eliminate this constructor in favor of passing in fake connection
|
|
482
|
+
#[doc(hidden)]
|
|
483
|
+
pub fn new_from_core(worker: Arc<CoreWorker>, data_converter: DataConverter) -> Self {
|
|
484
|
+
Self::new_from_core_definitions(
|
|
485
|
+
worker,
|
|
486
|
+
data_converter,
|
|
487
|
+
Default::default(),
|
|
488
|
+
Default::default(),
|
|
489
|
+
)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// TODO [rust-sdk-branch]: Eliminate this constructor in favor of passing in fake connection
|
|
493
|
+
#[doc(hidden)]
|
|
494
|
+
pub fn new_from_core_definitions(
|
|
495
|
+
worker: Arc<CoreWorker>,
|
|
496
|
+
data_converter: DataConverter,
|
|
497
|
+
activities: ActivityDefinitions,
|
|
498
|
+
workflows: WorkflowDefinitions,
|
|
499
|
+
) -> Self {
|
|
181
500
|
Self {
|
|
182
501
|
common: CommonWorker {
|
|
502
|
+
task_queue: worker.get_config().task_queue.clone(),
|
|
183
503
|
worker,
|
|
184
|
-
task_queue: task_queue.into(),
|
|
185
504
|
worker_interceptor: None,
|
|
505
|
+
data_converter,
|
|
186
506
|
},
|
|
187
507
|
workflow_half: WorkflowHalf {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
workflow_removed_from_map: Default::default(),
|
|
508
|
+
workflow_definitions: workflows,
|
|
509
|
+
..Default::default()
|
|
191
510
|
},
|
|
192
511
|
activity_half: ActivityHalf {
|
|
193
|
-
|
|
194
|
-
|
|
512
|
+
activities,
|
|
513
|
+
..Default::default()
|
|
195
514
|
},
|
|
196
|
-
app_data: Some(Default::default()),
|
|
197
515
|
}
|
|
198
516
|
}
|
|
199
517
|
|
|
@@ -202,58 +520,67 @@ impl Worker {
|
|
|
202
520
|
&self.common.task_queue
|
|
203
521
|
}
|
|
204
522
|
|
|
205
|
-
/// Return a handle that can be used to initiate shutdown.
|
|
206
|
-
///
|
|
523
|
+
/// Return a handle that can be used to initiate shutdown. This is useful because [Worker::run]
|
|
524
|
+
/// takes self mutably, so you may want to obtain a handle for shutting down before running.
|
|
207
525
|
pub fn shutdown_handle(&self) -> impl Fn() + use<> {
|
|
208
526
|
let w = self.common.worker.clone();
|
|
209
527
|
move || w.initiate_shutdown()
|
|
210
528
|
}
|
|
211
529
|
|
|
212
|
-
///
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
530
|
+
/// Registers all activities on an activity implementer.
|
|
531
|
+
pub fn register_activities<AI: ActivityImplementer>(&mut self, instance: AI) -> &mut Self {
|
|
532
|
+
self.activity_half
|
|
533
|
+
.activities
|
|
534
|
+
.register_activities::<AI>(instance);
|
|
535
|
+
self
|
|
536
|
+
}
|
|
537
|
+
/// Registers a specific activitiy.
|
|
538
|
+
pub fn register_activity<AD>(&mut self, instance: Arc<AD::Implementer>) -> &mut Self
|
|
539
|
+
where
|
|
540
|
+
AD: ActivityDefinition + ExecutableActivity,
|
|
541
|
+
AD::Output: Send + Sync,
|
|
542
|
+
{
|
|
543
|
+
self.activity_half
|
|
544
|
+
.activities
|
|
545
|
+
.register_activity::<AD>(instance);
|
|
546
|
+
self
|
|
223
547
|
}
|
|
224
548
|
|
|
225
|
-
///
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
) {
|
|
232
|
-
self.activity_half.activity_fns.insert(
|
|
233
|
-
activity_type.into(),
|
|
234
|
-
ActivityFunction {
|
|
235
|
-
act_func: act_function.into_activity_fn(),
|
|
236
|
-
},
|
|
237
|
-
);
|
|
549
|
+
/// Registers all workflows on a workflow implementer.
|
|
550
|
+
pub fn register_workflow<WI: WorkflowImplementer>(&mut self) -> &mut Self {
|
|
551
|
+
self.workflow_half
|
|
552
|
+
.workflow_definitions
|
|
553
|
+
.register_workflow::<WI>();
|
|
554
|
+
self
|
|
238
555
|
}
|
|
239
556
|
|
|
240
|
-
///
|
|
241
|
-
|
|
242
|
-
|
|
557
|
+
/// Register a workflow with a custom factory for instance creation.
|
|
558
|
+
///
|
|
559
|
+
/// See [WorkerOptionsBuilder::register_workflow_with_factory] for more.
|
|
560
|
+
pub fn register_workflow_with_factory<W, F>(&mut self, factory: F) -> &mut Self
|
|
561
|
+
where
|
|
562
|
+
W: WorkflowImplementation,
|
|
563
|
+
<W::Run as WorkflowDefinition>::Input: Send,
|
|
564
|
+
F: Fn() -> W + Send + Sync + 'static,
|
|
565
|
+
{
|
|
566
|
+
self.workflow_half
|
|
567
|
+
.workflow_definitions
|
|
568
|
+
.register_workflow_run_with_factory::<W, F>(factory);
|
|
569
|
+
self
|
|
243
570
|
}
|
|
244
571
|
|
|
245
572
|
/// Runs the worker. Eventually resolves after the worker has been explicitly shut down,
|
|
246
573
|
/// or may return early with an error in the event of some unresolvable problem.
|
|
247
574
|
pub async fn run(&mut self) -> Result<(), anyhow::Error> {
|
|
248
575
|
let shutdown_token = CancellationToken::new();
|
|
249
|
-
let (common, wf_half, act_half
|
|
250
|
-
let safe_app_data = Arc::new(
|
|
251
|
-
app_data
|
|
252
|
-
.take()
|
|
253
|
-
.ok_or_else(|| anyhow!("app_data should exist on run"))?,
|
|
254
|
-
);
|
|
576
|
+
let (common, wf_half, act_half) = self.split_apart();
|
|
255
577
|
let (wf_future_tx, wf_future_rx) = unbounded_channel();
|
|
256
578
|
let (completions_tx, completions_rx) = unbounded_channel();
|
|
579
|
+
|
|
580
|
+
// Workflows run in a LocalSet because they use Rc<RefCell> for state management.
|
|
581
|
+
// This allows them to not require Send/Sync bounds.
|
|
582
|
+
let workflow_local_set = tokio::task::LocalSet::new();
|
|
583
|
+
|
|
257
584
|
let wf_future_joiner = async {
|
|
258
585
|
UnboundedReceiverStream::new(wf_future_rx)
|
|
259
586
|
.map(Result::<_, anyhow::Error>::Ok)
|
|
@@ -265,7 +592,14 @@ impl Worker {
|
|
|
265
592
|
}| {
|
|
266
593
|
let wf_half = &*wf_half;
|
|
267
594
|
async move {
|
|
268
|
-
join_handle.await
|
|
595
|
+
let result = join_handle.await?;
|
|
596
|
+
// Eviction is normal workflow lifecycle - workflows loop waiting for
|
|
597
|
+
// eviction after completion to manage cache cleanup
|
|
598
|
+
if let Err(e) = result
|
|
599
|
+
&& !matches!(e, WorkflowTermination::Evicted)
|
|
600
|
+
{
|
|
601
|
+
return Err(e.into());
|
|
602
|
+
}
|
|
269
603
|
debug!(run_id=%run_id, "Removing workflow from cache");
|
|
270
604
|
wf_half.workflows.borrow_mut().remove(&run_id);
|
|
271
605
|
wf_half.workflow_removed_from_map.notify_one();
|
|
@@ -279,7 +613,13 @@ impl Worker {
|
|
|
279
613
|
let wf_completion_processor = async {
|
|
280
614
|
UnboundedReceiverStream::new(completions_rx)
|
|
281
615
|
.map(Ok)
|
|
282
|
-
.try_for_each_concurrent(None, |completion| async {
|
|
616
|
+
.try_for_each_concurrent(None, |mut completion| async {
|
|
617
|
+
encode_payloads(
|
|
618
|
+
&mut completion,
|
|
619
|
+
common.data_converter.codec(),
|
|
620
|
+
&SerializationContextData::Workflow,
|
|
621
|
+
)
|
|
622
|
+
.await;
|
|
283
623
|
if let Some(ref i) = common.worker_interceptor {
|
|
284
624
|
i.on_workflow_activation_completion(&completion).await;
|
|
285
625
|
}
|
|
@@ -290,72 +630,89 @@ impl Worker {
|
|
|
290
630
|
.context("Workflow completions processor encountered an error")
|
|
291
631
|
};
|
|
292
632
|
tokio::try_join!(
|
|
293
|
-
// Workflow
|
|
633
|
+
// Workflow-related tasks run inside LocalSet (allows !Send futures)
|
|
294
634
|
async {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
635
|
+
workflow_local_set.run_until(async {
|
|
636
|
+
tokio::try_join!(
|
|
637
|
+
// Workflow polling loop
|
|
638
|
+
async {
|
|
639
|
+
loop {
|
|
640
|
+
let mut activation =
|
|
641
|
+
match common.worker.poll_workflow_activation().await {
|
|
642
|
+
Err(PollError::ShutDown) => {
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
o => o?,
|
|
646
|
+
};
|
|
647
|
+
decode_payloads(
|
|
648
|
+
&mut activation,
|
|
649
|
+
common.data_converter.codec(),
|
|
650
|
+
&SerializationContextData::Workflow,
|
|
651
|
+
)
|
|
652
|
+
.await;
|
|
653
|
+
if let Some(ref i) = common.worker_interceptor {
|
|
654
|
+
i.on_workflow_activation(&activation).await?;
|
|
655
|
+
}
|
|
656
|
+
if let Some(wf_fut) = wf_half
|
|
657
|
+
.workflow_activation_handler(
|
|
658
|
+
common,
|
|
659
|
+
shutdown_token.clone(),
|
|
660
|
+
activation,
|
|
661
|
+
&completions_tx,
|
|
662
|
+
)
|
|
663
|
+
.await?
|
|
664
|
+
&& wf_future_tx.send(wf_fut).is_err()
|
|
665
|
+
{
|
|
666
|
+
panic!(
|
|
667
|
+
"Receive half of completion processor channel cannot be dropped"
|
|
668
|
+
);
|
|
669
|
+
}
|
|
299
670
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
)
|
|
312
|
-
.await?
|
|
313
|
-
&& wf_future_tx.send(wf_fut).is_err()
|
|
314
|
-
{
|
|
315
|
-
panic!("Receive half of completion processor channel cannot be dropped");
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
// Tell still-alive workflows to evict themselves
|
|
319
|
-
shutdown_token.cancel();
|
|
320
|
-
// It's important to drop these so the future and completion processors will
|
|
321
|
-
// terminate.
|
|
322
|
-
drop(wf_future_tx);
|
|
323
|
-
drop(completions_tx);
|
|
324
|
-
Result::<_, anyhow::Error>::Ok(())
|
|
671
|
+
// Tell still-alive workflows to evict themselves
|
|
672
|
+
shutdown_token.cancel();
|
|
673
|
+
// It's important to drop these so the future and completion processors will
|
|
674
|
+
// terminate.
|
|
675
|
+
drop(wf_future_tx);
|
|
676
|
+
drop(completions_tx);
|
|
677
|
+
Result::<_, anyhow::Error>::Ok(())
|
|
678
|
+
},
|
|
679
|
+
wf_future_joiner,
|
|
680
|
+
)
|
|
681
|
+
}).await
|
|
325
682
|
},
|
|
326
683
|
// Only poll on the activity queue if activity functions have been registered. This
|
|
327
684
|
// makes tests which use mocks dramatically more manageable.
|
|
328
685
|
async {
|
|
329
|
-
if !act_half.
|
|
686
|
+
if !act_half.activities.is_empty() {
|
|
330
687
|
loop {
|
|
331
688
|
let activity = common.worker.poll_activity_task().await;
|
|
332
689
|
if matches!(activity, Err(PollError::ShutDown)) {
|
|
333
690
|
break;
|
|
334
691
|
}
|
|
692
|
+
let mut activity = activity?;
|
|
693
|
+
decode_payloads(
|
|
694
|
+
&mut activity,
|
|
695
|
+
common.data_converter.codec(),
|
|
696
|
+
&SerializationContextData::Activity,
|
|
697
|
+
)
|
|
698
|
+
.await;
|
|
335
699
|
act_half.activity_task_handler(
|
|
336
700
|
common.worker.clone(),
|
|
337
|
-
safe_app_data.clone(),
|
|
338
701
|
common.task_queue.clone(),
|
|
339
|
-
|
|
702
|
+
common.data_converter.clone(),
|
|
703
|
+
activity,
|
|
340
704
|
)?;
|
|
341
705
|
}
|
|
342
706
|
};
|
|
343
707
|
Result::<_, anyhow::Error>::Ok(())
|
|
344
708
|
},
|
|
345
|
-
wf_future_joiner,
|
|
346
709
|
wf_completion_processor,
|
|
347
710
|
)?;
|
|
348
711
|
|
|
349
|
-
info!("Polling loops exited");
|
|
350
712
|
if let Some(i) = self.common.worker_interceptor.as_ref() {
|
|
351
713
|
i.on_shutdown(self);
|
|
352
714
|
}
|
|
353
715
|
self.common.worker.shutdown().await;
|
|
354
|
-
debug!("Worker shutdown complete");
|
|
355
|
-
self.app_data = Some(
|
|
356
|
-
Arc::try_unwrap(safe_app_data)
|
|
357
|
-
.map_err(|_| anyhow!("some references of AppData exist on worker shutdown"))?,
|
|
358
|
-
);
|
|
359
716
|
Ok(())
|
|
360
717
|
}
|
|
361
718
|
|
|
@@ -367,7 +724,7 @@ impl Worker {
|
|
|
367
724
|
/// Turns this rust worker into a new worker with all the same workflows and activities
|
|
368
725
|
/// registered, but with a new underlying core worker. Can be used to swap the worker for
|
|
369
726
|
/// a replay worker, change task queues, etc.
|
|
370
|
-
pub fn with_new_core_worker(&mut self, new_core_worker: Arc<
|
|
727
|
+
pub fn with_new_core_worker(&mut self, new_core_worker: Arc<CoreWorker>) {
|
|
371
728
|
self.common.worker = new_core_worker;
|
|
372
729
|
}
|
|
373
730
|
|
|
@@ -377,19 +734,21 @@ impl Worker {
|
|
|
377
734
|
self.workflow_half.workflows.borrow().len()
|
|
378
735
|
}
|
|
379
736
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
737
|
+
/// Returns the instance key for this worker, used for worker heartbeating.
|
|
738
|
+
pub fn worker_instance_key(&self) -> Uuid {
|
|
739
|
+
self.common.worker.worker_instance_key()
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
#[doc(hidden)]
|
|
743
|
+
pub fn core_worker(&self) -> Arc<CoreWorker> {
|
|
744
|
+
self.common.worker.clone()
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
fn split_apart(&mut self) -> (&mut CommonWorker, &mut WorkflowHalf, &mut ActivityHalf) {
|
|
388
748
|
(
|
|
389
749
|
&mut self.common,
|
|
390
750
|
&mut self.workflow_half,
|
|
391
751
|
&mut self.activity_half,
|
|
392
|
-
&mut self.app_data,
|
|
393
752
|
)
|
|
394
753
|
}
|
|
395
754
|
}
|
|
@@ -419,13 +778,33 @@ impl WorkflowHalf {
|
|
|
419
778
|
Some(Variant::InitializeWorkflow(ref mut sw)) => Some(sw),
|
|
420
779
|
_ => None,
|
|
421
780
|
}) {
|
|
422
|
-
let workflow_type =
|
|
781
|
+
let workflow_type = sw.workflow_type.clone();
|
|
782
|
+
let payload_converter = common.data_converter.payload_converter().clone();
|
|
423
783
|
let (wff, activations) = {
|
|
424
|
-
let
|
|
425
|
-
|
|
426
|
-
|
|
784
|
+
if let Some(factory) = self.workflow_definitions.get_workflow(&workflow_type) {
|
|
785
|
+
match WorkflowFunction::from_invocation(factory).start_workflow(
|
|
786
|
+
common.worker.get_config().namespace.clone(),
|
|
787
|
+
common.task_queue.clone(),
|
|
788
|
+
run_id.clone(),
|
|
789
|
+
std::mem::take(sw),
|
|
790
|
+
completions_tx.clone(),
|
|
791
|
+
payload_converter,
|
|
792
|
+
) {
|
|
793
|
+
Ok(result) => result,
|
|
794
|
+
Err(e) => {
|
|
795
|
+
warn!("Failed to create workflow {workflow_type}: {e}");
|
|
796
|
+
completions_tx
|
|
797
|
+
.send(WorkflowActivationCompletion::fail(
|
|
798
|
+
run_id,
|
|
799
|
+
format!("Failed to create workflow: {e}").into(),
|
|
800
|
+
Some(WorkflowTaskFailedCause::WorkflowWorkerUnhandledFailure),
|
|
801
|
+
))
|
|
802
|
+
.expect("Completion channel intact");
|
|
803
|
+
return Ok(None);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
} else {
|
|
427
807
|
warn!("Workflow type {workflow_type} not found");
|
|
428
|
-
|
|
429
808
|
completions_tx
|
|
430
809
|
.send(WorkflowActivationCompletion::fail(
|
|
431
810
|
run_id,
|
|
@@ -434,22 +813,19 @@ impl WorkflowHalf {
|
|
|
434
813
|
))
|
|
435
814
|
.expect("Completion channel intact");
|
|
436
815
|
return Ok(None);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
wf_function.start_workflow(
|
|
440
|
-
common.worker.get_config().namespace.clone(),
|
|
441
|
-
common.task_queue.clone(),
|
|
442
|
-
std::mem::take(sw),
|
|
443
|
-
completions_tx.clone(),
|
|
444
|
-
)
|
|
816
|
+
}
|
|
445
817
|
};
|
|
446
|
-
|
|
818
|
+
// Wrap in unconstrained to prevent Tokio from imposing limits on commands per poll
|
|
819
|
+
// TODO [rust-sdk-branch]: Deadlock detection
|
|
820
|
+
let wff = tokio::task::unconstrained(wff);
|
|
821
|
+
// The LocalSet is created in Worker::run().
|
|
822
|
+
let jh = tokio::task::spawn_local(async move {
|
|
447
823
|
tokio::select! {
|
|
448
824
|
r = wff.fuse() => r,
|
|
449
825
|
// TODO: This probably shouldn't abort early, as it could cause an in-progress
|
|
450
826
|
// complete to abort. Send synthetic remove activation
|
|
451
827
|
_ = shutdown_token.cancelled() => {
|
|
452
|
-
|
|
828
|
+
Err(WorkflowTermination::Evicted)
|
|
453
829
|
}
|
|
454
830
|
}
|
|
455
831
|
});
|
|
@@ -513,23 +889,19 @@ impl ActivityHalf {
|
|
|
513
889
|
/// Spawns off a task to handle the provided activity task
|
|
514
890
|
fn activity_task_handler(
|
|
515
891
|
&mut self,
|
|
516
|
-
worker: Arc<
|
|
517
|
-
app_data: Arc<AppData>,
|
|
892
|
+
worker: Arc<CoreWorker>,
|
|
518
893
|
task_queue: String,
|
|
894
|
+
data_converter: DataConverter,
|
|
519
895
|
activity: ActivityTask,
|
|
520
896
|
) -> Result<(), anyhow::Error> {
|
|
521
897
|
match activity.variant {
|
|
522
898
|
Some(activity_task::Variant::Start(start)) => {
|
|
523
|
-
let act_fn = self
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
start.activity_type
|
|
530
|
-
)
|
|
531
|
-
})?
|
|
532
|
-
.clone();
|
|
899
|
+
let act_fn = self.activities.get(&start.activity_type).ok_or_else(|| {
|
|
900
|
+
anyhow!(
|
|
901
|
+
"No function registered for activity type {}",
|
|
902
|
+
start.activity_type
|
|
903
|
+
)
|
|
904
|
+
})?;
|
|
533
905
|
let span = info_span!(
|
|
534
906
|
"RunActivity",
|
|
535
907
|
"otel.name" = format!("RunActivity:{}", start.activity_type),
|
|
@@ -543,23 +915,18 @@ impl ActivityHalf {
|
|
|
543
915
|
self.task_tokens_to_cancels
|
|
544
916
|
.insert(task_token.clone().into(), ct.clone());
|
|
545
917
|
|
|
546
|
-
let (ctx,
|
|
547
|
-
worker.clone(),
|
|
548
|
-
|
|
549
|
-
ct,
|
|
550
|
-
task_queue,
|
|
551
|
-
task_token.clone(),
|
|
552
|
-
start,
|
|
553
|
-
);
|
|
918
|
+
let (ctx, args) =
|
|
919
|
+
ActivityContext::new(worker.clone(), ct, task_queue, task_token.clone(), start);
|
|
920
|
+
let codec_data_converter = data_converter.clone();
|
|
554
921
|
|
|
555
922
|
tokio::spawn(async move {
|
|
556
923
|
let act_fut = async move {
|
|
557
|
-
if let Some(info) = &ctx.
|
|
924
|
+
if let Some(info) = &ctx.info().workflow_execution {
|
|
558
925
|
Span::current()
|
|
559
926
|
.record("temporalWorkflowID", &info.workflow_id)
|
|
560
927
|
.record("temporalRunID", &info.run_id);
|
|
561
928
|
}
|
|
562
|
-
(act_fn
|
|
929
|
+
(act_fn)(args, data_converter, ctx).await
|
|
563
930
|
}
|
|
564
931
|
.instrument(span);
|
|
565
932
|
let output = AssertUnwindSafe(act_fut).catch_unwind().await;
|
|
@@ -568,16 +935,16 @@ impl ActivityHalf {
|
|
|
568
935
|
format!("Activity function panicked: {}", panic_formatter(e)),
|
|
569
936
|
true,
|
|
570
937
|
)),
|
|
571
|
-
Ok(Ok(
|
|
572
|
-
Ok(Ok(ActExitValue::WillCompleteAsync)) => {
|
|
573
|
-
ActivityExecutionResult::will_complete_async()
|
|
574
|
-
}
|
|
938
|
+
Ok(Ok(p)) => ActivityExecutionResult::ok(p),
|
|
575
939
|
Ok(Err(err)) => match err {
|
|
576
940
|
ActivityError::Retryable {
|
|
577
941
|
source,
|
|
578
942
|
explicit_delay,
|
|
579
943
|
} => ActivityExecutionResult::fail({
|
|
580
|
-
let mut f = Failure::application_failure_from_error(
|
|
944
|
+
let mut f = Failure::application_failure_from_error(
|
|
945
|
+
anyhow::Error::from_boxed(source),
|
|
946
|
+
false,
|
|
947
|
+
);
|
|
581
948
|
if let Some(d) = explicit_delay
|
|
582
949
|
&& let Some(failure::FailureInfo::ApplicationFailureInfo(fi)) =
|
|
583
950
|
f.failure_info.as_mut()
|
|
@@ -590,16 +957,27 @@ impl ActivityHalf {
|
|
|
590
957
|
ActivityExecutionResult::cancel_from_details(details)
|
|
591
958
|
}
|
|
592
959
|
ActivityError::NonRetryable(nre) => ActivityExecutionResult::fail(
|
|
593
|
-
Failure::application_failure_from_error(
|
|
960
|
+
Failure::application_failure_from_error(
|
|
961
|
+
anyhow::Error::from_boxed(nre),
|
|
962
|
+
true,
|
|
963
|
+
),
|
|
594
964
|
),
|
|
965
|
+
ActivityError::WillCompleteAsync => {
|
|
966
|
+
ActivityExecutionResult::will_complete_async()
|
|
967
|
+
}
|
|
595
968
|
},
|
|
596
969
|
};
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
970
|
+
let mut completion = ActivityTaskCompletion {
|
|
971
|
+
task_token,
|
|
972
|
+
result: Some(result),
|
|
973
|
+
};
|
|
974
|
+
encode_payloads(
|
|
975
|
+
&mut completion,
|
|
976
|
+
codec_data_converter.codec(),
|
|
977
|
+
&SerializationContextData::Activity,
|
|
978
|
+
)
|
|
979
|
+
.await;
|
|
980
|
+
worker.complete_activity_task(completion).await?;
|
|
603
981
|
Ok::<_, anyhow::Error>(())
|
|
604
982
|
});
|
|
605
983
|
}
|
|
@@ -630,7 +1008,7 @@ enum UnblockEvent {
|
|
|
630
1008
|
}
|
|
631
1009
|
|
|
632
1010
|
/// Result of awaiting on a timer
|
|
633
|
-
#[derive(Debug, Copy, Clone)]
|
|
1011
|
+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
634
1012
|
pub enum TimerResult {
|
|
635
1013
|
/// The timer was cancelled
|
|
636
1014
|
Cancelled,
|
|
@@ -639,13 +1017,13 @@ pub enum TimerResult {
|
|
|
639
1017
|
}
|
|
640
1018
|
|
|
641
1019
|
/// Successful result of sending a signal to an external workflow
|
|
642
|
-
#[derive(Debug)]
|
|
1020
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
643
1021
|
pub struct SignalExternalOk;
|
|
644
1022
|
/// Result of awaiting on sending a signal to an external workflow
|
|
645
1023
|
pub type SignalExternalWfResult = Result<SignalExternalOk, Failure>;
|
|
646
1024
|
|
|
647
1025
|
/// Successful result of sending a cancel request to an external workflow
|
|
648
|
-
#[derive(Debug)]
|
|
1026
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
649
1027
|
pub struct CancelExternalOk;
|
|
650
1028
|
/// Result of awaiting on sending a cancel request to an external workflow
|
|
651
1029
|
pub type CancelExternalWfResult = Result<CancelExternalOk, Failure>;
|
|
@@ -834,8 +1212,6 @@ enum RustWfCmd {
|
|
|
834
1212
|
NewCmd(CommandCreateRequest),
|
|
835
1213
|
NewNonblockingCmd(workflow_command::Variant),
|
|
836
1214
|
SubscribeChildWorkflowCompletion(CommandSubscribeChildWorkflowCompletion),
|
|
837
|
-
SubscribeSignal(String, UnboundedSender<SignalData>),
|
|
838
|
-
RegisterUpdate(String, UpdateFunctions),
|
|
839
1215
|
SubscribeNexusOperationCompletion {
|
|
840
1216
|
seq: u32,
|
|
841
1217
|
unblocker: oneshot::Sender<UnblockEvent>,
|
|
@@ -852,81 +1228,61 @@ struct CommandSubscribeChildWorkflowCompletion {
|
|
|
852
1228
|
unblocker: oneshot::Sender<UnblockEvent>,
|
|
853
1229
|
}
|
|
854
1230
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1231
|
+
/// The result of running a workflow.
|
|
1232
|
+
///
|
|
1233
|
+
/// Successful completion returns `Ok(T)` where `T` is the workflow's return type.
|
|
1234
|
+
/// Non-error terminations (cancel, eviction, continue-as-new) return `Err(WorkflowTermination)`.
|
|
1235
|
+
pub type WorkflowResult<T> = Result<T, WorkflowTermination>;
|
|
1236
|
+
|
|
1237
|
+
/// Represents ways a workflow can terminate without producing a normal result.
|
|
1238
|
+
///
|
|
1239
|
+
/// This is used as the error type in [`WorkflowResult<T>`] for non-error termination conditions
|
|
1240
|
+
/// like cancellation, eviction, continue-as-new, or actual failures.
|
|
1241
|
+
#[derive(Debug, thiserror::Error)]
|
|
1242
|
+
pub enum WorkflowTermination {
|
|
1243
|
+
/// The workflow was cancelled.
|
|
1244
|
+
#[error("Workflow cancelled")]
|
|
1245
|
+
Cancelled,
|
|
859
1246
|
|
|
860
|
-
/// The
|
|
861
|
-
|
|
862
|
-
|
|
1247
|
+
/// The workflow was evicted from the cache.
|
|
1248
|
+
#[error("Workflow evicted from cache")]
|
|
1249
|
+
Evicted,
|
|
1250
|
+
|
|
1251
|
+
/// The workflow should continue as a new execution.
|
|
1252
|
+
#[error("Continue as new")]
|
|
1253
|
+
ContinueAsNew(Box<ContinueAsNewWorkflowExecution>),
|
|
1254
|
+
|
|
1255
|
+
/// The workflow failed with an error.
|
|
1256
|
+
#[error("Workflow failed: {0}")]
|
|
1257
|
+
Failed(#[source] anyhow::Error),
|
|
863
1258
|
}
|
|
864
1259
|
|
|
865
|
-
impl
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
O: Serialize,
|
|
870
|
-
{
|
|
871
|
-
fn from(wf_func: F) -> Self {
|
|
872
|
-
Self::new(wf_func)
|
|
1260
|
+
impl WorkflowTermination {
|
|
1261
|
+
/// Construct a [WorkflowTermination::ContinueAsNew]
|
|
1262
|
+
pub fn continue_as_new(can: ContinueAsNewWorkflowExecution) -> Self {
|
|
1263
|
+
Self::ContinueAsNew(Box::new(can))
|
|
873
1264
|
}
|
|
874
|
-
}
|
|
875
1265
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
where
|
|
880
|
-
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
881
|
-
Fut: Future<Output = Result<WfExitValue<O>, anyhow::Error>> + Send + 'static,
|
|
882
|
-
O: Serialize,
|
|
883
|
-
{
|
|
884
|
-
Self {
|
|
885
|
-
wf_func: Box::new(move |ctx: WfContext| {
|
|
886
|
-
(f)(ctx)
|
|
887
|
-
.map(|r| {
|
|
888
|
-
r.and_then(|r| {
|
|
889
|
-
Ok(match r {
|
|
890
|
-
WfExitValue::ContinueAsNew(b) => WfExitValue::ContinueAsNew(b),
|
|
891
|
-
WfExitValue::Cancelled => WfExitValue::Cancelled,
|
|
892
|
-
WfExitValue::Evicted => WfExitValue::Evicted,
|
|
893
|
-
WfExitValue::Normal(o) => WfExitValue::Normal(o.as_json_payload()?),
|
|
894
|
-
})
|
|
895
|
-
})
|
|
896
|
-
})
|
|
897
|
-
.boxed()
|
|
898
|
-
}),
|
|
899
|
-
}
|
|
1266
|
+
/// Construct a [WorkflowTermination::Failed] variant from any error.
|
|
1267
|
+
pub fn failed(err: impl Into<anyhow::Error>) -> Self {
|
|
1268
|
+
Self::Failed(err.into())
|
|
900
1269
|
}
|
|
901
1270
|
}
|
|
902
1271
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
#[derive(Debug, derive_more::From)]
|
|
908
|
-
pub enum WfExitValue<T> {
|
|
909
|
-
/// Continue the workflow as a new execution
|
|
910
|
-
#[from(ignore)]
|
|
911
|
-
ContinueAsNew(Box<ContinueAsNewWorkflowExecution>),
|
|
912
|
-
/// Confirm the workflow was cancelled (can be automatic in a more advanced iteration)
|
|
913
|
-
#[from(ignore)]
|
|
914
|
-
Cancelled,
|
|
915
|
-
/// The run was evicted
|
|
916
|
-
#[from(ignore)]
|
|
917
|
-
Evicted,
|
|
918
|
-
/// Finish with a result
|
|
919
|
-
Normal(T),
|
|
1272
|
+
impl From<anyhow::Error> for WorkflowTermination {
|
|
1273
|
+
fn from(err: anyhow::Error) -> Self {
|
|
1274
|
+
Self::Failed(err)
|
|
1275
|
+
}
|
|
920
1276
|
}
|
|
921
1277
|
|
|
922
|
-
impl
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
Self::ContinueAsNew(Box::new(can))
|
|
1278
|
+
impl From<ActivityExecutionError> for WorkflowTermination {
|
|
1279
|
+
fn from(value: ActivityExecutionError) -> Self {
|
|
1280
|
+
Self::failed(value)
|
|
926
1281
|
}
|
|
927
1282
|
}
|
|
928
1283
|
|
|
929
1284
|
/// Activity functions may return these values when exiting
|
|
1285
|
+
#[derive(Debug)]
|
|
930
1286
|
pub enum ActExitValue<T> {
|
|
931
1287
|
/// Completion requires an asynchronous callback
|
|
932
1288
|
WillCompleteAsync,
|
|
@@ -940,180 +1296,6 @@ impl<T: AsJsonPayloadExt> From<T> for ActExitValue<T> {
|
|
|
940
1296
|
}
|
|
941
1297
|
}
|
|
942
1298
|
|
|
943
|
-
type BoxActFn = Arc<
|
|
944
|
-
dyn Fn(ActContext, Payload) -> BoxFuture<'static, Result<ActExitValue<Payload>, ActivityError>>
|
|
945
|
-
+ Send
|
|
946
|
-
+ Sync,
|
|
947
|
-
>;
|
|
948
|
-
|
|
949
|
-
/// Container for user-defined activity functions
|
|
950
|
-
#[derive(Clone)]
|
|
951
|
-
pub struct ActivityFunction {
|
|
952
|
-
act_func: BoxActFn,
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
/// Returned as errors from activity functions
|
|
956
|
-
#[derive(Debug)]
|
|
957
|
-
pub enum ActivityError {
|
|
958
|
-
/// This error can be returned from activities to allow the explicit configuration of certain
|
|
959
|
-
/// error properties. It's also the default error type that arbitrary errors will be converted
|
|
960
|
-
/// into.
|
|
961
|
-
Retryable {
|
|
962
|
-
/// The underlying error
|
|
963
|
-
source: anyhow::Error,
|
|
964
|
-
/// If specified, the next retry (if there is one) will occur after this delay
|
|
965
|
-
explicit_delay: Option<Duration>,
|
|
966
|
-
},
|
|
967
|
-
/// Return this error to indicate your activity is cancelling
|
|
968
|
-
Cancelled {
|
|
969
|
-
/// Some data to save as the cancellation reason
|
|
970
|
-
details: Option<Payload>,
|
|
971
|
-
},
|
|
972
|
-
/// Return this error to indicate that your activity non-retryable
|
|
973
|
-
/// this is a transparent wrapper around anyhow Error so essentially any type of error
|
|
974
|
-
/// could be used here.
|
|
975
|
-
NonRetryable(anyhow::Error),
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
impl<E> From<E> for ActivityError
|
|
979
|
-
where
|
|
980
|
-
E: Into<anyhow::Error>,
|
|
981
|
-
{
|
|
982
|
-
fn from(source: E) -> Self {
|
|
983
|
-
Self::Retryable {
|
|
984
|
-
source: source.into(),
|
|
985
|
-
explicit_delay: None,
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
impl ActivityError {
|
|
991
|
-
/// Construct a cancelled error without details
|
|
992
|
-
pub fn cancelled() -> Self {
|
|
993
|
-
Self::Cancelled { details: None }
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
/// Closures / functions which can be turned into activity functions implement this trait
|
|
998
|
-
pub trait IntoActivityFunc<Args, Res, Out> {
|
|
999
|
-
/// Consume the closure or fn pointer and turned it into a boxed activity function
|
|
1000
|
-
fn into_activity_fn(self) -> BoxActFn;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
impl<A, Rf, R, O, F> IntoActivityFunc<A, Rf, O> for F
|
|
1004
|
-
where
|
|
1005
|
-
F: (Fn(ActContext, A) -> Rf) + Sync + Send + 'static,
|
|
1006
|
-
A: FromJsonPayloadExt + Send,
|
|
1007
|
-
Rf: Future<Output = Result<R, ActivityError>> + Send + 'static,
|
|
1008
|
-
R: Into<ActExitValue<O>>,
|
|
1009
|
-
O: AsJsonPayloadExt,
|
|
1010
|
-
{
|
|
1011
|
-
fn into_activity_fn(self) -> BoxActFn {
|
|
1012
|
-
let wrapper = move |ctx: ActContext, input: Payload| {
|
|
1013
|
-
// Some minor gymnastics are required to avoid needing to clone the function
|
|
1014
|
-
match A::from_json_payload(&input) {
|
|
1015
|
-
Ok(deser) => self(ctx, deser)
|
|
1016
|
-
.map(|r| {
|
|
1017
|
-
r.and_then(|r| {
|
|
1018
|
-
let exit_val: ActExitValue<O> = r.into();
|
|
1019
|
-
match exit_val {
|
|
1020
|
-
ActExitValue::WillCompleteAsync => {
|
|
1021
|
-
Ok(ActExitValue::WillCompleteAsync)
|
|
1022
|
-
}
|
|
1023
|
-
ActExitValue::Normal(x) => match x.as_json_payload() {
|
|
1024
|
-
Ok(v) => Ok(ActExitValue::Normal(v)),
|
|
1025
|
-
Err(e) => Err(ActivityError::NonRetryable(e)),
|
|
1026
|
-
},
|
|
1027
|
-
}
|
|
1028
|
-
})
|
|
1029
|
-
})
|
|
1030
|
-
.boxed(),
|
|
1031
|
-
Err(e) => async move { Err(ActivityError::NonRetryable(e.into())) }.boxed(),
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
|
-
Arc::new(wrapper)
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
/// Extra information attached to workflow updates
|
|
1039
|
-
#[derive(Clone)]
|
|
1040
|
-
pub struct UpdateInfo {
|
|
1041
|
-
/// The update's id, unique within the workflow
|
|
1042
|
-
pub update_id: String,
|
|
1043
|
-
/// Headers attached to the update
|
|
1044
|
-
pub headers: HashMap<String, Payload>,
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
/// Context for a workflow update
|
|
1048
|
-
pub struct UpdateContext {
|
|
1049
|
-
/// The workflow context, can be used to do normal workflow things inside the update handler
|
|
1050
|
-
pub wf_ctx: WfContext,
|
|
1051
|
-
/// Additional update info
|
|
1052
|
-
pub info: UpdateInfo,
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
struct UpdateFunctions {
|
|
1056
|
-
validator: BoxUpdateValidatorFn,
|
|
1057
|
-
handler: BoxUpdateHandlerFn,
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
impl UpdateFunctions {
|
|
1061
|
-
pub(crate) fn new<Arg, Res>(
|
|
1062
|
-
v: impl IntoUpdateValidatorFunc<Arg> + Sized,
|
|
1063
|
-
h: impl IntoUpdateHandlerFunc<Arg, Res> + Sized,
|
|
1064
|
-
) -> Self {
|
|
1065
|
-
Self {
|
|
1066
|
-
validator: v.into_update_validator_fn(),
|
|
1067
|
-
handler: h.into_update_handler_fn(),
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
type BoxUpdateValidatorFn = Box<dyn Fn(&UpdateInfo, &Payload) -> Result<(), anyhow::Error> + Send>;
|
|
1073
|
-
/// Closures / functions which can be turned into update validation functions implement this trait
|
|
1074
|
-
pub trait IntoUpdateValidatorFunc<Arg> {
|
|
1075
|
-
/// Consume the closure/fn pointer and turn it into an update validator
|
|
1076
|
-
fn into_update_validator_fn(self) -> BoxUpdateValidatorFn;
|
|
1077
|
-
}
|
|
1078
|
-
impl<A, F> IntoUpdateValidatorFunc<A> for F
|
|
1079
|
-
where
|
|
1080
|
-
A: FromJsonPayloadExt + Send,
|
|
1081
|
-
F: (for<'a> Fn(&'a UpdateInfo, A) -> Result<(), anyhow::Error>) + Send + 'static,
|
|
1082
|
-
{
|
|
1083
|
-
fn into_update_validator_fn(self) -> BoxUpdateValidatorFn {
|
|
1084
|
-
let wrapper = move |ctx: &UpdateInfo, input: &Payload| match A::from_json_payload(input) {
|
|
1085
|
-
Ok(deser) => (self)(ctx, deser),
|
|
1086
|
-
Err(e) => Err(e.into()),
|
|
1087
|
-
};
|
|
1088
|
-
Box::new(wrapper)
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
type BoxUpdateHandlerFn = Box<
|
|
1092
|
-
dyn FnMut(UpdateContext, &Payload) -> BoxFuture<'static, Result<Payload, anyhow::Error>> + Send,
|
|
1093
|
-
>;
|
|
1094
|
-
/// Closures / functions which can be turned into update handler functions implement this trait
|
|
1095
|
-
pub trait IntoUpdateHandlerFunc<Arg, Res> {
|
|
1096
|
-
/// Consume the closure/fn pointer and turn it into an update handler
|
|
1097
|
-
fn into_update_handler_fn(self) -> BoxUpdateHandlerFn;
|
|
1098
|
-
}
|
|
1099
|
-
impl<A, F, Rf, R> IntoUpdateHandlerFunc<A, R> for F
|
|
1100
|
-
where
|
|
1101
|
-
A: FromJsonPayloadExt + Send,
|
|
1102
|
-
F: (FnMut(UpdateContext, A) -> Rf) + Send + 'static,
|
|
1103
|
-
Rf: Future<Output = Result<R, anyhow::Error>> + Send + 'static,
|
|
1104
|
-
R: AsJsonPayloadExt,
|
|
1105
|
-
{
|
|
1106
|
-
fn into_update_handler_fn(mut self) -> BoxUpdateHandlerFn {
|
|
1107
|
-
let wrapper = move |ctx: UpdateContext, input: &Payload| match A::from_json_payload(input) {
|
|
1108
|
-
Ok(deser) => (self)(ctx, deser)
|
|
1109
|
-
.map(|r| r.and_then(|r| r.as_json_payload()))
|
|
1110
|
-
.boxed(),
|
|
1111
|
-
Err(e) => async move { Err(e.into()) }.boxed(),
|
|
1112
|
-
};
|
|
1113
|
-
Box::new(wrapper)
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
1299
|
/// Attempts to turn caught panics into something printable
|
|
1118
1300
|
fn panic_formatter(panic: Box<dyn Any>) -> Box<dyn Display> {
|
|
1119
1301
|
_panic_formatter::<&str>(panic)
|
|
@@ -1149,3 +1331,138 @@ impl Display for EndPrintingAttempts {
|
|
|
1149
1331
|
impl PrintablePanicType for EndPrintingAttempts {
|
|
1150
1332
|
type NextType = EndPrintingAttempts;
|
|
1151
1333
|
}
|
|
1334
|
+
|
|
1335
|
+
#[cfg(test)]
|
|
1336
|
+
mod tests {
|
|
1337
|
+
use super::*;
|
|
1338
|
+
use temporalio_macros::{activities, workflow, workflow_methods};
|
|
1339
|
+
|
|
1340
|
+
struct MyActivities {}
|
|
1341
|
+
|
|
1342
|
+
#[activities]
|
|
1343
|
+
impl MyActivities {
|
|
1344
|
+
#[activity]
|
|
1345
|
+
async fn my_activity(_ctx: ActivityContext) -> Result<(), ActivityError> {
|
|
1346
|
+
Ok(())
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
#[activity]
|
|
1350
|
+
async fn takes_self(
|
|
1351
|
+
self: Arc<Self>,
|
|
1352
|
+
_ctx: ActivityContext,
|
|
1353
|
+
_: String,
|
|
1354
|
+
) -> Result<(), ActivityError> {
|
|
1355
|
+
Ok(())
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
#[test]
|
|
1360
|
+
fn test_activity_registration() {
|
|
1361
|
+
let act_instance = MyActivities {};
|
|
1362
|
+
let _ = WorkerOptions::new("task_q").register_activities(act_instance);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// Compile-only test for workflow context invocation
|
|
1366
|
+
#[allow(unused, clippy::diverging_sub_expression)]
|
|
1367
|
+
fn test_activity_via_workflow_context() {
|
|
1368
|
+
let wf_ctx: WorkflowContext<MyWorkflow> = unimplemented!();
|
|
1369
|
+
wf_ctx.start_activity(MyActivities::my_activity, (), ActivityOptions::default());
|
|
1370
|
+
wf_ctx.start_activity(
|
|
1371
|
+
MyActivities::takes_self,
|
|
1372
|
+
"Hi".to_owned(),
|
|
1373
|
+
ActivityOptions::default(),
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Compile-only test for direct invocation via .run()
|
|
1378
|
+
#[allow(dead_code, unreachable_code, unused, clippy::diverging_sub_expression)]
|
|
1379
|
+
async fn test_activity_direct_invocation() {
|
|
1380
|
+
let ctx: ActivityContext = unimplemented!();
|
|
1381
|
+
let _result = MyActivities::my_activity.run(ctx).await;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
#[workflow]
|
|
1385
|
+
struct MyWorkflow {
|
|
1386
|
+
counter: u32,
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
#[allow(dead_code)]
|
|
1390
|
+
#[workflow_methods]
|
|
1391
|
+
impl MyWorkflow {
|
|
1392
|
+
#[init]
|
|
1393
|
+
fn new(_ctx: &WorkflowContextView, _input: String) -> Self {
|
|
1394
|
+
Self { counter: 0 }
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
#[run]
|
|
1398
|
+
async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
|
|
1399
|
+
Ok(format!("Counter: {}", ctx.state(|s| s.counter)))
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
#[signal(name = "increment")]
|
|
1403
|
+
fn increment_counter(&mut self, _ctx: &mut SyncWorkflowContext<Self>, amount: u32) {
|
|
1404
|
+
self.counter += amount;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
#[signal]
|
|
1408
|
+
async fn async_signal(_ctx: &mut WorkflowContext<Self>) {}
|
|
1409
|
+
|
|
1410
|
+
#[query]
|
|
1411
|
+
fn get_counter(&self, _ctx: &WorkflowContextView) -> u32 {
|
|
1412
|
+
self.counter
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
#[update(name = "double")]
|
|
1416
|
+
fn double_counter(&mut self, _ctx: &mut SyncWorkflowContext<Self>) -> u32 {
|
|
1417
|
+
self.counter *= 2;
|
|
1418
|
+
self.counter
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
#[update]
|
|
1422
|
+
async fn async_update(_ctx: &mut WorkflowContext<Self>, val: i32) -> i32 {
|
|
1423
|
+
val * 2
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
#[test]
|
|
1428
|
+
fn test_workflow_registration() {
|
|
1429
|
+
let _ = WorkerOptions::new("task_q").register_workflow::<MyWorkflow>();
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
fn default_identity() -> String {
|
|
1433
|
+
format!(
|
|
1434
|
+
"{}@{}",
|
|
1435
|
+
std::process::id(),
|
|
1436
|
+
gethostname::gethostname().to_string_lossy()
|
|
1437
|
+
)
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
#[rstest::rstest]
|
|
1441
|
+
#[case::default_when_none_provided(None, "", Some(default_identity()))]
|
|
1442
|
+
#[case::connection_identity_preserved(None, "conn-identity", None)]
|
|
1443
|
+
#[case::worker_override_takes_precedence(
|
|
1444
|
+
Some("worker-identity"),
|
|
1445
|
+
"conn-identity",
|
|
1446
|
+
Some("worker-identity".into())
|
|
1447
|
+
)]
|
|
1448
|
+
#[case::worker_override_with_empty_connection(
|
|
1449
|
+
Some("worker-identity"),
|
|
1450
|
+
"",
|
|
1451
|
+
Some("worker-identity".into())
|
|
1452
|
+
)]
|
|
1453
|
+
#[test]
|
|
1454
|
+
fn client_identity_resolution(
|
|
1455
|
+
#[case] worker_override: Option<&str>,
|
|
1456
|
+
#[case] connection_identity: &str,
|
|
1457
|
+
#[case] expected: Option<String>,
|
|
1458
|
+
) {
|
|
1459
|
+
let opts = WorkerOptions::new("task_q")
|
|
1460
|
+
.task_types(WorkerTaskTypes::activity_only())
|
|
1461
|
+
.maybe_client_identity_override(worker_override.map(|s| s.to_owned()))
|
|
1462
|
+
.build();
|
|
1463
|
+
let config = opts
|
|
1464
|
+
.to_core_options("ns".into(), connection_identity.into())
|
|
1465
|
+
.unwrap();
|
|
1466
|
+
assert_eq!(config.client_identity_override, expected);
|
|
1467
|
+
}
|
|
1468
|
+
}
|