flow-db
SQLite-based persistent storage for feature management with optimized performance and change event logging.
What it does
Think of flow-db as a filing cabinet for your project's features. Instead of keeping feature information in your head or scattered across text files, this crate stores everything in a structured database. It's like having a smart librarian who not only organizes your features but also remembers every change you make to them - who changed what, when, and why.
The database uses SQLite, which is like having a mini-database engine built right into your application. No server setup needed - just a single file on disk.
Architecture
flow-db/
├── lib.rs - Database handle and connection management
├── feature.rs - FeatureStore: all feature CRUD operations
├── schema.rs - Performance optimizations (WAL mode, cache config)
├── migration.rs - Database schema versioning and upgrades
├── event_log.rs - Change event tracking and audit log
└── task_sync.rs - Convert between Feature and Task formats
Key Modules
- Database (
lib.rs): Thread-safe database handle with write lock and read-only connection support - FeatureStore (
feature.rs): Complete API for creating, reading, updating, and querying features - Schema (
schema.rs): SQLite performance tuning (WAL mode, 64MB cache, mmap I/O) - Migrations (
migration.rs): Automatic schema upgrades using version tracking - Event Log (
event_log.rs): Audit trail for all feature changes - Task Sync (
task_sync.rs): Bidirectional conversion between Features and Claude Code Tasks
Database Schema
Features Table:
features (
id INTEGER PRIMARY KEY,
priority INTEGER,
category TEXT,
name TEXT,
description TEXT,
steps TEXT (JSON array),
passes INTEGER (boolean),
in_progress INTEGER (boolean),
dependencies TEXT (JSON array of feature IDs),
created_at TEXT (ISO8601),
updated_at TEXT (ISO8601)
)
Change Events Table:
change_events (
id INTEGER PRIMARY KEY,
feature_id INTEGER,
event_type TEXT,
field TEXT,
old_value TEXT,
new_value TEXT,
agent TEXT,
source TEXT,
created_at TEXT (ISO8601)
)
Usage
Opening a database
use ;
use CreateFeatureInput;
// Open or create a database file
let db = open?;
// For testing, use in-memory database
let test_db = open_in_memory?;
Creating features
// Get the write connection (behind a mutex)
let conn = db.writer.lock.unwrap;
// Create a single feature
let feature = create?;
println!;
Bulk creation with index-based dependencies
use DependencyRef;
// Create multiple features in one transaction
let inputs = vec!;
let features = create_bulk?;
Querying features
// Get a specific feature
let feature = get_by_id?.unwrap;
// Get all features (sorted by priority)
let all_features = get_all?;
// Get features ready to work on
let ready = get_ready?;
// Get blocked features
let blocked = get_blocked?;
// Get statistics
let stats = get_stats?;
println!;
Atomic feature claiming
// Atomically claim a feature for work (prevents race conditions)
match claim_and_get
Managing dependencies
// Add a dependency
add_dependency?;
// Remove a dependency
remove_dependency?;
// Set all dependencies at once (replaces existing)
set_dependencies?;
// Get dependency graph with dependents computed
let graph = get_graph?;
for node in graph
Logging changes
use event_log;
// Log a change event
log_event?;
// Get all events for a feature
let events = get_events?;
for event in events
API Reference
| Function | Description |
|---|---|
Database::open(path) |
Open database file with automatic migrations |
Database::open_in_memory() |
Create in-memory database for testing |
FeatureStore::create(conn, input) |
Create a single feature |
FeatureStore::create_bulk(conn, inputs) |
Create multiple features atomically |
FeatureStore::get_by_id(conn, id) |
Get feature by ID |
FeatureStore::get_all(conn) |
Get all features sorted by priority |
FeatureStore::get_ready(conn) |
Get features ready to work on |
FeatureStore::get_blocked(conn) |
Get features blocked by dependencies |
FeatureStore::get_stats(conn) |
Get aggregate statistics |
FeatureStore::get_graph(conn) |
Get full dependency graph |
FeatureStore::claim_and_get(conn, id) |
Atomically claim a feature |
FeatureStore::mark_passing(conn, id) |
Mark feature as passing |
FeatureStore::mark_failing(conn, id) |
Mark feature as failing |
FeatureStore::mark_in_progress(conn, id) |
Mark feature in progress |
FeatureStore::clear_in_progress(conn, id) |
Clear in-progress flag |
FeatureStore::skip(conn, id) |
Move feature to end of priority queue |
FeatureStore::add_dependency(conn, id, dep) |
Add a dependency |
FeatureStore::remove_dependency(conn, id, dep) |
Remove a dependency |
FeatureStore::set_dependencies(conn, id, deps) |
Set all dependencies |
event_log::log_event(...) |
Log a change event |
event_log::get_events(conn, feature_id) |
Get all events for a feature |
Performance Optimizations
The database is tuned for high-performance operations:
- WAL Mode: Write-Ahead Logging for better concurrency
- 64MB Cache: Large in-memory cache for hot data
- Memory-Mapped I/O: Direct file mapping for faster reads
- Busy Timeout: 1000ms retry on lock contention
- Indexes: Optimized indexes on status, priority, and foreign keys
Typical query performance:
- Feature lookup by ID: ~0.1ms
- Get all features: ~1-2ms for 100 features
- Claim operation: ~0.5ms (atomic)
Testing
# Run all tests
# Run with output
# Run specific test
Related Crates
- flow-core: Core types (Feature, Task, CreateFeatureInput)
- flow-resolver: Dependency resolution and topological sorting
- flow-server: Web server that uses this database
- flow-mcp: MCP server exposing database operations as tools