存储维护与保留
存储维护与磁盘控制¶
session.maintenance 控制 SQLite 会话行、SQLite 转录行、归档产物和轨迹伴生文件的自动维护:
| 键 | 默认值 | 备注 |
|---|---|---|
mode |
"enforce" |
或 "warn"(报告时长、计数和磁盘预算策略,但不应用这些策略) |
pruneAfter |
"30d" |
过期条目的年龄截止值 |
archiveDashboardAfter |
"7d" |
仪表盘归档截止值;false 或 0 仅禁用此触发器 |
maxEntries |
5000 |
保护允许时未归档会话行的上限 |
preserveRecent |
disabled | 非活动时间窗口,保护交互式会话及其历史世代;false 禁用 |
resetArchiveRetention |
keep(无年龄截止) | *.reset.*/*.deleted.* 转录归档的年龄截止值;设置为时长表示选择删除 |
maxDiskBytes |
10gb |
每个 agent 的会话磁盘预算;false、0 或 "0" 禁用 |
highWaterBytes |
maxDiskBytes 的 80% |
清理后的目标值;解析为零的值使用默认值,负值无效 |
coldStorage.enabled |
false |
将有资格的非活动转录负载移至后台工作进程中的压缩 JSONL 文件 |
coldStorage.afterDays |
30 |
冷存储以天为单位的正整数非活动截止值 |
重置边界会开启一个新的历史窗口,而不会删除较早的转录行。当会话滚动推进当前的 sessionKey -> sessionId 映射时,之前的 SQLite 会话、转录、轨迹和搜索行也会保留;普通的条目和会话列表只显示当前映射。保留的重置历史受磁盘预算限制,而不是受 resetArchiveRetention 限制,后者仅用于给归档产物设置年龄上限。显式删除则不同:它在移除被删除会话行的同一写事务中,将压缩的转录归档存储并校验到 SQLite 中。当 zstd 可用时,它还会在报告成功之前发布、同步并读回派生出的 *.jsonl.deleted.<timestamp>.zst 文件。
如果删除提交后归档文件导出失败,其规范归档会保留在 SQLite 中以供重试。创建无关的新会话不会重试该导出,也不会因此失败。解决文件错误后重试删除操作,以发布保留的归档,即使被删除的会话已不存在。
归档会话会更改其可见性和保留元数据,同时将其转录行保留在 SQLite 中。将回收的历史转换为转录归档时,会将这些行替换为 SQLite session_transcript_archives 表中的压缩规范 blob,以及 sessions 目录中的派生 JSONL 文件。因此,压缩负载仍然占用数据库空间;该文件不是其唯一副本。不支持 zstd 的运行时会写入纯 JSONL 重置/删除归档。可选的冷转录存储将非活动转录负载完全移出 SQLite;pruneAfter 控制会话保留,resetArchiveRetention 控制重置/删除归档的删除行为。
maxDiskBytes 的强制执行使用物理字节:每个 agent 的 SQLite 主文件、其 -wal 文件以及 agent sessions 目录中计数的文件。它从不估算行 JSON 大小,也不会从该总数中减去逻辑行大小。这是一个清理预算,而不是保证的物理上限:受保护的历史和尚未可回收的数据库页面可能会使使用量保持在目标之上。
网关模型运行探测会话(键匹配 agent:*:explicit:model-run-<uuid>)获得单独的固定 24h 保留期。这种修剪基于压力触发:仅当达到会话条目维护/上限压力时才运行,并且只在全局过期条目清理/上限步骤之前运行。其他显式会话不使用此保留期。
当合并的物理使用量超过 maxDiskBytes 时,mode: "enforce" 首先回收可检查点的数据库空间,然后删除最旧的保留重置/删除归档。如果使用量仍高于 highWaterBytes,它会按 sessions.updated_at 遍历历史 SQLite 会话,最旧优先。历史意味着会话 ID 不被活跃会话条目、路由目标或已接纳/进行中的运行所引用。对于每个被清理的会话,清理会在移除会话行及其转录、轨迹、active、index 和 FTS 投影的同一写事务中存储压缩归档。提交后会发布、同步并读回派生文件。这包括包含轨迹事件但不包含转录事件的会话。如果这些层级仍不足,清理会永久删除记录归档原因为 active-session-cap 的最旧会话。手动、遗留、年龄保留、过期仪表盘和恢复归档保护每一个历史世代。清理会在删除时重新检查条目身份和接纳引用,在每个被清理的会话后重新测量物理使用量,并在达到 highWaterBytes 时停止。
已提交的写入和删除首先进入 WAL。清理会尝试截断它,而不等待读取者。如果截断被阻塞,在 PASSIVE 检查点将每个已观察到的 WAL 帧复制到主数据库后,清理可以继续;部分检查点仍会推迟清理。增量 vacuum 会从主文件中回收符合条件的空闲尾部页面。保留的 WAL 字节和尚未可回收的页面在下次物理测量中仍会被计入。mode: "warn" 报告当前物理超量,而不进行检查点、写入归档或删除行。
当需要在大规模清理后完整重写文件时,请使用 Doctor 的离线
--session-sqlite compact 模式。
它会对 WAL 执行 checkpoint 并运行 VACUUM;不会选择额外历史进行删除,也不会将会话内容替换为摘要。在更改大型安装的保留策略之前,请先参阅在副本上测试清理。
按需运行维护:
maxEntries 统计未归档的会话行;已归档行不占用该上限。清理会归档最旧的符合条件的普通会话,直到未归档总数达到 maxEntries 或不再有符合条件的可清理对象。固定会话、活动或已准入的工作、模型锁定会话,以及持久的外部会话指针(如群组会话和线程作用域的聊天会话)仍受保护,因此受保护的行可能使未归档总数高于上限。合成运行时条目(cron、hooks、heartbeat、ACP、子代理)仍可丢弃,一旦超过配置的年龄、数量或磁盘预算即可被移除。隔离的 cron 运行使用独立的 cron.sessionRetention 控制,独立于模型运行探针保留。
每次新的归档都会自动记录一个结构化原因。显式归档操作记录 manual;count-cap 和 stale-dashboard 维护会记录各自对应的原因;pruneAfter 会以 age-retention 归档符合条件的持久会话,同时删除可丢弃的自动化条目;恢复归档记录 restart-recovery。Control UI 会以人类可读的形式呈现说明。缺失或无法识别的原因会被视为受保护的遗留状态,而不会被推断。
--dry-run 会预览未归档行上限,并标识出可以满足该上限的未受保护行;--enforce 会立即执行该清理,但不会移除保护。要减少受保护历史,请取消归档、取消固定、等待活动工作完成,或显式删除你不再需要保留的会话。
正常的 Gateway 写入会经由会话访问器,该访问器通过运行时写入路径序列化每个代理的 SQLite 变更。运行时代码应优先使用 src/config/sessions/session-accessor.ts 中的访问器辅助函数;遗留的 sessions.json 辅助函数是迁移和离线维护工具。当 Gateway 可达时,非 dry-run 的 openclaw sessions cleanup 和 openclaw agents delete 会将存储变更委托给 Gateway,从而使清理加入同一写入队列;--store <path> 是所选遗留存储的显式离线修复路径,并且始终在本地执行(--dry-run 也是如此)。maxEntries 清理针对生产规模的存储进行批量处理,因此在下次高水位清理将其降到上限以下之前,未归档数量可能会短暂超过配置的上限。读取操作在 Gateway 启动期间绝不会清理或限制条目。普通条目写入会在下一个年龄边界触发一轮合并式后台遍历,并且在该数据库连接保持打开期间每 30 分钟定期复查一次,因此即使没有后续写入流量,保留清理也能运行。当年龄/数量事实未发生变化时,写入可跳过维护候选扫描。openclaw sessions cleanup --enforce 会立即应用上限,即使没有配置磁盘预算,也会清理旧的、未被引用的遗留转录、检查点和轨迹工件。
后台计划在写入事务之外准备,然后在提交之前重新检查所选行、转录版本和会话保护。无关写入不会取消该计划。冲突的输入最多会获得三次即时计划尝试,之后维护将暂停,直到发生后续写入;最终确定拒绝后,工作线程连接仍会保留。保留策略和存储的数据格式保持不变。
OpenClaw 在 Gateway 写入期间不再自动创建 sessions.json.bak.* 轮转备份。当前架构会拒绝遗留的 session.maintenance.rotateBytes 配置键,openclaw doctor --fix 会将其从旧版配置中移除。
迁移恢复原始文件和 Doctor 之前的确切恢复文件独立于普通会话保留:它们不计入实时会话磁盘预算,也没有任何自动过期机制。验证升级后,使用 openclaw update cleanup --dry-run 在线检查它们。显式的离线更新清理可以移除已验证的原始文件,而不会删除当前的 SQLite 历史;从磁盘预算中排除并不等于获得删除授权。
转录变更会经由会话访问器和 SQLite 写入队列。每个变更都会在其提交事务中验证活动运行的持久写入声明,因此被取代的运行无法写入转录。
转录冷存储¶
启用冷存储可将较旧的转录负载保存在压缩的 .jsonl.zst 文件中,同时在 SQLite 中保留其会话标识:
一旦当前窗口和历史转录窗口的活动时间超过 afterDays,它们都可以符合条件。逻辑会话上的近期活动或运行状态会保护其当前窗口;历史窗口使用各自的活动和运行状态。恢复所有权、实际运行准入以及显式历史引用(如检查点)会继续保护所需的窗口。固定或归档会话不被视为持续活动,单凭这一操作不会将其负载保留在 SQLite 中。Gateway 会在启动时以及每分钟检查一次,以 128 条转录和 64 MiB 的工作预算运行后台批次,无需新消息即可执行。这些设置的更改无需重启 Gateway 即可应用于未来的工作;关闭此功能不会丢弃或搁置现有的冷历史。
coldStorage.enabled 是一项独立的选择启用策略。即使 session.maintenance.mode 为 warn,它也会运行;该模式控制年龄、数量和磁盘预算清理。禁用 coldStorage.enabled 可停止未来的提取。
打开聊天、请求历史记录以及频道写入会在使用前异步恢复冷历史。批量 sessions.preview 请求将载荷保持归档状态,并返回显式的 cold 状态,因此菜单预热无法重新填充数据库。会话列表同样将冷载荷保持归档状态。当转录标题字段尚未缓存时,列表使用会话元数据,并在历史恢复之前省略最后一条消息的预览。
低层同步转录 API 在转录处于冷状态时改为返回“需要恢复”的错误;其调用方必须首先等待异步恢复。存储和使用清单可以在不恢复转录的情况下统计冷转录。文本搜索排除冷转录内容,并报告排除了多少归档转录;恢复后的转录将重新可搜索。导入和跨存储修复会拒绝尚未恢复的冷转录,而不是复制不完整的历史。
恢复后,运行中的 Gateway 会在 24 小时内保持该转录为热状态,以避免重复提取最近查看的历史。此冷却时间仅对当前进程有效,并在进程重启时重置。未压缩 JSONL(包括恢复元数据)超过 64 MiB 的转录保留在 SQLite 中;该上限限制了恢复内存和 worker 时间。
worker 在受保护的事务记录其位置并移除相应转录行之前,会写入、同步并验证不可变归档。归档保留原始序列化事件及其恢复元数据。失败或中断的准备工作会使 SQLite 中的转录保持完整。在事务未提交前写入的文件可能会作为未被引用的归档保留。
冷归档位于 agent 的会话工件目录下的 cold/ 中。它们是权威历史,而非一次性缓存。重置/删除归档保留策略和常规磁盘预算清理不会删除它们。转录恢复后文件仍然保留,因此进行中的备份仍可捕获其原始快照。因此,冷存储会减小工作数据库的体积;但并不保证每次轮次总磁盘使用量都会缩减。后台维护会对 SQLite 执行 checkpoint,并在有界 worker 轮次中回收空闲页面,包括后续没有新归档候选的轮次。因此物理数据库大小会逐渐缩小;读取方可以延迟回收。当需要完全重写时,请使用 Doctor 的离线 compact 操作。
如果归档缺失或其记录的尺寸或哈希不匹配,读取或恢复该转录将显式失败。OpenClaw 不会以空转录替代。请从备份中恢复匹配的文件,或恢复完整的受支持数据库备份;校验和无法重建已删除的字节。在启用提取之前,请保留独立备份。
即使归档缺失,不需要转录内容的更新也可以成功。它会保留冷引用;更新软件包不会恢复缺失的历史。
用于完整归档、SQLite 快照和 Git 备份的受支持备份命令会将已验证的冷载荷嵌入其私有数据库副本中。它们恢复后的数据库是自包含的,不需要原始归档目录。启用冷存储后,后台维护会将这些嵌入的压缩载荷移回已验证的归档文件,从而允许回收其数据库空间。这不会将转录解包为事件行。设置报告会将这些移动与新归档的转录分开报告。直接数据库复制同样需要 cold/ 文件;请参阅备份冷转录。
冷存储使用 agent schema 20。请使用受支持的更新路径和升级前已验证的备份。旧版本构建不得在冷载荷已移出其事件表后打开数据库,因此降低 schema 标记并非降级流程。
SQLite 切换后的降级¶
停止 Gateway 并备份其状态。使用当前支持 SQLite 的 OpenClaw 版本,在启动旧版文件后端版本之前,恢复已归档的旧版会话存储和转录工件:
迁移过程将导入的热转录 JSONL 文件和已验证、完全覆盖的旧版 sessions.json 存储归档在 session-sqlite-import-archive/ 中。覆盖不完整或存在阻塞性迁移问题的旧版存储保持原位。旧版文件后端运行时在启动前需要 sessions.json 及其 sessionFile 路径引用的工件都位于原始位置。
恢复使用迁移清单,仅移动原始路径缺失的已记录归档工件,报告冲突而非覆盖现有文件,并保留 SQLite 数据库以供后续向前恢复。
由 openclaw update cleanup 退役的原始文件无法再从迁移归档中恢复。恢复操作会报告有意处置或待清理状态,而不是将两者视为意外缺失的文件。如果在处置后仍需要这些旧版工件,则必须拥有包含它们的独立备份;请参阅更新前备份。
恢复不会导出迁移后仅在 SQLite 中进行的更改。SQLite 切换后创建的会话仅存在于 SQLite 中,不会出现在旧版文件后端运行时中。如果在降级后重新升级,请再次运行 Doctor 检查和验证流程,以便 OpenClaw 在导入前验证恢复的旧版工件。
Cron 会话与运行日志¶
隔离的 cron 运行会创建自己的会话条目/转录,并具有专门的保留策略:
cron.sessionRetention(默认"24h")会从存储中修剪旧的隔离 cron 运行会话;false或零时长(如"0h")可禁用。- 终态运行历史保留 7 天(
lost行保留 24 小时),每个作业和历史类别额外强制执行最近 2000 行的上限。
当 cron 强制创建新的隔离运行会话时,它在写入新行之前会清理之前的 cron:<jobId> 会话条目:它会保留安全偏好设置(thinking/fast/verbose/reasoning 设置、标签、显示名称)以及用户显式选择的模型/认证覆盖,但会丢弃环境会话上下文(频道/群组路由、发送/队列策略、提权、来源、ACP 运行时绑定),以确保新的隔离运行不会从旧运行中继承过期的投递或运行时权限。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw