Skip to main content

lellm_graph/
checkpoint.rs

1//! Checkpoint — 执行恢复的唯一数据源。
2//!
3//! 分层架构:
4//! ```text
5//! Checkpoint<S>           ← Workflow 层,强类型,纯 Snapshot 模型
6//!        │
7//!        ▼ serialize/deserialize
8//! CheckpointCodec<S>      ← 序列化层,对象 ↔ 二进制表示
9//!        │
10//!        ▼
11//! CheckpointBlob           ← 跨 Codec 的统一载体
12//!        │
13//!        ▼ save/load
14//! BlobCheckpointStore      ← 存储层 SPI,bytes in / bytes out
15//!        │
16//!        ▼
17//! Memory / File / S3 / SQLite  ← 后端实现
18//! ```
19
20use serde::{Deserialize, Serialize};
21
22use crate::state::State;
23use crate::workflow_state::WorkflowState;
24
25// ─── CheckpointId ──────────────────────────────────────────────
26
27/// Checkpoint 唯一标识。
28#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub struct CheckpointId(pub uuid::Uuid);
30
31impl std::fmt::Display for CheckpointId {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{}", self.0)
34    }
35}
36
37// ─── NodeId ────────────────────────────────────────────────────
38
39/// 节点标识。
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct NodeId(pub String);
42
43impl std::fmt::Display for NodeId {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49// ─── Checkpoint ────────────────────────────────────────────────
50
51/// 执行检查点 — 物化快照 + 执行游标。
52///
53/// Checkpoint 的唯一职责:恢复(Restore)。
54/// 给我一个 Checkpoint,我就能从 `current_node` 开始,用 `state` 继续执行。
55///
56/// # Graph Compatibility
57///
58/// `graph_hash` 记录创建 Checkpoint 时的图结构指纹。
59/// 恢复时必须校验:`graph_hash` 不匹配 → 拒绝恢复(不允许 silent mismatch)。
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct Checkpoint<S = State> {
62    /// 唯一标识
63    pub checkpoint_id: CheckpointId,
64    /// 下一个要执行的节点
65    pub current_node: NodeId,
66    /// 物化状态快照
67    pub state: S,
68    /// 图结构指纹 — 恢复时校验兼容性
69    pub graph_hash: u64,
70    /// 创建时间
71    pub created_at: std::time::SystemTime,
72}
73
74impl<S: WorkflowState> Checkpoint<S> {
75    pub fn new(current_node: impl Into<String>, state: S, graph_hash: u64) -> Self {
76        Self {
77            checkpoint_id: CheckpointId(uuid::Uuid::new_v4()),
78            current_node: NodeId(current_node.into()),
79            state,
80            graph_hash,
81            created_at: std::time::SystemTime::now(),
82        }
83    }
84}
85
86// ─── CheckpointBlob ────────────────────────────────────────────
87
88/// 跨 Codec 的统一载体 — 存储层操作的对象。
89///
90/// 将序列化后的二进制数据与元数据打包,供 CheckpointStore 使用。
91/// 存储层无需知道 State 类型或序列化格式。
92///
93/// `graph_hash` 作为 correctness invariant 存储:
94/// 恢复时校验 `graph_hash` 不匹配 → reject,不允许 silent mismatch。
95#[derive(Debug, Clone)]
96pub struct CheckpointBlob {
97    /// Checkpoint 唯一标识
98    pub id: CheckpointId,
99    /// 序列化后的二进制数据(格式由 Codec 决定)
100    pub data: Vec<u8>,
101    /// 图结构指纹 — 恢复时校验兼容性
102    pub graph_hash: u64,
103    /// 创建时间
104    pub created_at: std::time::SystemTime,
105}
106
107impl CheckpointBlob {
108    pub fn new(
109        id: CheckpointId,
110        data: Vec<u8>,
111        graph_hash: u64,
112        created_at: std::time::SystemTime,
113    ) -> Self {
114        Self {
115            id,
116            data,
117            graph_hash,
118            created_at,
119        }
120    }
121}
122
123// ─── CheckpointStoreError ──────────────────────────────────────
124
125/// Checkpoint 存储操作错误。
126#[derive(Debug, thiserror::Error)]
127pub enum CheckpointStoreError {
128    #[error("storage error: {0}")]
129    Storage(String),
130    #[error("checkpoint not found: {0}")]
131    NotFound(CheckpointId),
132    #[error("corrupted checkpoint: {0}")]
133    Corrupted(String),
134    #[error("serialization error: {0}")]
135    Serialization(String),
136    #[error("graph mismatch: expected hash {expected:#018x}, got {actual:#018x}")]
137    GraphMismatch { expected: u64, actual: u64 },
138}
139
140// ─── TraceId Re-export ─────────────────────────────────────────
141
142/// 从 ids 模块重导出 TraceId。
143///
144/// 注意:Checkpoint 结构体**不包含** trace_id。
145/// 关联关系由存储层组织(如同一目录下的文件)。
146pub use crate::ids::TraceId;
147
148// ─── CheckpointPolicy 已迁移 ──────────────────────────────────
149
150/// 向后兼容 — CheckpointPolicy 已迁移至 checkpoint_policy 模块。
151/// v0.5 使用 TriggerPolicy + RetentionPolicy 替代。
152#[allow(deprecated)]
153#[doc(inline)]
154pub use crate::checkpoint_policy::CheckpointPolicy;