RuntaraSdk

Struct RuntaraSdk 

Source
pub struct RuntaraSdk { /* private fields */ }
Expand description

High-level SDK client for instance communication with runtara-core.

This client wraps a backend (QUIC or embedded) and provides ergonomic methods for all instance lifecycle operations.

§Example (QUIC mode)

use runtara_sdk::RuntaraSdk;

let mut sdk = RuntaraSdk::localhost("my-instance", "my-tenant")?;
sdk.connect().await?;
sdk.register(None).await?;

// Process items with checkpointing
for i in 0..items.len() {
    let state = serde_json::to_vec(&my_state)?;
    if let Some(existing) = sdk.checkpoint(&format!("item-{}", i), &state).await? {
        // Resuming - restore state and skip
        my_state = serde_json::from_slice(&existing)?;
        continue;
    }
    // Fresh execution - process item
    process_item(&items[i]);
}

sdk.completed(b"result").await?;

§Example (Embedded mode)

use runtara_sdk::RuntaraSdk;
use std::sync::Arc;

// Create persistence layer (e.g., SQLite or PostgreSQL)
let persistence: Arc<dyn Persistence> = create_persistence().await?;

let mut sdk = RuntaraSdk::embedded(persistence, "my-instance", "my-tenant");
sdk.connect().await?;  // No-op for embedded
sdk.register(None).await?;

// Same checkpoint API works with embedded mode
for i in 0..items.len() {
    let state = serde_json::to_vec(&my_state)?;
    let result = sdk.checkpoint(&format!("item-{}", i), &state).await?;
    // ...
}

sdk.completed(b"result").await?;

Implementations§

Source§

impl RuntaraSdk

Source

pub fn new(config: SdkConfig) -> Result<RuntaraSdk, SdkError>

Create a new SDK instance with the given configuration.

This creates a QUIC-based SDK that connects to runtara-core over the network.

Source

pub fn from_env() -> Result<RuntaraSdk, SdkError>

Create an SDK instance from environment variables.

See SdkConfig::from_env for required and optional environment variables.

Source

pub fn localhost( instance_id: impl Into<String>, tenant_id: impl Into<String>, ) -> Result<RuntaraSdk, SdkError>

Create an SDK instance for local development.

This connects to 127.0.0.1:8001 with TLS verification disabled.

Source

pub async fn init(self, checkpoint_id: Option<&str>) -> Result<(), SdkError>

Initialize SDK: connect, register, and make available globally for #[durable].

This is a convenience method that combines:

  1. connect() - establish connection to runtara-core
  2. register(checkpoint_id) - register this instance
  3. register_sdk() - make SDK available globally for #[durable] functions
§Example
use runtara_sdk::RuntaraSdk;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // One-liner setup for #[durable] functions
    RuntaraSdk::localhost("my-instance", "my-tenant")?
        .init(None)
        .await?;

    // Now #[durable] functions work automatically
    my_durable_function("key".to_string(), args).await?;
    Ok(())
}
Source

pub async fn connect(&self) -> Result<(), SdkError>

Connect to runtara-core.

Source

pub async fn is_connected(&self) -> bool

Check if connected to runtara-core.

Source

pub async fn close(&self)

Close the connection to runtara-core.

Source

pub async fn register( &mut self, checkpoint_id: Option<&str>, ) -> Result<(), SdkError>

Register this instance with runtara-core.

This should be called at instance startup. If checkpoint_id is provided, the instance is resuming from a checkpoint.

Source

pub async fn checkpoint( &self, checkpoint_id: &str, state: &[u8], ) -> Result<CheckpointResult, SdkError>

Checkpoint with the given ID and state.

This is the primary checkpoint method that handles both save and resume:

  • If a checkpoint with this ID already exists, returns the existing state (for resume)
  • If no checkpoint exists, saves the provided state and returns None

This also serves as a heartbeat - each checkpoint call reports progress to runtara-core.

The returned CheckpointResult also includes any pending signal (cancel, pause) that the instance should handle after processing the checkpoint.

§Example
// In a loop - checkpoint handles both fresh runs and resumes
for i in 0..items.len() {
    let checkpoint_id = format!("item-{}", i);
    let result = sdk.checkpoint(&checkpoint_id, &state).await?;

    // Check for pending signals
    if result.should_cancel() {
        return Err("Cancelled".into());
    }
    if result.should_pause() {
        // Exit cleanly - will be resumed later
        return Ok(());
    }

    if let Some(existing_state) = result.existing_state() {
        // Resuming - restore state and skip already-processed work
        state = serde_json::from_slice(existing_state)?;
        continue;
    }
    // Fresh execution - process item
    process_item(&items[i]);
}
Source

