v0.5.0 - Transforms, Persistence, Graph Introspection & Dynamic Routing
AimDB v0.5.0 Release Notes
Transforms, persistence, graph introspection, and dynamic routing!
🎯 What's New in v0.5.0
This release introduces reactive data transformations, a pluggable persistence layer, a dependency graph introspection API, and dynamic topic/address routing for connectors. It also ships two new crates (aimdb-persistence, aimdb-persistence-sqlite).
✨ Major Features
🔄 Transform API (Design 020)
Reactive data transformations between records — computed values that update automatically!
use aimdb_core::{AimDbBuilder, RecordKey};
// Single-input transform: derive Fahrenheit from Celsius
builder.configure::<Celsius>(AppKey::TempCelsius, |reg| {
reg.buffer(BufferCfg::SingleLatest);
});
builder.configure::<Fahrenheit>(AppKey::TempFahrenheit, |reg| {
reg.transform_raw(AppKey::TempCelsius, |c: Celsius| {
Fahrenheit { value: c.value * 9.0 / 5.0 + 32.0 }
});
});
// Multi-input join: combine humidity + temperature into comfort index
builder.configure::<ComfortIndex>(AppKey::Comfort, |reg| {
reg.transform_join_raw(|join| {
join.input::<Celsius>(AppKey::TempCelsius)
.input::<Humidity>(AppKey::Humidity)
.on_trigger(|trigger, state| {
// Called whenever any input changes
Some(ComfortIndex::compute(state))
})
});
});Features:
- 🔗 Single-input
transform_raw()for simple derivations - 🔗 Multi-input
transform_join_raw()withJoinTriggerevent dispatch - 🔒 Mutual exclusion: a record cannot have both
.source()and.transform() - 🚀 Automatic spawning: transforms run as async tasks during
AimDb::build() - 🔍 Tracing integration: full lifecycle event logging
📊 Graph Introspection API (Design 021)
Visualize the dependency graph of your AimDB instance!
// Get full dependency graph
let nodes = db.graph_nodes();
let edges = db.graph_edges();
let order = db.graph_topo_order();
// Nodes show origin type and buffer config
for node in &nodes {
println!("{}: {:?} ({:?})", node.key, node.origin, node.buffer_info);
}
// Edges show data flow
for edge in &edges {
println!("{} → {} ({:?})", edge.from, edge.to, edge.edge_type);
}RecordOrigin variants:
Source— direct producer writesLink— connector-bridged external dataTransform— single-input reactive derivationTransformJoin— multi-input reactive joinPassive— no producer (consumer-only)
Also available via aimdb-client and aimdb-mcp tools.
💾 Persistence Layer (New Crates)
Long-term record history with pluggable backends!
use aimdb_persistence::AimDbBuilderPersistExt;
use aimdb_persistence_sqlite::SqliteBackend;
// Set up SQLite persistence with 7-day retention
let backend = SqliteBackend::new("./aimdb_history.db")?;
builder.with_persistence(backend, Duration::from_secs(7 * 24 * 3600));
// Mark records to persist
builder.configure::<Temperature>(AppKey::Temp, |reg| {
reg.buffer(BufferCfg::SingleLatest)
.persist("sensor.temperature"); // ← stored to SQLite
});
let db = builder.build().await?;
// Query historical data
let last_100 = db.query_latest::<Temperature>("sensor.*", Some(100)).await;
let this_week = db.query_range::<Temperature>(
"sensor.*",
week_start_ms,
week_end_ms,
None, // no per-record limit
).await;aimdb-persistence features:
PersistenceBackendtrait — implement your own storage- Automatic retention cleanup task (24-hour interval)
query_latest,query_range,query_rawAPIs
aimdb-persistence-sqlite features:
- WAL journal mode for concurrent reads
- Window-function queries for efficient top-N per record
*wildcard pattern matching- Actor-model writer thread;
Clone= O(1) handle copy
🌐 Dynamic Topic/Address Routing (Design 018)
Resolve MQTT topics or KNX group addresses at runtime based on data values!
// Outbound: per-message topic from payload
builder.configure::<SensorReading>(AppKey::Reading, |reg| {
reg.link_to("mqtt://broker/sensors/default")
.with_topic_provider(|reading: &SensorReading| {
format!("sensors/{}/data", reading.sensor_id)
});
});
// Inbound: late-binding topic from config/discovery
builder.configure::<Command>(AppKey::Command, |reg| {
reg.link_from("mqtt://broker/")
.with_topic_resolver(|| {
// Called once at connector startup
format!("commands/{}", load_device_id())
});
});Works in both std and no_std + alloc environments.
📡 Record Drain API (Design 019)
Non-blocking batch pull for accumulated history — perfect for LLM analysis!
// Via AimDbClient (remote access)
let response = client.drain_record("sensor.temperature").await?;
println!("Drained {} values", response.count);
// First call is always empty (cold start — creates the reader)
// Subsequent calls return everything since last drainCold-start semantics: the first drain call creates a reader and returns empty. Subsequent calls return all values accumulated since the previous drain. This enables stateful batch analysis without missing data.
💥 Breaking Changes
1. .with_serialization() → .with_remote_access()
// Before (v0.4.x)
reg.buffer(...).with_serialization();
// After (v0.5.0)
reg.buffer(...).with_remote_access();2. RecordId::new() requires RecordOrigin
This affects custom buffer/record implementations only:
// Before (v0.4.x)
RecordId::new(type_id, idx)
// After (v0.5.0)
RecordId::new(type_id, idx, RecordOrigin::Source)3. MCP subscription tools removed
The subscribe_record, unsubscribe_record, list_subscriptions, and get_notification_directory MCP tools have been replaced by drain_record. Update any LLM prompts or MCP tool configurations accordingly.
📦 Published Crates
New Crates
- 🆕
aimdb-persistence@0.1.0— pluggable persistence layer - 🆕
aimdb-persistence-sqlite@0.1.0— SQLite backend for persistence
Updated
| Crate | Version |
|---|---|
aimdb-core |
0.5.0 |
aimdb-tokio-adapter |
0.5.0 |
aimdb-embassy-adapter |
0.5.0 |
aimdb-client |
0.5.0 |
aimdb-sync |
0.5.0 |
aimdb-mqtt-connector |
0.5.0 |
aimdb-knx-connector |
0.3.0 |
aimdb-cli |
0.5.0 |
aimdb-mcp |
0.5.0 |
Unchanged
aimdb-derive@0.1.0aimdb-executor@0.1.0
🔧 New MCP Tools
The aimdb-mcp server now provides these tools:
| Tool | Description |
|---|---|
discover_instances |
Find running AimDB servers |
list_records |
List all records with metadata |
get_record |
Get current value |
set_record |
Set writable record value |
query_schema |
Infer JSON Schema from value |
get_instance_info |
Server version and capabilities |
drain_record |
Batch pull accumulated history |
graph_nodes |
All graph nodes with origin/buffer info |
graph_edges |
Directed data-flow edges |
graph_topo_order |
Topological record ordering |
🖥️ New CLI Commands (aimdb graph)
# List all graph nodes (color-coded by origin)
aimdb graph nodes
# Show directed data-flow edges
aimdb graph edges
# Show topological (spawn) order
aimdb graph order
# Export to Graphviz DOT format
aimdb graph dot > pipeline.dot
dot -Tsvg pipeline.dot -o pipeline.svg🚀 Migration Guide
Step 1: Update dependencies
[dependencies]
aimdb-core = "0.5.0"
aimdb-tokio-adapter = "0.5.0"
# Optional: add persistence
aimdb-persistence = "0.1.0"
aimdb-persistence-sqlite = "0.1.0"Step 2: Rename .with_serialization() → .with_remote_access()
# Quick find & replace
grep -rn "with_serialization" src/
# Replace with:
# .with_remote_access()Step 3: Update RecordId::new() calls (custom buffer implementations only)
// Add RecordOrigin argument
RecordId::new(type_id, idx, RecordOrigin::Source)Step 4: Update MCP tool usage
Replace subscribe_record / unsubscribe_record workflows with drain_record polling:
# Old: subscribe → wait → unsubscribe
# New: call drain_record periodically (first call always empty)
drain_record(socket_path: "...", record_name: "sensor.*")
📚 Examples
All examples updated for v0.5.0:
git clone https://github.com/aimdb-dev/aimdb.git && cd aimdb
# MQTT connector demo
cargo run -p tokio-mqtt-connector-demo
# KNX connector demo
cargo run -p tokio-knx-connector-demo
# Sync API demo
cargo run -p sync-api-demo
# Weather mesh demo (multi-station)
cargo run -p weather-hub
# Embedded (cross-compile)
cd examples/embassy-mqtt-connector-demo
cargo build --release --target thumbv7em-none-eabihf🤝 Contributing
git clone https://github.com/aimdb-dev/aimdb.git
cd aimdb
make check # fmt + clippy + test + embedded cross-compile📄 License
Apache License 2.0 — see LICENSE.