@temporalio/core-bridge 1.15.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +172 -70
- package/lib/native.d.ts +1 -1
- package/package.json +2 -2
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/per-pr.yml +6 -6
- package/sdk-core/AGENTS.md +41 -30
- package/sdk-core/Cargo.toml +3 -0
- package/sdk-core/README.md +15 -9
- package/sdk-core/crates/client/Cargo.toml +4 -0
- package/sdk-core/crates/client/README.md +139 -0
- package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
- package/sdk-core/crates/client/src/callback_based.rs +7 -0
- package/sdk-core/crates/client/src/errors.rs +294 -0
- package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
- package/sdk-core/crates/client/src/lib.rs +920 -1326
- package/sdk-core/crates/client/src/metrics.rs +24 -33
- package/sdk-core/crates/client/src/options_structs.rs +457 -0
- package/sdk-core/crates/client/src/replaceable.rs +5 -4
- package/sdk-core/crates/client/src/request_extensions.rs +8 -9
- package/sdk-core/crates/client/src/retry.rs +99 -54
- package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
- package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
- package/sdk-core/crates/common/Cargo.toml +61 -2
- package/sdk-core/crates/common/build.rs +742 -12
- package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
- package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
- package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
- package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
- package/sdk-core/crates/common/src/activity_definition.rs +20 -0
- package/sdk-core/crates/common/src/data_converters.rs +770 -0
- package/sdk-core/crates/common/src/envconfig.rs +5 -0
- package/sdk-core/crates/common/src/lib.rs +15 -211
- package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
- package/sdk-core/crates/common/src/priority.rs +110 -0
- package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
- package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
- package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
- package/sdk-core/crates/common/src/protos/mod.rs +122 -27
- package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
- package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
- package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
- package/sdk-core/crates/common/src/telemetry.rs +264 -4
- package/sdk-core/crates/common/src/worker.rs +68 -603
- package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
- package/sdk-core/crates/macros/Cargo.toml +5 -1
- package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
- package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
- package/sdk-core/crates/macros/src/lib.rs +138 -512
- package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
- package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
- package/sdk-core/crates/sdk/Cargo.toml +19 -6
- package/sdk-core/crates/sdk/README.md +415 -0
- package/sdk-core/crates/sdk/src/activities.rs +417 -0
- package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
- package/sdk-core/crates/sdk/src/lib.rs +757 -442
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
- package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
- package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
- package/sdk-core/crates/sdk/src/workflows.rs +711 -0
- package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
- package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
- package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
- package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
- package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
- package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
- package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
- package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
- package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
- package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
- package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
- package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
- package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
- package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
- package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
- package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
- package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
- package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
- package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
- package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
- package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
- package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
- package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
- package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
- package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
- package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
- package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
- package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
- package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
- package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
- package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
- package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
- package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
- package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
- package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
- package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
- package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
- package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
- package/sdk-core/rustfmt.toml +2 -1
- package/src/client.rs +205 -318
- package/src/metrics.rs +22 -30
- package/src/runtime.rs +4 -5
- package/src/worker.rs +16 -19
- package/ts/native.ts +1 -1
- package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
- package/sdk-core/crates/common/src/errors.rs +0 -85
- package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
- package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
- package/sdk-core/crates/sdk/src/app_data.rs +0 -37
- package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
|
@@ -8,40 +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, RuntimeOptions};
|
|
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};
|
|
23
|
+
//!
|
|
24
|
+
//! struct MyActivities;
|
|
25
|
+
//!
|
|
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
|
+
//! }
|
|
18
36
|
//!
|
|
19
37
|
//! #[tokio::main]
|
|
20
38
|
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
21
|
-
//! let
|
|
22
|
-
//!
|
|
39
|
+
//! let connection_options =
|
|
40
|
+
//! ConnectionOptions::new(Url::from_str("http://localhost:7233")?).build();
|
|
23
41
|
//! let telemetry_options = TelemetryOptions::builder().build();
|
|
24
|
-
//! let runtime_options = RuntimeOptions::builder()
|
|
42
|
+
//! let runtime_options = RuntimeOptions::builder()
|
|
43
|
+
//! .telemetry_options(telemetry_options)
|
|
44
|
+
//! .build()?;
|
|
25
45
|
//! let runtime = CoreRuntime::new_assume_tokio(runtime_options)?;
|
|
26
46
|
//!
|
|
27
|
-
//! let
|
|
47
|
+
//! let connection = Connection::connect(connection_options).await?;
|
|
48
|
+
//! let client = Client::new(connection, ClientOptions::new("my_namespace").build())?;
|
|
28
49
|
//!
|
|
29
|
-
//! let
|
|
30
|
-
//! .namespace("default")
|
|
31
|
-
//! .task_queue("task_queue")
|
|
50
|
+
//! let worker_options = WorkerOptions::new("task_queue")
|
|
32
51
|
//! .task_types(WorkerTaskTypes::activity_only())
|
|
33
|
-
//! .
|
|
34
|
-
//!
|
|
35
|
-
//!
|
|
36
|
-
//!
|
|
37
|
-
//!
|
|
38
|
-
//!
|
|
39
|
-
//!
|
|
40
|
-
//!
|
|
41
|
-
//!
|
|
42
|
-
//!
|
|
43
|
-
//! );
|
|
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();
|
|
44
62
|
//!
|
|
63
|
+
//! let mut worker = Worker::new(&runtime, client, worker_options)?;
|
|
45
64
|
//! worker.run().await?;
|
|
46
65
|
//!
|
|
47
66
|
//! Ok(())
|
|
@@ -50,48 +69,70 @@
|
|
|
50
69
|
|
|
51
70
|
#[macro_use]
|
|
52
71
|
extern crate tracing;
|
|
72
|
+
extern crate self as temporalio_sdk;
|
|
53
73
|
|
|
54
|
-
mod
|
|
55
|
-
mod app_data;
|
|
74
|
+
pub mod activities;
|
|
56
75
|
pub mod interceptors;
|
|
57
76
|
mod workflow_context;
|
|
58
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;
|
|
59
97
|
|
|
60
|
-
pub use activity_context::ActContext;
|
|
61
98
|
pub use temporalio_client::Namespace;
|
|
62
|
-
use tracing::{Instrument, Span, field};
|
|
63
99
|
pub use workflow_context::{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
100
|
+
ActivityExecutionError, ActivityOptions, BaseWorkflowContext, CancellableFuture, ChildWorkflow,
|
|
101
|
+
ChildWorkflowOptions, LocalActivityOptions, NexusOperationOptions, ParentWorkflowInfo,
|
|
102
|
+
PendingChildWorkflow, RootWorkflowInfo, Signal, SignalData, SignalWorkflowOptions,
|
|
103
|
+
StartedChildWorkflow, SyncWorkflowContext, TimerOptions, WorkflowContext, WorkflowContextView,
|
|
67
104
|
};
|
|
68
105
|
|
|
69
106
|
use crate::{
|
|
107
|
+
activities::{
|
|
108
|
+
ActivityContext, ActivityDefinitions, ActivityError, ActivityImplementer,
|
|
109
|
+
ExecutableActivity,
|
|
110
|
+
},
|
|
70
111
|
interceptors::WorkerInterceptor,
|
|
71
112
|
workflow_context::{ChildWfCommon, NexusUnblockData, StartedNexusOperation},
|
|
113
|
+
workflows::{WorkflowDefinitions, WorkflowImplementation, WorkflowImplementer},
|
|
72
114
|
};
|
|
73
115
|
use anyhow::{Context, anyhow, bail};
|
|
74
|
-
use
|
|
75
|
-
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::BoxFuture};
|
|
76
|
-
use serde::Serialize;
|
|
116
|
+
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
|
77
117
|
use std::{
|
|
78
118
|
any::{Any, TypeId},
|
|
79
119
|
cell::RefCell,
|
|
80
|
-
collections::HashMap,
|
|
120
|
+
collections::{HashMap, HashSet},
|
|
81
121
|
fmt::{Debug, Display, Formatter},
|
|
82
122
|
future::Future,
|
|
83
123
|
panic::AssertUnwindSafe,
|
|
84
124
|
sync::Arc,
|
|
85
125
|
time::Duration,
|
|
86
126
|
};
|
|
87
|
-
use temporalio_client::{
|
|
127
|
+
use temporalio_client::{Client, NamespacedClient};
|
|
88
128
|
use temporalio_common::{
|
|
89
|
-
|
|
90
|
-
|
|
129
|
+
ActivityDefinition, WorkflowDefinition,
|
|
130
|
+
data_converters::{DataConverter, SerializationContextData},
|
|
131
|
+
payload_visitor::{decode_payloads, encode_payloads},
|
|
91
132
|
protos::{
|
|
92
133
|
TaskToken,
|
|
93
134
|
coresdk::{
|
|
94
|
-
ActivityTaskCompletion, AsJsonPayloadExt,
|
|
135
|
+
ActivityTaskCompletion, AsJsonPayloadExt,
|
|
95
136
|
activity_result::{ActivityExecutionResult, ActivityResolution},
|
|
96
137
|
activity_task::{ActivityTask, activity_task},
|
|
97
138
|
child_workflow::ChildWorkflowResult,
|
|
@@ -113,8 +154,12 @@ use temporalio_common::{
|
|
|
113
154
|
failure::v1::{Failure, failure},
|
|
114
155
|
},
|
|
115
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,
|
|
116
162
|
};
|
|
117
|
-
use temporalio_sdk_core::Url;
|
|
118
163
|
use tokio::{
|
|
119
164
|
sync::{
|
|
120
165
|
Notify,
|
|
@@ -125,40 +170,271 @@ use tokio::{
|
|
|
125
170
|
};
|
|
126
171
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
127
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
|
+
}
|
|
128
326
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/// for the Rust SDK.
|
|
133
|
-
pub fn sdk_client_options(
|
|
134
|
-
url: impl Into<Url>,
|
|
135
|
-
) -> ClientOptionsBuilder<impl client_options_builder::IsComplete> {
|
|
136
|
-
ClientOptions::builder()
|
|
137
|
-
.target_url(url)
|
|
138
|
-
.client_name("temporal-rust".to_string())
|
|
139
|
-
.client_version(VERSION.to_string())
|
|
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())
|
|
140
330
|
}
|
|
141
331
|
|
|
142
|
-
|
|
143
|
-
///
|
|
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
|
+
}
|
|
377
|
+
|
|
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
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
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].
|
|
144
420
|
pub struct Worker {
|
|
145
421
|
common: CommonWorker,
|
|
146
422
|
workflow_half: WorkflowHalf,
|
|
147
423
|
activity_half: ActivityHalf,
|
|
148
|
-
app_data: Option<AppData>,
|
|
149
424
|
}
|
|
150
425
|
|
|
151
426
|
struct CommonWorker {
|
|
152
|
-
worker: Arc<
|
|
427
|
+
worker: Arc<CoreWorker>,
|
|
153
428
|
task_queue: String,
|
|
154
429
|
worker_interceptor: Option<Box<dyn WorkerInterceptor>>,
|
|
430
|
+
data_converter: DataConverter,
|
|
155
431
|
}
|
|
156
432
|
|
|
433
|
+
#[derive(Default)]
|
|
157
434
|
struct WorkflowHalf {
|
|
158
435
|
/// Maps run id to cached workflow state
|
|
159
436
|
workflows: RefCell<HashMap<String, WorkflowData>>,
|
|
160
|
-
|
|
161
|
-
workflow_fns: RefCell<HashMap<String, WorkflowFunction>>,
|
|
437
|
+
workflow_definitions: WorkflowDefinitions,
|
|
162
438
|
workflow_removed_from_map: Notify,
|
|
163
439
|
}
|
|
164
440
|
struct WorkflowData {
|
|
@@ -171,31 +447,71 @@ struct WorkflowFutureHandle<F: Future<Output = Result<WorkflowResult<Payload>, J
|
|
|
171
447
|
run_id: String,
|
|
172
448
|
}
|
|
173
449
|
|
|
450
|
+
#[derive(Default)]
|
|
174
451
|
struct ActivityHalf {
|
|
175
452
|
/// Maps activity type to the function for executing activities of that type
|
|
176
|
-
|
|
453
|
+
activities: ActivityDefinitions,
|
|
177
454
|
task_tokens_to_cancels: HashMap<TaskToken, CancellationToken>,
|
|
178
455
|
}
|
|
179
456
|
|
|
180
457
|
impl Worker {
|
|
181
|
-
/// Create a new
|
|
182
|
-
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 {
|
|
183
500
|
Self {
|
|
184
501
|
common: CommonWorker {
|
|
502
|
+
task_queue: worker.get_config().task_queue.clone(),
|
|
185
503
|
worker,
|
|
186
|
-
task_queue: task_queue.into(),
|
|
187
504
|
worker_interceptor: None,
|
|
505
|
+
data_converter,
|
|
188
506
|
},
|
|
189
507
|
workflow_half: WorkflowHalf {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
workflow_removed_from_map: Default::default(),
|
|
508
|
+
workflow_definitions: workflows,
|
|
509
|
+
..Default::default()
|
|
193
510
|
},
|
|
194
511
|
activity_half: ActivityHalf {
|
|
195
|
-
|
|
196
|
-
|
|
512
|
+
activities,
|
|
513
|
+
..Default::default()
|
|
197
514
|
},
|
|
198
|
-
app_data: Some(Default::default()),
|
|
199
515
|
}
|
|
200
516
|
}
|
|
201
517
|
|
|
@@ -204,58 +520,67 @@ impl Worker {
|
|
|
204
520
|
&self.common.task_queue
|
|
205
521
|
}
|
|
206
522
|
|
|
207
|
-
/// Return a handle that can be used to initiate shutdown.
|
|
208
|
-
///
|
|
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.
|
|
209
525
|
pub fn shutdown_handle(&self) -> impl Fn() + use<> {
|
|
210
526
|
let w = self.common.worker.clone();
|
|
211
527
|
move || w.initiate_shutdown()
|
|
212
528
|
}
|
|
213
529
|
|
|
214
|
-
///
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
225
547
|
}
|
|
226
548
|
|
|
227
|
-
///
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
) {
|
|
234
|
-
self.activity_half.activity_fns.insert(
|
|
235
|
-
activity_type.into(),
|
|
236
|
-
ActivityFunction {
|
|
237
|
-
act_func: act_function.into_activity_fn(),
|
|
238
|
-
},
|
|
239
|
-
);
|
|
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
|
|
240
555
|
}
|
|
241
556
|
|
|
242
|
-
///
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
245
570
|
}
|
|
246
571
|
|
|
247
572
|
/// Runs the worker. Eventually resolves after the worker has been explicitly shut down,
|
|
248
573
|
/// or may return early with an error in the event of some unresolvable problem.
|
|
249
574
|
pub async fn run(&mut self) -> Result<(), anyhow::Error> {
|
|
250
575
|
let shutdown_token = CancellationToken::new();
|
|
251
|
-
let (common, wf_half, act_half
|
|
252
|
-
let safe_app_data = Arc::new(
|
|
253
|
-
app_data
|
|
254
|
-
.take()
|
|
255
|
-
.ok_or_else(|| anyhow!("app_data should exist on run"))?,
|
|
256
|
-
);
|
|
576
|
+
let (common, wf_half, act_half) = self.split_apart();
|
|
257
577
|
let (wf_future_tx, wf_future_rx) = unbounded_channel();
|
|
258
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
|
+
|
|
259
584
|
let wf_future_joiner = async {
|
|
260
585
|
UnboundedReceiverStream::new(wf_future_rx)
|
|
261
586
|
.map(Result::<_, anyhow::Error>::Ok)
|
|
@@ -267,7 +592,14 @@ impl Worker {
|
|
|
267
592
|
}| {
|
|
268
593
|
let wf_half = &*wf_half;
|
|
269
594
|
async move {
|
|
270
|
-
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
|
+
}
|
|
271
603
|
debug!(run_id=%run_id, "Removing workflow from cache");
|
|
272
604
|
wf_half.workflows.borrow_mut().remove(&run_id);
|
|
273
605
|
wf_half.workflow_removed_from_map.notify_one();
|
|
@@ -281,7 +613,13 @@ impl Worker {
|
|
|
281
613
|
let wf_completion_processor = async {
|
|
282
614
|
UnboundedReceiverStream::new(completions_rx)
|
|
283
615
|
.map(Ok)
|
|
284
|
-
.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;
|
|
285
623
|
if let Some(ref i) = common.worker_interceptor {
|
|
286
624
|
i.on_workflow_activation_completion(&completion).await;
|
|
287
625
|
}
|
|
@@ -292,72 +630,89 @@ impl Worker {
|
|
|
292
630
|
.context("Workflow completions processor encountered an error")
|
|
293
631
|
};
|
|
294
632
|
tokio::try_join!(
|
|
295
|
-
// Workflow
|
|
633
|
+
// Workflow-related tasks run inside LocalSet (allows !Send futures)
|
|
296
634
|
async {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
}
|
|
301
670
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
314
|
-
.await?
|
|
315
|
-
&& wf_future_tx.send(wf_fut).is_err()
|
|
316
|
-
{
|
|
317
|
-
panic!("Receive half of completion processor channel cannot be dropped");
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
// Tell still-alive workflows to evict themselves
|
|
321
|
-
shutdown_token.cancel();
|
|
322
|
-
// It's important to drop these so the future and completion processors will
|
|
323
|
-
// terminate.
|
|
324
|
-
drop(wf_future_tx);
|
|
325
|
-
drop(completions_tx);
|
|
326
|
-
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
|
|
327
682
|
},
|
|
328
683
|
// Only poll on the activity queue if activity functions have been registered. This
|
|
329
684
|
// makes tests which use mocks dramatically more manageable.
|
|
330
685
|
async {
|
|
331
|
-
if !act_half.
|
|
686
|
+
if !act_half.activities.is_empty() {
|
|
332
687
|
loop {
|
|
333
688
|
let activity = common.worker.poll_activity_task().await;
|
|
334
689
|
if matches!(activity, Err(PollError::ShutDown)) {
|
|
335
690
|
break;
|
|
336
691
|
}
|
|
692
|
+
let mut activity = activity?;
|
|
693
|
+
decode_payloads(
|
|
694
|
+
&mut activity,
|
|
695
|
+
common.data_converter.codec(),
|
|
696
|
+
&SerializationContextData::Activity,
|
|
697
|
+
)
|
|
698
|
+
.await;
|
|
337
699
|
act_half.activity_task_handler(
|
|
338
700
|
common.worker.clone(),
|
|
339
|
-
safe_app_data.clone(),
|
|
340
701
|
common.task_queue.clone(),
|
|
341
|
-
|
|
702
|
+
common.data_converter.clone(),
|
|
703
|
+
activity,
|
|
342
704
|
)?;
|
|
343
705
|
}
|
|
344
706
|
};
|
|
345
707
|
Result::<_, anyhow::Error>::Ok(())
|
|
346
708
|
},
|
|
347
|
-
wf_future_joiner,
|
|
348
709
|
wf_completion_processor,
|
|
349
710
|
)?;
|
|
350
711
|
|
|
351
|
-
debug!("Polling loops exited");
|
|
352
712
|
if let Some(i) = self.common.worker_interceptor.as_ref() {
|
|
353
713
|
i.on_shutdown(self);
|
|
354
714
|
}
|
|
355
715
|
self.common.worker.shutdown().await;
|
|
356
|
-
debug!("Worker shutdown complete");
|
|
357
|
-
self.app_data = Some(
|
|
358
|
-
Arc::try_unwrap(safe_app_data)
|
|
359
|
-
.map_err(|_| anyhow!("some references of AppData exist on worker shutdown"))?,
|
|
360
|
-
);
|
|
361
716
|
Ok(())
|
|
362
717
|
}
|
|
363
718
|
|
|
@@ -369,7 +724,7 @@ impl Worker {
|
|
|
369
724
|
/// Turns this rust worker into a new worker with all the same workflows and activities
|
|
370
725
|
/// registered, but with a new underlying core worker. Can be used to swap the worker for
|
|
371
726
|
/// a replay worker, change task queues, etc.
|
|
372
|
-
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>) {
|
|
373
728
|
self.common.worker = new_core_worker;
|
|
374
729
|
}
|
|
375
730
|
|
|
@@ -379,19 +734,21 @@ impl Worker {
|
|
|
379
734
|
self.workflow_half.workflows.borrow().len()
|
|
380
735
|
}
|
|
381
736
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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) {
|
|
390
748
|
(
|
|
391
749
|
&mut self.common,
|
|
392
750
|
&mut self.workflow_half,
|
|
393
751
|
&mut self.activity_half,
|
|
394
|
-
&mut self.app_data,
|
|
395
752
|
)
|
|
396
753
|
}
|
|
397
754
|
}
|
|
@@ -421,13 +778,33 @@ impl WorkflowHalf {
|
|
|
421
778
|
Some(Variant::InitializeWorkflow(ref mut sw)) => Some(sw),
|
|
422
779
|
_ => None,
|
|
423
780
|
}) {
|
|
424
|
-
let workflow_type =
|
|
781
|
+
let workflow_type = sw.workflow_type.clone();
|
|
782
|
+
let payload_converter = common.data_converter.payload_converter().clone();
|
|
425
783
|
let (wff, activations) = {
|
|
426
|
-
let
|
|
427
|
-
|
|
428
|
-
|
|
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 {
|
|
429
807
|
warn!("Workflow type {workflow_type} not found");
|
|
430
|
-
|
|
431
808
|
completions_tx
|
|
432
809
|
.send(WorkflowActivationCompletion::fail(
|
|
433
810
|
run_id,
|
|
@@ -436,22 +813,19 @@ impl WorkflowHalf {
|
|
|
436
813
|
))
|
|
437
814
|
.expect("Completion channel intact");
|
|
438
815
|
return Ok(None);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
wf_function.start_workflow(
|
|
442
|
-
common.worker.get_config().namespace.clone(),
|
|
443
|
-
common.task_queue.clone(),
|
|
444
|
-
std::mem::take(sw),
|
|
445
|
-
completions_tx.clone(),
|
|
446
|
-
)
|
|
816
|
+
}
|
|
447
817
|
};
|
|
448
|
-
|
|
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 {
|
|
449
823
|
tokio::select! {
|
|
450
824
|
r = wff.fuse() => r,
|
|
451
825
|
// TODO: This probably shouldn't abort early, as it could cause an in-progress
|
|
452
826
|
// complete to abort. Send synthetic remove activation
|
|
453
827
|
_ = shutdown_token.cancelled() => {
|
|
454
|
-
|
|
828
|
+
Err(WorkflowTermination::Evicted)
|
|
455
829
|
}
|
|
456
830
|
}
|
|
457
831
|
});
|
|
@@ -515,23 +889,19 @@ impl ActivityHalf {
|
|
|
515
889
|
/// Spawns off a task to handle the provided activity task
|
|
516
890
|
fn activity_task_handler(
|
|
517
891
|
&mut self,
|
|
518
|
-
worker: Arc<
|
|
519
|
-
app_data: Arc<AppData>,
|
|
892
|
+
worker: Arc<CoreWorker>,
|
|
520
893
|
task_queue: String,
|
|
894
|
+
data_converter: DataConverter,
|
|
521
895
|
activity: ActivityTask,
|
|
522
896
|
) -> Result<(), anyhow::Error> {
|
|
523
897
|
match activity.variant {
|
|
524
898
|
Some(activity_task::Variant::Start(start)) => {
|
|
525
|
-
let act_fn = self
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
start.activity_type
|
|
532
|
-
)
|
|
533
|
-
})?
|
|
534
|
-
.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
|
+
})?;
|
|
535
905
|
let span = info_span!(
|
|
536
906
|
"RunActivity",
|
|
537
907
|
"otel.name" = format!("RunActivity:{}", start.activity_type),
|
|
@@ -545,23 +915,18 @@ impl ActivityHalf {
|
|
|
545
915
|
self.task_tokens_to_cancels
|
|
546
916
|
.insert(task_token.clone().into(), ct.clone());
|
|
547
917
|
|
|
548
|
-
let (ctx,
|
|
549
|
-
worker.clone(),
|
|
550
|
-
|
|
551
|
-
ct,
|
|
552
|
-
task_queue,
|
|
553
|
-
task_token.clone(),
|
|
554
|
-
start,
|
|
555
|
-
);
|
|
918
|
+
let (ctx, args) =
|
|
919
|
+
ActivityContext::new(worker.clone(), ct, task_queue, task_token.clone(), start);
|
|
920
|
+
let codec_data_converter = data_converter.clone();
|
|
556
921
|
|
|
557
922
|
tokio::spawn(async move {
|
|
558
923
|
let act_fut = async move {
|
|
559
|
-
if let Some(info) = &ctx.
|
|
924
|
+
if let Some(info) = &ctx.info().workflow_execution {
|
|
560
925
|
Span::current()
|
|
561
926
|
.record("temporalWorkflowID", &info.workflow_id)
|
|
562
927
|
.record("temporalRunID", &info.run_id);
|
|
563
928
|
}
|
|
564
|
-
(act_fn
|
|
929
|
+
(act_fn)(args, data_converter, ctx).await
|
|
565
930
|
}
|
|
566
931
|
.instrument(span);
|
|
567
932
|
let output = AssertUnwindSafe(act_fut).catch_unwind().await;
|
|
@@ -570,16 +935,16 @@ impl ActivityHalf {
|
|
|
570
935
|
format!("Activity function panicked: {}", panic_formatter(e)),
|
|
571
936
|
true,
|
|
572
937
|
)),
|
|
573
|
-
Ok(Ok(
|
|
574
|
-
Ok(Ok(ActExitValue::WillCompleteAsync)) => {
|
|
575
|
-
ActivityExecutionResult::will_complete_async()
|
|
576
|
-
}
|
|
938
|
+
Ok(Ok(p)) => ActivityExecutionResult::ok(p),
|
|
577
939
|
Ok(Err(err)) => match err {
|
|
578
940
|
ActivityError::Retryable {
|
|
579
941
|
source,
|
|
580
942
|
explicit_delay,
|
|
581
943
|
} => ActivityExecutionResult::fail({
|
|
582
|
-
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
|
+
);
|
|
583
948
|
if let Some(d) = explicit_delay
|
|
584
949
|
&& let Some(failure::FailureInfo::ApplicationFailureInfo(fi)) =
|
|
585
950
|
f.failure_info.as_mut()
|
|
@@ -592,16 +957,27 @@ impl ActivityHalf {
|
|
|
592
957
|
ActivityExecutionResult::cancel_from_details(details)
|
|
593
958
|
}
|
|
594
959
|
ActivityError::NonRetryable(nre) => ActivityExecutionResult::fail(
|
|
595
|
-
Failure::application_failure_from_error(
|
|
960
|
+
Failure::application_failure_from_error(
|
|
961
|
+
anyhow::Error::from_boxed(nre),
|
|
962
|
+
true,
|
|
963
|
+
),
|
|
596
964
|
),
|
|
965
|
+
ActivityError::WillCompleteAsync => {
|
|
966
|
+
ActivityExecutionResult::will_complete_async()
|
|
967
|
+
}
|
|
597
968
|
},
|
|
598
969
|
};
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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?;
|
|
605
981
|
Ok::<_, anyhow::Error>(())
|
|
606
982
|
});
|
|
607
983
|
}
|
|
@@ -632,7 +1008,7 @@ enum UnblockEvent {
|
|
|
632
1008
|
}
|
|
633
1009
|
|
|
634
1010
|
/// Result of awaiting on a timer
|
|
635
|
-
#[derive(Debug, Copy, Clone)]
|
|
1011
|
+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
636
1012
|
pub enum TimerResult {
|
|
637
1013
|
/// The timer was cancelled
|
|
638
1014
|
Cancelled,
|
|
@@ -641,13 +1017,13 @@ pub enum TimerResult {
|
|
|
641
1017
|
}
|
|
642
1018
|
|
|
643
1019
|
/// Successful result of sending a signal to an external workflow
|
|
644
|
-
#[derive(Debug)]
|
|
1020
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
645
1021
|
pub struct SignalExternalOk;
|
|
646
1022
|
/// Result of awaiting on sending a signal to an external workflow
|
|
647
1023
|
pub type SignalExternalWfResult = Result<SignalExternalOk, Failure>;
|
|
648
1024
|
|
|
649
1025
|
/// Successful result of sending a cancel request to an external workflow
|
|
650
|
-
#[derive(Debug)]
|
|
1026
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
651
1027
|
pub struct CancelExternalOk;
|
|
652
1028
|
/// Result of awaiting on sending a cancel request to an external workflow
|
|
653
1029
|
pub type CancelExternalWfResult = Result<CancelExternalOk, Failure>;
|
|
@@ -836,8 +1212,6 @@ enum RustWfCmd {
|
|
|
836
1212
|
NewCmd(CommandCreateRequest),
|
|
837
1213
|
NewNonblockingCmd(workflow_command::Variant),
|
|
838
1214
|
SubscribeChildWorkflowCompletion(CommandSubscribeChildWorkflowCompletion),
|
|
839
|
-
SubscribeSignal(String, UnboundedSender<SignalData>),
|
|
840
|
-
RegisterUpdate(String, UpdateFunctions),
|
|
841
1215
|
SubscribeNexusOperationCompletion {
|
|
842
1216
|
seq: u32,
|
|
843
1217
|
unblocker: oneshot::Sender<UnblockEvent>,
|
|
@@ -854,81 +1228,61 @@ struct CommandSubscribeChildWorkflowCompletion {
|
|
|
854
1228
|
unblocker: oneshot::Sender<UnblockEvent>,
|
|
855
1229
|
}
|
|
856
1230
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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,
|
|
861
1246
|
|
|
862
|
-
/// The
|
|
863
|
-
|
|
864
|
-
|
|
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),
|
|
865
1258
|
}
|
|
866
1259
|
|
|
867
|
-
impl
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
O: Serialize,
|
|
872
|
-
{
|
|
873
|
-
fn from(wf_func: F) -> Self {
|
|
874
|
-
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))
|
|
875
1264
|
}
|
|
876
|
-
}
|
|
877
1265
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
where
|
|
882
|
-
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
883
|
-
Fut: Future<Output = Result<WfExitValue<O>, anyhow::Error>> + Send + 'static,
|
|
884
|
-
O: Serialize,
|
|
885
|
-
{
|
|
886
|
-
Self {
|
|
887
|
-
wf_func: Box::new(move |ctx: WfContext| {
|
|
888
|
-
(f)(ctx)
|
|
889
|
-
.map(|r| {
|
|
890
|
-
r.and_then(|r| {
|
|
891
|
-
Ok(match r {
|
|
892
|
-
WfExitValue::ContinueAsNew(b) => WfExitValue::ContinueAsNew(b),
|
|
893
|
-
WfExitValue::Cancelled => WfExitValue::Cancelled,
|
|
894
|
-
WfExitValue::Evicted => WfExitValue::Evicted,
|
|
895
|
-
WfExitValue::Normal(o) => WfExitValue::Normal(o.as_json_payload()?),
|
|
896
|
-
})
|
|
897
|
-
})
|
|
898
|
-
})
|
|
899
|
-
.boxed()
|
|
900
|
-
}),
|
|
901
|
-
}
|
|
1266
|
+
/// Construct a [WorkflowTermination::Failed] variant from any error.
|
|
1267
|
+
pub fn failed(err: impl Into<anyhow::Error>) -> Self {
|
|
1268
|
+
Self::Failed(err.into())
|
|
902
1269
|
}
|
|
903
1270
|
}
|
|
904
1271
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
#[derive(Debug, derive_more::From)]
|
|
910
|
-
pub enum WfExitValue<T> {
|
|
911
|
-
/// Continue the workflow as a new execution
|
|
912
|
-
#[from(ignore)]
|
|
913
|
-
ContinueAsNew(Box<ContinueAsNewWorkflowExecution>),
|
|
914
|
-
/// Confirm the workflow was cancelled (can be automatic in a more advanced iteration)
|
|
915
|
-
#[from(ignore)]
|
|
916
|
-
Cancelled,
|
|
917
|
-
/// The run was evicted
|
|
918
|
-
#[from(ignore)]
|
|
919
|
-
Evicted,
|
|
920
|
-
/// Finish with a result
|
|
921
|
-
Normal(T),
|
|
1272
|
+
impl From<anyhow::Error> for WorkflowTermination {
|
|
1273
|
+
fn from(err: anyhow::Error) -> Self {
|
|
1274
|
+
Self::Failed(err)
|
|
1275
|
+
}
|
|
922
1276
|
}
|
|
923
1277
|
|
|
924
|
-
impl
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
Self::ContinueAsNew(Box::new(can))
|
|
1278
|
+
impl From<ActivityExecutionError> for WorkflowTermination {
|
|
1279
|
+
fn from(value: ActivityExecutionError) -> Self {
|
|
1280
|
+
Self::failed(value)
|
|
928
1281
|
}
|
|
929
1282
|
}
|
|
930
1283
|
|
|
931
1284
|
/// Activity functions may return these values when exiting
|
|
1285
|
+
#[derive(Debug)]
|
|
932
1286
|
pub enum ActExitValue<T> {
|
|
933
1287
|
/// Completion requires an asynchronous callback
|
|
934
1288
|
WillCompleteAsync,
|
|
@@ -942,180 +1296,6 @@ impl<T: AsJsonPayloadExt> From<T> for ActExitValue<T> {
|
|
|
942
1296
|
}
|
|
943
1297
|
}
|
|
944
1298
|
|
|
945
|
-
type BoxActFn = Arc<
|
|
946
|
-
dyn Fn(ActContext, Payload) -> BoxFuture<'static, Result<ActExitValue<Payload>, ActivityError>>
|
|
947
|
-
+ Send
|
|
948
|
-
+ Sync,
|
|
949
|
-
>;
|
|
950
|
-
|
|
951
|
-
/// Container for user-defined activity functions
|
|
952
|
-
#[derive(Clone)]
|
|
953
|
-
pub struct ActivityFunction {
|
|
954
|
-
act_func: BoxActFn,
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
/// Returned as errors from activity functions
|
|
958
|
-
#[derive(Debug)]
|
|
959
|
-
pub enum ActivityError {
|
|
960
|
-
/// This error can be returned from activities to allow the explicit configuration of certain
|
|
961
|
-
/// error properties. It's also the default error type that arbitrary errors will be converted
|
|
962
|
-
/// into.
|
|
963
|
-
Retryable {
|
|
964
|
-
/// The underlying error
|
|
965
|
-
source: anyhow::Error,
|
|
966
|
-
/// If specified, the next retry (if there is one) will occur after this delay
|
|
967
|
-
explicit_delay: Option<Duration>,
|
|
968
|
-
},
|
|
969
|
-
/// Return this error to indicate your activity is cancelling
|
|
970
|
-
Cancelled {
|
|
971
|
-
/// Some data to save as the cancellation reason
|
|
972
|
-
details: Option<Payload>,
|
|
973
|
-
},
|
|
974
|
-
/// Return this error to indicate that your activity non-retryable
|
|
975
|
-
/// this is a transparent wrapper around anyhow Error so essentially any type of error
|
|
976
|
-
/// could be used here.
|
|
977
|
-
NonRetryable(anyhow::Error),
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
impl<E> From<E> for ActivityError
|
|
981
|
-
where
|
|
982
|
-
E: Into<anyhow::Error>,
|
|
983
|
-
{
|
|
984
|
-
fn from(source: E) -> Self {
|
|
985
|
-
Self::Retryable {
|
|
986
|
-
source: source.into(),
|
|
987
|
-
explicit_delay: None,
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
impl ActivityError {
|
|
993
|
-
/// Construct a cancelled error without details
|
|
994
|
-
pub fn cancelled() -> Self {
|
|
995
|
-
Self::Cancelled { details: None }
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
/// Closures / functions which can be turned into activity functions implement this trait
|
|
1000
|
-
pub trait IntoActivityFunc<Args, Res, Out> {
|
|
1001
|
-
/// Consume the closure or fn pointer and turned it into a boxed activity function
|
|
1002
|
-
fn into_activity_fn(self) -> BoxActFn;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
impl<A, Rf, R, O, F> IntoActivityFunc<A, Rf, O> for F
|
|
1006
|
-
where
|
|
1007
|
-
F: (Fn(ActContext, A) -> Rf) + Sync + Send + 'static,
|
|
1008
|
-
A: FromJsonPayloadExt + Send,
|
|
1009
|
-
Rf: Future<Output = Result<R, ActivityError>> + Send + 'static,
|
|
1010
|
-
R: Into<ActExitValue<O>>,
|
|
1011
|
-
O: AsJsonPayloadExt,
|
|
1012
|
-
{
|
|
1013
|
-
fn into_activity_fn(self) -> BoxActFn {
|
|
1014
|
-
let wrapper = move |ctx: ActContext, input: Payload| {
|
|
1015
|
-
// Some minor gymnastics are required to avoid needing to clone the function
|
|
1016
|
-
match A::from_json_payload(&input) {
|
|
1017
|
-
Ok(deser) => self(ctx, deser)
|
|
1018
|
-
.map(|r| {
|
|
1019
|
-
r.and_then(|r| {
|
|
1020
|
-
let exit_val: ActExitValue<O> = r.into();
|
|
1021
|
-
match exit_val {
|
|
1022
|
-
ActExitValue::WillCompleteAsync => {
|
|
1023
|
-
Ok(ActExitValue::WillCompleteAsync)
|
|
1024
|
-
}
|
|
1025
|
-
ActExitValue::Normal(x) => match x.as_json_payload() {
|
|
1026
|
-
Ok(v) => Ok(ActExitValue::Normal(v)),
|
|
1027
|
-
Err(e) => Err(ActivityError::NonRetryable(e)),
|
|
1028
|
-
},
|
|
1029
|
-
}
|
|
1030
|
-
})
|
|
1031
|
-
})
|
|
1032
|
-
.boxed(),
|
|
1033
|
-
Err(e) => async move { Err(ActivityError::NonRetryable(e.into())) }.boxed(),
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
Arc::new(wrapper)
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
/// Extra information attached to workflow updates
|
|
1041
|
-
#[derive(Clone)]
|
|
1042
|
-
pub struct UpdateInfo {
|
|
1043
|
-
/// The update's id, unique within the workflow
|
|
1044
|
-
pub update_id: String,
|
|
1045
|
-
/// Headers attached to the update
|
|
1046
|
-
pub headers: HashMap<String, Payload>,
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
/// Context for a workflow update
|
|
1050
|
-
pub struct UpdateContext {
|
|
1051
|
-
/// The workflow context, can be used to do normal workflow things inside the update handler
|
|
1052
|
-
pub wf_ctx: WfContext,
|
|
1053
|
-
/// Additional update info
|
|
1054
|
-
pub info: UpdateInfo,
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
struct UpdateFunctions {
|
|
1058
|
-
validator: BoxUpdateValidatorFn,
|
|
1059
|
-
handler: BoxUpdateHandlerFn,
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
impl UpdateFunctions {
|
|
1063
|
-
pub(crate) fn new<Arg, Res>(
|
|
1064
|
-
v: impl IntoUpdateValidatorFunc<Arg> + Sized,
|
|
1065
|
-
h: impl IntoUpdateHandlerFunc<Arg, Res> + Sized,
|
|
1066
|
-
) -> Self {
|
|
1067
|
-
Self {
|
|
1068
|
-
validator: v.into_update_validator_fn(),
|
|
1069
|
-
handler: h.into_update_handler_fn(),
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
type BoxUpdateValidatorFn = Box<dyn Fn(&UpdateInfo, &Payload) -> Result<(), anyhow::Error> + Send>;
|
|
1075
|
-
/// Closures / functions which can be turned into update validation functions implement this trait
|
|
1076
|
-
pub trait IntoUpdateValidatorFunc<Arg> {
|
|
1077
|
-
/// Consume the closure/fn pointer and turn it into an update validator
|
|
1078
|
-
fn into_update_validator_fn(self) -> BoxUpdateValidatorFn;
|
|
1079
|
-
}
|
|
1080
|
-
impl<A, F> IntoUpdateValidatorFunc<A> for F
|
|
1081
|
-
where
|
|
1082
|
-
A: FromJsonPayloadExt + Send,
|
|
1083
|
-
F: (for<'a> Fn(&'a UpdateInfo, A) -> Result<(), anyhow::Error>) + Send + 'static,
|
|
1084
|
-
{
|
|
1085
|
-
fn into_update_validator_fn(self) -> BoxUpdateValidatorFn {
|
|
1086
|
-
let wrapper = move |ctx: &UpdateInfo, input: &Payload| match A::from_json_payload(input) {
|
|
1087
|
-
Ok(deser) => (self)(ctx, deser),
|
|
1088
|
-
Err(e) => Err(e.into()),
|
|
1089
|
-
};
|
|
1090
|
-
Box::new(wrapper)
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
type BoxUpdateHandlerFn = Box<
|
|
1094
|
-
dyn FnMut(UpdateContext, &Payload) -> BoxFuture<'static, Result<Payload, anyhow::Error>> + Send,
|
|
1095
|
-
>;
|
|
1096
|
-
/// Closures / functions which can be turned into update handler functions implement this trait
|
|
1097
|
-
pub trait IntoUpdateHandlerFunc<Arg, Res> {
|
|
1098
|
-
/// Consume the closure/fn pointer and turn it into an update handler
|
|
1099
|
-
fn into_update_handler_fn(self) -> BoxUpdateHandlerFn;
|
|
1100
|
-
}
|
|
1101
|
-
impl<A, F, Rf, R> IntoUpdateHandlerFunc<A, R> for F
|
|
1102
|
-
where
|
|
1103
|
-
A: FromJsonPayloadExt + Send,
|
|
1104
|
-
F: (FnMut(UpdateContext, A) -> Rf) + Send + 'static,
|
|
1105
|
-
Rf: Future<Output = Result<R, anyhow::Error>> + Send + 'static,
|
|
1106
|
-
R: AsJsonPayloadExt,
|
|
1107
|
-
{
|
|
1108
|
-
fn into_update_handler_fn(mut self) -> BoxUpdateHandlerFn {
|
|
1109
|
-
let wrapper = move |ctx: UpdateContext, input: &Payload| match A::from_json_payload(input) {
|
|
1110
|
-
Ok(deser) => (self)(ctx, deser)
|
|
1111
|
-
.map(|r| r.and_then(|r| r.as_json_payload()))
|
|
1112
|
-
.boxed(),
|
|
1113
|
-
Err(e) => async move { Err(e.into()) }.boxed(),
|
|
1114
|
-
};
|
|
1115
|
-
Box::new(wrapper)
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
1299
|
/// Attempts to turn caught panics into something printable
|
|
1120
1300
|
fn panic_formatter(panic: Box<dyn Any>) -> Box<dyn Display> {
|
|
1121
1301
|
_panic_formatter::<&str>(panic)
|
|
@@ -1151,3 +1331,138 @@ impl Display for EndPrintingAttempts {
|
|
|
1151
1331
|
impl PrintablePanicType for EndPrintingAttempts {
|
|
1152
1332
|
type NextType = EndPrintingAttempts;
|
|
1153
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
|
+
}
|