@temporalio/core-bridge 0.19.2 → 0.20.2

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.
Files changed (125) hide show
  1. package/Cargo.lock +90 -157
  2. package/Cargo.toml +1 -0
  3. package/index.d.ts +11 -27
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.cargo/config.toml +1 -0
  13. package/sdk-core/CODEOWNERS +1 -1
  14. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
  15. package/sdk-core/bridge-ffi/src/lib.rs +311 -315
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
  17. package/sdk-core/client/Cargo.toml +13 -9
  18. package/sdk-core/client/LICENSE.txt +23 -0
  19. package/sdk-core/client/src/lib.rs +286 -174
  20. package/sdk-core/client/src/metrics.rs +86 -12
  21. package/sdk-core/client/src/raw.rs +566 -0
  22. package/sdk-core/client/src/retry.rs +137 -99
  23. package/sdk-core/core/Cargo.toml +15 -10
  24. package/sdk-core/core/LICENSE.txt +23 -0
  25. package/sdk-core/core/benches/workflow_replay.rs +79 -0
  26. package/sdk-core/core/src/abstractions.rs +38 -0
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
  29. package/sdk-core/core/src/core_tests/determinism.rs +24 -12
  30. package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
  31. package/sdk-core/core/src/core_tests/mod.rs +30 -43
  32. package/sdk-core/core/src/core_tests/queries.rs +82 -81
  33. package/sdk-core/core/src/core_tests/workers.rs +111 -296
  34. package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
  35. package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
  36. package/sdk-core/core/src/lib.rs +73 -318
  37. package/sdk-core/core/src/pollers/mod.rs +4 -6
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
  39. package/sdk-core/core/src/protosext/mod.rs +7 -10
  40. package/sdk-core/core/src/replay/mod.rs +11 -150
  41. package/sdk-core/core/src/telemetry/metrics.rs +35 -2
  42. package/sdk-core/core/src/telemetry/mod.rs +49 -16
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
  44. package/sdk-core/core/src/test_help/mod.rs +104 -170
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
  46. package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
  47. package/sdk-core/core/src/worker/activities.rs +23 -16
  48. package/sdk-core/core/src/worker/client/mocks.rs +86 -0
  49. package/sdk-core/core/src/worker/client.rs +209 -0
  50. package/sdk-core/core/src/worker/mod.rs +207 -108
  51. package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
  52. package/sdk-core/core/src/workflow/history_update.rs +107 -24
  53. package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
  54. package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
  55. package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
  56. package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
  57. package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
  58. package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
  59. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
  60. package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
  61. package/sdk-core/core/src/workflow/mod.rs +13 -1
  62. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
  63. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
  64. package/sdk-core/core-api/Cargo.toml +9 -1
  65. package/sdk-core/core-api/LICENSE.txt +23 -0
  66. package/sdk-core/core-api/src/errors.rs +7 -38
  67. package/sdk-core/core-api/src/lib.rs +44 -52
  68. package/sdk-core/core-api/src/worker.rs +10 -2
  69. package/sdk-core/etc/deps.svg +127 -96
  70. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
  73. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
  74. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
  75. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
  80. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
  81. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
  82. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
  84. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
  85. package/sdk-core/sdk/Cargo.toml +16 -2
  86. package/sdk-core/sdk/LICENSE.txt +23 -0
  87. package/sdk-core/sdk/src/interceptors.rs +11 -0
  88. package/sdk-core/sdk/src/lib.rs +139 -151
  89. package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
  90. package/sdk-core/sdk/src/workflow_context.rs +36 -17
  91. package/sdk-core/sdk/src/workflow_future.rs +19 -25
  92. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  93. package/sdk-core/sdk-core-protos/build.rs +1 -0
  94. package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
  95. package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
  96. package/sdk-core/test-utils/Cargo.toml +3 -1
  97. package/sdk-core/test-utils/src/canned_histories.rs +27 -0
  98. package/sdk-core/test-utils/src/histfetch.rs +3 -3
  99. package/sdk-core/test-utils/src/lib.rs +223 -68
  100. package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
  101. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
  102. package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
  103. package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
  104. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
  105. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
  106. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
  107. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
  108. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
  109. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
  110. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
  111. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
  112. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
  113. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
  114. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
  115. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
  116. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
  117. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
  118. package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
  119. package/sdk-core/tests/load_tests.rs +9 -6
  120. package/sdk-core/tests/main.rs +43 -10
  121. package/src/conversions.rs +7 -12
  122. package/src/lib.rs +322 -357
  123. package/sdk-core/client/src/mocks.rs +0 -167
  124. package/sdk-core/core/src/worker/dispatcher.rs +0 -171
  125. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
