temporalio_sdk/
runtime.rs1use std::time::Duration;
8
9use temporalio_common::telemetry::TelemetryOptions;
10use temporalio_sdk_core::{
11 CoreRuntime, PollerBehavior as CorePollerBehavior, RuntimeOptions as CoreRuntimeOptions,
12 TokioRuntimeBuilder as CoreTokioRuntimeBuilder, WorkflowErrorType as CoreWorkflowErrorType,
13};
14
15use crate::error::RuntimeError;
16
17pub mod worker_tuner;
19
20#[cfg(feature = "experimental")]
23pub use temporalio_sdk_core::{Worker as CoreWorker, WorkerConfig};
24
25#[derive(bon::Builder)]
27#[builder(state_mod(vis = "pub"))]
28#[non_exhaustive]
29pub struct TokioRuntimeBuilder {
30 pub inner: tokio::runtime::Builder,
32}
33
34impl Default for TokioRuntimeBuilder {
35 fn default() -> Self {
36 Self {
37 inner: tokio::runtime::Builder::new_multi_thread(),
38 }
39 }
40}
41
42impl TokioRuntimeBuilder {
43 fn into_core(self) -> CoreTokioRuntimeBuilder<Box<dyn Fn() + Send + Sync>> {
44 CoreTokioRuntimeBuilder {
45 inner: self.inner,
46 lang_on_thread_start: None,
47 }
48 }
49}
50
51#[derive(bon::Builder, Clone, Copy, Debug, PartialEq)]
53#[builder(state_mod(vis = "pub"))]
54#[non_exhaustive]
55pub struct AutoscalingOptions {
56 pub minimum: usize,
58 pub maximum: usize,
60 pub initial: usize,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq)]
66#[non_exhaustive]
67pub enum PollerBehavior {
68 SimpleMaximum(usize),
70 Autoscaling(AutoscalingOptions),
72}
73
74impl PollerBehavior {
75 pub(crate) fn into_core(self) -> CorePollerBehavior {
76 match self {
77 PollerBehavior::SimpleMaximum(maximum) => CorePollerBehavior::SimpleMaximum(maximum),
78 PollerBehavior::Autoscaling(AutoscalingOptions {
79 minimum,
80 maximum,
81 initial,
82 }) => CorePollerBehavior::Autoscaling {
83 minimum,
84 maximum,
85 initial,
86 },
87 }
88 }
89}
90
91#[derive(Clone, Debug, Eq, PartialEq, Hash)]
93#[non_exhaustive]
94pub enum WorkflowErrorType {
95 Nondeterminism,
97}
98
99impl WorkflowErrorType {
100 pub(crate) fn into_core(self) -> CoreWorkflowErrorType {
101 match self {
102 WorkflowErrorType::Nondeterminism => CoreWorkflowErrorType::Nondeterminism,
103 }
104 }
105}
106
107#[derive(bon::Builder)]
109#[builder(finish_fn(vis = "", name = build_internal))]
110#[non_exhaustive]
111pub struct RuntimeOptions {
112 #[builder(default)]
114 telemetry_options: TelemetryOptions,
115 #[builder(required, default = Some(Duration::from_secs(60)))]
119 heartbeat_interval: Option<Duration>,
120 #[builder(default)]
122 disable_environment_info: bool,
123}
124
125impl Default for RuntimeOptions {
126 fn default() -> Self {
127 Self::builder().build().expect("builder defaults are valid")
128 }
129}
130
131impl<S: runtime_options_builder::State> RuntimeOptionsBuilder<S> {
132 pub fn build(self) -> Result<RuntimeOptions, String> {
138 let options = self.build_internal();
139 if let Some(interval) = options.heartbeat_interval
140 && (interval < Duration::from_secs(1) || interval > Duration::from_secs(60))
141 {
142 return Err(format!(
143 "heartbeat_interval ({interval:?}) must be between 1s and 60s",
144 ));
145 }
146 Ok(options)
147 }
148}
149
150impl RuntimeOptions {
151 fn into_core(self) -> CoreRuntimeOptions {
152 CoreRuntimeOptions::builder()
153 .telemetry_options(self.telemetry_options)
154 .heartbeat_interval(self.heartbeat_interval)
155 .disable_environment_info(self.disable_environment_info)
156 .build()
157 .expect("SDK runtime options have already been validated")
158 }
159}
160
161pub struct Runtime(CoreRuntime);
163
164impl Runtime {
165 pub fn new(
170 options: RuntimeOptions,
171 tokio_builder: TokioRuntimeBuilder,
172 ) -> Result<Self, RuntimeError> {
173 CoreRuntime::new(options.into_core(), tokio_builder.into_core())
174 .map(Self)
175 .map_err(RuntimeError::from_core)
176 }
177
178 pub fn from_current_tokio(options: RuntimeOptions) -> Result<Self, RuntimeError> {
184 tokio::runtime::Handle::try_current().map_err(|_| RuntimeError::NoCurrentTokioRuntime)?;
185 CoreRuntime::new_assume_tokio(options.into_core())
186 .map(Self)
187 .map_err(RuntimeError::from_core)
188 }
189
190 #[deprecated(note = "use `Runtime::from_current_tokio` instead")]
196 pub fn new_assume_tokio(options: RuntimeOptions) -> Result<Self, RuntimeError> {
197 Self::from_current_tokio(options)
198 }
199
200 pub(crate) fn core(&self) -> &CoreRuntime {
201 &self.0
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::{Runtime, TokioRuntimeBuilder};
208 use crate::error::RuntimeError;
209
210 #[test]
211 fn from_current_tokio_without_runtime_returns_error() {
212 assert!(matches!(
213 Runtime::from_current_tokio(Default::default()),
214 Err(RuntimeError::NoCurrentTokioRuntime)
215 ));
216 }
217
218 #[test]
219 fn tokio_runtime_builder_constructs_with_an_inner_builder() {
220 let _builder = TokioRuntimeBuilder::builder()
221 .inner(tokio::runtime::Builder::new_current_thread())
222 .build();
223 }
224}