@temporalio/core-bridge 0.16.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +339 -226
- package/Cargo.toml +7 -3
- package/common.js +50 -0
- package/index.d.ts +7 -0
- package/index.js +12 -0
- package/package.json +7 -4
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/{index.node → releases/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/scripts/build.js +10 -50
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
- package/sdk-core/.buildkite/pipeline.yml +2 -0
- package/sdk-core/Cargo.toml +1 -88
- package/sdk-core/README.md +30 -6
- package/sdk-core/bridge-ffi/Cargo.toml +24 -0
- package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
- package/sdk-core/bridge-ffi/build.rs +25 -0
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
- package/sdk-core/bridge-ffi/src/lib.rs +829 -0
- package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
- package/sdk-core/client/Cargo.toml +32 -0
- package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
- package/sdk-core/client/src/metrics.rs +89 -0
- package/sdk-core/client/src/mocks.rs +167 -0
- package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
- package/sdk-core/core/Cargo.toml +96 -0
- package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
- package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
- package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
- package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
- package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
- package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
- package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
- package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
- package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
- package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
- package/sdk-core/core/src/lib.rs +374 -0
- package/sdk-core/{src → core/src}/log_export.rs +3 -27
- package/sdk-core/core/src/pending_activations.rs +162 -0
- package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
- package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
- package/sdk-core/core/src/protosext/mod.rs +396 -0
- package/sdk-core/core/src/replay/mod.rs +210 -0
- package/sdk-core/core/src/retry_logic.rs +144 -0
- package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
- package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
- package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
- package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
- package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
- package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
- package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
- package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
- package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
- package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
- package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
- package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
- package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
- package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
- package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
- package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
- package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
- package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
- package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
- package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
- package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
- package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
- package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
- package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
- package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
- package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
- package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
- package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
- package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
- package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
- package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
- package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
- package/sdk-core/{src → core-api/src}/errors.rs +42 -92
- package/sdk-core/core-api/src/lib.rs +158 -0
- package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
- package/sdk-core/etc/deps.svg +156 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
- package/sdk-core/histories/fail_wf_task.bin +0 -0
- package/sdk-core/histories/timer_workflow_history.bin +0 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
- package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
- package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
- package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
- package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
- package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
- package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
- package/sdk-core/sdk/Cargo.toml +32 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
- package/sdk-core/sdk/src/lib.rs +699 -0
- package/sdk-core/sdk/src/payload_converter.rs +11 -0
- package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
- package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
- package/sdk-core/sdk-core-protos/build.rs +28 -6
- package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
- package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
- package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
- package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
- package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
- package/sdk-core/test-utils/Cargo.toml +32 -0
- package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
- package/sdk-core/test-utils/src/histfetch.rs +28 -0
- package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
- package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
- package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
- package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
- package/sdk-core/tests/load_tests.rs +6 -6
- package/sdk-core/tests/main.rs +2 -2
- package/src/conversions.rs +24 -21
- package/src/errors.rs +8 -0
- package/src/lib.rs +323 -211
- package/sdk-core/protos/local/activity_result.proto +0 -46
- package/sdk-core/protos/local/activity_task.proto +0 -66
- package/sdk-core/src/core_tests/retry.rs +0 -147
- package/sdk-core/src/lib.rs +0 -403
- package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
- package/sdk-core/src/pending_activations.rs +0 -249
- package/sdk-core/src/protosext/mod.rs +0 -160
- package/sdk-core/src/prototype_rust_sdk.rs +0 -412
- package/sdk-core/src/task_token.rs +0 -20
- package/sdk-core/src/test_help/history_info.rs +0 -157
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
#![warn(missing_docs)] // error if there are missing docs
|
|
2
|
+
|
|
3
|
+
//! This crate is a rough prototype Rust SDK. It can be used to create closures that look sort of
|
|
4
|
+
//! like normal workflow code. It should only depend on things in the core crate that are already
|
|
5
|
+
//! publicly exposed.
|
|
6
|
+
//!
|
|
7
|
+
//! Needs lots of love to be production ready but the basis is there
|
|
8
|
+
|
|
9
|
+
#[macro_use]
|
|
10
|
+
extern crate tracing;
|
|
11
|
+
|
|
12
|
+
mod conversions;
|
|
13
|
+
mod payload_converter;
|
|
14
|
+
mod workflow_context;
|
|
15
|
+
mod workflow_future;
|
|
16
|
+
|
|
17
|
+
pub use workflow_context::{
|
|
18
|
+
ActivityOptions, CancellableFuture, ChildWorkflow, ChildWorkflowOptions, LocalActivityOptions,
|
|
19
|
+
WfContext,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
use crate::workflow_context::{ChildWfCommon, PendingChildWorkflow};
|
|
23
|
+
use anyhow::{anyhow, bail};
|
|
24
|
+
use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt};
|
|
25
|
+
use std::{
|
|
26
|
+
collections::HashMap,
|
|
27
|
+
fmt::{Debug, Display, Formatter},
|
|
28
|
+
future::Future,
|
|
29
|
+
ops::{Deref, DerefMut},
|
|
30
|
+
sync::{
|
|
31
|
+
atomic::{AtomicUsize, Ordering},
|
|
32
|
+
Arc,
|
|
33
|
+
},
|
|
34
|
+
time::Duration,
|
|
35
|
+
};
|
|
36
|
+
use temporal_sdk_core_api::{
|
|
37
|
+
errors::{PollActivityError, PollWfError},
|
|
38
|
+
Core,
|
|
39
|
+
};
|
|
40
|
+
use temporal_sdk_core_protos::{
|
|
41
|
+
coresdk::{
|
|
42
|
+
activity_result::{ActivityExecutionResult, ActivityResolution},
|
|
43
|
+
activity_task::{activity_task, ActivityTask},
|
|
44
|
+
child_workflow::ChildWorkflowResult,
|
|
45
|
+
common::{NamespacedWorkflowExecution, Payload},
|
|
46
|
+
workflow_activation::{
|
|
47
|
+
resolve_child_workflow_execution_start::Status as ChildWorkflowStartStatus,
|
|
48
|
+
workflow_activation_job::Variant, WorkflowActivation, WorkflowActivationJob,
|
|
49
|
+
},
|
|
50
|
+
workflow_commands::{workflow_command, ContinueAsNewWorkflowExecution},
|
|
51
|
+
workflow_completion::WorkflowActivationCompletion,
|
|
52
|
+
ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
|
|
53
|
+
},
|
|
54
|
+
temporal::api::failure::v1::Failure,
|
|
55
|
+
TaskToken,
|
|
56
|
+
};
|
|
57
|
+
use tokio::{
|
|
58
|
+
sync::{
|
|
59
|
+
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
|
60
|
+
oneshot, watch,
|
|
61
|
+
watch::Receiver,
|
|
62
|
+
},
|
|
63
|
+
task::JoinError,
|
|
64
|
+
};
|
|
65
|
+
use tokio_util::sync::CancellationToken;
|
|
66
|
+
|
|
67
|
+
/// A worker that can poll for and respond to workflow tasks by using [WorkflowFunction]s
|
|
68
|
+
pub struct TestRustWorker {
|
|
69
|
+
core: Arc<dyn Core>,
|
|
70
|
+
task_queue: String,
|
|
71
|
+
task_timeout: Option<Duration>,
|
|
72
|
+
workflow_half: WorkflowHalf,
|
|
73
|
+
activity_half: ActivityHalf,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
struct WorkflowHalf {
|
|
77
|
+
/// Maps run id to the driver
|
|
78
|
+
workflows: HashMap<String, UnboundedSender<WorkflowActivation>>,
|
|
79
|
+
/// Maps workflow type to the function for executing workflow runs with that ID
|
|
80
|
+
workflow_fns: HashMap<String, WorkflowFunction>,
|
|
81
|
+
/// Number of live workflows
|
|
82
|
+
incomplete_workflows: Arc<AtomicUsize>,
|
|
83
|
+
/// Handles for each spawned workflow run are inserted here to be cleaned up when all runs
|
|
84
|
+
/// are finished
|
|
85
|
+
join_handles: FuturesUnordered<BoxFuture<'static, Result<WorkflowResult<()>, JoinError>>>,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
struct ActivityHalf {
|
|
89
|
+
/// Maps activity type to the function for executing activities of that type
|
|
90
|
+
activity_fns: HashMap<String, ActivityFunction>,
|
|
91
|
+
task_tokens_to_cancels: HashMap<TaskToken, CancellationToken>,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
impl TestRustWorker {
|
|
95
|
+
/// Create a new rust worker
|
|
96
|
+
pub fn new(core: Arc<dyn Core>, task_queue: String, task_timeout: Option<Duration>) -> Self {
|
|
97
|
+
Self {
|
|
98
|
+
core,
|
|
99
|
+
task_queue,
|
|
100
|
+
task_timeout,
|
|
101
|
+
workflow_half: WorkflowHalf {
|
|
102
|
+
workflows: Default::default(),
|
|
103
|
+
workflow_fns: Default::default(),
|
|
104
|
+
incomplete_workflows: Arc::new(AtomicUsize::new(0)),
|
|
105
|
+
join_handles: FuturesUnordered::new(),
|
|
106
|
+
},
|
|
107
|
+
activity_half: ActivityHalf {
|
|
108
|
+
activity_fns: Default::default(),
|
|
109
|
+
task_tokens_to_cancels: Default::default(),
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Returns the task queue name this worker polls on
|
|
115
|
+
pub fn task_queue(&self) -> &str {
|
|
116
|
+
&self.task_queue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Create a workflow, asking the server to start it with the provided workflow ID and using the
|
|
120
|
+
/// provided workflow function.
|
|
121
|
+
///
|
|
122
|
+
/// Increments the expected Workflow run count.
|
|
123
|
+
///
|
|
124
|
+
/// Returns the run id of the started workflow
|
|
125
|
+
pub async fn submit_wf(
|
|
126
|
+
&self,
|
|
127
|
+
workflow_id: impl Into<String>,
|
|
128
|
+
workflow_type: impl Into<String>,
|
|
129
|
+
input: Vec<Payload>,
|
|
130
|
+
) -> Result<String, tonic::Status> {
|
|
131
|
+
let res = self
|
|
132
|
+
.core
|
|
133
|
+
.server_gateway()
|
|
134
|
+
.start_workflow(
|
|
135
|
+
input,
|
|
136
|
+
self.task_queue.clone(),
|
|
137
|
+
workflow_id.into(),
|
|
138
|
+
workflow_type.into(),
|
|
139
|
+
self.task_timeout,
|
|
140
|
+
)
|
|
141
|
+
.await?;
|
|
142
|
+
|
|
143
|
+
self.incr_expected_run_count(1);
|
|
144
|
+
Ok(res.run_id)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Register a Workflow function to invoke when the Worker is asked to run a workflow of
|
|
148
|
+
/// `workflow_type`
|
|
149
|
+
pub fn register_wf<F: Into<WorkflowFunction>>(
|
|
150
|
+
&mut self,
|
|
151
|
+
workflow_type: impl Into<String>,
|
|
152
|
+
wf_function: F,
|
|
153
|
+
) {
|
|
154
|
+
self.workflow_half
|
|
155
|
+
.workflow_fns
|
|
156
|
+
.insert(workflow_type.into(), wf_function.into());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// Register an Activity function to invoke when the Worker is asked to run an activity of
|
|
160
|
+
/// `activity_type`
|
|
161
|
+
pub fn register_activity<A, R>(
|
|
162
|
+
&mut self,
|
|
163
|
+
activity_type: impl Into<String>,
|
|
164
|
+
act_function: impl IntoActivityFunc<A, R>,
|
|
165
|
+
) {
|
|
166
|
+
self.activity_half.activity_fns.insert(
|
|
167
|
+
activity_type.into(),
|
|
168
|
+
ActivityFunction {
|
|
169
|
+
act_func: act_function.into_activity_fn(),
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// TODO: Should be removed before making this worker prod ready. There can be a test worker
|
|
175
|
+
// which wraps this one and implements the workflow counting / run_until_done concepts.
|
|
176
|
+
// This worker can expose an interceptor for completions that could be used to assist with
|
|
177
|
+
// workflow tracking
|
|
178
|
+
/// Increment the expected Workflow run count on this Worker. The Worker tracks the run count
|
|
179
|
+
/// and will resolve `run_until_done` when it goes down to 0.
|
|
180
|
+
/// You do not have to increment if scheduled a Workflow with `submit_wf`.
|
|
181
|
+
pub fn incr_expected_run_count(&self, count: usize) {
|
|
182
|
+
self.workflow_half
|
|
183
|
+
.incomplete_workflows
|
|
184
|
+
.fetch_add(count, Ordering::SeqCst);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// See [Self::run_until_done], except calls the provided callback just before performing core
|
|
188
|
+
/// shutdown.
|
|
189
|
+
pub async fn run_until_done_shutdown_hook(
|
|
190
|
+
&mut self,
|
|
191
|
+
before_shutdown: impl FnOnce(),
|
|
192
|
+
) -> Result<(), anyhow::Error> {
|
|
193
|
+
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
|
194
|
+
let pollers = async move {
|
|
195
|
+
let (core, task_q, wf_half, act_half) = self.split_apart();
|
|
196
|
+
let (completions_tx, mut completions_rx) = unbounded_channel();
|
|
197
|
+
let (wf_poll_res, act_poll_res) = tokio::join!(
|
|
198
|
+
// Workflow polling loop
|
|
199
|
+
async {
|
|
200
|
+
loop {
|
|
201
|
+
let activation = match core.poll_workflow_activation(task_q).await {
|
|
202
|
+
Err(PollWfError::ShutDown) => {
|
|
203
|
+
break Result::<_, anyhow::Error>::Ok(());
|
|
204
|
+
}
|
|
205
|
+
o => o?,
|
|
206
|
+
};
|
|
207
|
+
wf_half
|
|
208
|
+
.workflow_activation_handler(
|
|
209
|
+
core.as_ref(),
|
|
210
|
+
task_q,
|
|
211
|
+
&shutdown_rx,
|
|
212
|
+
&completions_tx,
|
|
213
|
+
&mut completions_rx,
|
|
214
|
+
activation,
|
|
215
|
+
)
|
|
216
|
+
.await?;
|
|
217
|
+
if wf_half.incomplete_workflows.load(Ordering::SeqCst) == 0 {
|
|
218
|
+
// Die rebel scum - evict all workflows (which are complete now),
|
|
219
|
+
// and turn off activity polling.
|
|
220
|
+
let _ = shutdown_tx.send(true);
|
|
221
|
+
break Result::<_, anyhow::Error>::Ok(());
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
// Only poll on the activity queue if activity functions have been registered. This
|
|
226
|
+
// makes tests which use mocks dramatically more manageable.
|
|
227
|
+
async {
|
|
228
|
+
let mut shutdown_rx = shutdown_rx.clone();
|
|
229
|
+
if !act_half.activity_fns.is_empty() {
|
|
230
|
+
loop {
|
|
231
|
+
tokio::select! {
|
|
232
|
+
activity = core.poll_activity_task(task_q) => {
|
|
233
|
+
if matches!(activity, Err(PollActivityError::ShutDown)) {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
act_half.activity_task_handler(core.clone(), task_q,
|
|
237
|
+
activity?)?;
|
|
238
|
+
},
|
|
239
|
+
_ = shutdown_rx.changed() => { break }
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
Result::<_, anyhow::Error>::Ok(())
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
wf_poll_res?;
|
|
247
|
+
// TODO: Activity loop errors don't show up until wf loop exits/errors
|
|
248
|
+
act_poll_res?;
|
|
249
|
+
Result::<_, anyhow::Error>::Ok(self)
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
let myself = pollers.await?;
|
|
253
|
+
while let Some(h) = myself.workflow_half.join_handles.next().await {
|
|
254
|
+
h??;
|
|
255
|
+
}
|
|
256
|
+
before_shutdown();
|
|
257
|
+
myself.core.shutdown().await;
|
|
258
|
+
myself.workflow_half.workflows.clear();
|
|
259
|
+
Ok(())
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// Drives all workflows & activities until they have all finished, repeatedly polls server to
|
|
263
|
+
/// fetch work for them.
|
|
264
|
+
pub async fn run_until_done(&mut self) -> Result<(), anyhow::Error> {
|
|
265
|
+
self.run_until_done_shutdown_hook(|| {}).await
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/// Temporarily swap the core implementation used by this worker. When the returned value is
|
|
269
|
+
/// dropped, the old core implementation will be restored. This can be used to run against
|
|
270
|
+
/// mocked-out core instances.
|
|
271
|
+
pub fn swap_core(&mut self, other_core: Arc<dyn Core>) -> impl DerefMut<Target = Self> + '_ {
|
|
272
|
+
let old_core = std::mem::replace(&mut self.core, other_core);
|
|
273
|
+
RustWorkerSwappedCore {
|
|
274
|
+
old_core: Some(old_core),
|
|
275
|
+
worker: self,
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
fn split_apart(&mut self) -> (Arc<dyn Core>, &str, &mut WorkflowHalf, &mut ActivityHalf) {
|
|
280
|
+
(
|
|
281
|
+
self.core.clone(),
|
|
282
|
+
&self.task_queue,
|
|
283
|
+
&mut self.workflow_half,
|
|
284
|
+
&mut self.activity_half,
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
impl WorkflowHalf {
|
|
290
|
+
async fn workflow_activation_handler(
|
|
291
|
+
&mut self,
|
|
292
|
+
core: &dyn Core,
|
|
293
|
+
task_queue: &str,
|
|
294
|
+
shutdown_rx: &Receiver<bool>,
|
|
295
|
+
completions_tx: &UnboundedSender<WorkflowActivationCompletion>,
|
|
296
|
+
completions_rx: &mut UnboundedReceiver<WorkflowActivationCompletion>,
|
|
297
|
+
activation: WorkflowActivation,
|
|
298
|
+
) -> Result<(), anyhow::Error> {
|
|
299
|
+
// If the activation is to start a workflow, create a new workflow driver for it,
|
|
300
|
+
// using the function associated with that workflow id
|
|
301
|
+
if let Some(WorkflowActivationJob {
|
|
302
|
+
variant: Some(Variant::StartWorkflow(sw)),
|
|
303
|
+
}) = activation.jobs.get(0)
|
|
304
|
+
{
|
|
305
|
+
let wf_function = self
|
|
306
|
+
.workflow_fns
|
|
307
|
+
.get(&sw.workflow_type)
|
|
308
|
+
.ok_or_else(|| anyhow!("Workflow type not found"))?;
|
|
309
|
+
|
|
310
|
+
let (wff, activations) = wf_function.start_workflow(
|
|
311
|
+
core.server_gateway().get_options().namespace.clone(),
|
|
312
|
+
task_queue.to_string(),
|
|
313
|
+
// NOTE: Don't clone args if this gets ported to be a non-test rust worker
|
|
314
|
+
sw.arguments.clone(),
|
|
315
|
+
completions_tx.clone(),
|
|
316
|
+
);
|
|
317
|
+
let mut shutdown_rx = shutdown_rx.clone();
|
|
318
|
+
let jh = tokio::spawn(async move {
|
|
319
|
+
tokio::select! {
|
|
320
|
+
r = wff => r,
|
|
321
|
+
_ = shutdown_rx.changed() => Ok(WfExitValue::Evicted)
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
self.workflows
|
|
325
|
+
.insert(activation.run_id.clone(), activations);
|
|
326
|
+
self.join_handles.push(jh.boxed());
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// The activation is expected to apply to some workflow we know about. Use it to
|
|
330
|
+
// unblock things and advance the workflow.
|
|
331
|
+
if let Some(tx) = self.workflows.get_mut(&activation.run_id) {
|
|
332
|
+
tx.send(activation)
|
|
333
|
+
.expect("Workflow should exist if we're sending it an activation");
|
|
334
|
+
} else {
|
|
335
|
+
bail!("Got activation for unknown workflow");
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
let completion = completions_rx.recv().await.expect("No workflows left?");
|
|
339
|
+
if completion.has_execution_ending() {
|
|
340
|
+
debug!("Workflow {} says it's finishing", &completion.run_id);
|
|
341
|
+
self.incomplete_workflows.fetch_sub(1, Ordering::SeqCst);
|
|
342
|
+
}
|
|
343
|
+
core.complete_workflow_activation(completion).await?;
|
|
344
|
+
Ok(())
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
tokio::task_local! {
|
|
349
|
+
// This works, but maybe just passing a context object for activities like WFs is better
|
|
350
|
+
static ACT_CANCEL_TOK: CancellationToken
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// Returns a future the completes if and when the activity this was called inside has been
|
|
354
|
+
/// cancelled
|
|
355
|
+
pub async fn act_cancelled() {
|
|
356
|
+
ACT_CANCEL_TOK.with(|ct| ct.clone()).cancelled().await
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/// Returns true if this activity has already been cancelled
|
|
360
|
+
pub fn act_is_cancelled() -> bool {
|
|
361
|
+
ACT_CANCEL_TOK.with(|ct| ct.is_cancelled())
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
impl ActivityHalf {
|
|
365
|
+
/// Spawns off a task to handle the provided activity task
|
|
366
|
+
fn activity_task_handler(
|
|
367
|
+
&mut self,
|
|
368
|
+
core: Arc<dyn Core>,
|
|
369
|
+
task_queue: &str,
|
|
370
|
+
activity: ActivityTask,
|
|
371
|
+
) -> Result<(), anyhow::Error> {
|
|
372
|
+
match activity.variant {
|
|
373
|
+
Some(activity_task::Variant::Start(start)) => {
|
|
374
|
+
let task_queue = task_queue.to_string();
|
|
375
|
+
let act_fn = self
|
|
376
|
+
.activity_fns
|
|
377
|
+
.get(&start.activity_type)
|
|
378
|
+
.ok_or_else(|| {
|
|
379
|
+
anyhow!(
|
|
380
|
+
"No function registered for activity type {}",
|
|
381
|
+
start.activity_type
|
|
382
|
+
)
|
|
383
|
+
})?
|
|
384
|
+
.clone();
|
|
385
|
+
let ct = CancellationToken::new();
|
|
386
|
+
self.task_tokens_to_cancels
|
|
387
|
+
.insert(activity.task_token.clone().into(), ct.clone());
|
|
388
|
+
|
|
389
|
+
tokio::spawn(ACT_CANCEL_TOK.scope(ct, async move {
|
|
390
|
+
let mut inputs = start.input;
|
|
391
|
+
let arg = inputs.pop().unwrap_or_default();
|
|
392
|
+
let output = (&act_fn.act_func)(arg).await;
|
|
393
|
+
let result = match output {
|
|
394
|
+
Ok(res) => ActivityExecutionResult::ok(res),
|
|
395
|
+
Err(err) => match err.downcast::<ActivityCancelledError>() {
|
|
396
|
+
Ok(ce) => ActivityExecutionResult::cancel_from_details(ce.details),
|
|
397
|
+
Err(other_err) => ActivityExecutionResult::fail(other_err.into()),
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
core.complete_activity_task(ActivityTaskCompletion {
|
|
401
|
+
task_token: activity.task_token,
|
|
402
|
+
task_queue,
|
|
403
|
+
result: Some(result),
|
|
404
|
+
})
|
|
405
|
+
.await?;
|
|
406
|
+
Result::<_, anyhow::Error>::Ok(())
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
Some(activity_task::Variant::Cancel(_)) => {
|
|
410
|
+
if let Some(ct) = self.task_tokens_to_cancels.get(&activity.task_token.into()) {
|
|
411
|
+
ct.cancel();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
None => bail!("Undefined activity task variant"),
|
|
415
|
+
}
|
|
416
|
+
Ok(())
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#[derive(Debug)]
|
|
421
|
+
enum UnblockEvent {
|
|
422
|
+
Timer(u32, TimerResult),
|
|
423
|
+
Activity(u32, Box<ActivityResolution>),
|
|
424
|
+
WorkflowStart(u32, Box<ChildWorkflowStartStatus>),
|
|
425
|
+
WorkflowComplete(u32, Box<ChildWorkflowResult>),
|
|
426
|
+
SignalExternal(u32, Option<Failure>),
|
|
427
|
+
CancelExternal(u32, Option<Failure>),
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/// Result of awaiting on a timer
|
|
431
|
+
#[derive(Debug, Copy, Clone)]
|
|
432
|
+
pub enum TimerResult {
|
|
433
|
+
/// The timer was cancelled
|
|
434
|
+
Cancelled,
|
|
435
|
+
/// The timer elapsed and fired
|
|
436
|
+
Fired,
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/// Successful result of sending a signal to an external workflow
|
|
440
|
+
pub struct SignalExternalOk;
|
|
441
|
+
/// Result of awaiting on sending a signal to an external workflow
|
|
442
|
+
pub type SignalExternalWfResult = Result<SignalExternalOk, Failure>;
|
|
443
|
+
|
|
444
|
+
/// Successful result of sending a cancel request to an external workflow
|
|
445
|
+
pub struct CancelExternalOk;
|
|
446
|
+
/// Result of awaiting on sending a cancel request to an external workflow
|
|
447
|
+
pub type CancelExternalWfResult = Result<CancelExternalOk, Failure>;
|
|
448
|
+
|
|
449
|
+
trait Unblockable {
|
|
450
|
+
type OtherDat;
|
|
451
|
+
|
|
452
|
+
fn unblock(ue: UnblockEvent, od: Self::OtherDat) -> Self;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
impl Unblockable for TimerResult {
|
|
456
|
+
type OtherDat = ();
|
|
457
|
+
fn unblock(ue: UnblockEvent, _: Self::OtherDat) -> Self {
|
|
458
|
+
match ue {
|
|
459
|
+
UnblockEvent::Timer(_, result) => result,
|
|
460
|
+
_ => panic!("Invalid unblock event for timer"),
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
impl Unblockable for ActivityResolution {
|
|
466
|
+
type OtherDat = ();
|
|
467
|
+
fn unblock(ue: UnblockEvent, _: Self::OtherDat) -> Self {
|
|
468
|
+
match ue {
|
|
469
|
+
UnblockEvent::Activity(_, result) => *result,
|
|
470
|
+
_ => panic!("Invalid unblock event for activity"),
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
impl Unblockable for PendingChildWorkflow {
|
|
476
|
+
// Other data here is workflow id
|
|
477
|
+
type OtherDat = ChildWfCommon;
|
|
478
|
+
fn unblock(ue: UnblockEvent, od: Self::OtherDat) -> Self {
|
|
479
|
+
match ue {
|
|
480
|
+
UnblockEvent::WorkflowStart(_, result) => Self {
|
|
481
|
+
status: *result,
|
|
482
|
+
common: od,
|
|
483
|
+
},
|
|
484
|
+
_ => panic!("Invalid unblock event for child workflow start"),
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
impl Unblockable for ChildWorkflowResult {
|
|
490
|
+
type OtherDat = ();
|
|
491
|
+
fn unblock(ue: UnblockEvent, _: Self::OtherDat) -> Self {
|
|
492
|
+
match ue {
|
|
493
|
+
UnblockEvent::WorkflowComplete(_, result) => *result,
|
|
494
|
+
_ => panic!("Invalid unblock event for child workflow complete"),
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
impl Unblockable for SignalExternalWfResult {
|
|
500
|
+
type OtherDat = ();
|
|
501
|
+
fn unblock(ue: UnblockEvent, _: Self::OtherDat) -> Self {
|
|
502
|
+
match ue {
|
|
503
|
+
UnblockEvent::SignalExternal(_, maybefail) => {
|
|
504
|
+
maybefail.map_or(Ok(SignalExternalOk), Err)
|
|
505
|
+
}
|
|
506
|
+
_ => panic!("Invalid unblock event for signal external workflow result"),
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
impl Unblockable for CancelExternalWfResult {
|
|
512
|
+
type OtherDat = ();
|
|
513
|
+
fn unblock(ue: UnblockEvent, _: Self::OtherDat) -> Self {
|
|
514
|
+
match ue {
|
|
515
|
+
UnblockEvent::CancelExternal(_, maybefail) => {
|
|
516
|
+
maybefail.map_or(Ok(CancelExternalOk), Err)
|
|
517
|
+
}
|
|
518
|
+
_ => panic!("Invalid unblock event for signal external workflow result"),
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/// Identifier for cancellable operations
|
|
524
|
+
#[derive(Debug, Clone)]
|
|
525
|
+
pub enum CancellableID {
|
|
526
|
+
/// Timer sequence number
|
|
527
|
+
Timer(u32),
|
|
528
|
+
/// Activity sequence number
|
|
529
|
+
Activity(u32),
|
|
530
|
+
/// Activity sequence number
|
|
531
|
+
LocalActivity(u32),
|
|
532
|
+
/// Start child sequence number
|
|
533
|
+
ChildWorkflow(u32),
|
|
534
|
+
/// Signal workflow
|
|
535
|
+
SignalExternalWorkflow(u32),
|
|
536
|
+
/// An external workflow identifier as may have been created by a started child workflow
|
|
537
|
+
ExternalWorkflow {
|
|
538
|
+
/// Sequence number which will be used for the cancel command
|
|
539
|
+
seqnum: u32,
|
|
540
|
+
/// Identifying information about the workflow to be cancelled
|
|
541
|
+
execution: NamespacedWorkflowExecution,
|
|
542
|
+
/// Set to true if this workflow is a child of the issuing workflow
|
|
543
|
+
only_child: bool,
|
|
544
|
+
},
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
#[derive(derive_more::From)]
|
|
548
|
+
#[allow(clippy::large_enum_variant)]
|
|
549
|
+
enum RustWfCmd {
|
|
550
|
+
#[from(ignore)]
|
|
551
|
+
Cancel(CancellableID),
|
|
552
|
+
ForceWFTFailure(anyhow::Error),
|
|
553
|
+
NewCmd(CommandCreateRequest),
|
|
554
|
+
NewNonblockingCmd(workflow_command::Variant),
|
|
555
|
+
SubscribeChildWorkflowCompletion(CommandSubscribeChildWorkflowCompletion),
|
|
556
|
+
SubscribeSignal(String, UnboundedSender<Vec<Payload>>),
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
struct CommandCreateRequest {
|
|
560
|
+
cmd: workflow_command::Variant,
|
|
561
|
+
unblocker: oneshot::Sender<UnblockEvent>,
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
struct CommandSubscribeChildWorkflowCompletion {
|
|
565
|
+
seq: u32,
|
|
566
|
+
unblocker: oneshot::Sender<UnblockEvent>,
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
type WfFunc = dyn Fn(WfContext) -> BoxFuture<'static, WorkflowResult<()>> + Send + Sync + 'static;
|
|
570
|
+
|
|
571
|
+
/// The user's async function / workflow code
|
|
572
|
+
pub struct WorkflowFunction {
|
|
573
|
+
wf_func: Box<WfFunc>,
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
impl<F, Fut> From<F> for WorkflowFunction
|
|
577
|
+
where
|
|
578
|
+
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
579
|
+
Fut: Future<Output = WorkflowResult<()>> + Send + 'static,
|
|
580
|
+
{
|
|
581
|
+
fn from(wf_func: F) -> Self {
|
|
582
|
+
Self::new(wf_func)
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
impl WorkflowFunction {
|
|
587
|
+
/// Build a workflow function from a closure or function pointer which accepts a [WfContext]
|
|
588
|
+
pub fn new<F, Fut>(wf_func: F) -> Self
|
|
589
|
+
where
|
|
590
|
+
F: Fn(WfContext) -> Fut + Send + Sync + 'static,
|
|
591
|
+
Fut: Future<Output = WorkflowResult<()>> + Send + 'static,
|
|
592
|
+
{
|
|
593
|
+
Self {
|
|
594
|
+
wf_func: Box::new(move |ctx: WfContext| wf_func(ctx).boxed()),
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/// The result of running a workflow
|
|
600
|
+
pub type WorkflowResult<T> = Result<WfExitValue<T>, anyhow::Error>;
|
|
601
|
+
|
|
602
|
+
/// Workflow functions may return these values when exiting
|
|
603
|
+
#[derive(Debug, derive_more::From)]
|
|
604
|
+
pub enum WfExitValue<T: Debug> {
|
|
605
|
+
/// Continue the workflow as a new execution
|
|
606
|
+
#[from(ignore)]
|
|
607
|
+
ContinueAsNew(Box<ContinueAsNewWorkflowExecution>),
|
|
608
|
+
/// Confirm the workflow was cancelled (can be automatic in a more advanced iteration)
|
|
609
|
+
#[from(ignore)]
|
|
610
|
+
Cancelled,
|
|
611
|
+
/// The run was evicted
|
|
612
|
+
#[from(ignore)]
|
|
613
|
+
Evicted,
|
|
614
|
+
/// Finish with a result
|
|
615
|
+
Normal(T),
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
impl<T: Debug> WfExitValue<T> {
|
|
619
|
+
/// Construct a [WfExitValue::ContinueAsNew] variant (handles boxing)
|
|
620
|
+
pub fn continue_as_new(can: ContinueAsNewWorkflowExecution) -> Self {
|
|
621
|
+
Self::ContinueAsNew(Box::new(can))
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
type BoxActFn =
|
|
626
|
+
Arc<dyn Fn(Payload) -> BoxFuture<'static, Result<Payload, anyhow::Error>> + Send + Sync>;
|
|
627
|
+
/// Container for user-defined activity functions
|
|
628
|
+
#[derive(Clone)]
|
|
629
|
+
pub struct ActivityFunction {
|
|
630
|
+
act_func: BoxActFn,
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/// Return this error to indicate your activity is cancelling
|
|
634
|
+
#[derive(Debug, Default)]
|
|
635
|
+
pub struct ActivityCancelledError {
|
|
636
|
+
details: Option<Payload>,
|
|
637
|
+
}
|
|
638
|
+
impl std::error::Error for ActivityCancelledError {}
|
|
639
|
+
impl Display for ActivityCancelledError {
|
|
640
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
641
|
+
write!(f, "Activity cancelled")
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/// Closures / functions which can be turned into activity functions implement this trait
|
|
646
|
+
pub trait IntoActivityFunc<Args, Res> {
|
|
647
|
+
/// Consume the closure or fn pointer and turned it into a boxed activity function
|
|
648
|
+
fn into_activity_fn(self) -> BoxActFn;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
impl<A, Rf, R, F> IntoActivityFunc<A, Rf> for F
|
|
652
|
+
where
|
|
653
|
+
F: (Fn(A) -> Rf) + Sync + Send + 'static,
|
|
654
|
+
A: FromJsonPayloadExt + Send,
|
|
655
|
+
Rf: Future<Output = Result<R, anyhow::Error>> + Send + 'static,
|
|
656
|
+
R: AsJsonPayloadExt,
|
|
657
|
+
{
|
|
658
|
+
fn into_activity_fn(self) -> BoxActFn {
|
|
659
|
+
let wrapper = move |input: Payload| {
|
|
660
|
+
// Some minor gymnastics are required to avoid needing to clone the function
|
|
661
|
+
match A::from_json_payload(&input) {
|
|
662
|
+
Ok(deser) => (self)(deser)
|
|
663
|
+
.map(|r| r.map(|r| r.as_json_payload())?)
|
|
664
|
+
.boxed(),
|
|
665
|
+
Err(e) => async move { Err(e.into()) }.boxed(),
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
Arc::new(wrapper)
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/// Allows the worker to swap back to an original core instance upon Drop
|
|
673
|
+
struct RustWorkerSwappedCore<'a> {
|
|
674
|
+
old_core: Option<Arc<dyn Core>>,
|
|
675
|
+
worker: &'a mut TestRustWorker,
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
impl<'a> Deref for RustWorkerSwappedCore<'a> {
|
|
679
|
+
type Target = TestRustWorker;
|
|
680
|
+
|
|
681
|
+
fn deref(&self) -> &Self::Target {
|
|
682
|
+
self.worker
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
impl<'a> DerefMut for RustWorkerSwappedCore<'a> {
|
|
687
|
+
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
688
|
+
self.worker
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
impl<'a> Drop for RustWorkerSwappedCore<'a> {
|
|
693
|
+
fn drop(&mut self) {
|
|
694
|
+
let _ = std::mem::replace(
|
|
695
|
+
&mut self.worker.core,
|
|
696
|
+
self.old_core.take().expect("Old core always exists"),
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
}
|