Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installation

acts is a fast, lightweight, extensible workflow engine library that executes workflows defined in YAML format with a message-driven architecture.

Install acts Library

Install via cargo:

cargo add acts

External Storage

The persistent storage backends (sqlite/postgres/redis/nats/sled) live in the acts-store crate — enable the matching feature and import the backend from acts_store, then pass it to EngineBuilder::set_store (when unset, an in-memory MemoryStore is used):

# SQLite
cargo add acts-store --features sqlite

# PostgreSQL
cargo add acts-store --features postgres

# NATS
cargo add acts-store --features nats

# Redis
cargo add acts-store --features redis

# Sled
cargo add acts-store --features sled
use acts::Engine;
use acts_store::SqliteStore; // or PostgresStore / RedisStore / NatsStore / SledStore
use std::sync::Arc;

#[tokio::main]
async fn main() -> acts::Result<()> {
    let store = SqliteStore::open("data/acts.db").await?;
    let engine = Engine::builder()
        .set_store(Arc::new(store))
        .start()
        .await?;
    Ok(())
}

acts itself ships only MemoryStore and the KvStore trait custom stores implement; a custom backend implementing acts::KvStore is injected the same way via set_store.

Each database has one writer. The engine’s document locks are process-local — every engine in one process shares one lock table (two databases that happen to use the same keys are serialized together, which costs contention and nothing else) — and they do not cross processes, so two processes writing one database have no mutual exclusion: their concurrent updates of a row can leave the index entries disagreeing with the data row, a query matching a value the row no longer holds or missing the one it does. A single-instance deployment is unaffected; a deployment with several gives each its own database, or coordinates outside the engine (a backend conditional write, or a lock spanning the read) — batch makes one write atomic, not a read plus another process’s write.

Create Engine

#![allow(unused)]
fn main() {
use acts::{Engine, Principal};

let engine = Engine::builder().start().await.unwrap();
// The executor acts for a caller; an embedder driving the engine itself is
// the engine's own principal (see the access-control chapter).
let executor = engine.executor(&Principal::unrestricted());
}

Deploy and Start Workflow

#![allow(unused)]
fn main() {
use acts::{Engine, Principal, Vars, Workflow};

let engine = Engine::builder().start().await.unwrap();

// Load YAML model
let model = r#"
id: my_model
name: my model
steps:
  - name: step 1
    uses: acts.transform.set
    params:
      a: 10
  - name: step 2
    uses: acts.transform.code
    params: |
      return { data: a + 10 };
"#;
let workflow = Workflow::from_yml(model).unwrap();

// Deploy model
let executor = engine.executor(&Principal::unrestricted());
executor.model().deploy(&workflow).expect("fail to deploy workflow");

// Start workflow
let mut vars = Vars::new();
vars.set("a", 0);
vars.set("pid", "w1");
executor.proc().start(&workflow.id, vars).expect("fail to start workflow");
}
ProjectDescription
acts-servergRPC-based workflow service
acts-channelRust client library
acts-channel-pyPython client library
acts-channel-goGo client library