Skip to main content

Client

Struct Client 

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

Provides blocking, typed control of registered Dex Flows.

Client methods block the calling thread while an internal Tokio runtime performs gRPC and blob hydration. The Client owns its transport resources; dropping it releases them. Flow, Step, RPC, Attribute, and Channel arguments are checked against the Registry. Construction performs no network I/O. Before its first service request, every Client negotiates protocol version 4. Successful negotiation and incompatibility errors are cached for its lifetime; transient negotiation failures are retried on the next call. Incompatibility errors name the Server version and required server-zd release. Concurrent first calls share one negotiation. An unreachable Server during negotiation returns the usual service error with its gRPC code. A positive request timeout on a wait method also bounds negotiation and the wait for another caller’s negotiation; its expiry returns SdkError::RequestTimeout and is not cached.

Changed in 1.35.0: protocol 4 is required (server-zd newer than v0.1.16). Upgrade the Server first.

Changed in 1.26.0: only server-zd is supported as the Dex Server.

A Client call never retries a failed application RPC automatically. Inspect SdkError variants and crate::ServiceError status details before retrying an operation whose external effects may already have happened.

§Examples

use dex_sdk::{BlobCache, BlobCacheConfig, Client, ClientOptions, Registry};
use std::sync::Arc;

let cache = Arc::new(BlobCache::open(BlobCacheConfig::new(
    "/tmp/dex-blobs", 64 * 1024 * 1024, 0,
)?)?);
let client = Client::try_new(Registry::new(), cache, ClientOptions::new())?;
client.health_check()?;

Implementations§

Source§

impl Client

Source

pub fn get_attributes<Reads: AttributeReads>( &self, flow_id: &str, reads: Reads, ) -> SdkResult<Reads::Values>

Reads committed Attribute values from the current or latest run of a Flow.

Since 1.31.0.

Use this method to read Flow state from outside the Flow without defining an RPC. Pass a tuple of reads. Each read is an Attribute or an AttributeMapInstance from AttributeMap::instance, usually by reference. The method returns a tuple of the same arity. Each element is Some(value) when the value is set and None when it is not set. A set value that equals the type’s default, such as an empty string or 0, is still Some. Pass a one-element tuple, such as (&ORDER_STATUS,), to read one value. A call accepts 1 to 12 reads. Read more values with several calls. Values from different calls can come from different commits.

The Client sends every key in one server request. All values of one call therefore come from the same commit. No Worker runs, so the call works while no Worker is available. The call reads the current run, or the latest run when no run is open. A closed run stays readable until it is purged.

The call sees only committed state. It does not see writes that a running RPC or Step has staged but not yet committed. An RPC handler that reads through its Context, in contrast, also sees its own staged writes.

Hard-delete Attributes and AttributeMaps, defined with Attribute::hard_delete_on_null, are readable. Dex resolves their stored values before it responds. A deleted hard-delete value reads as None. The Client resolves large-value references through the shared BlobCache and the server, as other Client reads do. It decodes every value before it returns. On any error the call returns no values. The method blocks the calling thread until the server responds and every value is decoded.

Every read must match a definition declared by some Flow in the Client’s Registry. A definition matches when it has the same name, the same kind (Attribute or AttributeMap), the same Rust value type, and the same index, sync_to_attribute_store, and hard_delete_on_null settings. Channels and Streams with the same name are ignored. The server does not check that the target Flow’s type declares the definition. A same-named Attribute of another type in the target Flow fails to decode.

§Examples
use dex_sdk::{Attribute, AttributeMap, Client, SdkError};
use std::sync::LazyLock;

#[derive(serde::Deserialize, serde::Serialize)]
struct Note {
    text: String,
}

static ORDER_STATUS: LazyLock<Attribute<String>> =
    LazyLock::new(|| Attribute::new("order-status"));
static NOTES: LazyLock<AttributeMap<Note>> = LazyLock::new(|| AttributeMap::new("notes"));

