Skip to content

第13篇:状态管理 · 第7节 持久化策略 — 什么该留下、什么该随风

并非所有 AppState 都值得写入磁盘。持久化策略在隐私、性能与可恢复性之间划界:~/.claude/ 是默认大本营,但子目录职责各不相同。


学习目标

能力项说明
分类列举应持久化 / 会话级 / 纯内存三类状态
位置默认识别 ~/.claude/ 下各子路径职责
粒度决定同步写、debounce、退出时 flush
清理缓存与日志轮转策略
跨机哪些目录可安全同步(Syncthing、dotfiles 仓库)

生活类比:行李箱与酒店房卡

托运大箱 = 持久化配置与长期记忆(回家还要用)。随身小包 = 会话内临时状态(退房即弃)。酒店房卡 = 纯内存 token(丢卡可补办,不应复印一份贴墙上)。错误地把房卡复印件存进托运箱,等于把短期凭证长期化——安全风险。持久化策略就是:什么进托运、什么只揣兜里


~/.claude/ 布局总表

路径内容类型典型持久化备注
settings.json用户配置经 migrations
mem/**Memdir 偏好与笔记用户可读
projects/**/sessions/**History可体积大
cache/**嵌入、索引可丢可重建
logs/**诊断日志轮转敏感需脱敏
plugins/**扩展视安装方式版本化
.credentialsOAuth token是(加密最佳)权限严格

AppState slice 持久化矩阵

Slice持久化理由
config用户显式选择应跨会话保留
session部分sessionId/cwd 写入 meta;瞬时指针不必
tools.active进程内队列;续接时重建
tools.registryVersion可写入 meta续接一致性
ui.theme用户偏好
ui.modalStack会话 UI 瞬态
tools.lastError提示后即消

策略代码示意

typescript
// persistence/policy.ts — 教学示意
export const PERSIST_DEBOUNCE_MS = 400;

export function shouldPersistConfig(prev: AppState, next: AppState): boolean {
  return prev.config !== next.config;
}

export function shouldTouchSessionMeta(prev: AppState, next: AppState): boolean {
  return (
    prev.session.sessionId !== next.session.sessionId ||
    prev.session.cwd !== next.session.cwd
  );
}

export function ephemeralSlices(): (keyof AppState)[] {
  return []; // 不持久化整 slice;仅持久化投影
}

Mermaid:数据落盘决策树

图2:写入时序


丢弃策略

数据丢弃时机实现
内存 tool 队列进程退出自然 GC
cache磁盘配额 / TTLLRU
旧 session用户 prune 命令按日期归档
实验性 UI 状态每次启动不 hydrate

环境变量与覆盖(教学名)

变量作用
CLAUDE_CONFIG_DIR替换 ~/.claude
XDG_CONFIG_HOME与 Linux FHS 对齐时的基路径
CLAUDE_NO_PERSIST干跑模式,不写盘(测试)

安全基线

typescript
export async function ensurePrivateDir(dir: string) {
  await fs.mkdir(dir, { recursive: true, mode: 0o700 });
}
建议
settingschmod 600
token 文件避免 world-readable
日志默认不含 API key;过滤 env

与 History / Memdir 的协同

模块持久化角色
History大块:transcript + checkpoint
Memdir中块:偏好与笔记
settings小块:结构化配置
冲突Memdir 与 settings 勿重复存同一真相源

表:同步到多台机器

目录适合 dotfiles风险
settings.json模型默认值可能不同机不同
mem/冲突需人工合并
cache/体积与机器相关
sessions/低-中可同步但体积大

小结

持久化策略回答三个问题:要不要存、存哪、何时写configui 偏好进 settings/Memdir;会话真相进 projects sessions;工具运行时队列留在内存;缓存随时可牺牲。统一根目录 ~/.claude/ 降低支持成本。


自测

  1. 为何 transcript 适合 append,而 settings 适合全量原子写?
  2. debounce 与「进程被 kill -9」如何折中(退出钩子)?
  3. CLAUDE_CONFIG_DIR 改变后 History 旧路径如何迁移或提示?

退出钩子与 fsync 策略

信号行为
SIGINT / SIGTERM清空 debounce 队列并同步 flush
uncaughtException尽力写 crash.json;避免复杂逻辑
kill -9无法捕获;依赖原子写与下次启动修复
typescript
export function registerExitFlush(flush: () => Promise<void>) {
  const run = () => {
    void flush().finally(() => process.exit(0));
  };
  process.on("SIGINT", run);
  process.on("SIGTERM", run);
}

生产环境可酌情对关键文件 fsync,在延迟崩溃丢失窗口之间取舍。


配额与轮转(日志 / cache)

类型建议上限动作
单会话 transcript例如 512MB强制 checkpoint + 截断
全局 cache例如 2GBLRU
诊断日志按日 + 总大小压缩归档

上一节06-migrations.md · 下一节08-overview.md

本项目仅用于教育学习目的。Claude Code 源码版权归 Anthropic, PBC 所有。