Skip to content

Commit 3d673be

Browse files
committed
feat(rust): initial draft of sdk docs
1 parent 656ad76 commit 3d673be

14 files changed

Lines changed: 2891 additions & 185 deletions
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
id: basics
3+
title: Activity basics - Rust SDK
4+
sidebar_label: Activity basics
5+
description: This section explains how to implement Activities with the Rust SDK
6+
toc_max_heading_level: 4
7+
keywords:
8+
- Rust SDK
9+
tags:
10+
- Rust SDK
11+
- Temporal SDKs
12+
---
13+
14+
## Develop a basic Activity {#develop-activities}
15+
16+
**How to develop a basic Activity using the Temporal Rust SDK.**
17+
18+
One of the primary things that Workflows do is orchestrate the execution of Activities. An Activity is a normal function or method execution that's intended to execute a single, well-defined action (either short or long-running), such as querying a database, calling a third-party API, or transcoding a media file. An Activity can interact with the world outside the Temporal Platform or use a Temporal Client to interact with a Temporal Service. For the Workflow to be able to execute the Activity, we must define the [Activity Definition](/activity-definition).
19+
20+
In the Temporal Rust SDK, Activities are defined using the `#[activities]` macro on an impl block, with individual Activity methods marked by the `#[activity]` macro.
21+
22+
```rust
23+
use temporalio_sdk::activities::{ActivityContext, ActivityError};
24+
use temporalio_macros::activities;
25+
26+
#[activities]
27+
impl GreetingActivities {
28+
#[activity]
29+
pub async fn greet(_ctx: ActivityContext, name: String) -> Result<String, ActivityError> {
30+
Ok(format!("Hello, {}!", name))
31+
}
32+
33+
#[activity]
34+
pub async fn send_notification(_ctx: ActivityContext, message: String) -> Result<(), ActivityError> {
35+
println!("Sending notification: {}", message);
36+
Ok(())
37+
}
38+
}
39+
40+
pub struct GreetingActivities;
41+
```
42+
43+
The `#[activities]` macro marks the impl block as containing Activity definitions. Each method decorated with `#[activity]` becomes an Activity that can be invoked from a Workflow.
44+
45+
### Activity method signature requirements
46+
47+
Each Activity method must:
48+
49+
- Be `async` (return a future)
50+
- Take `ActivityContext` as the first parameter
51+
- Return `Result<T, ActivityError>` where `T` is the return type
52+
- Be `pub` (public)
53+
54+
The `ActivityContext` parameter provides access to Activity execution information and capabilities like heartbeating. If you don't need it, you can use `_ctx` as a parameter name.
55+
56+
### Define Activity parameters {#activity-parameters}
57+
58+
**How to define Activity parameters using the Temporal Rust SDK.**
59+
60+
There is no explicit limit to the total number of parameters that an [Activity Definition](/activity-definition) may support. However, there is a limit to the total size of the data that ends up encoded into a gRPC message Payload.
61+
62+
A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the arguments, is limited to a maximum of 4 MB.
63+
64+
Also, keep in mind that all Payload data is recorded in the [Workflow Execution Event History](/workflow-execution/event#event-history) and large Event Histories can affect Worker performance.
65+
66+
We recommend that you use a single struct as an argument that wraps all the application data passed to Activities. This way you can change what data is passed to the Activity without breaking the function signature.
67+
68+
Activity parameters must be serializable and deserializable using serde. Use `#[derive(Serialize, Deserialize)]` on your data types:
69+
70+
```rust
71+
use serde::{Serialize, Deserialize};
72+
73+
#[derive(Serialize, Deserialize)]
74+
pub struct GreetingInput {
75+
pub greeting: String,
76+
pub name: String,
77+
}
78+
79+
#[activities]
80+
impl GreetingActivities {
81+
#[activity]
82+
pub async fn compose_greeting(
83+
_ctx: ActivityContext,
84+
input: GreetingInput,
85+
) -> Result<String, ActivityError> {
86+
Ok(format!("{} {}!", input.greeting, input.name))
87+
}
88+
}
89+
```
90+
91+
### Define Activity return values {#activity-return-values}
92+
93+
**How to define Activity return values using the Temporal Rust SDK.**
94+
95+
All data returned from an Activity must be serializable.
96+
97+
Activity return values are subject to payload size limits in Temporal. The default payload size limit is 2MB, and there is a hard limit of 4MB for any gRPC message size in the Event History transaction. Keep in mind that all return values are recorded in a [Workflow Execution Event History](/workflow-execution/event#event-history).
98+
99+
The return type of an Activity is `Result<T, ActivityError>`. The `T` type must implement `Serialize`. Use `ActivityError::Retryable` for errors that should be retried, and `ActivityError::NonRetryable` for permanent failures:
100+
101+
```rust
102+
#[activities]
103+
impl MyActivities {
104+
#[activity]
105+
pub async fn process_data(
106+
_ctx: ActivityContext,
107+
input: String,
108+
) -> Result<ProcessedData, ActivityError> {
109+
// If an error should be retried
110+
if !validate_input(&input) {
111+
return Err(ActivityError::Retryable(
112+
"Invalid input format".into()
113+
));
114+
}
115+
116+
// If an error should not be retried
117+
if input.len() > 1000000 {
118+
return Err(ActivityError::NonRetryable(
119+
"Input too large".into()
120+
));
121+
}
122+
123+
let result = ProcessedData {
124+
processed: input.to_uppercase(),
125+
};
126+
127+
Ok(result)
128+
}
129+
}
130+
131+
#[derive(Serialize, Deserialize)]
132+
pub struct ProcessedData {
133+
pub processed: String,
134+
}
135+
```
136+
137+
### Customize your Activity Type {#activity-type}
138+
139+
**How to customize your Activity Type using the Temporal Rust SDK.**
140+
141+
Activities have a Type that refers to the Activity name. The Activity name is used to identify Activity Types in the Workflow Execution Event History, Visibility Queries, and Metrics.
142+
143+
By default, the Activity name is the method name. You can customize it by providing a `name` parameter to the `#[activity]` macro:
144+
145+
```rust
146+
#[activities]
147+
impl GreetingActivities {
148+
#[activity(name = "compose_greeting")]
149+
pub async fn greet(_ctx: ActivityContext, name: String) -> Result<String, ActivityError> {
150+
Ok(format!("Hello, {}!", name))
151+
}
152+
153+
#[activity(name = "send_email")]
154+
pub async fn send_notification(_ctx: ActivityContext, email: String) -> Result<(), ActivityError> {
155+
println!("Email to: {}", email);
156+
Ok(())
157+
}
158+
}
159+
```
160+
161+
## Activity Error Handling {#error-handling}
162+
163+
Activities return `Result<T, ActivityError>` with different error types for different failure scenarios:
164+
165+
- **`ActivityError::Retryable`** - Transient failure that should be retried according to the Activity's retry policy
166+
- **`ActivityError::NonRetryable`** - Permanent failure that will not be retried
167+
- **`ActivityError::Cancelled`** - Activity was cancelled by the Workflow
168+
169+
```rust
170+
#[activities]
171+
impl ActivitiesImpl {
172+
#[activity]
173+
pub async fn call_external_service(
174+
_ctx: ActivityContext,
175+
url: String,
176+
) -> Result<String, ActivityError> {
177+
match make_http_request(&url).await {
178+
Ok(response) => Ok(response),
179+
Err(e) if is_network_error(&e) => {
180+
// Network errors are transient and should be retried
181+
Err(ActivityError::Retryable(format!("Network error: {}", e)))
182+
}
183+
Err(e) if is_auth_error(&e) => {
184+
// Authentication errors are permanent and should not be retried
185+
Err(ActivityError::NonRetryable(format!("Auth error: {}", e)))
186+
}
187+
Err(e) => Err(ActivityError::Retryable(format!("Unknown error: {}", e))),
188+
}
189+
}
190+
}
191+
```
192+
193+
## External Activity References
194+
195+
**How to reference externally defined Activities**
196+
197+
When referencing Activities that are defined in external codebase or in a different language, you can create unimplemented Activity stubs:
198+
199+
```rust
200+
// Reference to activities defined in another service
201+
#[activities]
202+
impl ExternalActivities {
203+
#[activity(name = "external-process")]
204+
async fn process_data(_ctx: ActivityContext, _data: String) -> Result<String, ActivityError> {
205+
unimplemented!()
206+
}
207+
}
208+
```
209+
210+
This allows you to call these Activities from your Workflow code without implementing them locally. The actual implementation will be provided by the external service that has these Activities registered with a Worker.

0 commit comments

Comments
 (0)