pub trait ContextSideEffects<'ctx>: SealedContext<'ctx> {
// Provided methods
fn run<R, F, T>(
&self,
run_closure: R,
) -> impl RunFuture<Result<T, TerminalError>> + 'ctx
where R: RunClosure<Fut = F, Output = T> + Send + 'ctx,
F: Future<Output = HandlerResult<T>> + Send + 'ctx,
T: Serialize + Deserialize + 'static { ... }
fn random_seed(&self) -> u64 { ... }
fn rand(&mut self) -> &mut StdRng { ... }
fn rand_uuid(&mut self) -> Uuid { ... }
}
Expand description
§Journaling Results
Restate uses an execution log for replay after failures and suspensions. This means that non-deterministic results (e.g. database responses, UUID generation) need to be stored in the execution log. The SDK offers some functionalities to help you with this:
- Journaled actions: Run any block of code and store the result in Restate. Restate replays the result instead of re-executing the block on retries.
- UUID generator: Built-in helpers for generating stable UUIDs. Restate seeds the random number generator with the invocation ID, so it always returns the same value on retries.
- Random generator: Built-in helpers for generating randoms. Restate seeds the random number generator with the invocation ID, so it always returns the same value on retries.
Provided Methods§
Sourcefn run<R, F, T>(
&self,
run_closure: R,
) -> impl RunFuture<Result<T, TerminalError>> + 'ctxwhere
R: RunClosure<Fut = F, Output = T> + Send + 'ctx,
F: Future<Output = HandlerResult<T>> + Send + 'ctx,
T: Serialize + Deserialize + 'static,
fn run<R, F, T>(
&self,
run_closure: R,
) -> impl RunFuture<Result<T, TerminalError>> + 'ctxwhere
R: RunClosure<Fut = F, Output = T> + Send + 'ctx,
F: Future<Output = HandlerResult<T>> + Send + 'ctx,
T: Serialize + Deserialize + 'static,
§Journaled actions
You can store the result of a (non-deterministic) operation in the Restate execution log (e.g. database requests, HTTP calls, etc). Restate replays the result instead of re-executing the operation on retries.
Here is an example of a database request for which the string response is stored in Restate:
let response = ctx.run(|| do_db_request()).await?;
You cannot use the Restate context within ctx.run
.
This includes actions such as getting state, calling another service, and nesting other journaled actions.
For more info about serialization of the return values, see crate::serde.
You can configure the retry policy for the ctx.run
block:
let my_run_retry_policy = RunRetryPolicy::default()
.initial_delay(Duration::from_millis(100))
.exponentiation_factor(2.0)
.max_delay(Duration::from_millis(1000))
.max_attempts(10)
.max_duration(Duration::from_secs(10));
ctx.run(|| write_to_other_system())
.retry_policy(my_run_retry_policy)
.await
.map_err(|e| {
// Handle the terminal error after retries exhausted
// For example, undo previous actions (see sagas guide) and
// propagate the error back to the caller
e
})?;
This way you can override the default retry behavior of your Restate service for specific operations.
Have a look at RunFuture::retry_policy
for more information.
If you set a maximum number of attempts, then the ctx.run
block will fail with a TerminalError once the retries are exhausted.
Have a look at the Sagas guide to learn how to undo previous actions of the handler to keep the system in a consistent state.
Caution: Immediately await journaled actions:
Always immediately await ctx.run
, before doing any other context calls.
If not, you might bump into non-determinism errors during replay,
because the journaled result can get interleaved with the other context calls in the journal in a non-deterministic way.
Sourcefn random_seed(&self) -> u64
fn random_seed(&self) -> u64
Return a random seed inherently predictable, based on the invocation id, which is not secret.
This value is stable during the invocation lifecycle, thus across retries.
Sourcefn rand(&mut self) -> &mut StdRng
fn rand(&mut self) -> &mut StdRng
§Generating random numbers
Return a rand::Rng
instance inherently predictable, seeded with ContextSideEffects::random_seed
.
For example, you can use this to generate a random number:
async fn rand_generate(mut ctx: Context<'_>) {
let x: u32 = ctx.rand().gen();
This instance is useful to generate identifiers, idempotency keys, and for uniform sampling from a set of options.
If a cryptographically secure value is needed, please generate that externally using ContextSideEffects::run
.
Sourcefn rand_uuid(&mut self) -> Uuid
fn rand_uuid(&mut self) -> Uuid
§Generating UUIDs
Returns a random uuid::Uuid
, generated using ContextSideEffects::rand
.
You can use these UUIDs to generate stable idempotency keys, to deduplicate operations. For example, you can use this to let a payment service avoid duplicate payments during retries.
Do not use this in cryptographic contexts.
let uuid: Uuid = ctx.rand_uuid();
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.