Rust · Tokio · MIT licensed

Background jobs at Rust speed.

KioMQ is an all-in-one task queue and orchestration library for Rust. Enqueue work, process it across a pool of workers, and keep it durable with Redis — all inside the Tokio runtime you already have.

$cargo add kiomq
kiodsh queues & progress
A recording of kiodsh, KioMQ's terminal dashboard: five queues with their job counts, counters for the waiting, prioritized, delayed, stalled, paused, active, completed and failed states, a list of running jobs with advancing progress bars, then one job's metadata and per-worker poll timings with a duration histogram and percentiles. soon kiodsh, the terminal dashboard in this recording, is coming soon.

Why KioMQ

The building blocks for background work

Inspired by BullMQ's ergonomics, implemented as an embeddable Rust library — no broker to babysit, no sidecar to deploy.

Async & sync processors

Async for I/O-bound work; blocking closures run on spawn_blocking so CPU-heavy jobs never stall the runtime.

Configurable concurrency

Each worker processes up to concurrency jobs at once — defaults to the host's logical CPU count.

Event-driven idle workers

Lock-free atomics plus Notify mean an empty queue costs almost nothing. No polling loops burning cores.

Bulk enqueue

Push thousands of jobs in one round trip with bulk_add and bulk_add_only.

Priorities, delays & cron

Run a job now, in N milliseconds, on a cron schedule, or ahead of everything else by priority score.

Retries & stalled detection

Exponential or fixed backoff, per-job attempt limits, and lock renewal that recovers jobs from dead workers.

Events & metrics built in

Subscribe to job-state events, stream progress updates, and read Tokio runtime plus per-worker timings.

Pluggable stores

Start with InMemoryStore, graduate to RedisStore when you need durability and many machines.

Quick start

A working queue in about ten lines

Create a store, wrap it in a Queue, hand a closure to a Worker. That's the whole model — the same code scales from a unit test to a Redis-backed fleet.

  • No macros to learn. Processors are ordinary async or sync closures.
  • Typed end to end. Job data, return value, and progress are your own types.
  • Panic-safe. A panicking job fails and retries; the process stays up.
  • Swap the store, keep the code. In-memory in tests, Redis in production.

Read the quick start

use std::sync::Arc;
use kiomq::{InMemoryStore, Job, KioError, Queue, Worker, WorkerOpts};

#[tokio::main]
async fn main() -> kiomq::KioResult<()> {
    let store: InMemoryStore<u64, u64, ()> = InMemoryStore::new(None, "demo");
    let queue = Queue::new(store, None).await?;

    // A processor is just a closure: (store, job) -> Result<R, KioError>
    let processor = |_store: Arc<_>, job: Job<u64, u64, ()>| async move {
        Ok::<u64, KioError>(job.data.unwrap_or_default() * 2)
    };

    let worker = Worker::new_async(&queue, processor, Some(WorkerOpts::default()))?;
    worker.run()?;

    queue
        .bulk_add_only((0..10u64).map(|i| (format!("job-{i}"), None, i)))
        .await?;

    let metrics = queue.current_metrics.clone();
    while !metrics.all_jobs_completed() {
        tokio::task::yield_now().await;
    }

    worker.close();
    Ok(())
}

Architecture

One queue, many workers, any store

Producers hand jobs to a queue; the store owns the state; workers reserve, execute, and report. Scale up by raising concurrency, scale out by pointing more processes at the same Redis queue.

KioMQ data flowProducers call add_job on a queue. The queue keeps jobs in wait, prioritized, delayed and paused lanes inside a pluggable store. Workers reserve jobs from the store, run the processor with a configurable concurrency, and emit events, progress and metrics back to the queue.Your Tokio appqueue.add_job()queue.bulk_add()queue.pause_or_resume()Queueattempts · backoff · repeatwaitprioritizeddelayed · cronpausedStorestate · locks · eventsInMemoryStoreRedisStoreone Store traitWorker poolconcurrency = num_cpusWorker::new_async()Worker::new_sync()lock renewal ·stalled recoveryenqueuepersistreserveevents · progress · metrics
Swap InMemoryStore for RedisStore and the same worker code runs on as many machines as you like.

Backends

Pick durability to match the workload

Stores are pluggable behind a single Store trait — the rest of your code doesn't change.

StoreFeature flagDurableMulti-processBest for
InMemoryStorealways availableNoNoTests, dev loops, short-lived in-process work
RedisStoreredis-store (default)YesYesProduction fleets spread across machines
1

Store trait — swap the backend per environment, keep the code.

2

Processor flavours — async futures and blocking closures.

~0

CPU burned while queues sit empty, thanks to atomics + Notify.

MIT

Permissive licence. No CLA, no open-core tier.

Observability

You can see what the queue is doing

Metrics collection runs in the background with no extra setup, and every job state transition is an event you can subscribe to.

  • Job events. Completed, Failed, Stalled, Progress and more, per state or all at once.
  • Progress updates. Persist typed progress from inside a processor.
  • Process metrics. CPU, RSS, heap stats, and Tokio runtime counters, keyed by PID.
  • Worker metrics. Per-task poll counts and idle durations for latency profiling.

Metrics reference

use kiomq::{EventParameters, JobState};

// Listen to one state…
let id = queue.on(JobState::Completed, |evt| async move {
    if let EventParameters::Completed { job_id, .. } = evt {
        println!("job {job_id} completed");
    }
});

// …or match on every event the queue emits.
queue.on_all_events(|evt: EventParameters<u64, u8>| async move {
    match evt {
        EventParameters::Failed { job_id, reason, .. } => {
            eprintln!("job {job_id} failed: {}", reason.reason);
        }
        EventParameters::Progress { job_id, data } => {
            println!("job {job_id} is {data}% done");
        }
        _ => {}
    }
});

// Listeners are removable by id.
queue.remove_event_listener(id);

Ready to move work off the request path?

Add one dependency, keep your runtime. KioMQ is MIT licensed and developed in the open — issues and pull requests welcome.

Esc

Type to search the docs.