@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
|
@@ -7,28 +7,36 @@ pub use options::{
|
|
|
7
7
|
|
|
8
8
|
use crate::{
|
|
9
9
|
CancelExternalWfResult, CancellableID, CancellableIDWithReason, CommandCreateRequest,
|
|
10
|
-
CommandSubscribeChildWorkflowCompletion,
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
CommandSubscribeChildWorkflowCompletion, NexusStartResult, RustWfCmd, SignalExternalWfResult,
|
|
11
|
+
SupportsCancelReason, TimerResult, UnblockEvent, Unblockable,
|
|
12
|
+
workflow_context::options::IntoWorkflowCommand,
|
|
13
|
+
};
|
|
14
|
+
use futures_util::{
|
|
15
|
+
FutureExt,
|
|
16
|
+
future::{FusedFuture, Shared},
|
|
17
|
+
task::Context,
|
|
13
18
|
};
|
|
14
|
-
use futures_util::{FutureExt, Stream, StreamExt, future::Shared, task::Context};
|
|
15
|
-
use parking_lot::{RwLock, RwLockReadGuard};
|
|
16
19
|
use std::{
|
|
20
|
+
cell::{Ref, RefCell},
|
|
17
21
|
collections::HashMap,
|
|
18
|
-
future,
|
|
19
|
-
future::Future,
|
|
22
|
+
future::{self, Future},
|
|
20
23
|
marker::PhantomData,
|
|
21
|
-
ops::Deref,
|
|
24
|
+
ops::{Deref, DerefMut},
|
|
22
25
|
pin::Pin,
|
|
26
|
+
rc::Rc,
|
|
23
27
|
sync::{
|
|
24
|
-
Arc,
|
|
25
28
|
atomic::{AtomicBool, Ordering},
|
|
26
29
|
mpsc::{Receiver, Sender},
|
|
27
30
|
},
|
|
28
|
-
task::Poll,
|
|
31
|
+
task::{Poll, Waker},
|
|
29
32
|
time::{Duration, SystemTime},
|
|
30
33
|
};
|
|
31
34
|
use temporalio_common::{
|
|
35
|
+
ActivityDefinition,
|
|
36
|
+
data_converters::{
|
|
37
|
+
GenericPayloadConverter, PayloadConversionError, PayloadConverter, SerializationContext,
|
|
38
|
+
SerializationContextData, TemporalDeserializable,
|
|
39
|
+
},
|
|
32
40
|
protos::{
|
|
33
41
|
coresdk::{
|
|
34
42
|
activity_result::{ActivityResolution, activity_resolution},
|
|
@@ -48,144 +56,315 @@ use temporalio_common::{
|
|
|
48
56
|
},
|
|
49
57
|
temporal::api::{
|
|
50
58
|
common::v1::{Memo, Payload, SearchAttributes},
|
|
59
|
+
failure::v1::Failure,
|
|
51
60
|
sdk::v1::UserMetadata,
|
|
52
61
|
},
|
|
53
62
|
},
|
|
54
63
|
worker::WorkerDeploymentVersion,
|
|
55
64
|
};
|
|
56
|
-
use tokio::sync::{
|
|
57
|
-
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
65
|
+
use tokio::sync::{oneshot, watch};
|
|
58
66
|
|
|
59
|
-
///
|
|
67
|
+
/// Non-generic base context containing all workflow execution infrastructure.
|
|
68
|
+
///
|
|
69
|
+
/// This is used internally by futures and commands that don't need typed workflow state.
|
|
60
70
|
#[derive(Clone)]
|
|
61
|
-
pub struct
|
|
71
|
+
pub struct BaseWorkflowContext {
|
|
72
|
+
inner: Rc<WorkflowContextInner>,
|
|
73
|
+
}
|
|
74
|
+
impl BaseWorkflowContext {
|
|
75
|
+
pub(crate) fn shared_mut(&self) -> impl DerefMut<Target = WorkflowContextSharedData> {
|
|
76
|
+
self.inner.shared.borrow_mut()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Create a read-only view of this context.
|
|
80
|
+
pub(crate) fn view(&self) -> WorkflowContextView {
|
|
81
|
+
WorkflowContextView::new(
|
|
82
|
+
self.inner.namespace.clone(),
|
|
83
|
+
self.inner.task_queue.clone(),
|
|
84
|
+
self.inner.run_id.clone(),
|
|
85
|
+
&self.inner.inital_information,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
struct WorkflowContextInner {
|
|
62
91
|
namespace: String,
|
|
63
92
|
task_queue: String,
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
run_id: String,
|
|
94
|
+
inital_information: InitializeWorkflow,
|
|
66
95
|
chan: Sender<RustWfCmd>,
|
|
67
96
|
am_cancelled: watch::Receiver<Option<String>>,
|
|
68
|
-
|
|
97
|
+
shared: RefCell<WorkflowContextSharedData>,
|
|
98
|
+
seq_nums: RefCell<WfCtxProtectedDat>,
|
|
99
|
+
payload_converter: PayloadConverter,
|
|
100
|
+
}
|
|
69
101
|
|
|
70
|
-
|
|
102
|
+
/// Context provided to synchronous signal and update handlers.
|
|
103
|
+
///
|
|
104
|
+
/// This type provides all workflow context capabilities except `state()`, `state_mut()`,
|
|
105
|
+
/// and `wait_condition()`. Those methods are not applicable in sync handler contexts.
|
|
106
|
+
///
|
|
107
|
+
/// Sync handlers receive `&mut self` directly, so they can reference and mutate workflow state without
|
|
108
|
+
/// needing `state()`/`state_mut()`.
|
|
109
|
+
pub struct SyncWorkflowContext<W> {
|
|
110
|
+
base: BaseWorkflowContext,
|
|
111
|
+
/// Headers from the current handler invocation (signal, update, etc.)
|
|
112
|
+
headers: Rc<HashMap<String, Payload>>,
|
|
113
|
+
_phantom: PhantomData<W>,
|
|
71
114
|
}
|
|
72
115
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
task_queue: String,
|
|
81
|
-
init_workflow_job: InitializeWorkflow,
|
|
82
|
-
am_cancelled: watch::Receiver<Option<String>>,
|
|
83
|
-
) -> (Self, Receiver<RustWfCmd>) {
|
|
84
|
-
// The receiving side is non-async
|
|
85
|
-
let (chan, rx) = std::sync::mpsc::channel();
|
|
86
|
-
(
|
|
87
|
-
Self {
|
|
88
|
-
namespace,
|
|
89
|
-
task_queue,
|
|
90
|
-
shared: Arc::new(RwLock::new(WfContextSharedData {
|
|
91
|
-
random_seed: init_workflow_job.randomness_seed,
|
|
92
|
-
search_attributes: init_workflow_job
|
|
93
|
-
.search_attributes
|
|
94
|
-
.clone()
|
|
95
|
-
.unwrap_or_default(),
|
|
96
|
-
..Default::default()
|
|
97
|
-
})),
|
|
98
|
-
inital_information: Arc::new(init_workflow_job),
|
|
99
|
-
chan,
|
|
100
|
-
am_cancelled,
|
|
101
|
-
seq_nums: Arc::new(RwLock::new(WfCtxProtectedDat {
|
|
102
|
-
next_timer_sequence_number: 1,
|
|
103
|
-
next_activity_sequence_number: 1,
|
|
104
|
-
next_child_workflow_sequence_number: 1,
|
|
105
|
-
next_cancel_external_wf_sequence_number: 1,
|
|
106
|
-
next_signal_external_wf_sequence_number: 1,
|
|
107
|
-
next_nexus_op_sequence_number: 1,
|
|
108
|
-
})),
|
|
109
|
-
},
|
|
110
|
-
rx,
|
|
111
|
-
)
|
|
116
|
+
impl<W> Clone for SyncWorkflowContext<W> {
|
|
117
|
+
fn clone(&self) -> Self {
|
|
118
|
+
Self {
|
|
119
|
+
base: self.base.clone(),
|
|
120
|
+
headers: self.headers.clone(),
|
|
121
|
+
_phantom: PhantomData,
|
|
122
|
+
}
|
|
112
123
|
}
|
|
124
|
+
}
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
/// Used within workflows to issue commands, get info, etc.
|
|
127
|
+
///
|
|
128
|
+
/// The type parameter `W` represents the workflow type. This enables type-safe
|
|
129
|
+
/// access to workflow state via `state_mut()` for mutations.
|
|
130
|
+
pub struct WorkflowContext<W> {
|
|
131
|
+
sync: SyncWorkflowContext<W>,
|
|
132
|
+
/// The workflow instance
|
|
133
|
+
workflow_state: Rc<RefCell<W>>,
|
|
134
|
+
/// Wakers registered by `wait_condition` futures. Drained and woken on
|
|
135
|
+
/// every `state_mut` call so that waker-based combinators (e.g.
|
|
136
|
+
/// `FuturesOrdered`) re-poll the condition after state changes.
|
|
137
|
+
condition_wakers: Rc<RefCell<Vec<Waker>>>,
|
|
138
|
+
}
|
|
118
139
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
140
|
+
impl<W> Clone for WorkflowContext<W> {
|
|
141
|
+
fn clone(&self) -> Self {
|
|
142
|
+
Self {
|
|
143
|
+
sync: self.sync.clone(),
|
|
144
|
+
workflow_state: self.workflow_state.clone(),
|
|
145
|
+
condition_wakers: self.condition_wakers.clone(),
|
|
146
|
+
}
|
|
122
147
|
}
|
|
148
|
+
}
|
|
123
149
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
/// Read-only view of workflow context for use in init and query handlers.
|
|
151
|
+
///
|
|
152
|
+
/// This provides access to workflow information but cannot issue commands.
|
|
153
|
+
#[derive(Clone, Debug)]
|
|
154
|
+
#[non_exhaustive]
|
|
155
|
+
pub struct WorkflowContextView {
|
|
156
|
+
/// The workflow's unique identifier
|
|
157
|
+
pub workflow_id: String,
|
|
158
|
+
/// The run id of this workflow execution
|
|
159
|
+
pub run_id: String,
|
|
160
|
+
/// The workflow type name
|
|
161
|
+
pub workflow_type: String,
|
|
162
|
+
/// The task queue this workflow is executing on
|
|
163
|
+
pub task_queue: String,
|
|
164
|
+
/// The namespace this workflow is executing in
|
|
165
|
+
pub namespace: String,
|
|
166
|
+
|
|
167
|
+
/// The current attempt number (starting from 1)
|
|
168
|
+
pub attempt: u32,
|
|
169
|
+
/// The run id of the very first execution in the chain
|
|
170
|
+
pub first_execution_run_id: String,
|
|
171
|
+
/// The run id of the previous execution if this is a continuation
|
|
172
|
+
pub continued_from_run_id: Option<String>,
|
|
173
|
+
|
|
174
|
+
/// When the workflow execution started
|
|
175
|
+
pub start_time: Option<SystemTime>,
|
|
176
|
+
/// Total workflow execution timeout including retries and continue as new
|
|
177
|
+
pub execution_timeout: Option<Duration>,
|
|
178
|
+
/// Timeout of a single workflow run
|
|
179
|
+
pub run_timeout: Option<Duration>,
|
|
180
|
+
/// Timeout of a single workflow task
|
|
181
|
+
pub task_timeout: Option<Duration>,
|
|
182
|
+
|
|
183
|
+
/// Information about the parent workflow, if this is a child workflow
|
|
184
|
+
pub parent: Option<ParentWorkflowInfo>,
|
|
185
|
+
/// Information about the root workflow in the execution chain
|
|
186
|
+
pub root: Option<RootWorkflowInfo>,
|
|
187
|
+
|
|
188
|
+
/// The workflow's retry policy
|
|
189
|
+
pub retry_policy: Option<temporalio_common::protos::temporal::api::common::v1::RetryPolicy>,
|
|
190
|
+
/// If this workflow runs on a cron schedule
|
|
191
|
+
pub cron_schedule: Option<String>,
|
|
192
|
+
/// User-defined memo
|
|
193
|
+
pub memo: Option<Memo>,
|
|
194
|
+
/// Initial search attributes
|
|
195
|
+
pub search_attributes: Option<SearchAttributes>,
|
|
196
|
+
}
|
|
128
197
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
198
|
+
/// Information about a parent workflow.
|
|
199
|
+
#[derive(Clone, Debug)]
|
|
200
|
+
#[non_exhaustive]
|
|
201
|
+
pub struct ParentWorkflowInfo {
|
|
202
|
+
/// The parent workflow's unique identifier
|
|
203
|
+
pub workflow_id: String,
|
|
204
|
+
/// The parent workflow's run id
|
|
205
|
+
pub run_id: String,
|
|
206
|
+
/// The parent workflow's namespace
|
|
207
|
+
pub namespace: String,
|
|
208
|
+
}
|
|
133
209
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
210
|
+
/// Information about the root workflow in an execution chain.
|
|
211
|
+
#[derive(Clone, Debug)]
|
|
212
|
+
#[non_exhaustive]
|
|
213
|
+
pub struct RootWorkflowInfo {
|
|
214
|
+
/// The root workflow's unique identifier
|
|
215
|
+
pub workflow_id: String,
|
|
216
|
+
/// The root workflow's run id
|
|
217
|
+
pub run_id: String,
|
|
218
|
+
}
|
|
138
219
|
|
|
139
|
-
|
|
140
|
-
///
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
220
|
+
impl WorkflowContextView {
|
|
221
|
+
/// Create a new view from workflow initialization data.
|
|
222
|
+
pub(crate) fn new(
|
|
223
|
+
namespace: String,
|
|
224
|
+
task_queue: String,
|
|
225
|
+
run_id: String,
|
|
226
|
+
init: &InitializeWorkflow,
|
|
227
|
+
) -> Self {
|
|
228
|
+
let parent = init
|
|
229
|
+
.parent_workflow_info
|
|
230
|
+
.as_ref()
|
|
231
|
+
.map(|p| ParentWorkflowInfo {
|
|
232
|
+
workflow_id: p.workflow_id.clone(),
|
|
233
|
+
run_id: p.run_id.clone(),
|
|
234
|
+
namespace: p.namespace.clone(),
|
|
235
|
+
});
|
|
145
236
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
237
|
+
let root = init.root_workflow.as_ref().map(|r| RootWorkflowInfo {
|
|
238
|
+
workflow_id: r.workflow_id.clone(),
|
|
239
|
+
run_id: r.run_id.clone(),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
let continued_from_run_id = if init.continued_from_execution_run_id.is_empty() {
|
|
243
|
+
None
|
|
244
|
+
} else {
|
|
245
|
+
Some(init.continued_from_execution_run_id.clone())
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
let cron_schedule = if init.cron_schedule.is_empty() {
|
|
249
|
+
None
|
|
250
|
+
} else {
|
|
251
|
+
Some(init.cron_schedule.clone())
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
Self {
|
|
255
|
+
workflow_id: init.workflow_id.clone(),
|
|
256
|
+
run_id,
|
|
257
|
+
workflow_type: init.workflow_type.clone(),
|
|
258
|
+
task_queue,
|
|
259
|
+
namespace,
|
|
260
|
+
attempt: init.attempt as u32,
|
|
261
|
+
first_execution_run_id: init.first_execution_run_id.clone(),
|
|
262
|
+
continued_from_run_id,
|
|
263
|
+
start_time: init.start_time.and_then(|t| t.try_into().ok()),
|
|
264
|
+
execution_timeout: init
|
|
265
|
+
.workflow_execution_timeout
|
|
266
|
+
.and_then(|d| d.try_into().ok()),
|
|
267
|
+
run_timeout: init.workflow_run_timeout.and_then(|d| d.try_into().ok()),
|
|
268
|
+
task_timeout: init.workflow_task_timeout.and_then(|d| d.try_into().ok()),
|
|
269
|
+
parent,
|
|
270
|
+
root,
|
|
271
|
+
retry_policy: init.retry_policy.clone(),
|
|
272
|
+
cron_schedule,
|
|
273
|
+
memo: init.memo.clone(),
|
|
274
|
+
search_attributes: init.search_attributes.clone(),
|
|
275
|
+
}
|
|
149
276
|
}
|
|
277
|
+
}
|
|
150
278
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
279
|
+
/// Error type for activity execution outcomes.
|
|
280
|
+
#[derive(Debug, thiserror::Error)]
|
|
281
|
+
pub enum ActivityExecutionError {
|
|
282
|
+
/// The activity failed with the given failure details.
|
|
283
|
+
#[error("Activity failed: {}", .0.message)]
|
|
284
|
+
Failed(Box<Failure>),
|
|
285
|
+
/// The activity was cancelled.
|
|
286
|
+
#[error("Activity cancelled: {}", .0.message)]
|
|
287
|
+
Cancelled(Box<Failure>),
|
|
288
|
+
// TODO: Timed out variant
|
|
289
|
+
/// Failed to serialize input or deserialize result payload.
|
|
290
|
+
#[error("Payload conversion failed: {0}")]
|
|
291
|
+
Serialization(#[from] PayloadConversionError),
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
impl ActivityExecutionError {
|
|
295
|
+
/// Returns true if this error represents a timeout.
|
|
296
|
+
pub fn is_timeout(&self) -> bool {
|
|
297
|
+
match self {
|
|
298
|
+
ActivityExecutionError::Failed(f) => f.is_timeout().is_some(),
|
|
299
|
+
_ => false,
|
|
300
|
+
}
|
|
154
301
|
}
|
|
302
|
+
}
|
|
155
303
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
304
|
+
impl BaseWorkflowContext {
|
|
305
|
+
/// Create a new base context, returning the context itself and a receiver which outputs commands
|
|
306
|
+
/// sent from the workflow.
|
|
307
|
+
pub(crate) fn new(
|
|
308
|
+
namespace: String,
|
|
309
|
+
task_queue: String,
|
|
310
|
+
run_id: String,
|
|
311
|
+
init_workflow_job: InitializeWorkflow,
|
|
312
|
+
am_cancelled: watch::Receiver<Option<String>>,
|
|
313
|
+
payload_converter: PayloadConverter,
|
|
314
|
+
) -> (Self, Receiver<RustWfCmd>) {
|
|
315
|
+
// The receiving side is non-async
|
|
316
|
+
let (chan, rx) = std::sync::mpsc::channel();
|
|
317
|
+
(
|
|
318
|
+
Self {
|
|
319
|
+
inner: Rc::new(WorkflowContextInner {
|
|
320
|
+
namespace,
|
|
321
|
+
task_queue,
|
|
322
|
+
run_id,
|
|
323
|
+
shared: RefCell::new(WorkflowContextSharedData {
|
|
324
|
+
random_seed: init_workflow_job.randomness_seed,
|
|
325
|
+
search_attributes: init_workflow_job
|
|
326
|
+
.search_attributes
|
|
327
|
+
.clone()
|
|
328
|
+
.unwrap_or_default(),
|
|
329
|
+
..Default::default()
|
|
330
|
+
}),
|
|
331
|
+
inital_information: init_workflow_job,
|
|
332
|
+
chan,
|
|
333
|
+
am_cancelled,
|
|
334
|
+
seq_nums: RefCell::new(WfCtxProtectedDat {
|
|
335
|
+
next_timer_sequence_number: 1,
|
|
336
|
+
next_activity_sequence_number: 1,
|
|
337
|
+
next_child_workflow_sequence_number: 1,
|
|
338
|
+
next_cancel_external_wf_sequence_number: 1,
|
|
339
|
+
next_signal_external_wf_sequence_number: 1,
|
|
340
|
+
next_nexus_op_sequence_number: 1,
|
|
341
|
+
}),
|
|
342
|
+
payload_converter,
|
|
343
|
+
}),
|
|
344
|
+
},
|
|
345
|
+
rx,
|
|
346
|
+
)
|
|
159
347
|
}
|
|
160
348
|
|
|
161
|
-
///
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
&self.inital_information
|
|
349
|
+
/// Buffer a command to be sent in the activation reply
|
|
350
|
+
pub(crate) fn send(&self, c: RustWfCmd) {
|
|
351
|
+
self.inner.chan.send(c).expect("command channel intact");
|
|
165
352
|
}
|
|
166
353
|
|
|
167
|
-
///
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return s.clone();
|
|
171
|
-
}
|
|
172
|
-
self.am_cancelled
|
|
173
|
-
.clone()
|
|
174
|
-
.changed()
|
|
175
|
-
.await
|
|
176
|
-
.expect("Cancelled send half not dropped");
|
|
177
|
-
self.am_cancelled
|
|
178
|
-
.borrow()
|
|
179
|
-
.as_ref()
|
|
180
|
-
.cloned()
|
|
181
|
-
.unwrap_or_default()
|
|
354
|
+
/// Cancel any cancellable operation by ID
|
|
355
|
+
fn cancel(&self, cancellable_id: CancellableID) {
|
|
356
|
+
self.send(RustWfCmd::Cancel(cancellable_id));
|
|
182
357
|
}
|
|
183
358
|
|
|
184
359
|
/// Request to create a timer
|
|
185
|
-
pub fn timer<T: Into<TimerOptions>>(
|
|
360
|
+
pub fn timer<T: Into<TimerOptions>>(
|
|
361
|
+
&self,
|
|
362
|
+
opts: T,
|
|
363
|
+
) -> impl CancellableFuture<TimerResult> + use<T> {
|
|
186
364
|
let opts: TimerOptions = opts.into();
|
|
187
|
-
let seq = self.seq_nums.
|
|
188
|
-
let (cmd, unblocker) =
|
|
365
|
+
let seq = self.inner.seq_nums.borrow_mut().next_timer_seq();
|
|
366
|
+
let (cmd, unblocker) =
|
|
367
|
+
CancellableWFCommandFut::new(CancellableID::Timer(seq), self.clone());
|
|
189
368
|
self.send(
|
|
190
369
|
CommandCreateRequest {
|
|
191
370
|
cmd: WorkflowCommand {
|
|
@@ -213,53 +392,263 @@ impl WfContext {
|
|
|
213
392
|
}
|
|
214
393
|
|
|
215
394
|
/// Request to run an activity
|
|
216
|
-
pub fn
|
|
395
|
+
pub fn start_activity<AD: ActivityDefinition>(
|
|
217
396
|
&self,
|
|
397
|
+
_activity: AD,
|
|
398
|
+
input: impl Into<AD::Input>,
|
|
218
399
|
mut opts: ActivityOptions,
|
|
219
|
-
) -> impl CancellableFuture<
|
|
400
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
401
|
+
where
|
|
402
|
+
AD::Output: TemporalDeserializable,
|
|
403
|
+
{
|
|
404
|
+
let input = input.into();
|
|
405
|
+
let ctx = SerializationContext {
|
|
406
|
+
data: &SerializationContextData::Workflow,
|
|
407
|
+
converter: &self.inner.payload_converter,
|
|
408
|
+
};
|
|
409
|
+
let payloads = match self.inner.payload_converter.to_payloads(&ctx, &input) {
|
|
410
|
+
Ok(p) => p,
|
|
411
|
+
Err(e) => {
|
|
412
|
+
return ActivityFut::eager(e.into());
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
let seq = self.inner.seq_nums.borrow_mut().next_activity_seq();
|
|
416
|
+
let (cmd, unblocker) =
|
|
417
|
+
CancellableWFCommandFut::new(CancellableID::Activity(seq), self.clone());
|
|
220
418
|
if opts.task_queue.is_none() {
|
|
221
|
-
opts.task_queue = Some(self.task_queue.clone());
|
|
419
|
+
opts.task_queue = Some(self.inner.task_queue.clone());
|
|
222
420
|
}
|
|
223
|
-
let seq = self.seq_nums.write().next_activity_seq();
|
|
224
|
-
let (cmd, unblocker) = CancellableWFCommandFut::new(CancellableID::Activity(seq));
|
|
225
421
|
self.send(
|
|
226
422
|
CommandCreateRequest {
|
|
227
|
-
cmd: opts.into_command(seq),
|
|
423
|
+
cmd: opts.into_command(AD::name().to_string(), payloads, seq),
|
|
228
424
|
unblocker,
|
|
229
425
|
}
|
|
230
426
|
.into(),
|
|
231
427
|
);
|
|
232
|
-
cmd
|
|
428
|
+
ActivityFut::running(cmd, self.inner.payload_converter.clone())
|
|
233
429
|
}
|
|
234
430
|
|
|
235
431
|
/// Request to run a local activity
|
|
236
|
-
pub fn
|
|
432
|
+
pub fn start_local_activity<AD: ActivityDefinition>(
|
|
237
433
|
&self,
|
|
434
|
+
_activity: AD,
|
|
435
|
+
input: impl Into<AD::Input>,
|
|
238
436
|
opts: LocalActivityOptions,
|
|
239
|
-
) -> impl CancellableFuture<
|
|
240
|
-
|
|
437
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
438
|
+
where
|
|
439
|
+
AD::Output: TemporalDeserializable,
|
|
440
|
+
{
|
|
441
|
+
let input = input.into();
|
|
442
|
+
let ctx = SerializationContext {
|
|
443
|
+
data: &SerializationContextData::Workflow,
|
|
444
|
+
converter: &self.inner.payload_converter,
|
|
445
|
+
};
|
|
446
|
+
let payloads = match self.inner.payload_converter.to_payloads(&ctx, &input) {
|
|
447
|
+
Ok(p) => p,
|
|
448
|
+
Err(e) => {
|
|
449
|
+
return ActivityFut::eager(e.into());
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
ActivityFut::running(
|
|
453
|
+
LATimerBackoffFut::new(AD::name().to_string(), payloads, opts, self.clone()),
|
|
454
|
+
self.inner.payload_converter.clone(),
|
|
455
|
+
)
|
|
241
456
|
}
|
|
242
457
|
|
|
243
458
|
/// Request to run a local activity with no implementation of timer-backoff based retrying.
|
|
244
459
|
fn local_activity_no_timer_retry(
|
|
245
|
-
|
|
460
|
+
self,
|
|
461
|
+
activity_type: String,
|
|
462
|
+
arguments: Vec<Payload>,
|
|
246
463
|
opts: LocalActivityOptions,
|
|
247
464
|
) -> impl CancellableFuture<ActivityResolution> {
|
|
248
|
-
let seq = self.seq_nums.
|
|
249
|
-
let (cmd, unblocker) =
|
|
465
|
+
let seq = self.inner.seq_nums.borrow_mut().next_activity_seq();
|
|
466
|
+
let (cmd, unblocker) =
|
|
467
|
+
CancellableWFCommandFut::new(CancellableID::LocalActivity(seq), self.clone());
|
|
468
|
+
self.inner
|
|
469
|
+
.chan
|
|
470
|
+
.send(
|
|
471
|
+
CommandCreateRequest {
|
|
472
|
+
cmd: opts.into_command(activity_type, arguments, seq),
|
|
473
|
+
unblocker,
|
|
474
|
+
}
|
|
475
|
+
.into(),
|
|
476
|
+
)
|
|
477
|
+
.expect("command channel intact");
|
|
478
|
+
cmd
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
fn send_signal_wf(
|
|
482
|
+
self,
|
|
483
|
+
target: sig_we::Target,
|
|
484
|
+
signal: Signal,
|
|
485
|
+
) -> impl CancellableFuture<SignalExternalWfResult> {
|
|
486
|
+
let seq = self
|
|
487
|
+
.inner
|
|
488
|
+
.seq_nums
|
|
489
|
+
.borrow_mut()
|
|
490
|
+
.next_signal_external_wf_seq();
|
|
491
|
+
let (cmd, unblocker) =
|
|
492
|
+
CancellableWFCommandFut::new(CancellableID::SignalExternalWorkflow(seq), self.clone());
|
|
250
493
|
self.send(
|
|
251
494
|
CommandCreateRequest {
|
|
252
|
-
cmd:
|
|
495
|
+
cmd: WorkflowCommand {
|
|
496
|
+
variant: Some(
|
|
497
|
+
SignalExternalWorkflowExecution {
|
|
498
|
+
seq,
|
|
499
|
+
signal_name: signal.signal_name,
|
|
500
|
+
args: signal.data.input,
|
|
501
|
+
target: Some(target),
|
|
502
|
+
headers: signal.data.headers,
|
|
503
|
+
}
|
|
504
|
+
.into(),
|
|
505
|
+
),
|
|
506
|
+
user_metadata: None,
|
|
507
|
+
},
|
|
253
508
|
unblocker,
|
|
254
509
|
}
|
|
255
510
|
.into(),
|
|
256
511
|
);
|
|
257
512
|
cmd
|
|
258
513
|
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
impl<W> SyncWorkflowContext<W> {
|
|
517
|
+
/// Return the workflow's unique identifier
|
|
518
|
+
pub fn workflow_id(&self) -> &str {
|
|
519
|
+
&self.base.inner.inital_information.workflow_id
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/// Return the run id of this workflow execution
|
|
523
|
+
pub fn run_id(&self) -> &str {
|
|
524
|
+
&self.base.inner.run_id
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/// Return the namespace the workflow is executing in
|
|
528
|
+
pub fn namespace(&self) -> &str {
|
|
529
|
+
&self.base.inner.namespace
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/// Return the task queue the workflow is executing in
|
|
533
|
+
pub fn task_queue(&self) -> &str {
|
|
534
|
+
&self.base.inner.task_queue
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// Return the current time according to the workflow (which is not wall-clock time).
|
|
538
|
+
pub fn workflow_time(&self) -> Option<SystemTime> {
|
|
539
|
+
self.base.inner.shared.borrow().wf_time
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/// Return the length of history so far at this point in the workflow
|
|
543
|
+
pub fn history_length(&self) -> u32 {
|
|
544
|
+
self.base.inner.shared.borrow().history_length
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/// Return the deployment version, if any, as it was when this point in the workflow was first
|
|
548
|
+
/// reached. If this code is being executed for the first time, return this Worker's deployment
|
|
549
|
+
/// version if it has one.
|
|
550
|
+
pub fn current_deployment_version(&self) -> Option<WorkerDeploymentVersion> {
|
|
551
|
+
self.base
|
|
552
|
+
.inner
|
|
553
|
+
.shared
|
|
554
|
+
.borrow()
|
|
555
|
+
.current_deployment_version
|
|
556
|
+
.clone()
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/// Return current values for workflow search attributes
|
|
560
|
+
pub fn search_attributes(&self) -> impl Deref<Target = SearchAttributes> + '_ {
|
|
561
|
+
Ref::map(self.base.inner.shared.borrow(), |s| &s.search_attributes)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/// Return the workflow's randomness seed
|
|
565
|
+
pub fn random_seed(&self) -> u64 {
|
|
566
|
+
self.base.inner.shared.borrow().random_seed
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/// Returns true if the current workflow task is happening under replay
|
|
570
|
+
pub fn is_replaying(&self) -> bool {
|
|
571
|
+
self.base.inner.shared.borrow().is_replaying
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/// Returns true if the server suggests this workflow should continue-as-new
|
|
575
|
+
pub fn continue_as_new_suggested(&self) -> bool {
|
|
576
|
+
self.base.inner.shared.borrow().continue_as_new_suggested
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/// Returns the headers for the current handler invocation (signal, update, query, etc.).
|
|
580
|
+
///
|
|
581
|
+
/// When called from within a signal handler, returns the headers that were sent with that
|
|
582
|
+
/// signal. When called from the main workflow run method, returns an empty map.
|
|
583
|
+
pub fn headers(&self) -> &HashMap<String, Payload> {
|
|
584
|
+
&self.headers
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// Returns the [PayloadConverter] currently used by the worker running this workflow.
|
|
588
|
+
pub fn payload_converter(&self) -> &PayloadConverter {
|
|
589
|
+
&self.base.inner.payload_converter
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/// Return various information that the workflow was initialized with. Will eventually become
|
|
593
|
+
/// a proper non-proto workflow info struct.
|
|
594
|
+
pub fn workflow_initial_info(&self) -> &InitializeWorkflow {
|
|
595
|
+
&self.base.inner.inital_information
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/// A future that resolves if/when the workflow is cancelled, with the user provided cause
|
|
599
|
+
pub fn cancelled(&self) -> impl FusedFuture<Output = String> + '_ {
|
|
600
|
+
let am_cancelled = self.base.inner.am_cancelled.clone();
|
|
601
|
+
async move {
|
|
602
|
+
if let Some(s) = am_cancelled.borrow().as_ref() {
|
|
603
|
+
return s.clone();
|
|
604
|
+
}
|
|
605
|
+
am_cancelled
|
|
606
|
+
.clone()
|
|
607
|
+
.changed()
|
|
608
|
+
.await
|
|
609
|
+
.expect("Cancelled send half not dropped");
|
|
610
|
+
am_cancelled.borrow().as_ref().cloned().unwrap_or_default()
|
|
611
|
+
}
|
|
612
|
+
.fuse()
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/// Request to create a timer
|
|
616
|
+
pub fn timer<T: Into<TimerOptions>>(&self, opts: T) -> impl CancellableFuture<TimerResult> {
|
|
617
|
+
self.base.timer(opts)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/// Request to run an activity
|
|
621
|
+
pub fn start_activity<AD: ActivityDefinition>(
|
|
622
|
+
&self,
|
|
623
|
+
activity: AD,
|
|
624
|
+
input: impl Into<AD::Input>,
|
|
625
|
+
opts: ActivityOptions,
|
|
626
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
627
|
+
where
|
|
628
|
+
AD::Output: TemporalDeserializable,
|
|
629
|
+
{
|
|
630
|
+
self.base.start_activity(activity, input, opts)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/// Request to run a local activity
|
|
634
|
+
pub fn start_local_activity<AD: ActivityDefinition>(
|
|
635
|
+
&self,
|
|
636
|
+
activity: AD,
|
|
637
|
+
input: impl Into<AD::Input>,
|
|
638
|
+
opts: LocalActivityOptions,
|
|
639
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
640
|
+
where
|
|
641
|
+
AD::Output: TemporalDeserializable,
|
|
642
|
+
{
|
|
643
|
+
self.base.start_local_activity(activity, input, opts)
|
|
644
|
+
}
|
|
259
645
|
|
|
260
646
|
/// Creates a child workflow stub with the provided options
|
|
261
647
|
pub fn child_workflow(&self, opts: ChildWorkflowOptions) -> ChildWorkflow {
|
|
262
|
-
ChildWorkflow {
|
|
648
|
+
ChildWorkflow {
|
|
649
|
+
opts,
|
|
650
|
+
base_ctx: self.base.clone(),
|
|
651
|
+
}
|
|
263
652
|
}
|
|
264
653
|
|
|
265
654
|
/// Check (or record) that this workflow history was created with the provided patch
|
|
@@ -274,7 +663,7 @@ impl WfContext {
|
|
|
274
663
|
}
|
|
275
664
|
|
|
276
665
|
fn patch_impl(&self, patch_id: &str, deprecated: bool) -> bool {
|
|
277
|
-
self.send(
|
|
666
|
+
self.base.send(
|
|
278
667
|
workflow_command::Variant::SetPatchMarker(SetPatchMarker {
|
|
279
668
|
patch_id: patch_id.to_string(),
|
|
280
669
|
deprecated,
|
|
@@ -282,16 +671,18 @@ impl WfContext {
|
|
|
282
671
|
.into(),
|
|
283
672
|
);
|
|
284
673
|
// See if we already know about the status of this change
|
|
285
|
-
if let Some(present) = self.shared.
|
|
674
|
+
if let Some(present) = self.base.inner.shared.borrow().changes.get(patch_id) {
|
|
286
675
|
return *present;
|
|
287
676
|
}
|
|
288
677
|
|
|
289
678
|
// If we don't already know about the change, that means there is no marker in history,
|
|
290
679
|
// and we should return false if we are replaying
|
|
291
|
-
let res = !self.shared.
|
|
680
|
+
let res = !self.base.inner.shared.borrow().is_replaying;
|
|
292
681
|
|
|
293
|
-
self.
|
|
294
|
-
.
|
|
682
|
+
self.base
|
|
683
|
+
.inner
|
|
684
|
+
.shared
|
|
685
|
+
.borrow_mut()
|
|
295
686
|
.changes
|
|
296
687
|
.insert(patch_id.to_string(), res);
|
|
297
688
|
|
|
@@ -306,19 +697,21 @@ impl WfContext {
|
|
|
306
697
|
) -> impl CancellableFuture<SignalExternalWfResult> {
|
|
307
698
|
let options: SignalWorkflowOptions = opts.into();
|
|
308
699
|
let target = sig_we::Target::WorkflowExecution(NamespacedWorkflowExecution {
|
|
309
|
-
namespace: self.namespace.clone(),
|
|
700
|
+
namespace: self.base.inner.namespace.clone(),
|
|
310
701
|
workflow_id: options.workflow_id,
|
|
311
702
|
run_id: options.run_id.unwrap_or_default(),
|
|
312
703
|
});
|
|
313
|
-
self.send_signal_wf(target, options.signal)
|
|
704
|
+
self.base.clone().send_signal_wf(target, options.signal)
|
|
314
705
|
}
|
|
315
706
|
|
|
316
707
|
/// Add or create a set of search attributes
|
|
317
708
|
pub fn upsert_search_attributes(&self, attr_iter: impl IntoIterator<Item = (String, Payload)>) {
|
|
318
|
-
self.send(RustWfCmd::NewNonblockingCmd(
|
|
709
|
+
self.base.send(RustWfCmd::NewNonblockingCmd(
|
|
319
710
|
workflow_command::Variant::UpsertWorkflowSearchAttributes(
|
|
320
711
|
UpsertWorkflowSearchAttributes {
|
|
321
|
-
search_attributes:
|
|
712
|
+
search_attributes: Some(SearchAttributes {
|
|
713
|
+
indexed_fields: HashMap::from_iter(attr_iter),
|
|
714
|
+
}),
|
|
322
715
|
},
|
|
323
716
|
),
|
|
324
717
|
))
|
|
@@ -326,7 +719,7 @@ impl WfContext {
|
|
|
326
719
|
|
|
327
720
|
/// Add or create a set of search attributes
|
|
328
721
|
pub fn upsert_memo(&self, attr_iter: impl IntoIterator<Item = (String, Payload)>) {
|
|
329
|
-
self.send(RustWfCmd::NewNonblockingCmd(
|
|
722
|
+
self.base.send(RustWfCmd::NewNonblockingCmd(
|
|
330
723
|
workflow_command::Variant::ModifyWorkflowProperties(ModifyWorkflowProperties {
|
|
331
724
|
upserted_memo: Some(Memo {
|
|
332
725
|
fields: HashMap::from_iter(attr_iter),
|
|
@@ -335,16 +728,9 @@ impl WfContext {
|
|
|
335
728
|
))
|
|
336
729
|
}
|
|
337
730
|
|
|
338
|
-
/// Return a stream that produces values when the named signal is sent to this workflow
|
|
339
|
-
pub fn make_signal_channel(&self, signal_name: impl Into<String>) -> DrainableSignalStream {
|
|
340
|
-
let (tx, rx) = mpsc::unbounded_channel();
|
|
341
|
-
self.send(RustWfCmd::SubscribeSignal(signal_name.into(), tx));
|
|
342
|
-
DrainableSignalStream(UnboundedReceiverStream::new(rx))
|
|
343
|
-
}
|
|
344
|
-
|
|
345
731
|
/// Force a workflow task failure (EX: in order to retry on non-sticky queue)
|
|
346
732
|
pub fn force_task_fail(&self, with: anyhow::Error) {
|
|
347
|
-
self.send(with.into());
|
|
733
|
+
self.base.send(with.into());
|
|
348
734
|
}
|
|
349
735
|
|
|
350
736
|
/// Request the cancellation of an external workflow. May resolve as a failure if the workflow
|
|
@@ -353,10 +739,15 @@ impl WfContext {
|
|
|
353
739
|
&self,
|
|
354
740
|
target: NamespacedWorkflowExecution,
|
|
355
741
|
reason: String,
|
|
356
|
-
) -> impl
|
|
357
|
-
let seq = self
|
|
742
|
+
) -> impl FusedFuture<Output = CancelExternalWfResult> {
|
|
743
|
+
let seq = self
|
|
744
|
+
.base
|
|
745
|
+
.inner
|
|
746
|
+
.seq_nums
|
|
747
|
+
.borrow_mut()
|
|
748
|
+
.next_cancel_external_wf_seq();
|
|
358
749
|
let (cmd, unblocker) = WFCommandFut::new();
|
|
359
|
-
self.send(
|
|
750
|
+
self.base.send(
|
|
360
751
|
CommandCreateRequest {
|
|
361
752
|
cmd: WorkflowCommand {
|
|
362
753
|
variant: Some(
|
|
@@ -376,40 +767,25 @@ impl WfContext {
|
|
|
376
767
|
cmd
|
|
377
768
|
}
|
|
378
769
|
|
|
379
|
-
/// Register an update handler by providing the handler name, a validator function, and an
|
|
380
|
-
/// update handler. The validator must not mutate workflow state and is synchronous. The handler
|
|
381
|
-
/// may mutate workflow state (though, that's annoying right now in the prototype) and is async.
|
|
382
|
-
///
|
|
383
|
-
/// Note that if you want a validator that always passes, you will likely need to provide type
|
|
384
|
-
/// annotations to make the compiler happy, like: `|_: &_, _: T| Ok(())`
|
|
385
|
-
pub fn update_handler<Arg, Res>(
|
|
386
|
-
&self,
|
|
387
|
-
name: impl Into<String>,
|
|
388
|
-
validator: impl IntoUpdateValidatorFunc<Arg>,
|
|
389
|
-
handler: impl IntoUpdateHandlerFunc<Arg, Res>,
|
|
390
|
-
) {
|
|
391
|
-
self.send(RustWfCmd::RegisterUpdate(
|
|
392
|
-
name.into(),
|
|
393
|
-
UpdateFunctions::new(validator, handler),
|
|
394
|
-
))
|
|
395
|
-
}
|
|
396
|
-
|
|
397
770
|
/// Start a nexus operation
|
|
398
771
|
pub fn start_nexus_operation(
|
|
399
772
|
&self,
|
|
400
773
|
opts: NexusOperationOptions,
|
|
401
774
|
) -> impl CancellableFuture<NexusStartResult> {
|
|
402
|
-
let seq = self.seq_nums.
|
|
775
|
+
let seq = self.base.inner.seq_nums.borrow_mut().next_nexus_op_seq();
|
|
403
776
|
let (result_future, unblocker) = WFCommandFut::new();
|
|
404
|
-
self.
|
|
777
|
+
self.base
|
|
778
|
+
.send(RustWfCmd::SubscribeNexusOperationCompletion { seq, unblocker });
|
|
405
779
|
let (cmd, unblocker) = CancellableWFCommandFut::new_with_dat(
|
|
406
780
|
CancellableID::NexusOp(seq),
|
|
407
781
|
NexusUnblockData {
|
|
408
782
|
result_future: result_future.shared(),
|
|
409
783
|
schedule_seq: seq,
|
|
784
|
+
base_ctx: self.base.clone(),
|
|
410
785
|
},
|
|
786
|
+
self.base.clone(),
|
|
411
787
|
);
|
|
412
|
-
self.send(
|
|
788
|
+
self.base.send(
|
|
413
789
|
CommandCreateRequest {
|
|
414
790
|
cmd: opts.into_command(seq),
|
|
415
791
|
unblocker,
|
|
@@ -419,55 +795,255 @@ impl WfContext {
|
|
|
419
795
|
cmd
|
|
420
796
|
}
|
|
421
797
|
|
|
422
|
-
///
|
|
423
|
-
pub fn
|
|
424
|
-
|
|
425
|
-
if condition() {
|
|
426
|
-
Poll::Ready(())
|
|
427
|
-
} else {
|
|
428
|
-
Poll::Pending
|
|
429
|
-
}
|
|
430
|
-
})
|
|
798
|
+
/// Create a read-only view of this context.
|
|
799
|
+
pub(crate) fn view(&self) -> WorkflowContextView {
|
|
800
|
+
self.base.view()
|
|
431
801
|
}
|
|
802
|
+
}
|
|
432
803
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
804
|
+
impl<W> WorkflowContext<W> {
|
|
805
|
+
/// Create a new wf context from a base context and workflow state.
|
|
806
|
+
pub(crate) fn from_base(base: BaseWorkflowContext, workflow_state: Rc<RefCell<W>>) -> Self {
|
|
807
|
+
Self {
|
|
808
|
+
sync: SyncWorkflowContext {
|
|
809
|
+
base,
|
|
810
|
+
headers: Rc::new(HashMap::new()),
|
|
811
|
+
_phantom: PhantomData,
|
|
812
|
+
},
|
|
813
|
+
workflow_state,
|
|
814
|
+
condition_wakers: Rc::new(RefCell::new(Vec::new())),
|
|
815
|
+
}
|
|
436
816
|
}
|
|
437
817
|
|
|
438
|
-
|
|
818
|
+
/// Returns a new context with the specified headers set.
|
|
819
|
+
pub(crate) fn with_headers(&self, headers: HashMap<String, Payload>) -> Self {
|
|
820
|
+
Self {
|
|
821
|
+
sync: SyncWorkflowContext {
|
|
822
|
+
base: self.sync.base.clone(),
|
|
823
|
+
headers: Rc::new(headers),
|
|
824
|
+
_phantom: PhantomData,
|
|
825
|
+
},
|
|
826
|
+
workflow_state: self.workflow_state.clone(),
|
|
827
|
+
condition_wakers: self.condition_wakers.clone(),
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/// Returns a [`SyncWorkflowContext`] extracted from this context.
|
|
832
|
+
pub(crate) fn sync_context(&self) -> SyncWorkflowContext<W> {
|
|
833
|
+
self.sync.clone()
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// --- Delegated methods from SyncWorkflowContext ---
|
|
837
|
+
|
|
838
|
+
/// Return the workflow's unique identifier
|
|
839
|
+
pub fn workflow_id(&self) -> &str {
|
|
840
|
+
self.sync.workflow_id()
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/// Return the run id of this workflow execution
|
|
844
|
+
pub fn run_id(&self) -> &str {
|
|
845
|
+
self.sync.run_id()
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/// Return the namespace the workflow is executing in
|
|
849
|
+
pub fn namespace(&self) -> &str {
|
|
850
|
+
self.sync.namespace()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/// Return the task queue the workflow is executing in
|
|
854
|
+
pub fn task_queue(&self) -> &str {
|
|
855
|
+
self.sync.task_queue()
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/// Return the current time according to the workflow (which is not wall-clock time).
|
|
859
|
+
pub fn workflow_time(&self) -> Option<SystemTime> {
|
|
860
|
+
self.sync.workflow_time()
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/// Return the length of history so far at this point in the workflow
|
|
864
|
+
pub fn history_length(&self) -> u32 {
|
|
865
|
+
self.sync.history_length()
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/// Return the deployment version, if any, as it was when this point in the workflow was first
|
|
869
|
+
/// reached. If this code is being executed for the first time, return this Worker's deployment
|
|
870
|
+
/// version if it has one.
|
|
871
|
+
pub fn current_deployment_version(&self) -> Option<WorkerDeploymentVersion> {
|
|
872
|
+
self.sync.current_deployment_version()
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/// Return current values for workflow search attributes
|
|
876
|
+
pub fn search_attributes(&self) -> impl Deref<Target = SearchAttributes> + '_ {
|
|
877
|
+
self.sync.search_attributes()
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/// Return the workflow's randomness seed
|
|
881
|
+
pub fn random_seed(&self) -> u64 {
|
|
882
|
+
self.sync.random_seed()
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/// Returns true if the current workflow task is happening under replay
|
|
886
|
+
pub fn is_replaying(&self) -> bool {
|
|
887
|
+
self.sync.is_replaying()
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/// Returns true if the server suggests this workflow should continue-as-new
|
|
891
|
+
pub fn continue_as_new_suggested(&self) -> bool {
|
|
892
|
+
self.sync.continue_as_new_suggested()
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/// Returns the headers for the current handler invocation (signal, update, query, etc.).
|
|
896
|
+
pub fn headers(&self) -> &HashMap<String, Payload> {
|
|
897
|
+
self.sync.headers()
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/// Returns the [PayloadConverter] currently used by the worker running this workflow.
|
|
901
|
+
pub fn payload_converter(&self) -> &PayloadConverter {
|
|
902
|
+
self.sync.payload_converter()
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/// Return various information that the workflow was initialized with.
|
|
906
|
+
pub fn workflow_initial_info(&self) -> &InitializeWorkflow {
|
|
907
|
+
self.sync.workflow_initial_info()
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/// A future that resolves if/when the workflow is cancelled, with the user provided cause
|
|
911
|
+
pub fn cancelled(&self) -> impl FusedFuture<Output = String> + '_ {
|
|
912
|
+
self.sync.cancelled()
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/// Request to create a timer
|
|
916
|
+
pub fn timer<T: Into<TimerOptions>>(&self, opts: T) -> impl CancellableFuture<TimerResult> {
|
|
917
|
+
self.sync.timer(opts)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/// Request to run an activity
|
|
921
|
+
pub fn start_activity<AD: ActivityDefinition>(
|
|
439
922
|
&self,
|
|
440
|
-
|
|
441
|
-
|
|
923
|
+
activity: AD,
|
|
924
|
+
input: impl Into<AD::Input>,
|
|
925
|
+
opts: ActivityOptions,
|
|
926
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
927
|
+
where
|
|
928
|
+
AD::Output: TemporalDeserializable,
|
|
929
|
+
{
|
|
930
|
+
self.sync.start_activity(activity, input, opts)
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/// Request to run a local activity
|
|
934
|
+
pub fn start_local_activity<AD: ActivityDefinition>(
|
|
935
|
+
&self,
|
|
936
|
+
activity: AD,
|
|
937
|
+
input: impl Into<AD::Input>,
|
|
938
|
+
opts: LocalActivityOptions,
|
|
939
|
+
) -> impl CancellableFuture<Result<AD::Output, ActivityExecutionError>>
|
|
940
|
+
where
|
|
941
|
+
AD::Output: TemporalDeserializable,
|
|
942
|
+
{
|
|
943
|
+
self.sync.start_local_activity(activity, input, opts)
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/// Creates a child workflow stub with the provided options
|
|
947
|
+
pub fn child_workflow(&self, opts: ChildWorkflowOptions) -> ChildWorkflow {
|
|
948
|
+
self.sync.child_workflow(opts)
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/// Check (or record) that this workflow history was created with the provided patch
|
|
952
|
+
pub fn patched(&self, patch_id: &str) -> bool {
|
|
953
|
+
self.sync.patched(patch_id)
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/// Record that this workflow history was created with the provided patch, and it is being
|
|
957
|
+
/// phased out.
|
|
958
|
+
pub fn deprecate_patch(&self, patch_id: &str) -> bool {
|
|
959
|
+
self.sync.deprecate_patch(patch_id)
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/// Send a signal to an external workflow.
|
|
963
|
+
pub fn signal_workflow(
|
|
964
|
+
&self,
|
|
965
|
+
opts: impl Into<SignalWorkflowOptions>,
|
|
442
966
|
) -> impl CancellableFuture<SignalExternalWfResult> {
|
|
443
|
-
|
|
444
|
-
let (cmd, unblocker) =
|
|
445
|
-
CancellableWFCommandFut::new(CancellableID::SignalExternalWorkflow(seq));
|
|
446
|
-
self.send(
|
|
447
|
-
CommandCreateRequest {
|
|
448
|
-
cmd: WorkflowCommand {
|
|
449
|
-
variant: Some(
|
|
450
|
-
SignalExternalWorkflowExecution {
|
|
451
|
-
seq,
|
|
452
|
-
signal_name: signal.signal_name,
|
|
453
|
-
args: signal.data.input,
|
|
454
|
-
target: Some(target),
|
|
455
|
-
headers: signal.data.headers,
|
|
456
|
-
}
|
|
457
|
-
.into(),
|
|
458
|
-
),
|
|
459
|
-
user_metadata: None,
|
|
460
|
-
},
|
|
461
|
-
unblocker,
|
|
462
|
-
}
|
|
463
|
-
.into(),
|
|
464
|
-
);
|
|
465
|
-
cmd
|
|
967
|
+
self.sync.signal_workflow(opts)
|
|
466
968
|
}
|
|
467
969
|
|
|
468
|
-
///
|
|
469
|
-
fn
|
|
470
|
-
self.
|
|
970
|
+
/// Add or create a set of search attributes
|
|
971
|
+
pub fn upsert_search_attributes(&self, attr_iter: impl IntoIterator<Item = (String, Payload)>) {
|
|
972
|
+
self.sync.upsert_search_attributes(attr_iter)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/// Add or create a set of memo fields
|
|
976
|
+
pub fn upsert_memo(&self, attr_iter: impl IntoIterator<Item = (String, Payload)>) {
|
|
977
|
+
self.sync.upsert_memo(attr_iter)
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/// Force a workflow task failure (EX: in order to retry on non-sticky queue)
|
|
981
|
+
pub fn force_task_fail(&self, with: anyhow::Error) {
|
|
982
|
+
self.sync.force_task_fail(with)
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/// Request the cancellation of an external workflow.
|
|
986
|
+
pub fn cancel_external(
|
|
987
|
+
&self,
|
|
988
|
+
target: NamespacedWorkflowExecution,
|
|
989
|
+
reason: String,
|
|
990
|
+
) -> impl FusedFuture<Output = CancelExternalWfResult> {
|
|
991
|
+
self.sync.cancel_external(target, reason)
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/// Start a nexus operation
|
|
995
|
+
pub fn start_nexus_operation(
|
|
996
|
+
&self,
|
|
997
|
+
opts: NexusOperationOptions,
|
|
998
|
+
) -> impl CancellableFuture<NexusStartResult> {
|
|
999
|
+
self.sync.start_nexus_operation(opts)
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/// Create a read-only view of this context.
|
|
1003
|
+
pub(crate) fn view(&self) -> WorkflowContextView {
|
|
1004
|
+
self.sync.view()
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/// Access workflow state immutably via closure.
|
|
1008
|
+
///
|
|
1009
|
+
/// The borrow is scoped to the closure and cannot escape, preventing
|
|
1010
|
+
/// borrows from being held across await points.
|
|
1011
|
+
pub fn state<R>(&self, f: impl FnOnce(&W) -> R) -> R {
|
|
1012
|
+
f(&*self.workflow_state.borrow())
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/// Access workflow state mutably via closure.
|
|
1016
|
+
///
|
|
1017
|
+
/// The borrow is scoped to the closure and cannot escape, preventing
|
|
1018
|
+
/// borrows from being held across await points.
|
|
1019
|
+
///
|
|
1020
|
+
/// After the mutation, all wakers registered by pending `wait_condition`
|
|
1021
|
+
/// futures are woken so that waker-based combinators (e.g.
|
|
1022
|
+
/// `FuturesOrdered`) re-poll them on the next pass.
|
|
1023
|
+
pub fn state_mut<R>(&self, f: impl FnOnce(&mut W) -> R) -> R {
|
|
1024
|
+
let result = f(&mut *self.workflow_state.borrow_mut());
|
|
1025
|
+
for waker in self.condition_wakers.borrow_mut().drain(..) {
|
|
1026
|
+
waker.wake();
|
|
1027
|
+
}
|
|
1028
|
+
result
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/// Wait for some condition on workflow state to become true, yielding the workflow if not.
|
|
1032
|
+
///
|
|
1033
|
+
/// The condition closure receives an immutable reference to the workflow state,
|
|
1034
|
+
/// which is borrowed only for the duration of each poll (not across await points).
|
|
1035
|
+
pub fn wait_condition<'a>(
|
|
1036
|
+
&'a self,
|
|
1037
|
+
mut condition: impl FnMut(&W) -> bool + 'a,
|
|
1038
|
+
) -> impl Future<Output = ()> + 'a {
|
|
1039
|
+
future::poll_fn(move |cx: &mut Context<'_>| {
|
|
1040
|
+
if condition(&*self.workflow_state.borrow()) {
|
|
1041
|
+
Poll::Ready(())
|
|
1042
|
+
} else {
|
|
1043
|
+
self.condition_wakers.borrow_mut().push(cx.waker().clone());
|
|
1044
|
+
Poll::Pending
|
|
1045
|
+
}
|
|
1046
|
+
})
|
|
471
1047
|
}
|
|
472
1048
|
}
|
|
473
1049
|
|
|
@@ -514,59 +1090,29 @@ impl WfCtxProtectedDat {
|
|
|
514
1090
|
}
|
|
515
1091
|
|
|
516
1092
|
#[derive(Clone, Debug, Default)]
|
|
517
|
-
pub(crate) struct
|
|
1093
|
+
pub(crate) struct WorkflowContextSharedData {
|
|
518
1094
|
/// Maps change ids -> resolved status
|
|
519
1095
|
pub(crate) changes: HashMap<String, bool>,
|
|
520
1096
|
pub(crate) is_replaying: bool,
|
|
521
1097
|
pub(crate) wf_time: Option<SystemTime>,
|
|
522
1098
|
pub(crate) history_length: u32,
|
|
1099
|
+
pub(crate) continue_as_new_suggested: bool,
|
|
523
1100
|
pub(crate) current_deployment_version: Option<WorkerDeploymentVersion>,
|
|
524
1101
|
pub(crate) search_attributes: SearchAttributes,
|
|
525
1102
|
pub(crate) random_seed: u64,
|
|
526
1103
|
}
|
|
527
1104
|
|
|
528
|
-
/// Helper Wrapper that can drain the channel into a Vec<SignalData> in a blocking way. Useful
|
|
529
|
-
/// for making sure channels are empty before ContinueAsNew-ing a workflow
|
|
530
|
-
pub struct DrainableSignalStream(UnboundedReceiverStream<SignalData>);
|
|
531
|
-
|
|
532
|
-
impl DrainableSignalStream {
|
|
533
|
-
pub fn drain_all(self) -> Vec<SignalData> {
|
|
534
|
-
let mut receiver = self.0.into_inner();
|
|
535
|
-
let mut signals = vec![];
|
|
536
|
-
while let Ok(s) = receiver.try_recv() {
|
|
537
|
-
signals.push(s);
|
|
538
|
-
}
|
|
539
|
-
signals
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
pub fn drain_ready(&mut self) -> Vec<SignalData> {
|
|
543
|
-
let mut signals = vec![];
|
|
544
|
-
while let Some(s) = self.0.next().now_or_never().flatten() {
|
|
545
|
-
signals.push(s);
|
|
546
|
-
}
|
|
547
|
-
signals
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
impl Stream for DrainableSignalStream {
|
|
552
|
-
type Item = SignalData;
|
|
553
|
-
|
|
554
|
-
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
555
|
-
Pin::new(&mut self.0).poll_next(cx)
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
1105
|
/// A Future that can be cancelled.
|
|
560
1106
|
/// Used in the prototype SDK for cancelling operations like timers and activities.
|
|
561
|
-
pub trait CancellableFuture<T>: Future<Output = T> {
|
|
1107
|
+
pub trait CancellableFuture<T>: Future<Output = T> + FusedFuture {
|
|
562
1108
|
/// Cancel this Future
|
|
563
|
-
fn cancel(&self
|
|
1109
|
+
fn cancel(&self);
|
|
564
1110
|
}
|
|
565
1111
|
|
|
566
1112
|
/// A Future that can be cancelled with a reason
|
|
567
1113
|
pub trait CancellableFutureWithReason<T>: CancellableFuture<T> {
|
|
568
1114
|
/// Cancel this Future with a reason
|
|
569
|
-
fn cancel_with_reason(&self,
|
|
1115
|
+
fn cancel_with_reason(&self, reason: String);
|
|
570
1116
|
}
|
|
571
1117
|
|
|
572
1118
|
struct WFCommandFut<T, D> {
|
|
@@ -603,8 +1149,6 @@ where
|
|
|
603
1149
|
|
|
604
1150
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
605
1151
|
self.result_rx.poll_unpin(cx).map(|x| {
|
|
606
|
-
// SAFETY: Because we can only enter this section once the future has resolved, we
|
|
607
|
-
// know it will never be polled again, therefore consuming the option is OK.
|
|
608
1152
|
let od = self
|
|
609
1153
|
.other_dat
|
|
610
1154
|
.take()
|
|
@@ -613,23 +1157,40 @@ where
|
|
|
613
1157
|
})
|
|
614
1158
|
}
|
|
615
1159
|
}
|
|
1160
|
+
impl<T, D> FusedFuture for WFCommandFut<T, D>
|
|
1161
|
+
where
|
|
1162
|
+
T: Unblockable<OtherDat = D>,
|
|
1163
|
+
{
|
|
1164
|
+
fn is_terminated(&self) -> bool {
|
|
1165
|
+
self.other_dat.is_none()
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
616
1168
|
|
|
617
1169
|
struct CancellableWFCommandFut<T, D, ID = CancellableID> {
|
|
618
1170
|
cmd_fut: WFCommandFut<T, D>,
|
|
619
1171
|
cancellable_id: ID,
|
|
1172
|
+
base_ctx: BaseWorkflowContext,
|
|
620
1173
|
}
|
|
621
1174
|
impl<T, ID> CancellableWFCommandFut<T, (), ID> {
|
|
622
|
-
fn new(
|
|
623
|
-
|
|
1175
|
+
fn new(
|
|
1176
|
+
cancellable_id: ID,
|
|
1177
|
+
base_ctx: BaseWorkflowContext,
|
|
1178
|
+
) -> (Self, oneshot::Sender<UnblockEvent>) {
|
|
1179
|
+
Self::new_with_dat(cancellable_id, (), base_ctx)
|
|
624
1180
|
}
|
|
625
1181
|
}
|
|
626
1182
|
impl<T, D, ID> CancellableWFCommandFut<T, D, ID> {
|
|
627
|
-
fn new_with_dat(
|
|
1183
|
+
fn new_with_dat(
|
|
1184
|
+
cancellable_id: ID,
|
|
1185
|
+
other_dat: D,
|
|
1186
|
+
base_ctx: BaseWorkflowContext,
|
|
1187
|
+
) -> (Self, oneshot::Sender<UnblockEvent>) {
|
|
628
1188
|
let (cmd_fut, sender) = WFCommandFut::new_with_dat(other_dat);
|
|
629
1189
|
(
|
|
630
1190
|
Self {
|
|
631
1191
|
cmd_fut,
|
|
632
1192
|
cancellable_id,
|
|
1193
|
+
base_ctx,
|
|
633
1194
|
},
|
|
634
1195
|
sender,
|
|
635
1196
|
)
|
|
@@ -646,50 +1207,74 @@ where
|
|
|
646
1207
|
self.cmd_fut.poll_unpin(cx)
|
|
647
1208
|
}
|
|
648
1209
|
}
|
|
1210
|
+
impl<T, D, ID> FusedFuture for CancellableWFCommandFut<T, D, ID>
|
|
1211
|
+
where
|
|
1212
|
+
T: Unblockable<OtherDat = D>,
|
|
1213
|
+
{
|
|
1214
|
+
fn is_terminated(&self) -> bool {
|
|
1215
|
+
self.cmd_fut.is_terminated()
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
649
1218
|
|
|
650
1219
|
impl<T, D, ID> CancellableFuture<T> for CancellableWFCommandFut<T, D, ID>
|
|
651
1220
|
where
|
|
652
1221
|
T: Unblockable<OtherDat = D>,
|
|
653
1222
|
ID: Clone + Into<CancellableID>,
|
|
654
1223
|
{
|
|
655
|
-
fn cancel(&self
|
|
656
|
-
|
|
1224
|
+
fn cancel(&self) {
|
|
1225
|
+
self.base_ctx.cancel(self.cancellable_id.clone().into());
|
|
657
1226
|
}
|
|
658
1227
|
}
|
|
659
1228
|
impl<T, D> CancellableFutureWithReason<T> for CancellableWFCommandFut<T, D, CancellableIDWithReason>
|
|
660
1229
|
where
|
|
661
1230
|
T: Unblockable<OtherDat = D>,
|
|
662
1231
|
{
|
|
663
|
-
fn cancel_with_reason(&self,
|
|
1232
|
+
fn cancel_with_reason(&self, reason: String) {
|
|
664
1233
|
let new_id = self.cancellable_id.clone().with_reason(reason);
|
|
665
|
-
|
|
1234
|
+
self.base_ctx.cancel(new_id);
|
|
666
1235
|
}
|
|
667
1236
|
}
|
|
668
1237
|
|
|
669
|
-
struct LATimerBackoffFut
|
|
1238
|
+
struct LATimerBackoffFut {
|
|
670
1239
|
la_opts: LocalActivityOptions,
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
1240
|
+
activity_type: String,
|
|
1241
|
+
arguments: Vec<Payload>,
|
|
1242
|
+
current_fut: Pin<Box<dyn CancellableFuture<ActivityResolution> + Unpin>>,
|
|
1243
|
+
timer_fut: Option<Pin<Box<dyn CancellableFuture<TimerResult> + Unpin>>>,
|
|
1244
|
+
base_ctx: BaseWorkflowContext,
|
|
674
1245
|
next_attempt: u32,
|
|
675
1246
|
next_sched_time: Option<prost_types::Timestamp>,
|
|
676
1247
|
did_cancel: AtomicBool,
|
|
1248
|
+
terminated: bool,
|
|
677
1249
|
}
|
|
678
|
-
impl
|
|
679
|
-
pub(crate) fn new(
|
|
1250
|
+
impl LATimerBackoffFut {
|
|
1251
|
+
pub(crate) fn new(
|
|
1252
|
+
activity_type: String,
|
|
1253
|
+
arguments: Vec<Payload>,
|
|
1254
|
+
opts: LocalActivityOptions,
|
|
1255
|
+
base_ctx: BaseWorkflowContext,
|
|
1256
|
+
) -> Self {
|
|
1257
|
+
let current_fut = Box::pin(base_ctx.clone().local_activity_no_timer_retry(
|
|
1258
|
+
activity_type.clone(),
|
|
1259
|
+
arguments.clone(),
|
|
1260
|
+
opts.clone(),
|
|
1261
|
+
));
|
|
680
1262
|
Self {
|
|
681
|
-
la_opts: opts
|
|
682
|
-
|
|
1263
|
+
la_opts: opts,
|
|
1264
|
+
activity_type,
|
|
1265
|
+
arguments,
|
|
1266
|
+
current_fut,
|
|
683
1267
|
timer_fut: None,
|
|
684
|
-
|
|
1268
|
+
base_ctx,
|
|
685
1269
|
next_attempt: 1,
|
|
686
1270
|
next_sched_time: None,
|
|
687
1271
|
did_cancel: AtomicBool::new(false),
|
|
1272
|
+
terminated: false,
|
|
688
1273
|
}
|
|
689
1274
|
}
|
|
690
1275
|
}
|
|
691
|
-
impl Unpin for LATimerBackoffFut
|
|
692
|
-
impl Future for LATimerBackoffFut
|
|
1276
|
+
impl Unpin for LATimerBackoffFut {}
|
|
1277
|
+
impl Future for LATimerBackoffFut {
|
|
693
1278
|
type Output = ActivityResolution;
|
|
694
1279
|
|
|
695
1280
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
@@ -704,9 +1289,15 @@ impl Future for LATimerBackoffFut<'_> {
|
|
|
704
1289
|
opts.attempt = Some(self.next_attempt);
|
|
705
1290
|
opts.original_schedule_time
|
|
706
1291
|
.clone_from(&self.next_sched_time);
|
|
707
|
-
self.current_fut =
|
|
1292
|
+
self.current_fut =
|
|
1293
|
+
Box::pin(self.base_ctx.clone().local_activity_no_timer_retry(
|
|
1294
|
+
self.activity_type.clone(),
|
|
1295
|
+
self.arguments.clone(),
|
|
1296
|
+
opts,
|
|
1297
|
+
));
|
|
708
1298
|
Poll::Pending
|
|
709
1299
|
} else {
|
|
1300
|
+
self.terminated = true;
|
|
710
1301
|
Poll::Ready(ActivityResolution {
|
|
711
1302
|
status: Some(
|
|
712
1303
|
activity_resolution::Status::Cancelled(Default::default()),
|
|
@@ -725,12 +1316,13 @@ impl Future for LATimerBackoffFut<'_> {
|
|
|
725
1316
|
// return cancel status. This can happen if cancel comes after the LA says it wants
|
|
726
1317
|
// to back off but before we have scheduled the timer.
|
|
727
1318
|
if self.did_cancel.load(Ordering::Acquire) {
|
|
1319
|
+
self.terminated = true;
|
|
728
1320
|
return Poll::Ready(ActivityResolution {
|
|
729
1321
|
status: Some(activity_resolution::Status::Cancelled(Default::default())),
|
|
730
1322
|
});
|
|
731
1323
|
}
|
|
732
1324
|
|
|
733
|
-
let timer_f = self.
|
|
1325
|
+
let timer_f = self.base_ctx.timer::<Duration>(
|
|
734
1326
|
b.backoff_duration
|
|
735
1327
|
.expect("Duration is set")
|
|
736
1328
|
.try_into()
|
|
@@ -741,34 +1333,165 @@ impl Future for LATimerBackoffFut<'_> {
|
|
|
741
1333
|
self.next_sched_time.clone_from(&b.original_schedule_time);
|
|
742
1334
|
return Poll::Pending;
|
|
743
1335
|
}
|
|
1336
|
+
if poll_res.is_ready() {
|
|
1337
|
+
self.terminated = true;
|
|
1338
|
+
}
|
|
744
1339
|
poll_res
|
|
745
1340
|
}
|
|
746
1341
|
}
|
|
747
|
-
impl
|
|
748
|
-
fn
|
|
1342
|
+
impl FusedFuture for LATimerBackoffFut {
|
|
1343
|
+
fn is_terminated(&self) -> bool {
|
|
1344
|
+
self.terminated
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
impl CancellableFuture<ActivityResolution> for LATimerBackoffFut {
|
|
1348
|
+
fn cancel(&self) {
|
|
749
1349
|
self.did_cancel.store(true, Ordering::Release);
|
|
750
1350
|
if let Some(tf) = self.timer_fut.as_ref() {
|
|
751
|
-
tf.cancel(
|
|
1351
|
+
tf.cancel();
|
|
1352
|
+
}
|
|
1353
|
+
self.current_fut.cancel();
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/// Future for activity results. Either an immediate error or a running activity.
|
|
1358
|
+
enum ActivityFut<F, Output> {
|
|
1359
|
+
/// Immediate error (e.g., input serialization failure). Resolves on first poll.
|
|
1360
|
+
Errored {
|
|
1361
|
+
error: Option<ActivityExecutionError>,
|
|
1362
|
+
_phantom: PhantomData<Output>,
|
|
1363
|
+
},
|
|
1364
|
+
/// Running activity that will deserialize output on completion.
|
|
1365
|
+
Running {
|
|
1366
|
+
inner: F,
|
|
1367
|
+
payload_converter: PayloadConverter,
|
|
1368
|
+
_phantom: PhantomData<Output>,
|
|
1369
|
+
},
|
|
1370
|
+
Terminated,
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
impl<F, Output> ActivityFut<F, Output> {
|
|
1374
|
+
fn eager(err: ActivityExecutionError) -> Self {
|
|
1375
|
+
Self::Errored {
|
|
1376
|
+
error: Some(err),
|
|
1377
|
+
_phantom: PhantomData,
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
fn running(inner: F, payload_converter: PayloadConverter) -> Self {
|
|
1382
|
+
Self::Running {
|
|
1383
|
+
inner,
|
|
1384
|
+
payload_converter,
|
|
1385
|
+
_phantom: PhantomData,
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
impl<F, Output> Unpin for ActivityFut<F, Output> where F: Unpin {}
|
|
1391
|
+
|
|
1392
|
+
impl<F, Output> Future for ActivityFut<F, Output>
|
|
1393
|
+
where
|
|
1394
|
+
F: Future<Output = ActivityResolution> + Unpin,
|
|
1395
|
+
Output: TemporalDeserializable + 'static,
|
|
1396
|
+
{
|
|
1397
|
+
type Output = Result<Output, ActivityExecutionError>;
|
|
1398
|
+
|
|
1399
|
+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
1400
|
+
let this = self.get_mut();
|
|
1401
|
+
let poll = match this {
|
|
1402
|
+
ActivityFut::Errored { error, .. } => {
|
|
1403
|
+
Poll::Ready(Err(error.take().expect("polled after completion")))
|
|
1404
|
+
}
|
|
1405
|
+
ActivityFut::Running {
|
|
1406
|
+
inner,
|
|
1407
|
+
payload_converter,
|
|
1408
|
+
..
|
|
1409
|
+
} => match Pin::new(inner).poll(cx) {
|
|
1410
|
+
Poll::Pending => Poll::Pending,
|
|
1411
|
+
Poll::Ready(resolution) => Poll::Ready({
|
|
1412
|
+
let status = resolution.status.ok_or_else(|| {
|
|
1413
|
+
ActivityExecutionError::Failed(Box::new(Failure {
|
|
1414
|
+
message: "Activity completed without a status".to_string(),
|
|
1415
|
+
..Default::default()
|
|
1416
|
+
}))
|
|
1417
|
+
})?;
|
|
1418
|
+
|
|
1419
|
+
match status {
|
|
1420
|
+
activity_resolution::Status::Completed(success) => {
|
|
1421
|
+
let payload = success.result.unwrap_or_default();
|
|
1422
|
+
let ctx = SerializationContext {
|
|
1423
|
+
data: &SerializationContextData::Workflow,
|
|
1424
|
+
converter: payload_converter,
|
|
1425
|
+
};
|
|
1426
|
+
payload_converter
|
|
1427
|
+
.from_payload::<Output>(&ctx, payload)
|
|
1428
|
+
.map_err(ActivityExecutionError::Serialization)
|
|
1429
|
+
}
|
|
1430
|
+
activity_resolution::Status::Failed(f) => Err(
|
|
1431
|
+
ActivityExecutionError::Failed(Box::new(f.failure.unwrap_or_default())),
|
|
1432
|
+
),
|
|
1433
|
+
activity_resolution::Status::Cancelled(c) => {
|
|
1434
|
+
Err(ActivityExecutionError::Cancelled(Box::new(
|
|
1435
|
+
c.failure.unwrap_or_default(),
|
|
1436
|
+
)))
|
|
1437
|
+
}
|
|
1438
|
+
activity_resolution::Status::Backoff(_) => {
|
|
1439
|
+
panic!("DoBackoff should be handled by LATimerBackoffFut")
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}),
|
|
1443
|
+
},
|
|
1444
|
+
ActivityFut::Terminated => panic!("polled after termination"),
|
|
1445
|
+
};
|
|
1446
|
+
if poll.is_ready() {
|
|
1447
|
+
*this = ActivityFut::Terminated;
|
|
1448
|
+
}
|
|
1449
|
+
poll
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
impl<F, Output> FusedFuture for ActivityFut<F, Output>
|
|
1454
|
+
where
|
|
1455
|
+
F: Future<Output = ActivityResolution> + Unpin,
|
|
1456
|
+
Output: TemporalDeserializable + 'static,
|
|
1457
|
+
{
|
|
1458
|
+
fn is_terminated(&self) -> bool {
|
|
1459
|
+
matches!(self, ActivityFut::Terminated)
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
impl<F, Output> CancellableFuture<Result<Output, ActivityExecutionError>> for ActivityFut<F, Output>
|
|
1464
|
+
where
|
|
1465
|
+
F: CancellableFuture<ActivityResolution> + Unpin,
|
|
1466
|
+
Output: TemporalDeserializable + 'static,
|
|
1467
|
+
{
|
|
1468
|
+
fn cancel(&self) {
|
|
1469
|
+
if let ActivityFut::Running { inner, .. } = self {
|
|
1470
|
+
inner.cancel()
|
|
752
1471
|
}
|
|
753
|
-
self.current_fut.cancel(ctx);
|
|
754
1472
|
}
|
|
755
1473
|
}
|
|
756
1474
|
|
|
757
1475
|
/// A stub representing an unstarted child workflow.
|
|
758
|
-
#[derive(
|
|
1476
|
+
#[derive(Clone, derive_more::Debug)]
|
|
759
1477
|
pub struct ChildWorkflow {
|
|
760
1478
|
opts: ChildWorkflowOptions,
|
|
1479
|
+
#[debug(skip)]
|
|
1480
|
+
base_ctx: BaseWorkflowContext,
|
|
761
1481
|
}
|
|
762
1482
|
|
|
763
1483
|
pub(crate) struct ChildWfCommon {
|
|
764
1484
|
workflow_id: String,
|
|
765
1485
|
result_future: CancellableWFCommandFut<ChildWorkflowResult, (), CancellableIDWithReason>,
|
|
1486
|
+
base_ctx: BaseWorkflowContext,
|
|
766
1487
|
}
|
|
767
1488
|
|
|
768
1489
|
/// Child workflow in pending state
|
|
1490
|
+
#[derive(derive_more::Debug)]
|
|
769
1491
|
pub struct PendingChildWorkflow {
|
|
770
1492
|
/// The status of the child workflow start
|
|
771
1493
|
pub status: ChildWorkflowStartStatus,
|
|
1494
|
+
#[debug(skip)]
|
|
772
1495
|
pub(crate) common: ChildWfCommon,
|
|
773
1496
|
}
|
|
774
1497
|
|
|
@@ -787,29 +1510,43 @@ impl PendingChildWorkflow {
|
|
|
787
1510
|
}
|
|
788
1511
|
|
|
789
1512
|
/// Child workflow in started state
|
|
1513
|
+
#[derive(derive_more::Debug)]
|
|
790
1514
|
pub struct StartedChildWorkflow {
|
|
791
1515
|
/// Run ID of the child workflow
|
|
792
1516
|
pub run_id: String,
|
|
1517
|
+
#[debug(skip)]
|
|
793
1518
|
common: ChildWfCommon,
|
|
794
1519
|
}
|
|
795
1520
|
|
|
796
1521
|
impl ChildWorkflow {
|
|
797
1522
|
/// Start the child workflow, the returned Future is cancellable.
|
|
798
|
-
pub fn start(self
|
|
799
|
-
let child_seq =
|
|
1523
|
+
pub fn start(self) -> impl CancellableFutureWithReason<PendingChildWorkflow> {
|
|
1524
|
+
let child_seq = self
|
|
1525
|
+
.base_ctx
|
|
1526
|
+
.inner
|
|
1527
|
+
.seq_nums
|
|
1528
|
+
.borrow_mut()
|
|
1529
|
+
.next_child_workflow_seq();
|
|
800
1530
|
// Immediately create the command/future for the result, otherwise if the user does
|
|
801
1531
|
// not await the result until *after* we receive an activation for it, there will be nothing
|
|
802
1532
|
// to match when unblocking.
|
|
803
|
-
let cancel_seq =
|
|
804
|
-
|
|
805
|
-
|
|
1533
|
+
let cancel_seq = self
|
|
1534
|
+
.base_ctx
|
|
1535
|
+
.inner
|
|
1536
|
+
.seq_nums
|
|
1537
|
+
.borrow_mut()
|
|
1538
|
+
.next_cancel_external_wf_seq();
|
|
1539
|
+
let (result_cmd, unblocker) = CancellableWFCommandFut::new(
|
|
1540
|
+
CancellableIDWithReason::ExternalWorkflow {
|
|
806
1541
|
seqnum: cancel_seq,
|
|
807
1542
|
execution: NamespacedWorkflowExecution {
|
|
808
1543
|
workflow_id: self.opts.workflow_id.clone(),
|
|
809
1544
|
..Default::default()
|
|
810
1545
|
},
|
|
811
|
-
}
|
|
812
|
-
|
|
1546
|
+
},
|
|
1547
|
+
self.base_ctx.clone(),
|
|
1548
|
+
);
|
|
1549
|
+
self.base_ctx.send(
|
|
813
1550
|
CommandSubscribeChildWorkflowCompletion {
|
|
814
1551
|
seq: child_seq,
|
|
815
1552
|
unblocker,
|
|
@@ -820,13 +1557,15 @@ impl ChildWorkflow {
|
|
|
820
1557
|
let common = ChildWfCommon {
|
|
821
1558
|
workflow_id: self.opts.workflow_id.clone(),
|
|
822
1559
|
result_future: result_cmd,
|
|
1560
|
+
base_ctx: self.base_ctx.clone(),
|
|
823
1561
|
};
|
|
824
1562
|
|
|
825
1563
|
let (cmd, unblocker) = CancellableWFCommandFut::new_with_dat(
|
|
826
1564
|
CancellableIDWithReason::ChildWorkflow { seqnum: child_seq },
|
|
827
1565
|
common,
|
|
1566
|
+
self.base_ctx.clone(),
|
|
828
1567
|
);
|
|
829
|
-
|
|
1568
|
+
self.base_ctx.send(
|
|
830
1569
|
CommandCreateRequest {
|
|
831
1570
|
cmd: self.opts.into_command(child_seq),
|
|
832
1571
|
unblocker,
|
|
@@ -846,8 +1585,8 @@ impl StartedChildWorkflow {
|
|
|
846
1585
|
}
|
|
847
1586
|
|
|
848
1587
|
/// Cancel the child workflow
|
|
849
|
-
pub fn cancel(&self,
|
|
850
|
-
|
|
1588
|
+
pub fn cancel(&self, reason: String) {
|
|
1589
|
+
self.common.base_ctx.send(RustWfCmd::NewNonblockingCmd(
|
|
851
1590
|
CancelChildWorkflowExecution {
|
|
852
1591
|
child_workflow_seq: self.common.result_future.cancellable_id.seq_num(),
|
|
853
1592
|
reason,
|
|
@@ -857,13 +1596,15 @@ impl StartedChildWorkflow {
|
|
|
857
1596
|
}
|
|
858
1597
|
|
|
859
1598
|
/// Signal the child workflow
|
|
860
|
-
pub fn signal<
|
|
1599
|
+
pub fn signal<S: Into<Signal>>(
|
|
861
1600
|
&self,
|
|
862
|
-
cx: &'a WfContext,
|
|
863
1601
|
data: S,
|
|
864
|
-
) -> impl CancellableFuture<SignalExternalWfResult> +
|
|
1602
|
+
) -> impl CancellableFuture<SignalExternalWfResult> + 'static {
|
|
865
1603
|
let target = sig_we::Target::ChildWorkflowId(self.common.workflow_id.clone());
|
|
866
|
-
|
|
1604
|
+
self.common
|
|
1605
|
+
.base_ctx
|
|
1606
|
+
.clone()
|
|
1607
|
+
.send_signal_wf(target, data.into())
|
|
867
1608
|
}
|
|
868
1609
|
}
|
|
869
1610
|
|
|
@@ -878,6 +1619,7 @@ pub struct StartedNexusOperation {
|
|
|
878
1619
|
pub(crate) struct NexusUnblockData {
|
|
879
1620
|
result_future: Shared<WFCommandFut<NexusOperationResult, ()>>,
|
|
880
1621
|
schedule_seq: u32,
|
|
1622
|
+
base_ctx: BaseWorkflowContext,
|
|
881
1623
|
}
|
|
882
1624
|
|
|
883
1625
|
impl StartedNexusOperation {
|
|
@@ -885,7 +1627,9 @@ impl StartedNexusOperation {
|
|
|
885
1627
|
self.unblock_dat.result_future.clone().await
|
|
886
1628
|
}
|
|
887
1629
|
|
|
888
|
-
pub fn cancel(&self
|
|
889
|
-
|
|
1630
|
+
pub fn cancel(&self) {
|
|
1631
|
+
self.unblock_dat
|
|
1632
|
+
.base_ctx
|
|
1633
|
+
.cancel(CancellableID::NexusOp(self.unblock_dat.schedule_seq));
|
|
890
1634
|
}
|
|
891
1635
|
}
|