fn describe_order(client: &Client, order_id: &str) -> Result<String, SdkError> {
    let (status, note) =
        match client.get_attributes(order_id, (&ORDER_STATUS, &NOTES.instance("n1"))) {
            Ok(values) => values,
            Err(SdkError::FlowNotFound { .. }) => return Ok(format!("{order_id} does not exist")),
            Err(error) => return Err(error),
        };
    let status = status.unwrap_or_else(|| "new".to_string());
    let note = note.map_or_else(|| "no note yet".to_string(), |note| note.text);
    Ok(format!("{order_id}: {status} ({note})"))
}
§Parameters
  • flow_id is the Flow ID. It must be non-blank.
  • reads is a tuple of 1 to 12 AttributeRead values. Two reads of the same Attribute or AttributeMap instance are rejected.

Returns one Option<T> per read, in read order, where T is the read’s value type.

§Errors

Every error message except the one for a blank Flow ID names the Flow ID. An error about one read or value also names its Attribute name or physical key. Service-backed errors report get_attributes as their ServiceError::operation.

  • SdkError::InvalidArgument for an empty or whitespace-only Flow ID, an empty map instance key, a map instance key that contains /, or a duplicate read. The Client does not contact Dex.
  • SdkError::FlowDefinition when no Flow in the Registry declares a matching definition. The Client does not contact Dex.
  • SdkError::FlowNotFound when the Flow does not exist or its latest run is being purged when the values are read.
  • SdkError::ValueMapping when a stored value cannot be decoded into its value type, or a large-value reference cannot be resolved through the blob cache. A decode error gives the target type and a reason, such as a missing field name, but never quotes the value. A purge of the run, or a concurrent overwrite of a large value, between the read and the large-value resolution also returns this error. Retry the call to read fresh state.
  • SdkError::Service for an invalid server response, a failed large-value request, or another server or transport failure. Other service-backed variants are possible when Dex reports a more specific status.
Source§

impl Client

Source

pub fn new( registry: Registry, blob_cache: Arc<BlobCache>, options: ClientOptions, ) -> Self

Creates a Client or panics when runtime or endpoint initialization fails.

Prefer Self::try_new when the application can recover from startup configuration errors.

§Panics

Panics if a Tokio runtime cannot be created or the server address or TLS settings are invalid.

Source

pub fn try_new( registry: Registry, blob_cache: Arc<BlobCache>, options: ClientOptions, ) -> SdkResult<Self>

Creates a Client from validated definitions, a shared BlobCache, and connection options.

§Errors

Returns SdkError::Service if runtime or endpoint initialization fails. The transport connects lazily, so server availability is checked by the first operation. Invalid TLS options return SdkError::InvalidArgument. Certificate and handshake failures occur on the first call.

Source

pub fn start_flow<SomeFlow: Flow>( &self, flow: &SomeFlow, flow_id: &str, input: SomeFlow::StartInput, ) -> SdkResult<String>

Starts a registered Flow on pull Workers with other options using server defaults.

Returns the server-assigned run ID. input is encoded for the registered starting Step. Changed in 1.25.0: starts always route to pull Workers. Success acknowledges the start; it does not wait for a Worker or Flow completion.

use dex_sdk::{Client, Flow, FlowStatus, SdkResult};
use std::time::Duration;

fn start_and_wait<F: Flow>(client: &Client, flow: &F, input: F::StartInput)
    -> SdkResult<(String, FlowStatus)> {
    let run_id = client.start_flow(flow, "order-1", input)?;
    let result = client.wait_for_flow_with_timeout("order-1", Duration::from_secs(60))?;
    Ok((run_id, result.status()))
}

Use Self::wait_for_flow to observe the terminal result.

§Errors

Returns a definition or mapping error locally, SdkError::FlowAlreadyStarted for a reuse conflict, or another service-backed SdkError when Dex rejects the request.

