Files

5.3 KiB

Implementation Plan: Core Database (Room KMP Persistence)

Branch: 015-core-database | Date: 2026-07-27 | Spec: spec.md Input: Feature specification from /specs/015-core-database/spec.md Status: Migrated — all implementation complete, plan reverse-engineered from existing code.

Summary

Core Database defines the Room KMP schema, per-device database management with LRU eviction, 35 auto-migrations, 11 entities, 7 DAOs, and cross-platform database builders. The DatabaseManager (301 LOC) is the central piece — it manages a cache of open databases, tracks usage via DataStore, and enforces configurable limits.

Technical Context

Language/Version: Kotlin 2.3+ targeting JDK 21
Primary Dependencies: Room KMP (androidx.room3), DataStore KMP, Okio, kotlinx.coroutines, Kermit
Testing: 3 commonTest files, 2 androidDeviceTest files, 1 androidHostTest file; ~600 LOC total
Target Platform: Android, Desktop (JVM), iOS
Constraints: Schema must auto-migrate; dropAllTables = false for destructive fallback; all entities in commonMain
Scale/Scope: 23 commonMain files (~2,800 LOC), 4 platform files (~200 LOC), 6 test files (~600 LOC)

Constitution Check

Principle Status Notes
I. Kotlin Multiplatform Core PASS All entities, DAOs, and database definitions in commonMain. Platform-specific builders via expect/actual.
II. Zero Lint Tolerance PASS detekt-baseline.xml present. @Suppress("TooManyFunctions") on DatabaseManager.
VII. Coroutine Safety PASS Mutex serialization for DB switching. limitedParallelism(4) for withDb(). CancellationException propagated.
IX. Branch & Scope Hygiene PASS Module scoped to org.meshtastic.core.database.

Gate Result: All applicable principles satisfied.

Project Structure

core/database/src/
├── commonMain/kotlin/org/meshtastic/core/database/
│   ├── di/CoreDatabaseModule.kt
│   ├── MeshtasticDatabase.kt       # 141 LOC — schema definition, 35 auto-migrations
│   ├── DatabaseManager.kt          # 301 LOC — per-device management, LRU eviction
│   ├── DatabaseProvider.kt          # Interface
│   ├── DatabaseBuilder.kt          # expect declaration
│   ├── DatabaseConstants.kt        # Naming, limits
│   ├── Converters.kt               # Type converters
│   ├── MeshtasticDatabaseConstructor.kt
│   ├── entity/ (8 entity files)
│   └── dao/ (7 DAO files)
├── commonTest/ (3 files)
├── androidDeviceTest/ (2 files)
├── androidHostTest/ (1 file — MigrationTest)
├── androidMain/ (2 files — builder, DI)
├── jvmMain/ (1 file — builder)
├── iosMain/ (1 file — builder)
└── schemas/ (exported Room schemas for migration validation)

Implementation Phases

Phase 1 — Schema & Entities (Complete)

11 Room entities covering nodes, messages, logs, quick chat, firmware, hardware, traceroutes. Type converters for proto ↔ blob and ByteString ↔ ByteArray.

Phase 2 — DAOs (Complete)

7 DAOs with reactive Flow-based queries, paging support (PagingSourceDaoReturnTypeConverter), and bulk operations.

Phase 3 — Database Manager (Complete)

DatabaseManager (301 LOC): per-device database cache, switchActiveDatabase() with mutex serialization, withDb() with retry, LRU eviction, legacy cleanup, DataStore-backed preferences. Platform-specific DatabaseBuilder expect/actual.

Technical Decisions

Decision Choice Rationale
ORM Room KMP (androidx.room3) Official Google ORM with KMP support; mature migration system
Per-device isolation Separate DB files per BLE address Prevents data cross-contamination; enables clean device removal
Cache strategy In-memory mutableMapOf with LRU eviction Fast access; bounded storage via configurable limit
DB switching Emit new DB before closing old Prevents "connection pool closed" races with active collectors
Retry on closed pool Single retry in withDb() Handles the narrow race between DB switch and in-flight queries
Concurrency limit limitedParallelism(4) for withDb() Prevents SQLite connection pool exhaustion
Migration strategy Auto-migration with exportSchema = true Simplest path; schema exports enable validation testing
Destructive fallback dropAllTables = false Preserves data in unaffected tables during emergency fallback

Gaps Identified

Gap Severity Recommendation
No commonTest for Converters ⚠️ Low Add round-trip tests for proto ↔ blob, ByteString ↔ ByteArray
Only 2 of 7 DAOs have unit tests ⚠️ Medium Add tests for MeshLogDao, QuickChatActionDao, DeviceHardwareDao, FirmwareReleaseDao, TracerouteNodePositionDao
Migration test is Android-only ⚠️ Low Room KMP migration testing is currently Android-only; acceptable limitation
DatabaseManager.close() uses runCatching not safeCatching ⚠️ Low Minor constitution deviation; acceptable in cleanup paths
No test for concurrent withDb() retry behavior ⚠️ Medium Add test that simulates DB switch during query execution