Trait ContextSideEffects

Source
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:

  1. 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.
  2. 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.
  3. 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§

Source

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,

§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.

Source

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.

Source

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.

Source

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.

Implementors§

Source§

impl<'ctx, CTX: SealedContext<'ctx>> ContextSideEffects<'ctx> for CTX