barca
The invisible asset orchestrator.
Rust plans it. Python runs it. You just write functions.
Barca is an asset orchestrator that adds zero perceptible overhead to your Python pipelines. A compiled Rust binary handles parsing, DAG construction, and execution planning. Python does what it's best at: running your code.
# pipeline.py
from barca import asset
@asset()
def raw_data() -> list[dict]:
return [{"x": 1}, {"x": 2}, {"x": 3}]
@asset(inputs={"data": raw_data})
def summary(data: list[dict]) -> dict:
return {"count": len(data), "total": sum(d["x"] for d in data)}
$ barca get pipeline.py
{"elapsed_seconds":0.27,"final_output":{"count":3,"total":6},"phases":1,"run_id":"...","steps_executed":2}
No config files. No YAML. No daemon. Just functions and a fast binary.
Install
Barca is designed for use with uv:
uv add barca
This gives you:
- The
barcaCLI binary (compiled Rust) - Python API:
barca.get(),barca.plan() - Decorator stubs for
@asset,@sensor,@task(IDE autocomplete + type checking)
For optional parquet (DataFrame) support:
uv add 'barca[parquet]'
All in one wheel, built with maturin. Requires Python >= 3.12.
From source
git clone https://github.com/barca-orc/barca.git
cd barca
uv sync
cargo build --release
maturin develop --release # installs into current .venv
Quick start
# assets.py
from barca import asset
@asset()
def hello() -> dict:
return {"message": "Hello from barca!"}
barca get assets.py
That's it. Barca parses your Python source with ruff's AST parser (no import, pure static analysis), builds a dependency graph, generates a phased execution plan, spawns Python workers, and persists results to a local SQLite database -- all in under 40ms for a trivial asset.
How it works
┌─────────────────────────────────────┐
│ barca get pipeline.py │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Rust binary (barca) │
│ │
│ 1. Parse Python source (ruff AST) │
│ 2. Build DAG (petgraph) │
│ 3. Generate execution plan │
│ 4. Initialize DB (.barca/metadata.db)│
│ 5. Spawn Python workers per phase │
│ 6. Collect outputs, persist to DB │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Python worker (per phase) │
│ │
│ - Loads modules via importlib │
│ - Executes steps in tier order │
│ - LRU cache for in-process results │
│ - Emits JSON lines to stdout │
└─────────────────────────────────────┘
Key design decisions:
- Static analysis only -- Rust never imports your Python code. It parses source text and extracts decorator metadata from the AST.
- Phased execution -- The planner decomposes the DAG into sequential phases. Within each phase, independent streams run in parallel workers.
- No framework lock-in -- Decorators are identity functions. Your code runs standalone without barca installed.
- Single binary -- One
pip installgives you everything. No JVM, no Docker, no scheduler service.
Decorators
from barca import asset, sensor, task, sink, unsafe
from barca import Always, Manual, Schedule
from barca import partitions, partitions_from, collect, asset_ref
@asset
Cached computation node. The workhorse.
@asset()
def prices() -> dict:
return {"AAPL": 150, "MSFT": 380}
@asset(inputs={"data": prices})
def report(data: dict) -> str:
return f"Tracked {len(data)} tickers"
@sensor
Observes external state. Returns (update_detected, output).
@sensor()
def inbox_files() -> tuple[bool, list[str]]:
files = list(Path("inbox").glob("*.csv"))
return bool(files), [str(f) for f in files]
@task
Workflow-management step — deploys, notifications, migrations, cache warming.
Always re-runs (never cached). May appear anywhere in the graph and may depend
on assets, sensors, or other tasks, but must not be an input to an asset or
sensor (that would poison caching). Run a task with barca run <task>.
# A task consuming an upstream asset (asset → task).
@task(inputs={"report": report})
def publish(report: str) -> None:
print(f"Publishing: {report}")
@sink
Declares a sink output target (stacks on @asset; file writing coming soon).
@asset()
@sink("output/data.json", serializer="json")
def my_data() -> dict:
return {"rows": 42}
Freshness markers
| Marker | Behavior |
|---|---|
Always |
Auto-materializes whenever stale (default for @asset and @task) |
Manual |
Only runs on explicit refresh |
Schedule("0 5 * * *") |
Cron expression (5-field, or 6-field for sub-minute) |
Partitions
Fan a single asset definition into N independent materializations:
@asset(partitions={"ticker": partitions(["AAPL", "MSFT", "GOOG"])})
def prices(ticker: str) -> dict:
return {"ticker": ticker, "price": get_price(ticker)}
| Function | Purpose |
|---|---|
partitions(values) |
Static list of partition keys |
partitions_from(source) |
Derive partitions from upstream asset |
collect(asset_fn) |
Aggregate all partitions of an upstream |
asset_ref(ref_string) |
Canonical asset reference |
CLI
barca get [target] <file.py> [file.py ...] Get asset(s) — cache-aware
barca run <task> <file.py> ... Run a task (always re-runs) and its cone
barca plan <file.py> [file.py ...] Emit execution plan as JSON
barca list <file.py> [--json] List all definitions with deps
barca history [--limit N] [--json] Show recent run history
barca stats <target> <file.py> [--json] Show timing/cache stats for an asset
barca serve [file.py ...] [--port N] Run the HTTP API server + cron scheduler
barca docs [topic] [--all] [--json] Built-in manual: concepts, formats, examples
barca --help Show help (every command ends with examples)
barca docs is the manual, compiled into the binary: topics for types and output formats,
caching, tasks, partitions, scheduling, runnable examples, and conventions for scripts and AI
agents (barca docs agents). Results are JSON on stdout, progress and errors on stderr.
Shorthand: barca pipeline.py works as barca get pipeline.py (all assets).
Scheduling
Barca doubles as a plain task scheduler. Decorate a function with a cron
Schedule and leave barca serve running — each job fires on its own tick. No
external cron, no YAML, no daemon service to install.
# job.py
from barca import task, Schedule
@task(freshness=Schedule("*/10 * * * *"))
def refresh() -> None:
... # hit an API, rebuild a file, send a report — a task always re-runs
@task(freshness=Schedule("*/15 * * * * *")) # 6-field cron → every 15 seconds
def heartbeat() -> None:
...
barca serve job.py
Standard 5-field cron (minute hour day-of-month month day-of-week) and a 6-field
form with a leading seconds field are both supported — the scheduler evaluates
at 1-second resolution. It's timezone-aware, fires once on restart to catch up a
missed tick, skips a tick if the previous run is still going, and exposes live
status at GET /schedule. Inspect the schedule any time with barca list job.py.
See the Scheduling guide for the full story
(timezones, catch-up, keeping it alive under systemd). A minimal example lives in
examples/scheduler.
Server
barca serve starts a long-running HTTP server that exposes the orchestrator as a
JSON API — for triggering runs programmatically, polling status, and (in the future)
a web UI. It also runs the built-in cron scheduler above. It binds to 127.0.0.1
by default (local only, no auth).
barca serve pipeline.py --port 8274 # default port 8274
barca serve pipeline.py --watch # dev mode: re-parse DAG on file change
barca serve pipeline.py --no-schedule # HTTP API only, don't fire scheduled jobs
barca serve pipeline.py --timezone utc # evaluate cron in UTC (default: local)
Runs are async: POST returns a run_id immediately, then you poll /status/{run_id}.
curl localhost:8274/health # {"status":"ok","version":"0.9.0"}
curl localhost:8274/assets # list assets + deps
curl localhost:8274/plan # execution plan JSON
curl -XPOST localhost:8274/run # → {"run_id":"…"}; poll /status/<id>
curl -XPOST localhost:8274/get/summary # run a single target
curl localhost:8274/status/<run_id> # poll run status + result
curl -XDELETE localhost:8274/run/<run_id> # cancel an in-flight run
See the Server API reference for the full endpoint reference.
Python API
import barca
# Get all assets in a file (returns the last asset's value)
value = barca.get("pipeline.py")
print(value) # {"count": 3, "total": 6}
# Get a specific asset's value (cache-aware)
value = barca.get("summary", "pipeline.py")
print(value) # {"count": 3, "total": 6}
# Inspect the execution plan
plan = barca.plan("pipeline.py")
print(plan["total_steps"]) # 2
All output formats work transparently: dicts, lists, sets, DataFrames, and arbitrary Python objects are serialized as JSON, pickle, or parquet and deserialized automatically. Every asset is fully materialized to an artifact file at step boundaries — that persistence is the cache checkpoint. Type annotations on parameters (e.g. pl.DataFrame) select the parquet reader; they do not skip materialization.
barca plan -- inspect without running
$ barca plan pipeline.py
{
"total_steps": 2,
"phases": [
{
"reason": "Initial",
"streams": [
{
"stream_id": "p0-w0",
"steps": ["pipeline.py:raw_data", "pipeline.py:summary"]
}
]
}
]
}
barca get -- execute and get results
Parses source, builds DAG, spawns workers, collects outputs, persists to .barca/metadata.db. With a target, only the target's subgraph runs. Without a target, all assets run.
Output is a JSON summary:
{
"run_id": "...",
"elapsed_seconds": 0.27,
"steps_executed": 2,
"phases": 1,
"final_output": {"count": 3, "total": 6}
}
Use --no-cache to skip cache lookups and execute everything fresh.
Diagnostics go to stderr:
[barca] 2/2 steps done in 0.0s
Benchmarks
All benchmarks measured with hyperfine (3 warmup runs, 10 measured runs) on the same machine. Barca is compared against Dagster and Prefect running equivalent pipelines.
Trivial (1 asset, zero work)
Measures pure framework overhead -- how long it takes to do nothing.
| Framework | Mean | Relative |
|---|---|---|
| barca | 38.0 ms | 1.00x |
| dagster | 538.1 ms | 14.2x |
| prefect | 3977.7 ms | 104.7x |
Barca's total overhead (parse + plan + spawn + persist) is 38ms. Dagster needs ~0.5s. Prefect needs ~4s.
Benchmark suite
The benchmarks/ directory covers a wide range of DAG topologies and workloads —
overhead/scaling, DAG shapes, real workloads (ETL, ML pipelines), partitioned
runs, and dynamic dispatch/resilience — each with equivalent Dagster and Prefect
implementations for apples-to-apples comparison. See
benchmarks/README.md for the full topology tables and
benchmarks/RESULTS.md for current results.
Run any benchmark:
cd benchmarks/trivial
./bench.sh 10 # 10 measured runs
Architecture
Cargo.toml Rust workspace root
crates/
barca-core/ Engine: parser, DAG, planner, dispatch, DB, cache
barca-cli/ Thin CLI shell (clap → barca-core)
python/barca/
__init__.py Decorator stubs + API exports
api.py Python API (get/plan via subprocess)
_worker.py Execution worker (invoked by Rust binary)
_artifacts.py Artifact serialization (json/pickle/parquet)
py.typed PEP 561 marker
pyproject.toml Maturin build config
Tech stack
| Layer | Technology |
|---|---|
| Parser | ruff Python AST (static, no import) |
| DAG | petgraph |
| Database | Turso/libSQL (local SQLite) |
| Serialization | serde + serde_json |
| Hashing | SHA-256 (content-addressed artifacts) |
| Build | maturin (Rust binary + Python stubs in one wheel) |
| Python runtime | Python >= 3.12 |
Node kinds
| Kind | Decorator | Cached | Can be input to |
|---|---|---|---|
| asset | @asset() |
Yes | assets, sensors, tasks |
| sensor | @sensor() |
No | assets, sensors, tasks |
| task | @task() |
No | tasks only (not assets/sensors) |
Development
git clone https://github.com/barca-orc/barca.git
cd barca
# Build
cargo build --release
maturin develop --release
# Test
cargo test
# Run
barca get examples/basic_app/example_project/assets.py
barca plan examples/basic_app/example_project/assets.py
Project status
Barca is in active development. The core pipeline (parse -> DAG -> plan -> execute -> persist) is working and benchmarked. See the guide for a walkthrough.
License
Metadata
Release files for barca 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| barca-0.9.0.tar.gz | 228.1 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| barca-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| barca-0.9.0-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
Total release size: 17.0 MB
Release files / barca-0.9.0.tar.gz
| Download URL | barca-0.9.0.tar.gz |
|---|---|
| Size | 228.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92e5e9aaa83d5a274f9560e9d148c02f9c0c004e3f69edd87b9769ea9984f068
|
|
BLAKE2b-256 checksum How to use checksums |
a45b05bbae531acee82a62ca41f01171ce217339050fd8c1a5ccbe58d863afcb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / barca-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | barca-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 8.9 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
38be9077c18bdfaadc61cf051e1ab3353493787c82ec3402c6334c72890eacbf
|
|
BLAKE2b-256 checksum How to use checksums |
930ba7be4e153352f454d667935935a86d31c31e50b06664b0a5389a975dab49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / barca-0.9.0-py3-none-macosx_11_0_arm64.whl
| Download URL | barca-0.9.0-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 8.0 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
91c4cbc24c46ad30a67b148750ba999261cd64ddffa11695f4ae01ca49d66e43
|
|
BLAKE2b-256 checksum How to use checksums |
dad8b37c87419ec848092ec6aaa483d6a11d5cbb5e6d683ab6abbdba56035a05
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|