# Taquba

A durable, single-process task queue for Rust, backed by object storage. Built
on [SlateDB](https://github.com/slatedb/slatedb).

Taquba uses SlateDB's single-writer model: all producers and workers for a
given store **must run inside one process**.

Taquba is the right fit when you want durable background jobs whose state
survives node loss, ephemeral disks, and region failures - without operating a
queue server.

## Features

- At-least-once delivery with lease-based claims and crash recovery.
- Multiple named queues per store with per-queue configuration.
- Priority levels (FIFO within each priority).
- Scheduled jobs, dedup keys, custom priority/attempts.
- Exponential retry backoff on `nack`.
- Bounded dead-letter retention with paginated inspection.
- Atomic batch enqueue.
- Worker loop with graceful shutdown and notify-based wakeups (no busy polling).

## Stability

Taquba is pre-1.0. The Rust API may evolve between minor versions per Cargo's
standard `0.x.y` semantics (`0.1` -> `0.2` may break source compatibility), and
the on-disk format on object storage is *not* guaranteed stable across minor
versions either. Treat a Taquba minor-version bump as a one-way migration:
drain your queue first, or be prepared to start the bucket fresh.

Patch releases (`0.1.0` -> `0.1.1`) preserve both the Rust API and the on-disk
format.

## Install

The in-memory and local-disk stores work with no feature flag, handy for
tests and the quick-start below:

```toml
[dependencies]
taquba = "0.1"
tokio = { version = "1", features = ["full"] }
```

For production, opt in to exactly one cloud backend:

```toml
taquba = { version = "0.1", features = ["aws"] }   # S3 / MinIO
taquba = { version = "0.1", features = ["gcp"] }   # Google Cloud Storage
taquba = { version = "0.1", features = ["azure"] } # Azure Blob
```

## Quick start

```rust
use std::sync::Arc;
use std::time::Duration;
use taquba::{Queue, object_store::memory::InMemory};

#[tokio::main]
async fn main() -> taquba::Result<()> {
    let q = Queue::open(Arc::new(InMemory::new()), "demo").await?;

    q.enqueue("email", b"alice@example.com".to_vec()).await?;

    if let Some(job) = q.claim("email", Duration::from_secs(30)).await? {
        // ... do the work ...
        q.ack(&job).await?;
    }

    q.close().await
}
```

## Worker loop

Implement `Worker` and let `run_worker` handle the claim / ack / nack
loop, retries, and graceful shutdown:

```rust
use taquba::{run_worker, JobRecord, Worker, WorkerError};

struct EmailWorker;

impl Worker for EmailWorker {
    async fn process(&self, job: &JobRecord) -> Result<(), WorkerError> {
        let to = std::str::from_utf8(&job.payload)?;
        send_email(to).await?;
        Ok(())
    }
}
```

Pass any future as the shutdown signal: `tokio::signal::ctrl_c()`,
a oneshot, etc. Shutdown is honoured at safe points: between jobs and during
idle waits. In-flight jobs always finish, so leases are never abandoned to the
reaper. See [`examples/worker.rs`](examples/worker.rs) for a full setup.

## License

Apache-2.0
