Skip to content
Taskvisor 0.8source v0.8.3

Configure Taskvisor

Configure each concern

Configuration is split by concern:

text
SupervisorConfig ──► runtime-wide limits and shutdown
TaskDefaults ──────► inherited task behavior
TaskSpec ──────────► per-task overrides
ControllerConfig ──► keyed-admission limits
Subscribe ─────────► per-subscriber event queue capacity

Set runtime and task defaults

rust
use std::num::{NonZeroU32, NonZeroUsize};
use std::sync::Arc;
use std::time::Duration;
use taskvisor::{Supervisor, SupervisorConfig, TaskDefaults};

fn configured_supervisor() -> Arc<Supervisor> {
    let runtime = SupervisorConfig::default()
        .with_grace(Duration::from_secs(30))
        .with_subscriber_shutdown_timeout(Duration::from_secs(5))
        .with_max_concurrent(NonZeroUsize::new(16))
        .with_ownership_capacity(NonZeroUsize::new(4096));

    let tasks = TaskDefaults::default()
        .with_timeout(Duration::from_secs(20))
        .with_max_retries(NonZeroU32::new(5).unwrap());

    Supervisor::builder(runtime)
        .with_task_defaults(tasks)
        .build()
}

The values in this example show where each setting belongs. They are not capacity recommendations for every application.

Know the defaults

Main defaults:

SettingDefault
Graceful task shutdown60 seconds.
Subscriber drain5 seconds, shared by all subscriber queues.
Concurrent task attemptsUnlimited.
Registered-task limit1024.
Ownership capacity1024 per supervisor across accepted tasks and subscribers.
Event bus capacity1024.
Subscriber queue capacity1024 per subscriber; override through queue_capacity.
Registry command capacity1024.
Restart policyOn retryable failure.
Failure backoff200 ms initial base, capped at 30 s, with equal jitter; the first delay is 100–200 ms.
Attempt timeoutNone.
Failure retry limitUnlimited.

Bound different resources

Three limits answer different questions:

LimitWhat it bounds
max_concurrentAttempts physically running at the same time.
max_registered_tasksRegistered and removing tasks through terminal cleanup; force-aborted work can remain charged longer.
ownership_capacityAccepted task and subscriber values still owned through physical cleanup.

SupervisorConfig::with_ownership_capacity(None) removes the ownership count bound. Cleanup still uses a bounded worker set, but retained values and cleanup backlog can then grow without a count limit. Use Supervisor::ownership_snapshot or SupervisorHandle::ownership_snapshot to inspect the configured and effective limits, available units, parked requests, and deferred-cleanup activity.

During cleanup handoff, one task can temporarily consume two max_registered_tasks units.

Choose values for an application

Work from the application contract instead of copying one set of numbers:

  1. Decide which failures may repeat and whether retries need a finite limit.
  2. Choose an attempt timeout only for operations that are safe to stop by dropping their future.
  3. Bound concurrent attempts and registered tasks according to the work and dependencies they consume.
  4. Keep finite ownership admission, or explicitly accept that retained user values and cleanup backlog can grow without a count bound.
  5. Choose task grace and subscriber drain deadlines that match the application's shutdown owner.
  6. When using keyed admission, configure controller queues and tracked-slot limits separately.
  7. Observe direct outcomes, best-effort events, and runtime snapshots at their documented boundaries.

Observe ownership pressure

The ownership snapshot distinguishes admission pressure from deferred cleanup:

rust
use taskvisor::SupervisorHandle;

fn report_ownership(handle: &SupervisorHandle) {
    let state = handle.ownership_snapshot();

    println!(
        "in_use={:?} available={:?} waiters={} cleanup_queued={} \
         cleanup_running={} retired={:?} admission_open={}",
        state.in_use(),
        state.available,
        state.waiters,
        state.cleanup_queued,
        state.cleanup_running,
        state.retired(),
        state.admission_open,
    );
}

waiters counts ownership requests currently parked for capacity. cleanup_queued and cleanup_running count deferred-cleanup batches on the isolated destructor path. retired() reports permanent loss from a finite ownership capacity after destructor failure. An available value of zero does not by itself prove that the next waiting request will fail; another lifetime may release its unit. Capacity fields are None when ownership admission is unlimited. The complete snapshot is rolling and can become stale immediately.

Capacity values are non-zero where zero would make the runtime unusable. Checked try_with_* methods accept raw integers and return a configuration error for invalid values.

Open-source task execution components.