Skip to content

Commit fd6b1fa

Browse files
committed
Updates to the main page
1 parent fb32139 commit fd6b1fa

File tree

2 files changed

+130
-11
lines changed

2 files changed

+130
-11
lines changed

examples/my_virtual_object.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::ops::Shl;
21
use restate_sdk::prelude::*;
32

43
#[restate_sdk::object]

src/lib.rs

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,164 @@
2020
//!
2121
//! The Restate Rust SDK lets you implement durable handlers. Handlers can be part of three types of services:
2222
//!
23+
//! - [Services](https://docs.restate.dev/concepts/services/#services-1): a collection of durable handlers
24+
//! - [Virtual Objects](https://docs.restate.dev/concepts/services/#virtual-objects): an object consists of a collection of durable handlers and isolated K/V state. Virtual Objects are useful for modeling stateful entities, where at most one handler can run at a time per object.
25+
//! - [Workflows](https://docs.restate.dev/concepts/services/#workflows): Workflows have a `run` handler that executes exactly once per workflow instance, and executes a set of steps durably. Workflows can have other handlers that can be called multiple times and interact with the workflow.
26+
//!
2327
//! Let's have a look at how to define them.
2428
//!
29+
//! ## Services
30+
//!
31+
//! [Services](https://docs.restate.dev/concepts/services/#services-1) and their handlers are defined as follows:
32+
//!
2533
//! ```rust,no_run
2634
//! // The prelude contains all the imports you need to get started
2735
//! use restate_sdk::prelude::*;
2836
//!
2937
//! // Define the service using Rust traits
3038
//! #[restate_sdk::service]
31-
//! trait Greeter {
32-
//! async fn greet(name: String) -> HandlerResult<String>;
39+
//! trait MyService {
40+
//! #[name = "myHandler"]
41+
//! async fn my_handler(greeting: String) -> Result<String, HandlerError>;
3342
//! }
3443
//!
3544
//! // Implement the service
36-
//! struct GreeterImpl;
37-
//! impl Greeter for GreeterImpl {
38-
//! async fn greet(&self, _: Context<'_>, name: String) -> HandlerResult<String> {
39-
//! Ok(format!("Greetings {name}"))
45+
//! struct MyServiceImpl;
46+
//! impl MyService for MyServiceImpl {
47+
//! async fn my_handler(&self, ctx: Context<'_>, greeting: String) -> Result<String, HandlerError> {
48+
//! Ok(format!("{greeting}!"))
4049
//! }
4150
//! }
4251
//!
4352
//! // Start the HTTP server to expose services
4453
//! #[tokio::main]
4554
//! async fn main() {
55+
//! tracing_subscriber::fmt::init();
56+
//! HttpServer::new(Endpoint::builder().bind(MyServiceImpl.serve()).build())
57+
//! .listen_and_serve("0.0.0.0:9080".parse().unwrap())
58+
//! .await;
59+
//! }
60+
//! ```
61+
//!
62+
//! - Specify that you want to create a service by using the [`#[restate_sdk::service]` macro](restate_sdk_macros::service).
63+
//! - Create a trait with the service handlers.
64+
//! - Handlers can accept zero or one parameter and return a `Result`.
65+
//! - The type of the input parameter of the handler needs to implement [`Serialize`](crate::serde::Deserialize) and [`Deserialize`](crate::serde::Deserialize). See [Serialization docs](crate::serde).
66+
//! - The Result contains the return value or a [`HandlerError`][crate::errors::HandlerError], which can be a [`TerminalError`] or any other Rust's `StdError`.
67+
//! - The service handler can now be called at `<RESTATE_INGRESS_URL>/MyService/myHandler`. You can optionally override the handler name used via `#[name = "myHandler"]`. More details on handler invocations can be found in the [docs](https://docs.restate.dev/invoke/http).
68+
//! - Implement the trait on a struct. The struct will contain the actual implementation of the handlers.
69+
//! - The first parameter of a handler after `&self` is always a [`Context`](crate::context::Context) to interact with Restate.
70+
//! The SDK stores the actions you do on the context in the Restate journal to make them durable.
71+
//! - Finally, create an HTTP endpoint and bind the service(s) to it. Listen on the specified port (here 9080) for connections and requests.
72+
//!
73+
//! ## Virtual Objects
74+
//! [Virtual Objects](https://docs.restate.dev/concepts/services/#virtual-objects) and their handlers are defined similarly to services, with the following differences:
75+
//!
76+
//! ```rust,no_run
77+
//!use restate_sdk::prelude::*;
78+
//!
79+
//! #[restate_sdk::object]
80+
//! pub trait MyVirtualObject {
81+
//! async fn my_handler(name: String) -> Result<String, HandlerError>;
82+
//! #[shared]
83+
//! async fn my_concurrent_handler(name: String) -> Result<String, HandlerError>;
84+
//! }
85+
//!
86+
//! pub struct MyVirtualObjectImpl;
87+
//!
88+
//! impl MyVirtualObject for MyVirtualObjectImpl {
89+
//! async fn my_handler(
90+
//! &self,
91+
//! ctx: ObjectContext<'_>,
92+
//! greeting: String,
93+
//! ) -> Result<String, HandlerError> {
94+
//! Ok(format!("{} {}", greeting, ctx.key()))
95+
//! }
96+
//! async fn my_concurrent_handler(
97+
//! &self,
98+
//! ctx: SharedObjectContext<'_>,
99+
//! greeting: String,
100+
//! ) -> Result<String, HandlerError> {
101+
//! Ok(format!("{} {}", greeting, ctx.key()))
102+
//! }
103+
//! }
104+
//!
105+
//! #[tokio::main]
106+
//! async fn main() {
107+
//! tracing_subscriber::fmt::init();
46108
//! HttpServer::new(
47109
//! Endpoint::builder()
48-
//! .bind(GreeterImpl.serve())
110+
//! .bind(MyVirtualObjectImpl.serve())
49111
//! .build(),
50112
//! )
51113
//! .listen_and_serve("0.0.0.0:9080".parse().unwrap())
52114
//! .await;
53115
//! }
54116
//! ```
55117
//!
56-
//! ## Service types
57-
//! Learn more about each service type:
118+
//! - Specify that you want to create a Virtual Object by using the [`#[restate_sdk::object]` macro](restate_sdk_macros::object).
119+
//! - The first argument of each handler must be the [`ObjectContext`](crate::context::ObjectContext) parameter. Handlers with the `ObjectContext` parameter can write to the K/V state store. Only one handler can be active at a time per object, to ensure consistency.
120+
//! - You can retrieve the key of the object you are in via `ctx.key()`.
121+
//! - If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, add `#[shared]` to the handler definition in the trait.
122+
//! Shared handlers need to use the [`SharedObjectContext`](crate::context::SharedObjectContext).
123+
//! You can use these handlers, for example, to read K/V state and expose it to the outside world, or to interact with the blocking handler and resolve awakeables etc.
124+
//!
125+
//! ## Workflows
126+
//!
127+
//! [Workflows](https://docs.restate.dev/concepts/services/#workflows) are a special type of Virtual Objects, their definition is similar but with the following differences:
128+
//!
129+
//! ```rust,no_run
130+
//! use restate_sdk::prelude::*;
131+
//!
132+
//! #[restate_sdk::workflow]
133+
//! pub trait MyWorkflow {
134+
//! async fn run(req: String) -> Result<String, HandlerError>;
135+
//! #[shared]
136+
//! async fn interact_with_workflow() -> Result<(), HandlerError>;
137+
//! }
138+
//!
139+
//! pub struct MyWorkflowImpl;
140+
//!
141+
//! impl MyWorkflow for MyWorkflowImpl {
142+
//! async fn run(&self, ctx: WorkflowContext<'_>, req: String) -> Result<String, HandlerError> {
143+
//! //! implement workflow logic here
144+
//!
145+
//! Ok(String::from("success"))
146+
//! }
147+
//! async fn interact_with_workflow(&self, ctx: SharedWorkflowContext<'_>) -> Result<(), HandlerError> {
148+
//! //! implement interaction logic here
149+
//! //! e.g. resolve a promise that the workflow is waiting on
150+
//!
151+
//! Ok(())
152+
//! }
153+
//! }
154+
//!
155+
//! #[tokio::main]
156+
//! async fn main() {
157+
//! tracing_subscriber::fmt::init();
158+
//! HttpServer::new(Endpoint::builder().bind(MyWorkflowImpl.serve()).build())
159+
//! .listen_and_serve("0.0.0.0:9080".parse().unwrap())
160+
//! .await;
161+
//! }
162+
//! ```
163+
//!
164+
//! - Specify that you want to create a Workflow by using the [`#[restate_sdk::workflow]` macro](workflow).
165+
//! - The workflow needs to have a `run` handler.
166+
//! - The first argument of the `run` handler must be the [`WorkflowContext`](crate::context::WorkflowContext) parameter.
167+
//! The `WorkflowContext` parameter is used to interact with Restate.
168+
//! The `run` handler executes exactly once per workflow instance.
169+
//! - The other handlers of the workflow are used to interact with the workflow: either query it, or signal it.
170+
//! They use the [`SharedWorkflowContext`](crate::context::SharedWorkflowContext) to interact with the SDK.
171+
//! These handlers can run concurrently with the run handler and can still be called after the run handler has finished.
172+
//! - Have a look at the [workflow docs](workflow) to learn more.
173+
//!
174+
//!
175+
//! Learn more about each service type here:
58176
//! - [Service](restate_sdk_macros::service)
59177
//! - [Virtual Object](object)
60178
//! - [Workflow](workflow)
61179
//!
62-
//! ## Features
180+
//! # Features
63181
//!
64182
//! Have a look at the following SDK capabilities:
65183
//!
@@ -99,6 +217,7 @@ pub mod http_server;
99217
pub mod hyper;
100218
pub mod serde;
101219

220+
use ::serde::de::StdError;
102221
/// Entry-point macro to define a Restate [Service](https://docs.restate.dev/concepts/services#services-1).
103222
///
104223
/// ```rust,no_run
@@ -364,6 +483,7 @@ pub use restate_sdk_macros::object;
364483
///
365484
/// For more details, check the [`service` macro](macro@crate::service) documentation.
366485
pub use restate_sdk_macros::workflow;
486+
use crate::errors::TerminalError;
367487

368488
/// Prelude contains all the useful imports you need to get started with Restate.
369489
pub mod prelude {

0 commit comments

Comments
 (0)