|
20 | 20 | //! |
21 | 21 | //! The Restate Rust SDK lets you implement durable handlers. Handlers can be part of three types of services: |
22 | 22 | //! |
| 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 | +//! |
23 | 27 | //! Let's have a look at how to define them. |
24 | 28 | //! |
| 29 | +//! ## Services |
| 30 | +//! |
| 31 | +//! [Services](https://docs.restate.dev/concepts/services/#services-1) and their handlers are defined as follows: |
| 32 | +//! |
25 | 33 | //! ```rust,no_run |
26 | 34 | //! // The prelude contains all the imports you need to get started |
27 | 35 | //! use restate_sdk::prelude::*; |
28 | 36 | //! |
29 | 37 | //! // Define the service using Rust traits |
30 | 38 | //! #[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>; |
33 | 42 | //! } |
34 | 43 | //! |
35 | 44 | //! // 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}!")) |
40 | 49 | //! } |
41 | 50 | //! } |
42 | 51 | //! |
43 | 52 | //! // Start the HTTP server to expose services |
44 | 53 | //! #[tokio::main] |
45 | 54 | //! 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(); |
46 | 108 | //! HttpServer::new( |
47 | 109 | //! Endpoint::builder() |
48 | | -//! .bind(GreeterImpl.serve()) |
| 110 | +//! .bind(MyVirtualObjectImpl.serve()) |
49 | 111 | //! .build(), |
50 | 112 | //! ) |
51 | 113 | //! .listen_and_serve("0.0.0.0:9080".parse().unwrap()) |
52 | 114 | //! .await; |
53 | 115 | //! } |
54 | 116 | //! ``` |
55 | 117 | //! |
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: |
58 | 176 | //! - [Service](restate_sdk_macros::service) |
59 | 177 | //! - [Virtual Object](object) |
60 | 178 | //! - [Workflow](workflow) |
61 | 179 | //! |
62 | | -//! ## Features |
| 180 | +//! # Features |
63 | 181 | //! |
64 | 182 | //! Have a look at the following SDK capabilities: |
65 | 183 | //! |
@@ -99,6 +217,7 @@ pub mod http_server; |
99 | 217 | pub mod hyper; |
100 | 218 | pub mod serde; |
101 | 219 |
|
| 220 | +use ::serde::de::StdError; |
102 | 221 | /// Entry-point macro to define a Restate [Service](https://docs.restate.dev/concepts/services#services-1). |
103 | 222 | /// |
104 | 223 | /// ```rust,no_run |
@@ -364,6 +483,7 @@ pub use restate_sdk_macros::object; |
364 | 483 | /// |
365 | 484 | /// For more details, check the [`service` macro](macro@crate::service) documentation. |
366 | 485 | pub use restate_sdk_macros::workflow; |
| 486 | +use crate::errors::TerminalError; |
367 | 487 |
|
368 | 488 | /// Prelude contains all the useful imports you need to get started with Restate. |
369 | 489 | pub mod prelude { |
|
0 commit comments