Rust API Overview
Zart’s Rust API is organized into three layers that build on each other. You can use all three together, or drop down to a lower layer when you need more control.
The Three Layers
Section titled “The Three Layers”┌─────────────────────────────────────────────────────────┐│ Macro Layer #[zart_durable], z_step!, z_wait_event! ││ (zart-macros) Ergonomic async fn → TaskHandler │├─────────────────────────────────────────────────────────┤│ Execution Layer TaskHandler, TaskContext, TaskRegistry ││ (zart) Durable steps, retries, events, sleep │├─────────────────────────────────────────────────────────┤│ Scheduler Layer Scheduler, DurableScheduler, Worker ││ (zart + backend) PostgreSQL / SQLite / MySQL polling │└─────────────────────────────────────────────────────────┘Scheduler Layer
Section titled “Scheduler Layer”Responsible for persisting and claiming executions. Implements SKIP LOCKED polling so multiple workers can run concurrently without coordination.
| Type | Role |
|---|---|
Scheduler | Trait — poll for due tasks, mark complete/failed |
DurableScheduler | Trait — schedule new executions, deliver events |
PostgresScheduler | Concrete — PostgreSQL backend |
SqliteScheduler | Concrete — SQLite backend |
Worker | Drives the poll loop, dispatches to TaskRegistry |
Execution Layer
Section titled “Execution Layer”Where your workflow logic lives. A TaskHandler receives a TaskContext and calls methods on it to execute durable steps.
| Type | Role |
|---|---|
TaskHandler | Trait you implement — defines Data, Output, run |
TaskContext<S> | Passed into run — the API for steps, sleep, events |
TaskRegistry | Maps task name strings to TaskHandler instances |
RetryConfig | Per-step retry policy (none / fixed / exponential) |
TaskError | Error type for workflow failures |
Macro Layer
Section titled “Macro Layer”Optional. The zart-macros crate provides proc-macros that transform an ordinary async fn into a full TaskHandler implementation, removing the boilerplate of the trait impl.
| Macro | Purpose |
|---|---|
#[zart_durable] | Marks an async fn as a durable workflow |
z_step! | Executes a named, persisted step |
z_step_with_retry! | Step with inline retry config |
z_wait_event! | Durably waits for an external event |
z_durable_loop! | Stateful loop with persistent iteration counter |
Quick Example
Section titled “Quick Example”use zart::{TaskHandler, TaskContext, TaskError, TaskRegistry, Worker, WorkerConfig};use zart::{DurableScheduler, RetryConfig};use zart_postgres::PostgresScheduler;use async_trait::async_trait;use std::time::Duration;
// 1. Implement TaskHandlerstruct MyWorkflow;
#[async_trait]impl TaskHandler for MyWorkflow { type Data = MyInput; type Output = MyOutput;
async fn run( &self, ctx: &mut TaskContext<impl Scheduler>, data: MyInput, ) -> Result<MyOutput, TaskError> { let result = ctx.step("step-one", || async { do_something(&data).await }).await?;
Ok(MyOutput { result }) }}
// 2. Register and start a worker#[tokio::main]async fn main() -> anyhow::Result<()> { let scheduler = PostgresScheduler::connect(&std::env::var("DATABASE_URL")?).await?;
let mut registry = TaskRegistry::new(); registry.register("my-workflow", MyWorkflow);
let worker = Worker::new(scheduler.clone(), registry, WorkerConfig::default());
// Schedule an execution scheduler.schedule("my-workflow", MyInput { /* ... */ }).await?;
// Run the worker (blocks until shutdown signal) worker.run().await}Execution Lifecycle
Section titled “Execution Lifecycle”- Schedule —
DurableScheduler::schedule()inserts a row intozart_executionswith statuspending. - Claim — Worker polls with
SELECT … FOR UPDATE SKIP LOCKED. One worker owns one execution at a time. - Run —
TaskHandler::run()is called. Eachctx.step()call checkszart_stepsfor an existing result.- Step hit — stored result deserialized and returned without calling the closure.
- Step miss — closure is called, result serialized and written to
zart_steps, then returned.
- Complete — execution status set to
completed, output stored. - Fail — if
runreturnsErr, execution is retried up tomax_retries(). Final failure sets statusfailed.
Next Steps
Section titled “Next Steps”- TaskHandler Trait — full method reference for
TaskContext - Macros — ergonomic
#[zart_durable]proc-macro layer - Durable Loops — iterate over collections durably
- Parallel Steps — concurrent fan-out with
wait_all - Wait for Event — external signals and human approvals