Skip to content

Commit 16748fc

Browse files
authored
Merge branch 'main' into event-groups-cancel-markers
2 parents 4120231 + d9efc49 commit 16748fc

56 files changed

Lines changed: 3002 additions & 492 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,46 @@ relevant information.
3333

3434
## Unreleased
3535

36+
### Added
37+
* `WorkflowContext::all_handlers_finished` and `SyncWorkflowContext::all_handlers_finished` let
38+
Rust workflows wait for active signal and update handler chains before completing or continuing
39+
as new.
40+
* `WorkflowStartOptions::memo` attaches a non-indexed memo when starting a workflow, using the
41+
same `MemoValues` type already used by continue-as-new and `WorkflowContext::upsert_memo`.
42+
Values are serialized with the client's payload converter and codec, matching how `describe`
43+
and `list` read them back.
44+
* `MemoValue` and `MemoValues` are now exported from `temporalio_common` as well as
45+
`temporalio_workflow`, so the same types can be used from clients and workflows.
46+
47+
### Breaking Changes :boom:
48+
* Values stored in a `MemoValue` must now be `Send + Sync`. It previously held its value in an
49+
`Rc` and now uses an `Arc`, so that memos can be built outside a workflow and handed to the
50+
client. Only affects memo values that are themselves non-`Send`/non-`Sync`, such as those
51+
holding an `Rc` or `RefCell`.
52+
53+
* `Client::signal_with_start_workflow` starts a workflow and sends a typed signal atomically.
54+
55+
### Breaking Changes :boom:
56+
57+
* Signal-with-start is now invoked with `Client::signal_with_start_workflow`; remove uses of
58+
`WorkflowStartOptions::start_signal` and `WorkflowStartSignal`.
59+
60+
## [0.7.0] - 2026-08-17
61+
3662
### Added
3763
* Support for running Standalone Activities in Rust SDK Worker.
3864
* Client methods for starting and managing execution of Standalone Activities.
65+
* `WorkflowTermination::cancelled_with_details` for recording structured details when a Workflow
66+
Execution completes as cancelled.
3967
* `LoggerFormat` for selecting compact, pretty, or JSON Core console log output. Configured log
4068
filters continue to apply to JSON output.
69+
* The Rust SDK now has an optional `testing` feature with a typed activity test environment and
70+
local or external workflow test environments. Local workflow environments manage a Temporal CLI
71+
dev server and expose shutdown through their local-server type state.
72+
* Worker heartbeats now report the SDK runtime, hosting environments, operating system, and
73+
architecture once per worker, retrying until the first successful delivery. Runtime options can
74+
disable this reporting, and language SDK bridges can supply their own runtime details. The Rust
75+
SDK exposes separate runtime options that omit bridge-only runtime overrides.
4176
* `RpcOptions::builder()` for constructing per-call RPC options.
4277
* `DnsLoadBalancingOptions::builder()` for configuring DNS re-resolution intervals.
4378
* Experimental plugin APIs for packaging reusable client and worker configuration, including data
@@ -68,6 +103,8 @@ relevant information.
68103
`WorkerInterceptor::with_workflow_replay_worker`.
69104

70105
### Breaking Changes :boom:
106+
* `WorkflowTermination::Cancelled` now has an optional `details` field. Use
107+
`WorkflowTermination::cancelled()` to construct a cancellation without details.
71108
* Changes to `ActivityInfo`: instead of `workflow_namespace`, `workflow_execution` and `run_id`,
72109
there is now `namespace`, `workflow_id`, `workflow_run_id` and `activity_run_id`.
73110
Also, `workflow_type` is now `Option<String>`.
@@ -81,6 +118,12 @@ relevant information.
81118
`OutgoingError`, `OutgoingActivityError`, and `OutgoingWorkflowError` are now non-exhaustive;
82119
downstream matches must include a wildcard arm.
83120
* Removed `InterceptorWithNext`. Register worker interceptors as an ordered vector instead.
121+
* Ephemeral server APIs now return `EphemeralServerError` instead of `anyhow::Error`, and dev-server
122+
log format and level use the non-exhaustive `DevServerLogFormat` and `DevServerLogLevel` enums.
123+
* Ephemeral server APIs now return the operation-oriented `EphemeralServerError` instead of
124+
`anyhow::Error`.
125+
* Activity macro support now exposes instance requirements through `ExecutableActivity`; the
126+
redundant `HasOnlyStaticMethods` marker trait has been removed.
84127
* `Worker::run` now returns `WorkerRunError` instead of `anyhow::Error`.
85128
Non-validation failures are reported as `WorkerRunError::Fatal` with a message and source.
86129
* `Logger::Console` now requires a `format: Option<LoggerFormat>` field. Use `None` to preserve the
@@ -131,6 +174,9 @@ relevant information.
131174
Non-validation failures are reported as `WorkerRunError::Fatal` with a message and source.
132175

