Skip to content
⚠️ 本页面为机器翻译,母语者审校尚未完成,译文可能存在错误。

ADR 002 —— 作为派生读取缓存的 SQLite

状态: 已接受
日期: 2026-04

更新(2026-06)

签名事件日志尚未实现 —— 今日摄取直接写入 SQLite(取舍中描述的“阶段 1”路径)。日志设计也已从 Hypercore 转向 Raiznet 原生的仅追加事件日志(ADR-004);SQLite 作为派生索引的原则保持不变。

背景

Raiznet 意图的真相来源是一份仅追加、经加密签名的事件日志。然而,直接从这样的日志提供快速 API 查询(时间范围读取、聚合、按 H3 单元过滤)是不切实际的:它是为顺序追加和复制设计的,而非随机访问的索引查询。

需要一个二级索引层。

决定

SQLite(经由 better-sqlite3)用作派生读取缓存。它不打算成为长期的真相来源。一旦事件日志存在,损坏或删除的 SQLite 数据库可通过从第一个事件重放日志来完全重建。

维护两个独立的数据库:

数据库来源访问
raiznet_public.db公共摄取(事件日志复制计划中)公共端点
raiznet_private.db仅本地摄取仅本地端点

理由

  • 查询性能REAL 类型的固定列允许带索引的标准 SQL 聚合(AVG、MIN、MAX、GROUP BY)。查询时无 JSON 解析。
  • 模式简洁:无 ORM —— 通过 better-sqlite3 的同步 API 直接 SQL,带类型化结果。
  • 重建保证(事件日志落地后):因为 SQLite 派生自日志,模式演进不意味着数据丢失。删除文件、重放,完成。
  • 通过隔离实现安全:公共端点的 Fastify 实例仅持有到 raiznet_public.db 的连接。公共端点上的查询无法返回私有数据,因为数据库连接对象根本不可用 —— 隔离在连接层,而非查询层。
  • better-sqlite3 的同步 API:自然契合 Fastify 的异步路由处理器,无需单独的线程池或回调间接。

取舍

  • 添加新传感器类型需要模式迁移(三个新列:_plain_cipher_nonce)。这是为快速聚合查询接受的代价。
  • 宽表设计(每条读数一行,所有传感器列在同一行)比窄的键值表使用更多磁盘空间,但无需 join 即可实现带索引的范围查询。
  • 阶段 1 直接写入 SQLite。阶段 2 增加事件日志 → 索引器 → SQLite 的管线。API 层在两个阶段始终从 SQLite 读取。

后果

  • apps/server/src/storage/public-db.tsprivate-db.ts 拥有模式创建(CREATE TABLE IF NOT EXISTS)。
  • 阶段 1 无迁移框架 —— 表在首次启动时创建,模式稳定。
  • 一旦事件日志复制激活,索引器(阶段 2)将成为 raiznet_public.db 的唯一写入者。
  • raiznet_private.db 始终由本地摄取路径直接写入 —— 从不复制。