Source

pub fn start_flow_with_options<SomeFlow: Flow>( &self, flow: &SomeFlow, flow_id: &str, input: SomeFlow::StartInput, options: StartFlowOptions, ) -> SdkResult<String>

Starts a registered Flow with explicit lifecycle, retry, and initial persistence options.

Always selects pull Workers, overriding any server-configured push target. Returns the server-assigned run ID. A missing request ID is replaced with a generated UUID. When the options contain an initial value for a hard-delete Attribute, the Client first uses the Server protocol version negotiated before the first service request.

§Errors

Returns SdkError for invalid IDs, unregistered or mismatched Flow types, encoding failures, invalid option durations, reuse conflicts, and service failures. Returns SdkError::InvalidArgument for an invalid initial hard-delete Attribute value or a definition that does not match the registered one. Returns SdkError::Service when the Server does not support the protocol version this SDK requires.

Source

pub fn invoke_rpc<Input: Value, Output: Value>( &self, flow_id: &str, rpc: Rpc<Input, Output>, input: Input, ) -> SdkResult<Output>

Invokes a registered RPC with typed input and decodes its typed output.

use dex_sdk::{Client, Rpc, SdkResult};
fn describe_order(client: &Client, rpc: Rpc<(), String>) -> SdkResult<String> {
    client.invoke_rpc("order-1", rpc, ())
}

The call blocks until the handler result is decoded or a request error occurs. RPCs do not enter the durable Step queue. When no Worker takes the RPC within the dispatch wait, Dex returns an Unavailable service error. The dispatch wait is the Server’s configurable matching.dispatchWait setting, which defaults to ten seconds and is bounded by the caller’s deadline. A timeout or lost response does not prove the handler performed no effects; make retries safe.

A non-transactional RPC without Attribute locks starts from a backend query. A retained terminal execution can serve it when the handler returns no durable effects. Locks, transactional execution, returned effects, or server policy can require an active execution.

§Errors

Returns SdkError::RpcLockConflict when locks cannot be acquired, WorkerInvocation for a handler failure, FlowNotActiveOrNotFound when the target is missing or the selected path cannot use its closed execution, or a mapping/service error.

Source

pub fn invoke_rpc_with_options<Input: Value, Output: Value>( &self, flow_id: &str, rpc: Rpc<Input, Output>, input: Input, options: RpcInvokeOptions, ) -> SdkResult<Output>

Invokes a registered RPC with typed input and additive runtime map-instance selections.

§Errors

Returns a local definition error for an unregistered or wrong-kind selection. Invalid map instance names and all errors documented by Self::invoke_rpc are also returned.

Source

pub fn invoke_rpc_without_input<Output: Value>( &self, flow_id: &str, rpc: Rpc<(), Output>, ) -> SdkResult<Output>

Invokes a registered no-input RPC and decodes its typed output.

Returns the same errors as Self::invoke_rpc.

Source

pub fn invoke_rpc_without_input_with_options<Output: Value>( &self, flow_id: &str, rpc: Rpc<(), Output>, options: RpcInvokeOptions, ) -> SdkResult<Output>

Invokes a registered no-input RPC with additive runtime map-instance selections.

§Errors

Returns the same errors as Self::invoke_rpc_with_options.

Source

pub fn write_stream<T: Value>( &self, flow_id: &str, stream: &Stream<T>, source: &str, value: T, ) -> SdkResult<()>

Appends one typed best-effort Stream message with source metadata.

The Flow instance need not exist or be active. Every call appends a message, including calls that repeat the same source. A source may contain #.

§Errors

Returns a definition error for an unregistered Stream, InvalidArgument for an empty Flow ID or source, a mapping error, or a FlowService failure.

Source

pub fn read_stream<T: Value>( &self, flow_id: &str, stream: &Stream<T>, resume_token: &str, ) -> SdkResult<StreamMessage<T>>