133176
### Fixed
177+
* Rust SDK workers now warn when autoscaling task polling encounters errors continuously for one
178+
minute. Repeated warnings use exponential backoff up to 15-minute intervals and stop after
179+
polling recovers.
134180
* Unhandled workflow payload conversion errors now fail the Workflow Task so it can retry instead
135181
of failing the Workflow Execution. Workflows may still explicitly handle these errors.
136182
* Workers no longer send worker heartbeats or appear in centralized heartbeat reports before

crates/client/Cargo.toml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "temporalio-client"
3-
version = "0.6.0"
3+
version = "0.7.0"
44
edition = "2024"
55
authors = ["Temporal Technologies Inc. <sdk@temporal.io>"]
66
license-file = { workspace = true }
@@ -45,7 +45,11 @@ tokio = { version = "1.47", default-features = false, features = [
4545
"sync",
4646
"time",
4747
] }
48-
tonic = { workspace = true, default-features = false, features = ["tls-native-roots", "channel", "gzip"] }
48+
tonic = { workspace = true, default-features = false, features = [
49+
"tls-native-roots",
50+
"channel",
51+
"gzip",
52+
] }
4953
tokio-rustls = { version = "0.26", default-features = false }
5054
rustls-native-certs = { version = "0.8", optional = true }
5155
tower = { version = "0.5", features = ["util"] }
@@ -57,7 +61,7 @@ serde_json = { workspace = true }
5761

5862
[dependencies.temporalio-common]
5963
path = "../common"
60-
version = "0.6"
64+
version = "0.7"
6165
default-features = false
6266
features = ["serde_serialize"]
6367

