Files
dagx/README.md
T
swaits 2a9561a32a
CI / Lint () (push) Canceled after 0s
CI / Lint (--all-features) (push) Canceled after 0s
CI / Lint (--no-default-features) (push) Canceled after 0s
CI / MSRV Verification () (push) Canceled after 0s
CI / MSRV Verification (--all-features) (push) Canceled after 0s
CI / MSRV Verification (--no-default-features) (push) Canceled after 0s
CI / Test (macos-latest, nightly, ) (push) Canceled after 0s
CI / Test (macos-latest, stable, ) (push) Canceled after 0s
CI / Test (ubuntu-latest, nightly, ) (push) Canceled after 0s
CI / Test (ubuntu-latest, stable, ) (push) Canceled after 0s
CI / Test (windows-latest, nightly, ) (push) Canceled after 0s
CI / Test (windows-latest, stable, ) (push) Canceled after 0s
CI / Test (macos-latest, nightly, --all-features) (push) Canceled after 0s
CI / Test (macos-latest, stable, --all-features) (push) Canceled after 0s
CI / Test (ubuntu-latest, nightly, --all-features) (push) Canceled after 0s
CI / Test (ubuntu-latest, stable, --all-features) (push) Canceled after 0s
CI / Test (windows-latest, nightly, --all-features) (push) Canceled after 0s
CI / Test (windows-latest, stable, --all-features) (push) Canceled after 0s
CI / Test (macos-latest, nightly, --no-default-features) (push) Canceled after 0s
CI / Test (macos-latest, stable, --no-default-features) (push) Canceled after 0s
CI / Test (ubuntu-latest, nightly, --no-default-features) (push) Canceled after 0s
CI / Test (ubuntu-latest, stable, --no-default-features) (push) Canceled after 0s
CI / Test (windows-latest, nightly, --no-default-features) (push) Canceled after 0s
CI / Test (windows-latest, stable, --no-default-features) (push) Canceled after 0s
CI / Cargo Check () (push) Canceled after 0s
CI / Cargo Check (--all-features) (push) Canceled after 0s
CI / Cargo Check (--no-default-features) (push) Canceled after 0s
CI / Examples (push) Canceled after 0s
CI / Code Coverage (push) Canceled after 0s
CI / Security Audit (push) Canceled after 0s
CI / Documentation () (push) Canceled after 0s
CI / Documentation (--all-features) (push) Canceled after 0s
CI / Documentation (--no-default-features) (push) Canceled after 0s
chore: repoint URLs at git.swaits.com and bump for release
Rewrites remaining github.com / git.sr.ht references to git.swaits.com,
translating GitHub URL shapes to Gitea's (/blob/<ref>/ -> /src/branch/<ref>/,
/tree/<tag> -> /src/tag/<tag>, /actions/workflows/X -> /actions?workflow=X,
/edit/ -> /_edit/).

Left pointing at GitHub on purpose: pull-request links (PRs were not migrated)
and GitHub-only surfaces Gitea lacks (security advisories, discussions).
2026-08-31 17:58:30 -06:00

277 lines
7.3 KiB
Markdown

