Skip to content

v2.0.0

Latest

Choose a tag to compare

@lxsaah lxsaah released this 18 Sep 20:23
b0dc993

AimDB v2.0.0 Release Notes

One wire protocol, one runtime abstraction, three new transports.

Typed dataplane for distributed systems

📄 Full release notes: docs/releases/v2.0.0.md


🎯 Highlights

AimDB 2.0 converges every transport on a single wire protocol (AimX 3.0), removes the runtime generic R from the public API, retires aimdb-executor, and adds Unix-socket, serial and TCP transports alongside the existing MQTT, KNX and WebSocket connectors. The simplification passes (designs 034/036/037/038) cut ~2,500 lines from the library crates with no capability loss.

This is a breaking release. A pre-3.0 client is now refused at the handshake rather than failing on its first call, so servers and clients must be upgraded together.


💥 Breaking Changes

1. AimX 3.0 — one protocol for every transport

aimdb-ws-protocol is deleted. The WebSocket connector and the browser WsBridge now speak the same AimX tagged frames as UDS, serial and TCP, over the same session engine. AimX gained the two features that justified the old WS fork: wildcard/multi-record subscribe and shared record.query / record.list result shapes.

PROTOCOL_VERSION moves "2.0" → "3.0", and hello now negotiates instead of ignoring the version. Compatibility is by major version; a missing or malformed version fails closed with RpcError::VersionMismatch.

record.query results changed shape:

// 1.x
{ "values": [{ "record": "…", "value": …, "stored_at": … }], "count": 3 }
// 2.0
{ "records": [{ "topic": "…", "payload": …, "ts": … }], "total": 3 }

Browser clients must declare the version in the upgrade URL — a browser cannot set WebSocket handshake headers, so the server refuses an incompatible or absent version with HTTP 426 Upgrade Required before the socket opens. The JS WsBridge API is unchanged and appends it automatically; hand-rolled clients need …/ws?v=3.0. See aimdb_core::remote::{VERSION_PARAM, ws_url_with_version}.

Caveat (design 047 §3.6): server-seeded auto-subscriptions are invisible to run_client-based consumers. Drive your own subscribe for records you want streamed.

2. Dot is the only topic separator

