Choose task behavior
Choose behavior after each attempt
TaskSpec selects what happens after success or a retryable failure:
| Constructor | After success | After a retryable failure |
|---|---|---|
| once | Stop. | Stop. |
| restartable | Stop. | Retry if the retry limit allows. |
| periodic | Repeat after the interval; see periodic timing for zero. | Retry through failure backoff if the retry limit allows. |
| from_defaults | Use TaskDefaults. | Use TaskDefaults. |
One task ID runs attempts sequentially. Two attempts for that ID never overlap. These constructors choose a RestartPolicy, or inherit it from TaskDefaults. An explicit with_restart call can change that choice.
Interpret attempt results
Each attempt returns Ok(()) or a TaskError:
| Attempt result | Meaning |
|---|---|
Ok(()) | Success. The restart policy decides whether another attempt follows. |
TaskError::Fail | Retryable failure. |
TaskError::Timeout | Retryable timeout reported by task code. |
TaskError::Fatal | Permanent failure; stop without retry. |
TaskError::Canceled | Cooperative cancellation; stop without retry. |
| Configured timeout | Drop the attempt future; report a retryable timeout if cleanup succeeds. |
A returned TaskError::Timeout produces the ordinary AttemptFailed event. A configured attempt deadline drops the attempt future. If cleanup succeeds, it produces AttemptTimedOut and a retryable timeout. Both timeout failures follow the restart policy and retry limit. If dropping the attempt future panics, Taskvisor instead produces AttemptFailed and ends with a final Panicked outcome without retrying.
With panic unwinding enabled, a panic while creating or polling the attempt future becomes a retryable failure. A panic while destroying attempt-owned data inside the actor can instead produce a final Panicked outcome. Later deferred destruction of the retained task object cannot change an outcome already delivered. panic = "abort" cannot be caught.
See Final outcomes and lifecycle events for final results, and Cancellation and shutdown for timeout limits.
Make repeated attempts safe
A restartable task can run again after a retryable failure, timeout, or caught task panic. Taskvisor cannot know whether an external system committed a side effect before the failure. Return a retryable result only when repeating the attempt is acceptable.
| Application decision | Attempt result |
|---|---|
| Temporary failure and repeating the operation is safe | TaskError::Fail |
| Task-reported deadline and repeating the operation is safe | TaskError::Timeout |
| The application classifies the failure as permanent | TaskError::Fatal |
| Cancellation was observed and cooperative cleanup finished | TaskError::Canceled |
| The attempt completed its required work | Ok(()) |
For external side effects, use an application-owned idempotency key, transaction, reconciliation read, deduplication record, or acknowledgement protocol as appropriate. Taskvisor manages retries. It does not provide rollback, durable execution, or exactly-once execution.
Bound failure retries
A retry limit set with with_max_retries counts retries after the first failed attempt. A limit of three therefore allows at most four consecutive failed attempts. A successful attempt resets the failure streak.
Configure retry timing
BackoffPolicy sets the delay after retryable failures. It does not set the delay after success.
use std::num::NonZeroU32;
use std::time::Duration;
use taskvisor::{BackoffPolicy, JitterPolicy, TaskRef, TaskSpec};
fn supervised(name: &str, task: TaskRef) -> TaskSpec {
TaskSpec::restartable(name, task)
.with_backoff(
BackoffPolicy::exponential(Duration::from_millis(200))
.with_max(Duration::from_secs(30))
.with_jitter(JitterPolicy::Equal),
)
.with_timeout(Duration::from_secs(10))
.with_max_retries(NonZeroU32::new(3).unwrap())
}Equal jitter chooses a delay between half of the current base delay and the full base delay. This spreads retries that would otherwise happen together. Per-task settings override values inherited from TaskDefaults; see configuration inheritance.
Understand periodic timing
A non-zero periodic interval starts after a successful attempt completes. It is fixed-delay scheduling, not a wall-clock or cron schedule. Taskvisor also keeps successive attempt starts at least one millisecond apart. When the attempt runtime plus the configured interval is shorter, Taskvisor extends the wait to reach that spacing. For example, a 500µs interval remains configured, but a sufficiently fast task starts no more often than once per millisecond. Passing Duration::ZERO removes the configured interval; the one-millisecond start-spacing guard still applies.
See periodic.rs, restart_policies.rs, and configuration.rs.
Source: attempt loop, one-attempt execution, and backoff calculation.