Orchestrate complex async processes with finite state machines, parallel execution, and built-in scheduling.
Cano is still far from a 1.0 release. The API is subject to changes and may include breaking changes.
Overview
Cano is a high-performance orchestration engine designed for building resilient, self-healing systems in Rust. Unlike simple task queues, Cano uses Finite State Machines (FSM) to define strict, type-safe transitions between processing steps.
It excels at managing complex lifecycles where state transitions matter:
- Data Pipelines: ETL jobs with parallel processing (Split/Join) and aggregation.
- AI Agents: Multi-step inference chains with shared context and memory.
- Background Systems: Scheduled maintenance, periodic reporting, and distributed cron jobs.
The engine is built on three core concepts: Tasks for logic, Workflows for state transitions, and Schedulers for timing.
Features
- Type-Safe State Machines: Enum-driven transitions with compile-time guarantees.
- Multiple Processing Models:
Taskfor general-purpose work, plusRouterTask,PollTask,TimerTask,BatchTask,SteppedTask, andStreamTaskfor specialized shapes — mixed freely in one workflow. - Resource Dependency Injection: Typed, lifecycle-managed
Resourcesdictionary withsetup/teardown/healthhooks, looked up by key and type, plus#[derive(FromResources)]for ergonomic wiring. - Parallel Execution (Split/Join): Run tasks concurrently and join results with strategies like
All,Any,Quorum, orPartialResults, with an optional bulkhead to cap concurrency. - Robust Retry Logic: Configurable strategies including exponential backoff with jitter and per-attempt timeouts.
- Circuit Breaker: Shared
CircuitBreakershort-circuits calls to failing dependencies before the retry loop, with configurable failure threshold, cool-down, and half-open probing. - Rate Limiting: Token-bucket (
RateLimiter) and fixed-window (WindowedRateLimiter) throttles that compose into aMultiRateLimiterenforcing several weighted tiers at once. - Built-in Scheduling: Cron-based, interval, and manual triggers for background jobs.
- Crash Recovery: Pluggable
CheckpointStorerecords every FSM state entry;Workflow::resume_fromrehydrates a crashed run and continues. Ships with an embedded, ACIDRedbCheckpointStorebehind therecoveryfeature. - Sagas / Compensation: Pair a forward step with a
compensateaction viaCompensatableTask+register_with_compensation; if a later step fails, the engine rolls back the work already done in reverse order (and replays the rollback across a crash when checkpointing is on). - Observability: Optional
tracing(spans + events, plusTracingObserver) andmetrics(aMetricsObserverplus low-cardinality counters / histograms / gauges via themetricsfacade) features for deep insight into workflow, task, retry, split/join, circuit-breaker, scheduler, processing-loop, recovery and saga internals; plus synchronousWorkflowObserverhooks for lifecycle/failure events andResource::health()probes (Resources::check_all_health). - Performance-Focused: Minimizes heap allocations by leveraging stack-based objects wherever possible, giving you control over where allocations occur.
For how the resilient, self-healing tagline maps to concrete primitives — retries, timeouts, circuit breakers, rate limiters, bulkheads, panic safety, checkpoint+resume, sagas, observers, health probes — see the Resilience, Recovery and Saga guides.
Simple Example: Parallel Processing
Here is a real-world example: fan a price lookup out across four exchanges, each returning a
batch of quotes, tolerate one of them being down, pool every quote that landed into a single
reference price, then stream a live tick feed against it. It combines Split/Join with a
quorum join strategy, per-task retries, resource injection, and a windowed
StreamTask.
use *;
use ; // StreamTask sources are plain `futures` streams
use Pin;
use Duration;
// One row per exchange: its name, the batch of quotes it returns, and whether it's
// simulated as unreachable (e.g. down for maintenance). Exchanges return different
// numbers of quotes, and every quote counts toward the reference price — so a deeper
// book pulls it further than a thin one.
const EXCHANGES: & = &;
// Fetches a batch of quotes from one exchange. Real networks are flaky, so each task
// carries its own retry budget: exponential backoff, up to 2 retries.
// Runs once the join is satisfied: pools every quote that landed. Flattening the batches
// means the reference is quote-weighted, not exchange-weighted.
// A bounded feed of trade ticks arriving after the reference price is known.
const TICKS: & = &;
// Flag a tick once it strays this far from the aggregated reference price.
const ALERT_PCT: f64 = 1.0;
// Consumes the tick feed continuously, emitting once per window instead of once at the
// end. The feed is bounded here so the example terminates; a real source (Kafka, a
// WebSocket) would run until the CancellationToken fires.
;
async
Documentation
For complete documentation, examples, and guides, please visit our website:
👉 https://nassor.github.io/cano/
You can also find:
- API Documentation on docs.rs
- Examples Directory in the repository
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
AI Disclosure
The primary developer of this repository uses AI coding assistants while working on Cano. At the time of writing, the assistants in regular use are:
- Claude Code (Anthropic API), and
- Qwen and DeepSeek models running locally.
All AI-assisted output is reviewed, edited, tested, and submitted by a human developer who is fully responsible for the resulting code. AI tools are treated as accelerators, not authors. See AI_USAGE_POLICY.md for the full policy that contributors are expected to follow when using AI assistants on this project.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.