pub async fn get_checkpoint( &self, checkpoint_id: &str, ) -> Result<Option<Vec<u8>>, SdkError>

Get a checkpoint by ID without saving (read-only lookup).

Returns the checkpoint state if found, or None if not found. Use this when you want to check if a cached result exists before executing.

§Example
// Check if result is already cached
if let Some(cached_state) = sdk.get_checkpoint("my-operation").await? {
    let result: MyResult = serde_json::from_slice(&cached_state)?;
    return Ok(result);
}
// Not cached - execute operation and save result
let result = do_expensive_operation();
let state = serde_json::to_vec(&result)?;
sdk.checkpoint("my-operation", &state).await?;
Source

pub async fn sleep( &self, duration: Duration, checkpoint_id: &str, state: &[u8], ) -> Result<(), SdkError>

Request to sleep for the specified duration.

This is a durable sleep that persists across restarts:

  • Saves a checkpoint with the provided state
  • Records the wake time (sleep_until) in the database
  • On resume, calculates remaining time and only sleeps for the remainder

In QUIC mode, the server tracks the wake time. In embedded mode, the persistence layer tracks it directly.

Source

pub async fn heartbeat(&self) -> Result<(), SdkError>

Send a heartbeat event (simple “I’m alive” signal).

Use this for progress reporting without checkpointing. For durable progress, use checkpoint() instead.

Source

pub async fn completed(&self, output: &[u8]) -> Result<(), SdkError>

Send a completed event with output.

Source

pub async fn failed(&self, error: &str) -> Result<(), SdkError>

Send a failed event with error message.

Source

pub async fn suspended(&self) -> Result<(), SdkError>

Send a suspended event.

Source

pub async fn custom_event( &self, subtype: &str, payload: Vec<u8>, ) -> Result<(), SdkError>

Send a custom event with arbitrary subtype and payload.

This is a fire-and-forget event stored by runtara-core with the given subtype. Core treats the subtype as an opaque string without any semantic interpretation.

§Arguments
  • subtype - Arbitrary event subtype string
  • payload - Event payload as raw bytes (typically JSON serialized)
§Example
let payload = serde_json::to_vec(&my_event_data)?;
sdk.custom_event("my_custom_event", payload).await?;
Source

pub async fn poll_signal(&mut self) -> Result<Option<Signal>, SdkError>

Poll for pending signals.

Rate-limited to avoid hammering the server. Returns Some(Signal) if a signal is pending, None otherwise.

Note: Only available with QUIC backend.

Source

pub async fn poll_signal_now(&mut self) -> Result<Option<Signal>, SdkError>

Force poll for signals (ignoring rate limit).

Note: Only available with QUIC backend.

Source

pub async fn acknowledge_signal( &self, signal_type: SignalType, acknowledged: bool, ) -> Result<(), SdkError>

Acknowledge a received signal.

Note: Only available with QUIC backend.

Source

pub async fn check_cancelled(&mut self) -> Result<(), SdkError>

Check for cancellation and return error if cancelled.

Convenience method for use in execution loops:

for item in items {
    sdk.check_cancelled().await?;
    // process item...
}

Note: Only available with QUIC backend.

Source

pub async fn check_paused(&mut self) -> Result<(), SdkError>

Check for pause and return error if paused.

Note: Only available with QUIC backend.

Source

pub async fn record_retry_attempt( &self, checkpoint_id: &str, attempt_number: u32, error_message: Option<&str>, ) -> Result<(), SdkError>

Record a retry attempt for audit trail.

This is a fire-and-forget operation that records a retry attempt in the checkpoint history. Called by the #[durable] macro when a function fails and is about to be retried.

§Arguments
  • checkpoint_id - The durable function’s cache key
  • attempt_number - The 1-indexed retry attempt number
  • error_message - Error message from the previous failed attempt
Source

pub async fn get_status(&self) -> Result<StatusResponse, SdkError>

Get the current status of this instance.

Source

pub async fn get_instance_status( &self, instance_id: &str, ) -> Result<StatusResponse, SdkError>

Get the status of another instance.

Note: Only available with QUIC backend.

Source

pub fn instance_id(&self) -> &str

Get the instance ID.

Source

pub fn tenant_id(&self) -> &str

Get the tenant ID.

Source

pub fn is_registered(&self) -> bool

Check if the instance is registered.

Source

pub fn heartbeat_interval_ms(&self) -> u64

Get the configured heartbeat interval in milliseconds. Returns 0 if automatic heartbeats are disabled.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more