@@ -68,6 +72,8 @@ prost = "0.14"
6872
prost-types = { workspace = true }
6973
rstest = "0.26"
7074
tempfile = "3"
75+
temporalio-macros = { path = "../macros", version = "0.7" }
76+
temporalio-workflow = { path = "../workflow", version = "0.7" }
7177
tokio = { version = "1.47", default-features = false, features = [
7278
"io-util",
7379
"macros",
@@ -76,6 +82,7 @@ tokio = { version = "1.47", default-features = false, features = [
7682
"sync",
7783
"time",
7884
] }
85+
trybuild = { version = "1.0", features = ["diff"] }
7986

8087
[lints]
8188
workspace = true

crates/client/src/interceptors.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,106 @@ impl StartWorkflowInput {
187187

188188
impl_with_args!(StartWorkflowInput);
189189

190+
/// Input to [`ClientInterceptor::signal_with_start_workflow`].
191+
#[non_exhaustive]
192+
#[derive(derive_more::Debug)]
193+
pub struct SignalWithStartWorkflowInput {
194+
/// The workflow type sent to the server.
195+
pub workflow_type: String,
196+
/// The signal name sent to the workflow.
197+
pub signal_name: String,
198+
/// Options for the workflow start.
199+
pub options: WorkflowStartOptions,
200+
/// Controls for the signal-with-start RPC.
201+
pub rpc_options: crate::RpcOptions,
202+
// These remain type-erased until after interception so interceptors can replace either value
203+
// before the client's payload converter and codec run.
204+
#[debug(skip)]
205+
workflow_args: Box<dyn TemporalClientValue>,
206+
#[debug(skip)]
207+
signal_args: Box<dyn TemporalClientValue>,
208+
}
209+
210+
impl SignalWithStartWorkflowInput {
211+
pub(crate) fn new<W, S>(
212+
workflow_type: String,
213+
workflow_args: W,
214+
signal_name: String,
215+
signal_args: S,
216+
mut options: WorkflowStartOptions,
217+
) -> Self
218+
where
219+
W: TemporalSerializable + Send + 'static,
220+
S: TemporalSerializable + Send + 'static,
221+
{
222+
let rpc_options = std::mem::take(&mut options.rpc_options);
223+
Self {
224+
workflow_type,
225+
signal_name,
226+
options,
227+
rpc_options,
228+
workflow_args: Box::new(workflow_args),
229+
signal_args: Box::new(signal_args),
230+
}
231+
}
232+
233+
pub(crate) fn into_parts(
234+
self,
235+
) -> (
236+
String,
237+
Box<dyn TemporalClientValue>,
238+
String,
239+
Box<dyn TemporalClientValue>,
240+
WorkflowStartOptions,
241+
crate::RpcOptions,
242+
) {
243+
(
244+
self.workflow_type,
245+
self.workflow_args,
246+
self.signal_name,
247+
self.signal_args,
248+
self.options,
249+
self.rpc_options,
250+
)
251+
}
252+
253+
/// Attempt to access the workflow arguments as a concrete type.
254+
pub fn workflow_args_ref<T: Any>(&self) -> Option<&T> {
255+
self.workflow_args.as_any().downcast_ref()
256+
}
257+
258+
/// Attempt to access the signal arguments as a concrete type.
259+
pub fn signal_args_ref<T: Any>(&self) -> Option<&T> {
260+
self.signal_args.as_any().downcast_ref()
261+
}
262+
263+
/// Attempt to mutably access the workflow arguments as a concrete type.
264+
pub fn workflow_args_mut<T: Any>(&mut self) -> Option<&mut T> {
265+
self.workflow_args.as_any_mut().downcast_mut()
266+
}
267+
268+
/// Attempt to mutably access the signal arguments as a concrete type.
269+
pub fn signal_args_mut<T: Any>(&mut self) -> Option<&mut T> {
270+
self.signal_args.as_any_mut().downcast_mut()
271+
}
272+
273+
/// Replace the workflow arguments before serialization.
274+
pub fn replace_workflow_args<T>(&mut self, args: T)
275+
where
276+
T: TemporalSerializable + Send + 'static,
277+
{
278+
self.workflow_args = Box::new(args);
279+
}
280+
281+
/// Replace the signal arguments before serialization.
282+
pub fn replace_signal_args<T>(&mut self, args: T)
283+
where
284+
T: TemporalSerializable + Send + 'static,
285+
{
286+
self.signal_args = Box::new(args);
287+
}
288+
}
289+
190290
/// Result of a successful intercepted workflow start.
191291
#[non_exhaustive]
192292
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1062,6 +1162,19 @@ pub trait ClientInterceptor: Send + Sync + 'static {
10621162
next.run(input)
10631163
}
10641164

1165+
/// Intercept a `signal_with_start_workflow` operation.
1166+
fn signal_with_start_workflow<'a>(
1167+
&'a self,
1168+
input: SignalWithStartWorkflowInput,
1169+
next: Next<
1170+
'a,
1171+
SignalWithStartWorkflowInput,
1172+
BoxFuture<'a, Result<StartWorkflowOutput, WorkflowStartError>>,
1173+
>,
1174+
) -> BoxFuture<'a, Result<StartWorkflowOutput, WorkflowStartError>> {
1175+
next.run(input)
1176+
}
1177+
10651178
/// Intercept a `list_workflows_page` operation.
10661179
fn list_workflows_page<'a>(
10671180
&'a self,
@@ -1351,6 +1464,13 @@ interceptor_chain!(
13511464
BoxFuture<'a, Result<StartWorkflowOutput, WorkflowStartError>>
13521465
);
13531466

1467+
interceptor_chain!(
1468+
call_signal_with_start_workflow,
1469+
signal_with_start_workflow,
1470+
SignalWithStartWorkflowInput,
1471+
BoxFuture<'a, Result<StartWorkflowOutput, WorkflowStartError>>
1472+
);
1473+
13541474
interceptor_chain!(
13551475
call_list_workflows_page,
13561476
list_workflows_page,

0 commit comments

Comments
 (0)