Skip to content
Taskvisor 0.9source v0.9.0

Quick start ​

This example runs one task, retries two temporary failures, and waits for the final outcome. It uses serve and add(spec).watch().execute() because the caller needs that task's result. Supervisor::run would wait for the supervisor lifecycle, not return one task's result.

Create a project ​

Taskvisor requires Rust 1.90 or newer. The default Taskvisor install includes everything this example uses; no additional Taskvisor feature is required.

sh
cargo new taskvisor-quick-start
cd taskvisor-quick-start
cargo add taskvisor@0.9
cargo add tokio@1 --features full

Add the task ​

Replace src/main.rs with:

rust
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use taskvisor::prelude::*;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let attempts = Arc::new(AtomicU32::new(0));
    let refresh: TaskRef = TaskFn::arc(move |_ctx| {
        let attempts = Arc::clone(&attempts);
        async move {
            let attempt = attempts.fetch_add(1, Ordering::Relaxed) + 1;
            println!("attempt {attempt}");

            if attempt < 3 {
                Err(TaskError::fail("temporary failure"))
            } else {
                Ok(())
            }
        }
    });

    let spec = TaskSpec::restartable("refresh-cache", refresh)
        .with_backoff(BackoffPolicy::constant(Duration::from_millis(50)))
        .try_with_max_retries(2)?;

    let supervisor = Supervisor::new(SupervisorConfig::default(), vec![]);
    let handle = supervisor.serve()?;

    let waiter = handle.add(spec).watch().execute().await?;
    println!("final outcome: {:?}", waiter.wait().await?);

    handle.shutdown().await?;
    Ok(())
}

Run it ​

Run the program:

sh
cargo run

It prints:

text
attempt 1
attempt 2
attempt 3
final outcome: Completed

Understand what happened ​

mermaid
flowchart TB
accTitle: A watched task and its result
accDescr: Task settings, supervised attempts, and the waiter connect through a watched registration.
Task["TaskFn"]
Spec["TaskSpec"]
Add["add(spec).watch().execute()"]
Attempts["Supervised attempts"]
Waiter["TaskWaiter"]
Task --> Spec
Spec --> Add
Add -->|"registers work"| Attempts
Add -->|"returns with task ID"| Waiter
Attempts -->|"final outcome"| Waiter

TaskFn creates a fresh future for every attempt. The first two attempts return retryable failures. Taskvisor waits for the configured backoff before each retry, and the third attempt succeeds.

A retry limit counts retries after the first failed attempt. The limit of two therefore permits the initial attempt and at most two retries.

add starts an operation builder. Its watch() modifier makes execute() return a TaskWaiter. It delivers the final TaskOutcome through a direct channel. It does not depend on event delivery. handle.shutdown().await then joins the shared shutdown and cleanup workflow.

Every retry starts a new attempt. Before returning a retryable failure for work with external side effects, make the operation safe to repeat. Read Make repeated attempts safe before applying this policy to real I/O.

Source: task settings, the retry loop, and watched results.

Continue from here ​

Next needContinue with
Fixed or resident workersRun Taskvisor and graceful_worker.rs
Runtime-discovered workManage tasks at runtime and dynamic_tasks.rs
Queue, replace, or reject work by keyCoordinate work by key and tenant_sync.rs
Logs, traces, metrics, or direct outcomesFinal outcomes and lifecycle events
Production limits and shutdown boundariesConfigure Taskvisor and Production boundaries

Open-source task execution components.