pub struct Client { /* private fields */ }Expand description
A registry-less, out-of-process handle over the system database.
Unlike DurableEngine, a Client holds no workflow
registry and runs nothing: it enqueues workflows for
executors to claim, sends messages, reads events, and queries
workflow state. It is the producer/observer half of the split between
enqueueing work and executing it.
use durare::{Client, PostgresProvider, WorkflowOptions};
use std::sync::Arc;
let provider = Arc::new(PostgresProvider::connect("postgres://localhost/app").await?);
let client = Client::new(provider);
// Enqueue for an executor fleet elsewhere to run; observe from here.
let handle = client
.enqueue::<_, String>("orders", "process_order", "1001".to_string(), WorkflowOptions::default())
.await?;
println!("state: {}", handle.get_status().await?.status);Implementations§
Source§impl Client
impl Client
Sourcepub fn new(provider: Arc<dyn StateProvider>) -> Self
pub fn new(provider: Arc<dyn StateProvider>) -> Self
Build a client over an existing StateProvider (e.g. a
PostgresProvider connected to the system
database). The database must already be initialized by the application.
Enqueued work is left version-agnostic (empty application version), so any
executor claims it regardless of its version. Use
with_app_version to gate it to one version.
Sourcepub fn with_app_version(self, version: impl Into<String>) -> Self
pub fn with_app_version(self, version: impl Into<String>) -> Self
Stamp enqueued workflows with this application version, so only an executor of the same version claims them (version-gated dispatch).
Sourcepub fn provider(&self) -> &Arc<dyn StateProvider> ⓘ
pub fn provider(&self) -> &Arc<dyn StateProvider> ⓘ
The underlying state provider.
Sourcepub fn debouncer(&self, target_workflow: &str) -> DebouncerClient<'_>
pub fn debouncer(&self, target_workflow: &str) -> DebouncerClient<'_>
Debounce a target workflow from out of process — coalesce rapid repeated triggers (grouped by key) into a single delayed run with the latest input. A launched engine elsewhere (which auto-registers the internal debouncer and has the target registered) executes the coalesced run.
Sourcepub async fn enqueue<I, O>(
&self,
queue_name: &str,
workflow_name: &str,
input: I,
opts: WorkflowOptions,
) -> Result<WorkflowHandle<O>>where
I: Serialize,
pub async fn enqueue<I, O>(
&self,
queue_name: &str,
workflow_name: &str,
input: I,
opts: WorkflowOptions,
) -> Result<WorkflowHandle<O>>where
I: Serialize,
Enqueue workflow_name on queue_name for an executor to claim and run.
The workflow need not be registered in this process. Returns a polling
WorkflowHandle over the result.
The row is persisted ENQUEUED (or DELAYED when opts.delay is set);
opts.queue is ignored — the queue is the first argument.
§Errors
Error::QueueDeduplicated if opts.dedup_id collides with an active
workflow on the queue (under the default Reject policy); an
application error for an empty queue or workflow name, a partition key
combined with a deduplication id, or a deduplication policy without a
deduplication id.
Sourcepub async fn send<T: Serialize>(
&self,
destination_id: &str,
message: T,
topic: &str,
) -> Result<()>
pub async fn send<T: Serialize>( &self, destination_id: &str, message: T, topic: &str, ) -> Result<()>
Send a message to a workflow (e.g. to nudge one waiting in
DurableContext::recv). Not durable —
there is no calling workflow to checkpoint into.
Sourcepub async fn send_with_idempotency_key<T: Serialize>(
&self,
destination_id: &str,
message: T,
topic: &str,
idempotency_key: &str,
) -> Result<()>
pub async fn send_with_idempotency_key<T: Serialize>( &self, destination_id: &str, message: T, topic: &str, idempotency_key: &str, ) -> Result<()>
Like send but idempotent: the message is delivered at
most once for a given idempotency_key and destination, so an
out-of-process producer that retries after a transient failure never
double-delivers. Distinct keys each deliver.
Sourcepub async fn get_event<T: DeserializeOwned>(
&self,
target_workflow_id: &str,
key: &str,
timeout: Duration,
) -> Result<Option<T>>
pub async fn get_event<T: DeserializeOwned>( &self, target_workflow_id: &str, key: &str, timeout: Duration, ) -> Result<Option<T>>
Read event key of a workflow, waiting up to timeout for it to be set.
Returns None on timeout.
Sourcepub async fn retrieve_workflow<O>(&self, id: &str) -> Result<WorkflowHandle<O>>
pub async fn retrieve_workflow<O>(&self, id: &str) -> Result<WorkflowHandle<O>>
A polling WorkflowHandle for an existing workflow. Errors if no
workflow exists under id.
Sourcepub async fn list_workflows(
&self,
filter: &ListFilter,
) -> Result<Vec<WorkflowStatus>>
pub async fn list_workflows( &self, filter: &ListFilter, ) -> Result<Vec<WorkflowStatus>>
List workflows matching filter.
Sourcepub async fn get_workflow_steps(
&self,
workflow_id: &str,
) -> Result<Vec<StepInfo>>
pub async fn get_workflow_steps( &self, workflow_id: &str, ) -> Result<Vec<StepInfo>>
The recorded steps of a workflow, ordered by step id.
Sourcepub async fn cancel_workflow(&self, id: &str) -> Result<()>
pub async fn cancel_workflow(&self, id: &str) -> Result<()>
Cancel a workflow: a non-terminal one is set CANCELLED and removed from
its queue; a running workflow stops at its next step.
Sourcepub async fn cancel_workflows(&self, ids: &[String]) -> Result<()>
pub async fn cancel_workflows(&self, ids: &[String]) -> Result<()>
Cancel many workflows in one round-trip. Missing or already-terminal ids are skipped; an empty slice is a no-op.
Sourcepub async fn delete_workflows(
&self,
ids: &[String],
delete_children: bool,
) -> Result<()>
pub async fn delete_workflows( &self, ids: &[String], delete_children: bool, ) -> Result<()>
Delete many workflows and (via ON DELETE CASCADE) their step / event /
stream rows. When delete_children, every descendant by
parent_workflow_id is removed too. Missing ids are skipped.
Sourcepub async fn resume_workflow<O>(&self, id: &str) -> Result<WorkflowHandle<O>>
pub async fn resume_workflow<O>(&self, id: &str) -> Result<WorkflowHandle<O>>
Resume a cancelled (or otherwise non-terminal) workflow: re-queue it onto
the internal queue so a dispatcher on a live engine re-runs it from its
checkpoints. Returns a polling handle. Resuming an already-completed
workflow is a no-op: the handle simply reads its recorded outcome. A
missing id is a typed Error::NonExistentWorkflow.
Sourcepub async fn resume_workflow_on<O>(
&self,
id: &str,
queue: &str,
) -> Result<WorkflowHandle<O>>
pub async fn resume_workflow_on<O>( &self, id: &str, queue: &str, ) -> Result<WorkflowHandle<O>>
Like resume_workflow but re-queues onto
queue instead of the internal queue, so the resumed run competes under
that queue’s concurrency and rate limits.
Sourcepub async fn resume_workflows<O>(
&self,
ids: &[String],
) -> Result<Vec<WorkflowHandle<O>>>
pub async fn resume_workflows<O>( &self, ids: &[String], ) -> Result<Vec<WorkflowHandle<O>>>
Resume many workflows in one round-trip onto the internal queue. A polling handle is returned for every id that exists, in input order — an already-terminal workflow is a no-op whose handle reads its recorded outcome; missing ids yield no handle (and no error).
Sourcepub async fn resume_workflows_on<O>(
&self,
ids: &[String],
queue: &str,
) -> Result<Vec<WorkflowHandle<O>>>
pub async fn resume_workflows_on<O>( &self, ids: &[String], queue: &str, ) -> Result<Vec<WorkflowHandle<O>>>
Like resume_workflows but re-queues onto
queue instead of the internal queue.
Sourcepub async fn fork_workflow<O>(
&self,
original_id: &str,
start_step: i32,
opts: WorkflowOptions,
) -> Result<WorkflowHandle<O>>
pub async fn fork_workflow<O>( &self, original_id: &str, start_step: i32, opts: WorkflowOptions, ) -> Result<WorkflowHandle<O>>
Fork a workflow from start_step: a new workflow reuses the original’s
checkpoints for steps < start_step and re-executes from there. Enqueued
onto opts.queue (the internal queue when unset) with
opts.partition_key for a dispatcher on a live engine to run; returns a
polling handle. The new id comes from opts.workflow_id or is generated.
opts.app_version overrides the version stamped on the fork; unset, the
fork inherits the original’s, staying runnable by the executors that
could run the original.
Sourcepub async fn set_workflow_delay(
&self,
id: &str,
delay: Duration,
) -> Result<bool>
pub async fn set_workflow_delay( &self, id: &str, delay: Duration, ) -> Result<bool>
Reschedule a DELAYED workflow to become eligible delay from now. A
running executor’s dispatcher promotes it once due. Returns false (no
error) if no DELAYED row matched.
Sourcepub async fn set_workflow_delay_until(
&self,
id: &str,
at: DateTime<Utc>,
) -> Result<bool>
pub async fn set_workflow_delay_until( &self, id: &str, at: DateTime<Utc>, ) -> Result<bool>
Like set_workflow_delay but with an absolute
instant rather than an offset from now.
Sourcepub async fn read_stream<T: DeserializeOwned>(
&self,
workflow_id: &str,
key: &str,
) -> Result<(Vec<T>, bool)>
pub async fn read_stream<T: DeserializeOwned>( &self, workflow_id: &str, key: &str, ) -> Result<(Vec<T>, bool)>
Read a workflow’s stream key in order, blocking until the stream closes
or the producing workflow goes inactive. Returns the values and whether
the stream is closed.
Sourcepub async fn read_stream_snapshot<T: DeserializeOwned>(
&self,
workflow_id: &str,
key: &str,
from_offset: i32,
) -> Result<(Vec<T>, bool)>
pub async fn read_stream_snapshot<T: DeserializeOwned>( &self, workflow_id: &str, key: &str, from_offset: i32, ) -> Result<(Vec<T>, bool)>
Read the currently-available values of stream key from from_offset
without blocking. Returns the values in order and whether the stream is
closed. Pass the count read so far as the next from_offset to poll.
Sourcepub fn read_stream_values<T: DeserializeOwned + 'static>(
&self,
workflow_id: &str,
key: &str,
) -> impl Stream<Item = Result<T>> + '_
pub fn read_stream_values<T: DeserializeOwned + 'static>( &self, workflow_id: &str, key: &str, ) -> impl Stream<Item = Result<T>> + '_
Read a workflow’s stream key as an asynchronous
Stream, yielding each value in order as it is
committed — the incremental counterpart to read_stream,
which blocks and returns the whole stream at once. The stream ends when the
producer closes it or goes inactive; a decode or backend failure (or a
missing workflow) is the final Err item. Consume it with
StreamExt::next.
Sourcepub async fn list_application_versions(&self) -> Result<Vec<VersionInfo>>
pub async fn list_application_versions(&self) -> Result<Vec<VersionInfo>>
Every registered application version, newest first.
Sourcepub async fn get_latest_application_version(
&self,
) -> Result<Option<VersionInfo>>
pub async fn get_latest_application_version( &self, ) -> Result<Option<VersionInfo>>
The latest registered application version, or None if none are
registered.
Sourcepub async fn set_latest_application_version(
&self,
version_name: &str,
) -> Result<bool>
pub async fn set_latest_application_version( &self, version_name: &str, ) -> Result<bool>
Mark a registered version as latest (bumps its version_timestamp).
Returns whether a matching version existed; rejects an empty name.
Sourcepub async fn create_schedule(
&self,
schedule_name: &str,
workflow_name: &str,
cron: &str,
opts: ScheduleOptions,
) -> Result<()>
pub async fn create_schedule( &self, schedule_name: &str, workflow_name: &str, cron: &str, opts: ScheduleOptions, ) -> Result<()>
Create a durable cron schedule firing workflow_name on each tick of
cron. Validates the cron and timezone, but — unlike the engine — does
not require the workflow to be registered here: the executor that runs the
schedule owns that. Errors on an invalid spec or a duplicate name.
Sourcepub async fn apply_schedules(&self, schedules: Vec<ApplySchedule>) -> Result<()>
pub async fn apply_schedules(&self, schedules: Vec<ApplySchedule>) -> Result<()>
Create or replace each schedule by name, in one call (validated whole
before any write). Like create_schedule, the
workflow need not be registered here.
Sourcepub async fn get_schedule(
&self,
schedule_name: &str,
) -> Result<Option<WorkflowSchedule>>
pub async fn get_schedule( &self, schedule_name: &str, ) -> Result<Option<WorkflowSchedule>>
The schedule named schedule_name, or None if there is none.
Sourcepub async fn list_schedules(
&self,
filter: &ScheduleFilter,
) -> Result<Vec<WorkflowSchedule>>
pub async fn list_schedules( &self, filter: &ScheduleFilter, ) -> Result<Vec<WorkflowSchedule>>
All schedules matching filter (a default filter returns every
schedule), ordered by name.
Sourcepub async fn pause_schedule(&self, schedule_name: &str) -> Result<bool>
pub async fn pause_schedule(&self, schedule_name: &str) -> Result<bool>
Pause a schedule so it stops firing. Returns whether a schedule matched.
Sourcepub async fn resume_schedule(&self, schedule_name: &str) -> Result<bool>
pub async fn resume_schedule(&self, schedule_name: &str) -> Result<bool>
Resume a paused schedule. Returns whether a schedule matched.
Sourcepub async fn delete_schedule(&self, schedule_name: &str) -> Result<bool>
pub async fn delete_schedule(&self, schedule_name: &str) -> Result<bool>
Delete a schedule. Returns whether a schedule was removed.
Sourcepub async fn trigger_schedule<O>(
&self,
schedule_name: &str,
) -> Result<WorkflowHandle<O>>
pub async fn trigger_schedule<O>( &self, schedule_name: &str, ) -> Result<WorkflowHandle<O>>
Fire a schedule’s workflow once, immediately, for an engine to run.
Returns a polling WorkflowHandle over the run. The tick uses a
distinct sched-{name}-trigger-{time} id, so it never collides with or
replaces a regular cron tick.
A schedule with a queue routes the run there; a direct (queue-less) schedule routes to the internal queue — the client runs nothing, so a live engine’s always-on internal dispatcher executes it.
Sourcepub async fn backfill_schedule(
&self,
schedule_name: &str,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<Vec<String>>
pub async fn backfill_schedule( &self, schedule_name: &str, start: DateTime<Utc>, end: DateTime<Utc>, ) -> Result<Vec<String>>
Enqueue a schedule’s ticks for every cron instant in (start, end) (both
bounds exclusive) for an engine to run, under the same deterministic
per-tick ids the live loop uses — so a tick that already ran is skipped,
not duplicated. Returns the id of every tick in the range, in order
(including skipped ones).
Like trigger_schedule, a direct (queue-less)
schedule routes its ticks to the internal queue.
Auto Trait Implementations§
impl !RefUnwindSafe for Client
impl !UnwindSafe for Client
impl Freeze for Client
impl Send for Client
impl Sync for Client
impl Unpin for Client
impl UnsafeUnpin for Client
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more