QBICE
Query-Based Incremental Computation Engine
QBICE is a high-performance, asynchronous incremental computation framework for Rust. Define your computation as a graph of queries, and QBICE automatically determines what needs to be recomputed when inputs change—minimizing redundant work through intelligent caching and dependency tracking.
Features
- 🚀 Incremental Computation — Only recomputes what's necessary when inputs change
- ⚡ Async-First Design — Built on Tokio for efficient concurrent execution
- 🔄 Cycle Detection — Automatically detects and handles cyclic dependencies
- 🔒 Type-Safe — Strongly-typed queries with associated value types
- 🧵 Thread-Safe — Safely share the engine across multiple threads
- 📊 Visualization — Generate interactive HTML dependency graphs
Quick Start
use Arc;
use ;
// Define an input query
;
// Define a computation query
// Define the executor
;
async
Core Concepts
Queries
A query represents a unit of computation with an input key and an output value. Queries are identified by their type and content hash, ensuring stable identification across program runs.
Performance Tip: Both query types and their values are cloned frequently internally. Use
Arc<T>,Arc<str>, orArc<[T]>for heap-allocated data to ensure O(1) cloning.
Executors
Executors define how to compute values for queries. They must behave as pure functions—given the same inputs, they must always produce the same output.
;
Important: Executors should not read from global mutable state, system time, or external sources without modeling them as query dependencies. This ensures correct incremental behavior.
Engine Lifecycle
// 1. Create and configure
let mut engine = new;
engine.;
// 2. Set inputs
// 3. Wrap in Arc and query
let engine = new;
let tracked = engine.clone.tracked;
let result = tracked.query.await?;
// 4. Update inputs (requires dropping TrackedEngine first)
drop;
// 5. Query again - only affected computations rerun
let tracked = engine.tracked;
let result = tracked.query.await?;
Incremental Updates
QBICE tracks dependencies automatically. When you update inputs, only queries that depend on changed values are recomputed:
// Initial computation
let tracked = engine.clone.tracked;
assert_eq!;
drop;
// Update one input
// Only Sum is recomputed, not unrelated queries
let tracked = engine.tracked;
assert_eq!;
Handling Cycles
QBICE detects cyclic dependencies and returns CyclicError. For intentional cycles (e.g., fixed-point computations), implement scc_value:
Visualization
Generate interactive HTML visualizations of your dependency graph:
engine.visualize_html?;
This creates an interactive page with:
- Pan and zoom navigation
- Click-to-inspect node details
- Search and filtering
- Color-coded dependency edges
Execution Styles
For advanced optimization, queries can have different execution styles:
- Normal — Standard dependency tracking (default)
- Projection — Fast extractors for parts of other queries
- Firewall — Boundaries that limit dirty propagation
Requirements
- Rust 1.88.0 or later (Edition 2024)
- Tokio runtime
License
This project is licensed under MIT License.
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.