|
| 1 | +# 🚀 Design: Unified Runtime Integration & Executor Abstractions (M1.1) |
| 2 | + |
| 3 | +**Milestone**: Runtime Independence & Multi-Platform Execution (follow-up to M1) |
| 4 | +**Goal**: Establish a clean, trait-based execution & time abstraction layer powering both `std` (Tokio) and `no_std` (Embassy) environments while keeping `aimdb-core` free of concrete runtime dependencies. |
| 5 | +**Scope of Rework**: This document retrofits experimental work done on branch `8-runtime-adapter-integration-#1` (≈5K LOC delta) into a structured, incremental upstream plan. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 🎯 Problem Statement |
| 10 | + |
| 11 | +AimDB must run the same logical services across: |
| 12 | +- MCU / embedded boards (`no_std` + Embassy executor, static tasks, limited heap) |
| 13 | +- Edge & Cloud (`std` + Tokio, dynamic spawning, richer timing APIs) |
| 14 | + |
| 15 | +Early spike code mixed concerns (database, runtime selection, spawning models, timing utilities) and added crates in one large change set, making review, testing, and stabilization difficult. |
| 16 | + |
| 17 | +We need a minimal, opinionated runtime abstraction that: |
| 18 | +- ✅ Keeps `aimdb-core` runtime-agnostic (traits only) |
| 19 | +- ✅ Supports both dynamic (Tokio) and static (Embassy) spawn models |
| 20 | +- ✅ Unifies time APIs (now, sleep, delayed spawn) without leaking concrete types |
| 21 | +- ✅ Scales to additional runtimes (custom executor, wasm) later |
| 22 | +- ✅ Works under `no_std` (no hidden allocations) & `std` (full ergonomics) |
| 23 | +- ✅ Enables service definition via macro (`#[service]`) that remains runtime-neutral |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## ✅ Success Criteria |
| 28 | + |
| 29 | +Functional: |
| 30 | +- [ ] `aimdb-executor` crate defines core traits: `RuntimeAdapter`, `SpawnDynamically`, `SpawnStatically`, `TimeSource` |
| 31 | +- [ ] `aimdb-core` depends only on `aimdb-executor` (no Tokio/Embassy direct deps) |
| 32 | +- [ ] Tokio & Embassy adapters implement full trait surface behind feature flags |
| 33 | +- [ ] `#[service]` macro generates a runtime-neutral service struct + spawn helpers |
| 34 | +- [ ] Unified sleep / timestamp access via `TimestampProvider` + `SleepCapable` (or consolidated `TimeSource`) |
| 35 | +- [ ] Database run loop uses only trait bounds (no cfg spaghetti internally) |
| 36 | + |
| 37 | +Non-Functional: |
| 38 | +- [ ] <50ms end-to-end service reaction across platforms |
| 39 | +- [ ] Zero heap allocations in Embassy path (tracked via tests / `alloc` feature absence) |
| 40 | +- [ ] No increase in core public API surface instability (documented semver intent) |
| 41 | +- [ ] Code size growth of `no_std` binary attributable per feature flag |
| 42 | +- [ ] CI matrix covers: core (no_std), core+tokio, core+embassy |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +## 🧩 Architecture Overview |
| 47 | + |
| 48 | +Mermaid Diagram: |
| 49 | + |
| 50 | +```mermaid |
| 51 | +flowchart TB |
| 52 | + App[Application] -->|expands| Macro[[#[service] macro]] |
| 53 | + Macro --> Service[Generated Service Struct] |
| 54 | + Service --> Core[aimdb-core] |
| 55 | + Core --> Executor[aimdb-executor traits] |
| 56 | + AdapterTokio[Tokio Adapter] --> Executor |
| 57 | + AdapterEmbassy[Embassy Adapter] --> Executor |
| 58 | + subgraph Adapters |
| 59 | + AdapterTokio |
| 60 | + AdapterEmbassy |
| 61 | + end |
| 62 | +``` |
| 63 | + |
| 64 | +Key Points: |
| 65 | +1. `aimdb-core` depends only on trait contracts (`aimdb-executor`). |
| 66 | +2. Adapters implement those traits; core never imports adapter symbols. |
| 67 | +3. Services are generic; macro output remains runtime-neutral. |
| 68 | +4. Application chooses which adapter(s) to instantiate and inject. |
| 69 | + |
| 70 | +### Core Trait Set (from `aimdb-executor`) |
| 71 | +| Trait | Responsibility | Notes | |
| 72 | +|-------|----------------|-------| |
| 73 | +| `RuntimeAdapter` | Identity + construction | Minimal: `new()`, `runtime_name()` | |
| 74 | +| `SpawnDynamically` | `spawn(Future)` returning JoinHandle | Tokio style | |
| 75 | +| `SpawnStatically` | Static task provisioning (`Spawner`) | Embassy style | |
| 76 | +| `TimeSource` | `now()`, `sleep(duration)`, conversion helpers | Abstracts time types | |
| 77 | + |
| 78 | +> Layering Clarification: Adapters DO NOT "implement aimdb-core". They implement the trait contracts defined in `aimdb-executor`. The `aimdb-core` crate depends only on those traits (inverting the dependency) and remains unaware of adapter concrete types. Wording elsewhere should be: “Tokio/Embassy adapters implement the executor & time traits consumed by aimdb-core,” not “implement aimdb-core.” |
| 79 | +
|
| 80 | +### Time Abstraction |
| 81 | +Two dominant ecosystems (Tokio `std::time`, Embassy `embassy_time`). `TimeSource` unifies: |
| 82 | +```rust |
| 83 | +trait TimeSource: RuntimeAdapter { |
| 84 | + type Instant; // runtime-specific |
| 85 | + type Duration; // std::time::Duration | embassy_time::Duration |
| 86 | + fn now(&self) -> Self::Instant; |
| 87 | + fn sleep(&self, d: Self::Duration) -> impl Future<Output = ()>; |
| 88 | + fn millis(&self, ms: u64) -> Self::Duration; // constructor helpers |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +### Service Macro (`aimdb-macros::service`) |
| 93 | +Input: `async fn my_service(ctx: RuntimeContext<R>) -> DbResult<()> { ... }` |
| 94 | +Generates: |
| 95 | +- Original function retained |
| 96 | +- `struct MyService;` |
| 97 | +- `MyService::spawn_tokio(&TokioAdapter)` (feature gated) |
| 98 | +- `MyService::spawn_embassy(&'static EmbassyAdapter)` (feature gated) |
| 99 | +Ensures service logic stays executor-agnostic. |
| 100 | + |
| 101 | +### Runtime Context |
| 102 | +`RuntimeContext<R>` wraps runtime adapter instance and exposes unified helpers for services (sleep, spawn child tasks, timestamps) w/out leaking adapter concrete type outside. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## 🛠 current branch DELTA (summary) |
| 107 | + |
| 108 | +| Category | Added | Modified | Notes | |
| 109 | +|----------|-------|----------|-------| |
| 110 | +| New Crates | `aimdb-executor`, `aimdb-macros` | – | Provide traits + macro | |
| 111 | +| Core | context.rs, runtime.rs, time.rs, database.rs | lib.rs, error.rs | Pulls traits + exposes reexports | |
| 112 | +| Adapters | embassy + tokio runtime/time/database modules | existing Cargo.toml | Feature gating WIP | |
| 113 | +| Examples | tokio + embassy runtime demos | removed single `quickstart.rs` | Split per-runtime | |
| 114 | +| Tooling | `.gitmodules` (Embassy), Makefile edits | CI workflows | Introduced external dependency | |
| 115 | + |
| 116 | +Risk: Monolithic merge obscures review; unstable patterns might ossify. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## 🔄 Refactoring / Reimplementation Plan |
| 121 | + |
| 122 | +Phased approach to upstream clean, reviewable slices: |
| 123 | + |
| 124 | +### Phase 0 – Baseline Preparation |
| 125 | +1. Extract ONLY `aimdb-executor` crate with minimal traits (no macros yet) |
| 126 | +2. Wire feature flags at workspace root (ensure no implicit default coupling) |
| 127 | +3. CI: add matrix (core-only, tokio, no_std check build) |
| 128 | + |
| 129 | +### Phase 1 – Core Integration |
| 130 | +4. Refactor `aimdb-core` to depend on `aimdb-executor` traits; remove any direct runtime references |
| 131 | +5. Introduce `RuntimeContext` (lightweight) exposing time + spawn abstractions |
| 132 | +6. Add doc tests showing generic service using `RuntimeContext<R>` |
| 133 | + |
| 134 | +### Phase 2 – Tokio Adapter |
| 135 | +7. Implement `TokioAdapter` with `SpawnDynamically + TimeSource` |
| 136 | +8. Provide basic database run example (tokio demo) – no macro yet |
| 137 | +9. Add integration test: spawn service + verify sleep timing tolerance |
| 138 | + |
| 139 | +### Phase 3 – Embassy Adapter |
| 140 | +10. Vendor Embassy submodule (document licensing + update CONTRIBUTING) |
| 141 | +11. Implement `EmbassyAdapter` with static spawn API (behind `embassy-runtime`) |
| 142 | +12. Provide `no_std` build test (thumb target) in CI (compile-only) |
| 143 | + |
| 144 | +### Phase 4 – Service Macro |
| 145 | +13. Introduce `aimdb-macros` with `#[service]` generating service struct |
| 146 | +14. Gate spawn helpers per runtime feature, ensure no accidental std usage in embassy path |
| 147 | +15. Add macro snapshot tests (compiletest/minitest harness) |
| 148 | + |
| 149 | +### Phase 5 – Unified Time Utilities |
| 150 | +16. Finalize `TimeSource` shape (evaluate folding SleepCapable/TimestampProvider) |
| 151 | +17. Replace ad-hoc time modules in adapters with canonical implementation |
| 152 | +18. Bench micro-latency: spawn + immediate sleep vs native runtime baseline (<5% overhead) |
| 153 | + |
| 154 | +### Phase 6 – Examples & Docs |
| 155 | +19. Replace experimental examples with curated `examples/{tokio,embassy}-runtime-demo` |
| 156 | +20. Add README section “Choosing a Runtime” linking to design docs |
| 157 | +21. Add architecture diagram asset if needed |
| 158 | + |
| 159 | +### Phase 7 – Cleanup & Deprecation |
| 160 | +22. Remove legacy spike modules not used after refactor |
| 161 | +23. Ensure semver guardrails: mark new APIs experimental via doc `#[doc(cfg(...))]` |
| 162 | +24. Close tracking issues & retire integration branch |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## 🧮 Feature Flags (proposed normalized set) |
| 167 | +```toml |
| 168 | +[features] |
| 169 | +default = ["tokio-runtime"] # Consider removing default to force explicit choice |
| 170 | +tokio-runtime = ["dep:tokio"] |
| 171 | +embassy-runtime = ["dep:embassy-executor", "dep:embassy-time"] |
| 172 | +macros = ["aimdb-macros"] |
| 173 | +services = ["macros"] # Higher-level convenience grouping |
| 174 | +std = [] # Forwarded where needed |
| 175 | +``` |
| 176 | + |
| 177 | +--- |
| 178 | + |
| 179 | +## ⚠️ Risks & Mitigations |
| 180 | +| Risk | Impact | Mitigation | |
| 181 | +|------|--------|------------| |
| 182 | +| Trait proliferation | API bloat | Keep minimal initial trait set; postpone advanced scheduling | |
| 183 | +| Embassy static model mismatch | Hard to generalize spawn | Keep separate trait (`SpawnStatically`) vs forcing unification | |
| 184 | +| Macro hides complexity | Debug difficulty | Generate readable code; provide `--emit=expanded` doc snippet | |
| 185 | +| Time abstraction cost | Performance regression | Micro-bench spawn + sleep vs raw runtime | |
| 186 | +| Submodule churn | CI instability | Pin commit hash + document update procedure | |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +## 📌 Issue Breakdown (to create) |
| 191 | +Reference each to this design doc section. |
| 192 | + |
| 193 | +1. Add `aimdb-executor` crate (Phase 0–1) |
| 194 | +2. Integrate executor traits into `aimdb-core` (Phase 1) |
| 195 | +3. Introduce `RuntimeContext` with docs (Phase 1) |
| 196 | +4. Implement `TokioAdapter` + basic example (Phase 2) |
| 197 | +5. Add Tokio integration tests for spawn & sleep (Phase 2) |
| 198 | +6. Vendor Embassy submodule + licensing notes (Phase 3) |
| 199 | +7. Implement `EmbassyAdapter` skeleton (Phase 3) |
| 200 | +8. Add `thumbv7em` compile check in CI (Phase 3) |
| 201 | +9. Introduce `aimdb-macros` crate + `#[service]` (Phase 4) |
| 202 | +10. Add macro expansion tests (Phase 4) |
| 203 | +11. Finalize `TimeSource` and remove redundant traits (Phase 5) |
| 204 | +12. Add performance benchmarks (Phase 5) |
| 205 | +13. Replace examples with curated runtime demos (Phase 6) |
| 206 | +14. Update README & diagrams (Phase 6) |
| 207 | +15. Remove spike code & retire branch (Phase 7) |
| 208 | +16. Add semver stability notes & doc cfg attributes (Phase 7) |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +## 🔓 Open Questions |
| 213 | +- Should `RuntimeContext` own or borrow the adapter? (Current spike clones; may prefer Arc for std & &'static for no_std) |
| 214 | +- Provide a unified delayed-spawn API or leave to runtime-specific extension traits? |
| 215 | +- Should service macro support concurrency limits / restart policies now or later? |
| 216 | +- Do we need a mock runtime crate for deterministic tests? (Recommended Phase 2.5 optional) |
| 217 | + |
| 218 | +--- |
| 219 | + |
0 commit comments