选择存储与持久化边界
按照持久性、分布式恢复和租户需求,选择 SQLite、PostgreSQL、pgvector、Qdrant 或内存实现。
按职责选择 Store
Runifold 定义窄 Store 契约,而不是一个全局数据库对象。Journal 追加 Run 证据;Effect Store 协调外部写;Checkpoint 与 Workflow Store 保存持久执行;Conversation Store 保存 Transcript;Vector Store 检索语义文档。
按照每类职责需要的保证选择后端。即使共用一个数据库部署,也不要合并这些语义边界。
| 需求 | 合适起点 |
|---|---|
| 确定性单元测试 | 内存实现 |
| 单进程崩溃恢复 | SQLite |
| 分布式 Worker 与多租户 | PostgreSQL |
| 在现有 Postgres 内语义检索 | pgvector |
| 专用向量服务 | Qdrant |
使用 SQLite 持久化并更新 Checkpoint
[dependencies]
runifold = { version = "=0.9.0", features = ["sqlite-bundled"] }
serde_json = "1"use runifold::{
Budget, BudgetTracker, CapabilitySet, Checkpoint, CheckpointId,
CheckpointStore, RunContext, sqlite::SqliteStore,
};
use serde_json::json;
let store = SqliteStore::open("runifold.db")?;
let run = RunContext::root(
BudgetTracker::new(Budget::default()),
CapabilitySet::new(),
);
let first = Checkpoint::initial(
CheckpointId::new(),
run.run_id(),
"example.order",
1,
json!({ "state": "received" }),
);
store.compare_and_swap(&first, None)?;
let current = store.load(first.id)?;
let next = current.next(json!({ "state": "validated" }))?;
store.compare_and_swap(&next, Some(current.revision))?;None 表示只允许创建。更新时传入刚刚读取的准确 Revision,并提交 Revision 加一的
Checkpoint。Conflict 是并发信号:丢弃拟写入状态,重新加载,再根据当前状态决策。
不要用无条件覆盖隐藏冲突。
SQLite
runifold-store-sqlite 提供持久 Effect、Checkpoint 与 Journal,适合本地应用、桌面服务、
单 Worker 进程,以及需要真实事务与重启证据的集成测试。
希望由 Cargo 构建 SQLite 时启用 bundled Feature。明确数据库路径,按照风险配置文件
系统持久性,并测试进程崩溃恢复。SQLite 不能把多个独立机器变成分布式 Lease 系统。
PostgreSQL
runifold-store-postgres 提供 Conversation、Semantic Memory 与分布式 Workflow 状态。
PostgreSQL 承担原子跳转、Store 权威时间、Row Lock、SKIP LOCKED Claim、租户隔离、
预算账本、Retention 与 Tombstone Audit。
数据库可用性属于执行契约。限制 Connection Wait,把 Lease Loss 与应用失败分开,并确保 Schema 变更与仍在运行的旧 Worker 兼容。
向量存储
runifold-retrieval 定义服务商中立的 Embedding、Document、Retriever 与 Vector Store
契约。内存 Index 适合测试和小型静态语料;pgvector 与 Qdrant Adapter 提供持久检索。
保存 Document Identity、Namespace、来源 Metadata、Embedding Model 与 Index Version。 检索结果是不可信证据:保留归因,Ranking 前应用租户过滤,并把 Recall 与最终回答质量 分别评估。
迁移与恢复
改变 Store 前:
- 指明迁移哪个契约与数据集;
- 保存稳定 ID 与租户范围;
- 定义 Dual Read 或 Cutover 行为;
- 测试中断迁移与回滚;
- 验证旧 Worker 无法写入不兼容状态;
- 保留不确定 Effect 或 Terminal Task 所需的审计证据。
Backup 证明字节可以恢复;Recovery Test 证明恢复后的状态仍满足 Runifold 的所有权与幂等 不变量。
选择并运行 Backend
| 场景 | 起点 | 何时迁移 |
|---|---|---|
| 单元测试 | In-memory Store | 不能用于任何持久性声明 |
| 单服务实例或桌面应用 | SQLite | 多个活跃 Writer 需要 Lease / Fairness |
| 分布式 Workflow Worker | PostgreSQL | 这是预期的分布式边界 |
| 小型不可变语义语料 | In-memory Vector Index | 语料、持久性或共享需求增长 |
| 已使用 Postgres 的检索 | pgvector | 只有负载要求隔离时迁移 |
| 专用向量负载 | Qdrant | 根据实际运维数据选择 |
SQLite 数据库应位于持久本地存储,数据库与 WAL 必须一致备份,并测试强制终止后的 重新打开。PostgreSQL 连接池应小于数据库连接上限,Connection Acquisition 必须有 Deadline,Lease 使用 Store Authoritative Time,并在部署依赖新 Schema 的 Worker 前先完成 Schema 变更。
存储故障排查
| 现象 | 常见原因 | 检查项 |
|---|---|---|
| Checkpoint Conflict | 旧 Revision 或竞争 Writer | 读取的 Revision 与 Owner Identity |
| SQLite Busy Timeout | 长事务或 Writer 过多 | 事务时长与进程拓扑 |
| Lease 丢失 | Heartbeat 延误或数据库阻塞 | 数据库时间、Pool Wait、Heartbeat 间隔 |
| 恢复后 Usage 变少 | Restore 逻辑不完整 | 持久 Usage 与 Budget 恢复 |
| 返回其他租户结果 | 缺失 Tenant Predicate | 查询前 Authorization 与条件绑定 |
| 检索维度错误 | 混用 Embedding Contract | 模型、维度、归一化与 Index 版本 |
不要记录连接字符串、Checkpoint Payload、Transcript 或预签名归档 URL。即使 Telemetry 已经脱敏,数据层仍保存应用正文。