Blocks for the next retained Stream message using the server default wait.

An empty token starts at the retained head. Pass the returned token unchanged to resume.

Source

pub fn read_stream_with_timeout<T: Value>( &self, flow_id: &str, stream: &Stream<T>, resume_token: &str, timeout: Duration, ) -> SdkResult<StreamMessage<T>>

Blocks for the next retained Stream message with an explicit server wait.

§Errors

Returns SdkError::LongPollTimeout when no message arrives, plus the same definition, duration, mapping, and service errors as Self::read_stream.

Source

pub fn list_stream_messages<T: Value>( &self, flow_id: &str, stream: &Stream<T>, page_size: i32, before_page_token: &str, ) -> SdkResult<StreamMessagesPage<T>>

Returns one newest-first page of retained Stream messages without waiting for writes.

An empty before_page_token starts at the retained tail. Pass StreamMessagesPage::next_page_token unchanged to read the next, older page. Stream trimming may remove messages between pages. Tokens are exclusive and scoped to the Flow ID, the selected Flow type, and the Stream. A token from another Flow ID or Stream is rejected. If trimming passes the page anchor, the server returns an empty page rather than restarting at the tail. New writes after the first page do not enter that older-page chain.

page_size must be between 1 and the Server’s streamStore.maxReadMessages setting, which defaults to 1000. The Server rejects a larger value; it does not reduce it.

§Errors

Returns InvalidArgument when flow_id is empty, page_size is not positive, or page_size exceeds streamStore.maxReadMessages. Returns a definition error for an unregistered Stream, a mapping error for an incompatible retained value, or a FlowService error when the request fails.

Source

pub fn wait_for_flow(&self, flow_id: &str) -> SdkResult<FlowResult>

Blocks until a Flow closes and returns its terminal result.

§Errors

Returns FlowNotFound for an unknown ID or a mapping/service error. Every terminal status is represented by FlowResult. This overload has no client-side timeout.

Source

pub fn wait_for_flow_with_timeout( &self, flow_id: &str, timeout: Duration, ) -> SdkResult<FlowResult>

Blocks up to timeout and returns the terminal result with all output-bearing completions.

Like Self::wait_for_flow, terminal failures are returned as FlowResult, not as request errors. A wait timeout does not stop the Flow. Durations use whole seconds. See the Self::start_flow example for a start followed by a bounded wait.

§Errors

Returns SdkError::LongPollTimeout when the timeout elapses while the Flow remains active; other errors match Self::wait_for_flow.

Source

pub fn describe_flow(&self, flow_id: &str) -> SdkResult<FlowInfo>

Returns current identity, status, type, and start time for the latest run.

§Errors

Returns SdkError::FlowNotFound for an unknown ID or a service/protocol error.

Source

pub fn search_flows( &self, query: &str, page_size: i32, ) -> SdkResult<SearchFlowsPage>

Executes a server search query and returns its first page.

page_size must be non-negative; zero uses the server default. Indexed values are hydrated and decoded as serde_json::Value.

Source

pub fn search_flows_page( &self, query: &str, page_size: i32, next_page_token: &str, ) -> SdkResult<SearchFlowsPage>

Continues a search with the opaque token returned by a previous page.

Keep query and page_size consistent across pages. An empty returned token marks the last page.

Source

pub fn search_flows_with_options( &self, query: &str, page_size: i32, next_page_token: &str, options: SearchFlowsOptions, ) -> SdkResult<SearchFlowsPage>

Searches with explicit inclusion options; retain query and options across pages. An empty token starts pagination.

Source

pub fn stop_flow( &self, flow_id: &str, options: StopFlowOptions, ) -> SdkResult<()>

Cancels, terminates, or fails an active Flow according to options.

Success acknowledges the stop request. Use Self::wait_for_flow to observe its terminal status. This does not stop the Worker or undo effects in an external service.

§Errors