topic_matches now splits patterns on . only — RabbitMQ topic-exchange semantics (* one segment, # zero or more). Previously it split on /, so a dot-separated key like temp.vienna silently failed to match temp.*. / survives only inside external broker addresses (mqtt://sensors/temp/x), which are unaffected.

- ws.subscribe("sensors/#")
+ ws.subscribe("sensors.#")

3. The runtime generic R is gone

Records (T) are the only generic surface left; the runtime travels as Arc<dyn RuntimeOps>. AimDb, AimDbBuilder, TypedRecord<T>, RecordRegistrar<'a, T>, TransformBuilder<I, O>, JoinBuilder<O> and ConnectorBuilder are all non-generic over the runtime, and there is no NoRuntime typestate.

- async fn task(mut rx: Consumer<Temperature, TokioAdapter>) { … }
+ async fn task(mut rx: Consumer<Temperature>) { … }

RuntimeContext is now a concrete struct: time().now() returns u64 nanos, with sleep(Duration) / sleep_millis / sleep_secs.

Close semantics changed on Embassy: a join fan-in queue now closes on all runtimes once every input forwarder exits, so a no_std handler's while let Ok(_) = rx.recv().await loop ends instead of parking forever. Treat Err(QueueClosed) as end-of-inputs.

4. aimdb-executor is retired

RuntimeOps, LogLevel, BoxFuture and ExecutorError / ExecutorResult now live in aimdb_core::executor, re-exported at the crate root. The superseded generic trait family (RuntimeAdapter, TimeOps, Logger, Runtime, RuntimeInfo) and aimdb_core::time are deleted — adapters implement one trait instead of four.

5. Remote access is a connector

AimDbBuilder::with_remote_access(config) is removed. Register a session connector instead; it binds synchronously, so bind errors still surface from build().

6. CLI and MCP take an endpoint, not a socket path

The per-command --socket flags are gone in favour of a global --connect <scheme://url> (unix://, serial://DEVICE?baud=N, or a bare path). AIMDB_SOCKET → AIMDB_CONNECT; every MCP tool's socket_path parameter is renamed endpoint. discover_instances keeps socket_path.

7. Capability traits and codecs (aimdb-data-contracts)

linkable is now format-neutral — derive users must enable the new linkable-json. Simulatable::simulate takes an associated Self::Params (SimulationConfig / SimulationParams are removed); Observable loses ICON and format_log() (use ObservableRegistrarExt::log(node_id)); Settable moves behind a new settable feature.

8. Smaller surface changes

  • DbError and SyncError are #[non_exhaustive] — add a wildcard arm, or match on the new kind() / DbErrorKind.
  • BufferReader is poll-based (design 037 W8), removing the last per-message heap allocation on the consume path.
  • ConsumerTrait::subscribe_any is infallible — drop the Ok(…) wrap and the dead Err arm.
  • RecordMetadata drops created_at / last_update; core keeps no wall-clock state for the no_std AimX server.
  • with_serializer_raw / with_deserializer_raw are gone — use .with_serializer(|_ctx, v| …).
  • The std feature no longer pulls tokio. Depend on it directly if you relied on the transitive dependency.
  • MQTT knobs (with_qos / with_retain) leave the generic link builders — use aimdb-mqtt-connector's MqttLinkExt, or generic with_config(key, value).

✨ Also New

  • Three transports — aimdb-uds-connector, aimdb-serial-connector and aimdb-tcp-connector, each contributing only a Dialer/Listener/Connection triple over the shared session engine. One code path serves both Tokio and Embassy.
  • A log destination for FFI hosts (design 050). An optional, non-default log feature on aimdb-core sits alongside tracing, for hosts that cannot be handed a process-global subscriber. Every crate that reports now goes through the same facade; with the feature off — the default, and every MCU build — nothing changes.
  • Per-link record codecs (#178). One Linkable type can select JSON, bounded Postcard or a custom codec independently per route, fused at registration time with no runtime registry.
  • ConnectorBuilder::owns_scheme — a connector can declare it must be the only one registered under its scheme; build() rejects a collision by name instead of silently double-publishing every route.
  • AimDbHandle::shutdown(&self) / is_closed() — the shutdown contract a foreign-language binding needs. A shutdown now also releases the database, waking a consumer parked in get().
  • Faster remote reads (#196). Typed records serialize straight to JSON bytes: 16 → 6 allocations per reply/event and a 1.55×–1.61× speedup on the measured host.

🔒 Security & Correctness Fixes

  • A non-terminal # no longer matches everything after it. topic_matches("a.#.secret", "a.public") returned true, so an ACL grant of that shape behaved like a blanket a.# over the whole subtree. # now absorbs zero or more segments with the pattern continuing afterwards. Only ever tightens what an interior # admits; trailing # and * grants are unaffected.
  • SQLite queries honour pattern semantics. The * → SQL % rewrite is gone: % crosses ., so sensors.* also returned sensors.secret.deep — outside the pattern, and on the WebSocket path outside the grant that authorized it.
  • A peer's seq can no longer crash the client demux. An update with seq == u64::MAX overflowed on the next frame — a debug-build panic from one crafted frame — and a repeated or reordered seq invented phantom gaps.
  • An abandoned RPC call no longer leaks. ClientHandle::call is now cancel-safe; a timed-out request frees its pending-call entry in O(1) instead of growing the demux map for the life of the connection.

🚀 Migration Guide

Step 1 — swap the executor import.

- use aimdb_executor::{RuntimeOps, BoxFuture};
+ use aimdb_core::{RuntimeOps, BoxFuture};

Custom adapters implement RuntimeOps only; delete the RuntimeAdapter / TimeOps / Logger / Runtime impls.

Step 2 — drop the runtime type parameter.

- Producer<Temperature, TokioAdapter>   Consumer<Temperature, TokioAdapter>
+ Producer<Temperature>                 Consumer<Temperature>

Regenerate any aimdb-codegen scaffolds — the emitted configure_schema is now non-generic (fn configure_schema(builder: &mut AimDbBuilder)) and the emitted prelude imports from aimdb_core only.

Step 3 — replace with_remote_access.

  let db = AimDbBuilder::new()
-     .with_remote_access(config)
+     .with_connector(aimdb_uds_connector::UdsServer::from_config(config))
      .build()?;

Step 4 — move subscriptions to dot patterns.

- ws.subscribe("sensors/#")
+ ws.subscribe("sensors.#")

Step 5 — update tooling invocations.

- aimdb record list --socket /tmp/app.sock
+ aimdb record list --connect unix:///tmp/app.sock
- export AIMDB_SOCKET=/tmp/app.sock
+ export AIMDB_CONNECT=unix:///tmp/app.sock

Step 6 — if you use #[derive(Linkable)], enable linkable-json.

- aimdb-data-contracts = { version = "0.2", features = ["linkable"] }
+ aimdb-data-contracts = { version = "0.2", features = ["linkable-json"] }

Step 7 — upgrade clients and servers together. A 2.x client cannot talk to a 1.x server, and vice versa.


📦 Published Crates

New

Crate Version
aimdb-uds-connector 0.1.0
aimdb-serial-connector 0.1.0
aimdb-tcp-connector 0.1.0

Updated

Crate Version Crate Version
aimdb-core 1.1.0 → 2.0.0 aimdb-persistence 0.1.1 → 0.2.0
aimdb-derive 0.1.0 → 0.2.0 aimdb-persistence-sqlite 0.1.1 → 0.2.0
aimdb-data-contracts 0.1.1 → 0.2.0 aimdb-mqtt-connector 0.6.0 → 0.7.0
aimdb-client 0.6.0 → 0.7.0 aimdb-knx-connector 0.4.0 → 0.5.0
aimdb-codegen 0.2.0 → 0.3.0 aimdb-websocket-connector 0.2.0 → 0.3.0
aimdb-embassy-adapter 0.6.0 → 0.7.0 aimdb-wasm-adapter 0.2.0 → 0.3.0
aimdb-tokio-adapter 0.6.0 → 0.7.0 aimdb-cli 0.6.0 → 0.7.0
aimdb-sync 0.5.0 → 0.6.0 aimdb-mcp 0.8.0 → 0.9.0

Retired

  • aimdb-executor — folded into aimdb_core::executor. Last published version 0.2.0.
  • aimdb-ws-protocol — superseded by AimX. Last published version 0.1.0, which speaks a protocol 2.0.0 refuses at the handshake.

📖 Design Documents

  • 036–038 — follow-up refactoring, zero-alloc consume path, and the technical-debt/simplification review
  • 041 — data-contracts integration: capability traits as first-class verbs
  • 045 — per-link codec selection
  • 047 — retire the WS protocol, converge on AimX
  • 050 — a log destination for FFI hosts
  • 052 — runtime-neutral connectors

🤝 Contributing

git clone https://github.com/aimdb-dev/aimdb.git
cd aimdb
make check  # fmt + clippy + test + embedded + wasm cross-compile

📄 License

Apache License 2.0 — see LICENSE.