# dagx
[![Crates.io](https://img.shields.io/crates/v/dagx.svg)](https://crates.io/crates/dagx)
[![Documentation](https://docs.rs/dagx/badge.svg)](https://docs.rs/dagx)
[![Build Status](https://git.swaits.com/swaits/dagx/actions/workflows/ci.yml/badge.svg)](https://git.swaits.com/swaits/dagx/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Rust Version](https://img.shields.io/badge/rust-1.81+-blue.svg)](https://www.rust-lang.org)
A minimal, type-safe, runtime-agnostic async DAG (Directed Acyclic Graph) executor with compile-time cycle prevention and true parallel execution.
## Why dagx?
### Blazing Fast: 1-100x faster than dagrs
| Workload | Tasks | dagx | dagrs | Speedup |
| ----------------- | ------ | --------- | --------- | ------------------ |
| Sequential chain | 5 | 1.02 µs | 770.42 µs | **755x faster** 🚀 |
| Diamond pattern | 4 | 5.16 µs | 770.87 µs | **149x faster** |
| Sequential chain | 100 | 25.09 µs | 1.19 ms | **47.4x faster** |
| Fan-out (1→100) | 101 | 100.75 µs | 1.02 ms | **10.1x faster** |
| Independent tasks | 10,000 | 8.61 ms | 15.37 ms | **1.79x faster** |
### Simple API
```rust
let sum = dag.add_task(Add).depends_on((x, y));
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await?;
```
That's it. No trait boilerplate, no manual channels, no node IDs.
## Quick Start
Add to your `Cargo.toml`:
```toml
[dependencies]
dagx = "0.3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```
Basic example:
```rust
use dagx::{task, DagRunner, Task};
// Define tasks with the #[task] macro
struct Value(i32);
#[task]
impl Value {
async fn run(&self) -> i32 {
self.0
}
}
struct Add;
#[task]
impl Add {
async fn run(a: &i32, b: &i32) -> i32 {
a + b
}
}
#[tokio::main]
async fn main() {
let mut dag = DagRunner::new();
// Add source tasks with no dependencies
let x = dag.add_task(Value(2));
let y = dag.add_task(Value(3));
// Add task that depends on both x and y.
let sum = dag.add_task(Add).depends_on((x, y));
// Execute with true parallelism
let mut output = dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap();
// Retrieve results
assert_eq!(output.get(sum), 5);
}
```
## Features
### Compile-Time Safety
- **Cycles are impossible** — the type system prevents them at compile time, zero runtime overhead
- **No runtime type errors** — dependencies validated at compile time
- **Compiler-verified correctness** — no surprise failures in production
See [how it works](docs/CYCLE_PREVENTION.md).
### Runtime Agnostic
dagx works with any async runtime. Provide a spawner function to `run()`:
```rust
// With Tokio
// The join handle result can be unwrapped because dagx catches panics internally
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap();
// With smol
dag.run(|fut| smol::spawn(fut)).await.unwrap();
// Single-threaded concurrency on the invoking runtime
// Can be faster in situations where waiting time dominates
dag.run(|fut| fut).await.unwrap()
```
### Task Patterns
dagx supports three task patterns:
**1. Stateless** - Pure functions with no state:
```rust
struct Add;
#[task]
impl Add {
async fn run(a: &i32, b: &i32) -> i32 { a + b }
}
```
**2. Read-only state** - Configuration accessed via `&self`:
```rust
struct Multiplier(i32);
#[task]
impl Multiplier {
async fn run(&self, input: &i32) -> i32 { input * self.0 }
}
```
**3. Mutable state** - State modification via `&mut self`:
```rust
struct Counter(i32);
#[task]
impl Counter {
async fn run(&mut self, value: &i32) -> i32 {
self.0 += value;
self.0
}
}
```
### Tracing
dagx provides optional observability using the `tracing` crate, controlled by the `tracing` feature flag.
**Enabling Tracing**
```toml
[dependencies]
dagx = { version = "0.3", features = ["tracing"] }
tracing-subscriber = "0.3"
```
**Log Levels**
- **INFO**: DAG execution start/completion
- **DEBUG**: Task additions, dependency wiring, layer computation
- **TRACE**: Individual task execution (inline vs spawned), detailed execution flow
- **ERROR**: Task panics, concurrent execution attempts
### Other
- **True parallelism**: Chosen runtime executes tasks concurrently and/or in parallel
- **No boilerplate**: The `derive` feature and the `#[task]` macro are enabled by default to simplify task implementation.
## Performance
dagx provides true parallel execution with sub-microsecond overhead per task.
**How is dagx so fast?**
- **Inline fast-path**: Sequential chains execute inline without spawning
- **Adaptive execution**: Inline for sequential work, executor-agnostic parallelism for concurrent work
- **Zero-cost abstractions**: Compile-time graph validation eliminates overhead
See [design philosophy](docs/DESIGN_PHILOSOPHY.md) for details.
## Tutorials & Examples
### Tutorials (Start Here)
Step-by-step introduction to dagx:
- [`01_basic.rs`](examples/01_basic.rs) - Your first DAG
- [`02_fan_out.rs`](examples/02_fan_out.rs) - One task feeds many (1→N)
- [`03_fan_in.rs`](examples/03_fan_in.rs) - Many tasks feed one (N→1)
- [`04_parallel_computation.rs`](examples/04_parallel_computation.rs) - Map-reduce with true parallelism
Run tutorial examples:
```bash
cargo run --example 01_basic
cargo run --example 02_fan_out
cargo run --example 03_fan_in
cargo run --example 04_parallel_computation
```
### Advanced Examples
Real-world patterns:
- [`circuit_breaker.rs`](examples/circuit_breaker.rs) - Circuit breaker pattern for resilient systems
- [`data_pipeline.rs`](examples/data_pipeline.rs) - ETL data processing pipeline
- [`error_handling.rs`](examples/error_handling.rs) - Error propagation and recovery
Run any example: `cargo run --example circuit_breaker`
## Documentation
Full API documentation is available at [docs.rs/dagx](https://docs.rs/dagx).
Detailed documentation on dagx's internals and advanced features:
- [**Compile-Time Cycle Prevention**](docs/CYCLE_PREVENTION.md) - How the type system prevents cycles
- [**Design Philosophy**](docs/DESIGN_PHILOSOPHY.md) - Primitives as scheduler, inline fast-path optimization
- [**Library Comparisons**](docs/COMPARISONS.md) - Detailed comparison with dagrs, async_dag, and others
## When to Use dagx
dagx is ideal for:
- **Data pipelines** with complex dependencies between stages
- **Build systems** where tasks depend on outputs of other tasks
- **Parallel computation** where work can be split and aggregated
- **Workflow engines** with typed data flow between stages
- **ETL processes** with validation and transformation steps
## Benchmarks
Run the full benchmark suite:
```bash
cargo bench
```
View detailed HTML reports:
```bash
# macOS
open target/criterion/report/index.html
# Linux
xdg-open target/criterion/report/index.html
# Windows
start target/criterion/report/index.html
```
_Benchmarks run on Intel i9-13950HX @ 5.5GHz._
## Code of Conduct
This project follows the [Builder's Code of Conduct](https://builderscode.org).
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
For security issues, see [SECURITY.md](SECURITY.md).
## License
Licensed under the MIT License. See [LICENSE](LICENSE) for details.
Copyright (c) 2025 Stephen Waits <steve@waits.net>