Returns SdkError::FlowNotActiveOrNotFound when no active run exists or another service error.

Source

pub fn time_travel( &self, flow_id: &str, options: TimeTravelOptions, ) -> SdkResult<String>

Re-runs an existing Flow from the selected historical point.

Time travel keeps the Flow’s run ID and starts the next run attempt of that run. Re-run Steps reuse the Flow ID, run ID, and Step execution IDs of the original execution. An external idempotency key built only from those IDs therefore matches the effect from before time travel. Reconcile such effects, or build keys that the application changes for each re-run.

Returns the run ID, which is the same run ID the Flow had before time travel.

§Errors

Returns InvalidArgument for an unrepresentable time, FlowNotFound for an unknown ID, or another service error.

Source

pub fn skip_timer( &self, flow_id: &str, step_execution: StepExecutionId, timer: TimerId, ) -> SdkResult<()>

Marks one timer in an active Step execution as satisfied immediately.

§Errors

Returns InvalidArgument for an oversized index, FlowNotActiveOrNotFound for a terminal Flow, or a service error when the Step execution or timer cannot be targeted.

Source

pub fn wait_for_step_completion( &self, flow_id: &str, step_execution: StepExecutionId, options: WaitForStepCompletionOptions, ) -> SdkResult<()>

Blocks until one Step execution completes or its request budget expires.

The server derives a stable Request ID from the Step execution when none is supplied.

§Errors

Returns SdkError::RequestTimeout when a positive request timeout expires, FlowNotActiveOrNotFound when appropriate, or another service error. Successful completion returns () and does not decode Step output.

Source

pub fn wait_for_attribute_match<T: Value>( &self, flow_id: &str, attribute: &Attribute<T>, attribute_match: AttributeMatch<T>, options: WaitForAttributeOptions, ) -> SdkResult<T>

Blocks until a singleton Attribute in the current run satisfies attribute_match.

Returns the current value observed by the successful wait. The server derives a stable Request ID from the condition when none is supplied. String and Boolean Attributes support equality matches. Integer and floating-point Attributes support every match. A positive request-timeout expiry returns SdkError::RequestTimeout. A hard-delete Attribute, defined with Attribute::hard_delete_on_null, returns SdkError::InvalidArgument before the Client contacts Dex.

Source

pub fn wait_for_attribute_map_instance_match<T: Value>( &self, flow_id: &str, attribute: &AttributeMap<T>, instance: &str, attribute_match: AttributeMatch<T>, options: WaitForAttributeOptions, ) -> SdkResult<T>

Blocks until one AttributeMap instance satisfies attribute_match.

This targets the current run and otherwise has the same match, request-timeout and handler-generation, request-ID, return-value, and error behavior as Self::wait_for_attribute_match. A hard-delete AttributeMap, defined with AttributeMap::hard_delete_on_null, returns SdkError::InvalidArgument before the Client contacts Dex.

Source

pub fn update_flow_config( &self, flow_id: &str, config: FlowConfig, ) -> SdkResult<()>

Replaces mutable runtime configuration fields on an active Flow.

Leaves the Worker target unset, preserving the active Flow target. Other unset fields also retain the active Flow setting.

Source

pub fn trigger_continue_as_new(&self, flow_id: &str) -> SdkResult<()>

Requests that an active Flow roll its history into a successor run.

Source

pub fn health_check(&self) -> SdkResult<()>

Verifies that Dex FlowService responds successfully.

Returns Ok(()) for a healthy service and a service-backed SdkError otherwise.

Auto Trait Implementations§

§

impl !Freeze for Client

§

impl !RefUnwindSafe for Client

§

impl !UnwindSafe for Client

§

impl Forget for Client

§

impl Send for Client

§

impl Sync for Client

§

impl Unpin for Client

§

impl UnsafeUnpin for Client

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> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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: Sized + 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 = !

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

fn try_from(value: U) -> Result<T, !>

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