3.1 KiB
3.1 KiB
title, status, created, updated, severity
| title | status | created | updated | severity |
|---|---|---|---|---|
| 文档存储 CoW(写时复制)现状审计 | done | 2026-09-21 | 2026-09-21 | low |
文档存储 CoW(写时复制)现状审计
背景
用户诉求:上传文档要「几个人传同一份只存一份」,涉及可编辑文档时「考虑 CoW,写入时复制」。本审计确认系统当前的去重与写时复制实现状态,并明确本次收口补齐了什么。
现状分析
系统存在两套内容存储,均已采用内容寻址(Content-Addressed Storage, CAS),天然满足去重与 CoW 语义:
1. 二进制资产:internal/storage/cas.go
- 物理对象表
storage_blobs以sha256为主键,ref_count记录引用数。 - 上传命中相同 sha 时(cas.go
storeFileDB)不写第二份物理文件,仅ref_count + 1,并新建一条file_references逻辑引用。→ 满足「多人传同一文档只存一份」。 - 文件路径
blobs/<xx>/<yy>/<sha>.bin由内容 hash 唯一决定;StoreFile对已存在 blob 只读不覆写(命中去重分支直接返回)。内容变化 → hash 变化 → 新路径新文件。→ 即 CoW:修改等价于写入新对象,旧对象不被就地改写。
2. 可编辑文档正文:internal/docrevision/pg_store.go
document_content_blobs以content_sha256为键,ensureBlobTX使用INSERT ... ON CONFLICT DO NOTHING(pg_store.go:309)。- 每次编辑保存生成新
document_snapshots,指向新的content_sha256;未变化的正文块复用同一 blob 行。→ 编辑保存不会复制未改动内容,写入时才产生新 blob。→ 满足「可编辑文档 CoW」。
结论
- 去重:物理层早已实现(sha256 + ref_count);本次补齐的是逻辑提示——把
FileReference.IsDeduplicated透出到上传/列表 API 的deduplicated字段,并在前端附件卡片与文档预览面板展示「重复·已复用」,让"只存一份"对用户可见。跨项目/同项目的逻辑引用记录保留(权限与审计需要),不做合并(已与用户确认方案:物理一份 + 提示)。 - CoW:语义已是事实实现,但缺防回归守卫。本次新增
TestCAS_ImmutabilityCoWContract(cas_test.go)固化契约:① 相同内容重复写入 → 物理 blob 数不变、字节不变;② 内容变化写入 → 新 blob、旧 blob 不被覆写、历史引用仍读原始字节。未来任何"就地改写 blob"的实现都会在测试期失败。
严重度
low —— 现状正确,本次为可见性增强 + 契约固化,无缺陷修复。
关联整改
开放问题
storage_blobs.ref_count目前只增不减;引用删除(若未来支持资产删除)需配套 decrement 与 blob 物理回收,属后续独立任务,不在本次范围。