mf_persistence/
api.rs

1//! 持久化抽象层的公共 API。
2//!
3//! 本模块定义了持久化的可调一致性选项、事件/快照的持久化格式,
4//! 以及具体后端需要实现的 trait。默认实现为基于 SQLite 的 WAL,
5//! 但上层仅依赖这些 trait 即可与具体后端解耦。
6
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11/// 写入持久性与延迟之间的权衡。
12///
13/// - `MemoryOnly`:事件入内存队列即返回,最低延迟、不具持久性,仅适合演示/开发。
14/// - `AsyncDurable`:写入 WAL/系统缓存后返回,fsync 由后台分组处理;
15///   在桌面环境具有很好的实际持久性与极低延迟(默认)。
16/// - `SyncDurable`:提交前强制 fsync,延迟最高、持久性最强。
17#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
18pub enum CommitMode {
19    MemoryOnly,
20    AsyncDurable { group_window_ms: u32 },
21    SyncDurable,
22}
23
24/// 快照节奏与压缩等可调参数。
25#[derive(Clone, Debug, Serialize, Deserialize)]
26pub struct PersistOptions {
27    pub commit_mode: CommitMode,
28    pub snapshot_every_n_events: u32,
29    pub snapshot_every_bytes: u64,
30    pub snapshot_every_ms: u64,
31    pub compression: bool,
32}
33
34/// 事件存储的"仅追加"记录。
35///
36/// 不变式:
37/// - `lsn` 为后端分配的单调递增日志序号
38/// - `idempotency_key` 全局唯一,用于请求重试的幂等去重
39/// - `checksum` 对(压缩/加密后的)`payload` 计算得到
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct PersistedEvent {
42    pub lsn: i64,
43    pub tr_id: Uuid, // ✅ 改用 UUID 作为事务唯一标识
44    pub doc_id: String,
45    pub ts: i64,
46    pub actor: Option<String>,
47    pub idempotency_key: String,
48    pub payload: Vec<u8>,
49    pub meta: serde_json::Value,
50    pub checksum: u32,
51}
52
53/// 截止到 `upto_lsn` 的物化状态快照。
54#[derive(Clone, Debug, Serialize, Deserialize)]
55pub struct Snapshot {
56    pub doc_id: String,
57    pub upto_lsn: i64,
58    pub created_at: i64,
59    pub state_blob: Vec<u8>,
60    pub version: i32,
61}
62
63/// 具体后端必须实现的最小持久化接口。
64#[async_trait]
65pub trait EventStore: Send + Sync {
66    /// 追加单条事件。返回分配的 `lsn`。
67    async fn append(
68        &self,
69        ev: PersistedEvent,
70    ) -> anyhow::Result<i64>;
71    /// 在单事务内批量追加事件。返回最后一条的 `lsn`。
72    async fn append_batch(
73        &self,
74        evs: Vec<PersistedEvent>,
75    ) -> anyhow::Result<i64>;
76    /// 读取指定文档分片自 `from_lsn` 之后的增量事件,上限为 `limit` 条。
77    async fn load_since(
78        &self,
79        doc_id: &str,
80        from_lsn: i64,
81        limit: u32,
82    ) -> anyhow::Result<Vec<PersistedEvent>>;
83    /// 获取指定文档分片的最新快照(如果存在)。
84    async fn latest_snapshot(
85        &self,
86        doc_id: &str,
87    ) -> anyhow::Result<Option<Snapshot>>;
88    /// 写入/替换快照,应保证原子性。
89    async fn write_snapshot(
90        &self,
91        snap: Snapshot,
92    ) -> anyhow::Result<()>;
93    /// 可选压缩:删除已被最新快照覆盖的历史事件。
94    async fn compact(
95        &self,
96        doc_id: &str,
97    ) -> anyhow::Result<()>;
98}
99
100/// 用于启动引导的状态加载结果(快照 + 增量事件)。
101#[derive(Clone, Debug)]
102pub struct LoadedState {
103    pub upto_lsn: i64,
104    pub snapshot: Option<Snapshot>,
105    pub events: Vec<PersistedEvent>,
106}
107
108/// 高层持久化编排:事务持久化与快照检查点。
109#[async_trait]
110pub trait Persistence: Send + Sync {
111    async fn persist_transaction(
112        &self,
113        tr: &mf_state::Transaction,
114        doc_id: &str,
115        context_meta: &serde_json::Value,
116    ) -> anyhow::Result<i64>;
117
118    async fn load_state(
119        &self,
120        doc_id: &str,
121    ) -> anyhow::Result<LoadedState>;
122
123    async fn checkpoint_if_needed(
124        &self,
125        doc_id: &str,
126    ) -> anyhow::Result<()>;
127}