taskvisor 0.9.0

In-process Tokio task supervisor: one job per key with queue/replace/reject admission, retries, graceful shutdown, and reliable final outcomes
Documentation
---
title: Quick start
description: Run a retrying Taskvisor task and wait for its direct final outcome.
---

# Quick start

This example runs one task, retries two temporary failures, and waits for the final outcome.
It uses [`serve`](https://docs.rs/taskvisor/latest/taskvisor/core/struct.Supervisor.html#method.serve) and `add(spec).watch().execute()` because the caller needs that task's result.
[`Supervisor::run`](https://docs.rs/taskvisor/latest/taskvisor/core/struct.Supervisor.html#method.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](https://docs.rs/taskvisor/latest/taskvisor/tasks/struct.TaskFn.html) 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`](https://docs.rs/taskvisor/latest/taskvisor/core/struct.SupervisorHandle.html#method.add) starts an operation builder. Its `watch()` modifier makes `execute()` return a [TaskWaiter](https://docs.rs/taskvisor/latest/taskvisor/core/struct.TaskWaiter.html).
It delivers the final [`TaskOutcome`](https://docs.rs/taskvisor/latest/taskvisor/core/enum.TaskOutcome.html) through a direct channel. It does not depend on event delivery.
[`handle.shutdown().await`](https://docs.rs/taskvisor/latest/taskvisor/core/struct.SupervisorHandle.html#method.shutdown) 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](lifecycle-policies.md#make-repeated-attempts-safe) before applying this policy to real I/O.

Source: [task settings](../src/tasks/spec.rs), [the retry loop](../src/core/actor.rs), and [watched results](../src/core/outcome.rs).

## Continue from here

| Next need                                 | Continue with                                                                                     |
|-------------------------------------------|---------------------------------------------------------------------------------------------------|
| Fixed or resident workers                 | [Run Taskvisor]running-and-managing.md and [graceful_worker.rs]../examples/graceful_worker.rs |
| Runtime-discovered work                   | [Manage tasks at runtime]managing-tasks.md and [dynamic_tasks.rs]../examples/dynamic_tasks.rs |
| Queue, replace, or reject work by key     | [Coordinate work by key]keyed-admission.md and [tenant_sync.rs]../examples/tenant_sync.rs     |
| Logs, traces, metrics, or direct outcomes | [Final outcomes and lifecycle events]outcomes-and-events.md                                     |
| Production limits and shutdown boundaries | [Configure Taskvisor]configuration.md and [Production boundaries]production-boundaries.md     |