@@ -1,41 +1,67 @@
1
1
  #![warn(missing_docs)] // error if there are missing docs
2
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.
3
+ //! This crate defines an alpha-stage Temporal Rust SDK.
6
4
  //!
7
- //! Needs lots of love to be production ready but the basis is there
5
+ //! Currently defining activities and running an activity-only worker is the most stable code.
6
+ //! Workflow definitions exist and running a workflow worker works, but the API is still very
7
+ //! unstable.
8
+ //!
9
+ //! An example of running an activity worker:
10
+ //! ```no_run
11
+ //! use std::{sync::Arc, str::FromStr};
12
+ //! use temporal_sdk::{sdk_client_options, Worker};
13
+ //! use temporal_sdk_core::{init_worker, Url};
14
+ //! use temporal_sdk_core_api::worker::{WorkerConfig, WorkerConfigBuilder};
15
+ //!
16
+ //! #[tokio::main]
17
+ //! async fn main() -> Result<(), Box<dyn std::error::Error>> {
18
+ //! let server_options = sdk_client_options(Url::from_str("http://localhost:7233")?).build()?;
19
+ //! let client = server_options.connect("my_namespace", None).await?;
20
+ //! let worker_config = WorkerConfigBuilder::default().build()?;
21
+ //! let core_worker = init_worker(worker_config, client);
22
+ //!
23
+ //! let mut worker = Worker::new_from_core(Arc::new(core_worker), "task_queue");
24
+ //! worker.register_activity(
25
+ //! "echo_activity",
26
+ //! |echo_me: String| async move { Ok(echo_me) },
27
+ //! );
28
+ //! worker.run().await?;
29
+ //! Ok(())
30
+ //! }
31
+ //! ```
8
32
 
9
33
  #[macro_use]
10
34
  extern crate tracing;
11
35
 
12
36
  mod conversions;
37
+ pub mod interceptors;
13
38
  mod payload_converter;
14
39
  mod workflow_context;
15
40
  mod workflow_future;
16
41
 
17
42
  pub use workflow_context::{
18
43
  ActivityOptions, CancellableFuture, ChildWorkflow, ChildWorkflowOptions, LocalActivityOptions,
19
- WfContext,
44
+ Signal, SignalData, SignalWorkflowOptions, WfContext,
20
45
  };
21
46
 
22
- use crate::workflow_context::{ChildWfCommon, PendingChildWorkflow};
47
+ use crate::{
48
+ interceptors::WorkerInterceptor,
49
+ workflow_context::{ChildWfCommon, PendingChildWorkflow},
50
+ };
23
51
  use anyhow::{anyhow, bail};
24
52
  use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt};
53
+ use once_cell::sync::OnceCell;
25
54
  use std::{
26
55
  collections::HashMap,
27
56
  fmt::{Debug, Display, Formatter},
28
57
  future::Future,
29
- ops::{Deref, DerefMut},
30
- sync::{
31
- atomic::{AtomicUsize, Ordering},
32
- Arc,
33
- },
34
- time::Duration,
58
+ sync::Arc,
35
59
  };
60
+ use temporal_client::ClientOptionsBuilder;
61
+ use temporal_sdk_core::Url;
36
62
  use temporal_sdk_core_api::{
37
63
  errors::{PollActivityError, PollWfError},
38
- Core,
64
+ Worker as CoreWorker,
39
65
  };
40
66
  use temporal_sdk_core_protos::{
41
67
  coresdk::{
@@ -64,22 +90,40 @@ use tokio::{
64
90
  };
65
91
  use tokio_util::sync::CancellationToken;
66
92
 
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>,
93
+ const VERSION: &str = env!("CARGO_PKG_VERSION");
94
+
95
+ /// Returns a [ClientOptionsBuilder] with required fields set to appropriate values
96
+ /// for the Rust SDK.
97
+ pub fn sdk_client_options(url: impl Into<Url>) -> ClientOptionsBuilder {
98
+ let mut builder = ClientOptionsBuilder::default();
99
+ builder
100
+ .target_url(url)
101
+ .client_name("rust-sdk".to_string())
102
+ .client_version(VERSION.to_string())
103
+ .worker_binary_id(binary_id().to_string());
104
+
105
+ builder
106
+ }
107
+
108
+ /// A worker that can poll for and respond to workflow tasks by using [WorkflowFunction]s,
109
+ /// and activity tasks by using [ActivityFunction]s
110
+ pub struct Worker {
111
+ common: CommonWorker,
72
112
  workflow_half: WorkflowHalf,
73
113
  activity_half: ActivityHalf,
74
114
  }
75
115
 
116
+ struct CommonWorker {
117
+ worker: Arc<dyn CoreWorker>,
118
+ task_queue: String,
119
+ worker_interceptor: Option<Box<dyn WorkerInterceptor>>,
120
+ }
121
+
76
122
  struct WorkflowHalf {
77
123
  /// Maps run id to the driver
78
124
  workflows: HashMap<String, UnboundedSender<WorkflowActivation>>,
79
125
  /// Maps workflow type to the function for executing workflow runs with that ID
80
126
  workflow_fns: HashMap<String, WorkflowFunction>,
81
- /// Number of live workflows
82
- incomplete_workflows: Arc<AtomicUsize>,
83
127
  /// Handles for each spawned workflow run are inserted here to be cleaned up when all runs
84
128
  /// are finished
85
129
  join_handles: FuturesUnordered<BoxFuture<'static, Result<WorkflowResult<()>, JoinError>>>,
@@ -91,17 +135,21 @@ struct ActivityHalf {
91
135
  task_tokens_to_cancels: HashMap<TaskToken, CancellationToken>,
92
136
  }
93
137
 
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 {
138
+ impl Worker {
139
+ // pub fn new(cfg: WorkerConfig) -> Self {}
140
+
141
+ #[doc(hidden)]
142
+ /// Create a new rust worker from a core worker
143
+ pub fn new_from_core(worker: Arc<dyn CoreWorker>, task_queue: impl Into<String>) -> Self {
97
144
  Self {
98
- core,
99
- task_queue,
100
- task_timeout,
145
+ common: CommonWorker {
146
+ worker,
147
+ task_queue: task_queue.into(),
148
+ worker_interceptor: None,
149
+ },
101
150
  workflow_half: WorkflowHalf {
102
151
  workflows: Default::default(),
103
152
  workflow_fns: Default::default(),
104
- incomplete_workflows: Arc::new(AtomicUsize::new(0)),
105
153
  join_handles: FuturesUnordered::new(),
106
154
  },
107
155
  activity_half: ActivityHalf {
@@ -113,35 +161,14 @@ impl TestRustWorker {
113
161
 
114
162
  /// Returns the task queue name this worker polls on
115
163
  pub fn task_queue(&self) -> &str {
116
- &self.task_queue
164
+ &self.common.task_queue
117
165
  }
118
166
 
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)
167
+ /// Return a handle that can be used to initiate shutdown.
168
+ /// TODO: Doc better after shutdown changes
169
+ pub fn shutdown_handle(&self) -> impl Fn() {
170
+ let w = self.common.worker.clone();
171
+ move || w.initiate_shutdown()
145
172
  }
146
173
 
147
174
  /// Register a Workflow function to invoke when the Worker is asked to run a workflow of
@@ -171,34 +198,19 @@ impl TestRustWorker {
171
198
  );
172
199
  }
173
200
 
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> {
201
+ /// Runs the worker. Eventually resolves after the worker has been explicitly shut down,
202
+ /// or may return early with an error in the event of some unresolvable problem.
203
+ pub async fn run(&mut self) -> Result<(), anyhow::Error> {
193
204
  let (shutdown_tx, shutdown_rx) = watch::channel(false);
194
205
  let pollers = async move {
195
- let (core, task_q, wf_half, act_half) = self.split_apart();
206
+ let (common, wf_half, act_half) = self.split_apart();
196
207
  let (completions_tx, mut completions_rx) = unbounded_channel();
197
208
  let (wf_poll_res, act_poll_res) = tokio::join!(
198
209
  // Workflow polling loop
199
210
  async {
200
211
  loop {
201
- let activation = match core.poll_workflow_activation(task_q).await {
212
+ info!("Polling");
213
+ let activation = match common.worker.poll_workflow_activation().await {
202
214
  Err(PollWfError::ShutDown) => {
203
215
  break Result::<_, anyhow::Error>::Ok(());
204
216
  }
@@ -206,20 +218,13 @@ impl TestRustWorker {
206
218
  };
207
219
  wf_half
208
220
  .workflow_activation_handler(
209
- core.as_ref(),
210
- task_q,
221
+ common,
211
222
  &shutdown_rx,
212
223
  &completions_tx,
213
224
  &mut completions_rx,
214
225
  activation,
215
226
  )
216
227
  .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
228
  }
224
229
  },
225
230
  // Only poll on the activity queue if activity functions have been registered. This
@@ -229,11 +234,11 @@ impl TestRustWorker {
229
234
  if !act_half.activity_fns.is_empty() {
230
235
  loop {
231
236
  tokio::select! {
232
- activity = core.poll_activity_task(task_q) => {
237
+ activity = common.worker.poll_activity_task() => {
233
238
  if matches!(activity, Err(PollActivityError::ShutDown)) {
234
239
  break;
235
240
  }
236
- act_half.activity_task_handler(core.clone(), task_q,
241
+ act_half.activity_task_handler(common.worker.clone(),
237
242
  activity?)?;
238
243
  },
239
244
  _ = shutdown_rx.changed() => { break }
@@ -250,36 +255,31 @@ impl TestRustWorker {
250
255
  };
251
256
 
252
257
  let myself = pollers.await?;
258
+ info!("Polling loop exited");
259
+ let _ = shutdown_tx.send(true);
253
260
  while let Some(h) = myself.workflow_half.join_handles.next().await {
254
261
  h??;
255
262
  }
256
- before_shutdown();
257
- myself.core.shutdown().await;
263
+ myself.common.worker.shutdown().await;
258
264
  myself.workflow_half.workflows.clear();
259
265
  Ok(())
260
266
  }
261
267
 
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
268
+ /// Set a [WorkerInterceptor]
269
+ pub fn set_worker_interceptor(&mut self, interceptor: Box<dyn WorkerInterceptor>) {
270
+ self.common.worker_interceptor = Some(interceptor);
266
271
  }
267
272
 
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
- }
273
+ /// Turns this rust worker into a new worker with all the same workflows and activities
274
+ /// registered, but with a new underlying core worker. Can be used to swap the worker for
275
+ /// a replay worker, change task queues, etc.
276
+ pub fn with_new_core_worker(&mut self, new_core_worker: Arc<dyn CoreWorker>) {
277
+ self.common.worker = new_core_worker;
277
278
  }
278
279
 
279
- fn split_apart(&mut self) -> (Arc<dyn Core>, &str, &mut WorkflowHalf, &mut ActivityHalf) {
280
+ fn split_apart(&mut self) -> (&mut CommonWorker, &mut WorkflowHalf, &mut ActivityHalf) {
280
281
  (
281
- self.core.clone(),
282
- &self.task_queue,
282
+ &mut self.common,
283
283
  &mut self.workflow_half,
284
284
  &mut self.activity_half,
285
285
  )
@@ -289,8 +289,7 @@ impl TestRustWorker {
289
289
  impl WorkflowHalf {
290
290
  async fn workflow_activation_handler(
291
291
  &mut self,
292
- core: &dyn Core,
293
- task_queue: &str,
292
+ common: &CommonWorker,
294
293
  shutdown_rx: &Receiver<bool>,
295
294
  completions_tx: &UnboundedSender<WorkflowActivationCompletion>,
296
295
  completions_rx: &mut UnboundedReceiver<WorkflowActivationCompletion>,
@@ -302,14 +301,15 @@ impl WorkflowHalf {
302
301
  variant: Some(Variant::StartWorkflow(sw)),
303
302
  }) = activation.jobs.get(0)
304
303
  {
304
+ let workflow_type = &sw.workflow_type;
305
305
  let wf_function = self
306
306
  .workflow_fns
307
- .get(&sw.workflow_type)
308
- .ok_or_else(|| anyhow!("Workflow type not found"))?;
307
+ .get(workflow_type)
308
+ .ok_or_else(|| anyhow!("Workflow type {workflow_type} not found"))?;
309
309
 
310
310
  let (wff, activations) = wf_function.start_workflow(
311
- core.server_gateway().get_options().namespace.clone(),
312
- task_queue.to_string(),
311
+ common.worker.get_config().namespace.clone(),
312
+ common.task_queue.clone(),
313
313
  // NOTE: Don't clone args if this gets ported to be a non-test rust worker
314
314
  sw.arguments.clone(),
315
315
  completions_tx.clone(),
@@ -336,11 +336,13 @@ impl WorkflowHalf {
336
336
  };
337
337
 
338
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);
339
+ if let Some(ref i) = common.worker_interceptor {
340
+ i.on_workflow_activation_completion(&completion);
342
341
  }
343
- core.complete_workflow_activation(completion).await?;
342
+ common
343
+ .worker
344
+ .complete_workflow_activation(completion)
345
+ .await?;
344
346
  Ok(())
345
347
  }
346
348
  }
@@ -365,13 +367,11 @@ impl ActivityHalf {
365
367
  /// Spawns off a task to handle the provided activity task
366
368
  fn activity_task_handler(
367
369
  &mut self,
368
- core: Arc<dyn Core>,
369
- task_queue: &str,
370
+ worker: Arc<dyn CoreWorker>,
370
371
  activity: ActivityTask,
371
372
  ) -> Result<(), anyhow::Error> {
372
373
  match activity.variant {
373
374
  Some(activity_task::Variant::Start(start)) => {
374
- let task_queue = task_queue.to_string();
375
375
  let act_fn = self
376
376
  .activity_fns
377
377
  .get(&start.activity_type)
@@ -389,7 +389,7 @@ impl ActivityHalf {
389
389
  tokio::spawn(ACT_CANCEL_TOK.scope(ct, async move {
390
390
  let mut inputs = start.input;
391
391
  let arg = inputs.pop().unwrap_or_default();
392
- let output = (&act_fn.act_func)(arg).await;
392
+ let output = (act_fn.act_func)(arg).await;
393
393
  let result = match output {
394
394
  Ok(res) => ActivityExecutionResult::ok(res),
395
395
  Err(err) => match err.downcast::<ActivityCancelledError>() {
@@ -397,12 +397,12 @@ impl ActivityHalf {
397
397
  Err(other_err) => ActivityExecutionResult::fail(other_err.into()),
398
398
  },
399
399
  };
400
- core.complete_activity_task(ActivityTaskCompletion {
401
- task_token: activity.task_token,
402
- task_queue,
403
- result: Some(result),
404
- })
405
- .await?;
400
+ worker
401
+ .complete_activity_task(ActivityTaskCompletion {
402
+ task_token: activity.task_token,
403
+ result: Some(result),
404
+ })
405
+ .await?;
406
406
  Result::<_, anyhow::Error>::Ok(())
407
407
  }));
408
408
  }
@@ -553,7 +553,7 @@ enum RustWfCmd {
553
553
  NewCmd(CommandCreateRequest),
554
554
  NewNonblockingCmd(workflow_command::Variant),
555
555
  SubscribeChildWorkflowCompletion(CommandSubscribeChildWorkflowCompletion),
556
- SubscribeSignal(String, UnboundedSender<Vec<Payload>>),
556
+ SubscribeSignal(String, UnboundedSender<SignalData>),
557
557
  }
558
558
 
559
559
  struct CommandCreateRequest {
@@ -669,31 +669,19 @@ where
669
669
  }
670
670
  }
671
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
- }
672
+ /// Reads own binary, hashes it, and returns b64 str version of that hash
673
+ fn binary_id() -> &'static str {
674
+ use sha2::{Digest, Sha256};
675
+ use std::{env, fs, io};
676
+
677
+ static INSTANCE: OnceCell<String> = OnceCell::new();
678
+ INSTANCE.get_or_init(|| {
679
+ let exe_path = env::current_exe().expect("Cannot read own binary to determine binary id");
680
+ let mut exe_file =
681
+ fs::File::open(exe_path).expect("Cannot read own binary to determine binary id");
682
+ let mut hasher = Sha256::new();
683
+ io::copy(&mut exe_file, &mut hasher).expect("Copying data into binary hasher works");
684
+ let hash = hasher.finalize();
685
+ base64::encode(hash)
686
+ })
699
687
  }
@@ -1,4 +1,4 @@
1
- use std::time::Duration;
1
+ use std::{collections::HashMap, time::Duration};
2
2
  use temporal_sdk_core_protos::coresdk::{
3
3
  child_workflow::ChildWorkflowCancellationType,
4
4
  common::{Payload, RetryPolicy},
@@ -178,3 +178,88 @@ impl IntoWorkflowCommand for ChildWorkflowOptions {
178
178
  }
179
179
  }
180
180
  }
181
+
182
+ /// Options for sending a signal to an external workflow
183
+ pub struct SignalWorkflowOptions {
184
+ /// The workflow's id
185
+ pub workflow_id: String,
186
+ /// The particular run to target, or latest if `None`
187
+ pub run_id: Option<String>,
188
+ /// The details of the signal to send
189
+ pub signal: Signal,
190
+ }
191
+
192
+ impl SignalWorkflowOptions {
193
+ /// Create options for sending a signal to another workflow
194
+ pub fn new(
195
+ workflow_id: impl Into<String>,
196
+ run_id: impl Into<String>,
197
+ name: impl Into<String>,
198
+ input: impl IntoIterator<Item = impl Into<Payload>>,
199
+ ) -> Self {
200
+ Self {
201
+ workflow_id: workflow_id.into(),
202
+ run_id: Some(run_id.into()),
203
+ signal: Signal::new(name, input),
204
+ }
205
+ }
206
+
207
+ /// Set a header k/v pair attached to the signal
208
+ pub fn with_header(
209
+ &mut self,
210
+ key: impl Into<String>,
211
+ payload: impl Into<Payload>,
212
+ ) -> &mut Self {
213
+ self.signal.data.with_header(key.into(), payload.into());
214
+ self
215
+ }
216
+ }
217
+
218
+ /// Information needed to send a specific signal
219
+ pub struct Signal {
220
+ /// The signal name
221
+ pub signal_name: String,
222
+ /// The data the signal carries
223
+ pub data: SignalData,
224
+ }
225
+
226
+ impl Signal {
227
+ /// Create a new signal
228
+ pub fn new(
229
+ name: impl Into<String>,
230
+ input: impl IntoIterator<Item = impl Into<Payload>>,
231
+ ) -> Self {
232
+ Self {
233
+ signal_name: name.into(),
234
+ data: SignalData::new(input),
235
+ }
236
+ }
237
+ }
238
+
239
+ /// Data contained within a signal
240
+ pub struct SignalData {
241
+ /// The arguments the signal will receive
242
+ pub input: Vec<Payload>,
243
+ /// Metadata attached to the signal
244
+ pub headers: HashMap<String, Payload>,
245
+ }
246
+
247
+ impl SignalData {
248
+ /// Create data for a signal
249
+ pub fn new(input: impl IntoIterator<Item = impl Into<Payload>>) -> Self {
250
+ Self {
251
+ input: input.into_iter().map(Into::into).collect(),
252
+ headers: HashMap::new(),
253
+ }
254
+ }
255
+
256
+ /// Set a header k/v pair attached to the signal
257
+ pub fn with_header(
258
+ &mut self,
259
+ key: impl Into<String>,
260
+ payload: impl Into<Payload>,
261
+ ) -> &mut Self {
262
+ self.headers.insert(key.into(), payload.into());
263
+ self
264
+ }
265
+ }