防复发:把「删数据集带走站点自建数据」做成结构性门禁 + 常驻体检

用户放弃继续救那批旧图,要求的是「别再发生同类事件」。所以不靠记性,靠结构:

1) 归属铁律(写进 schema.sql 抬头 + README 新章节)
   判断标准只有一句:**这份记录能不能从备份重新导入?**
   能(places/visits/movements/weather/activities/tags/entity_raw)⇒ 可挂
   `REFERENCES datasets(id) ON DELETE CASCADE`;
   不能(place_images/place_notes/place_votes/topics)⇒ 只留 dataset_id 当出处、**不许挂外键**。

2) 两道门禁(dataset_ownership_test.go),都做了变异验证:
   - 运行时:查 pg_constraint,凡挂 datasets 的表必须在白名单里;**反向也查**
     (白名单里的表不许被顺手摘掉,否则删数据集会留成片孤儿行)。
   - 静态:扫 schema.sql 的 CREATE TABLE。只看运行时有盲点——新建的库走 CREATE TABLE,
     开发库可能压根没跑过那次迁移。变异(把 topics 的 CASCADE 补回去)当场红。
   - 变异验证顺带发现:扫描必须先跳过 `--` 注释行,规矩的说明文字里就写着
     `REFERENCES datasets(id)`,不跳过会把自己写的文档当成违规。
   - 还发现运行时那道单独看**不够**:夹具每次跑迁移,迁移自己就把坏外键摘了,
     它永远不会在不健康的库上报警 ⇒ 补了「注入事故原样的外键再跑迁移,断言被摘掉」
     的自愈用例(并先确认变异真的生效,防假绿)。

3) 常驻体检:后台首页显示「库里 N 张 / 本地盘上 M 个 / 落单 K 个」,落单 >0 直接报警。
   事故的本质不是「删数据集」本身,而是**行与对象悄悄对不上、没人发现**,等照片看不见了才追。

自愈用例又抓出一个此前漏掉的缺口:place_notes / place_votes 上的同类外键迁移并不会摘
(原先只写了 place_images 与 topics 两句 DROP)⇒ 补成五张自建数据表全覆盖。

⚠️ 排序坑(本项目第二次踩):摘外键那段最初放在 place_images 建表**之前**,
开发库毫无症状、**全新库起不来**(ALTER 报「关系不存在」),被
TestFreshDatabaseServesAppQueries 抓住。整段已挪到 place_images 之后,并把坑写进注释。

顺带订正「删除数据集」的二次确认文案(专题改完之后它还在说专题会一起删)。
全量验收 PASS=336 FAIL=0 SKIP=0。
This commit is contained in:
tamakyi
2026-10-01 01:15:04 +08:00
parent 6b665b6056
commit 4c3032cf48
10 changed files with 415 additions and 6 deletions
+20
View File
@@ -152,6 +152,26 @@ go build -o rond .
- 换过的底座记档在 `datasets.baseline_name/baseline_sha256`:底座文件丢了会**按记档重建**,
而不是退回最初那份旧包(否则上一次换基线会静默失效)。
## 数据归属(删掉一份数据集会带走什么)
一条铁律:**从备份导入的数据随数据集删除;站点自建的数据不随数据集删除。**
| 会随数据集删掉 | 不随数据集删掉 |
|---|---|
| 到访、地点、位移、天气、活动、标签、原始行留档<br>(重新上传同一份备份就能再导回来) | 备注、结论、地点图片、专题 |
判断标准很简单:**这份记录能不能从备份重新导入?** 能 ⇒ 挂 `dataset_id … ON DELETE CASCADE`;
不能 ⇒ 只留 `dataset_id` 当出处、**不许挂外键**。图片那次事故就是反过来的:行被级联删了、
文件却留在盘上,而读取是按坐标键跨数据集的——两套口径打架,删数据集就等于永久丢失。
这条规矩有两道门禁守着,改坏当场红(不用靠记性):
- 运行时:查 `pg_constraint`,凡是挂 `datasets` 的表必须在白名单里(`TestOnlyBackupDerivedTablesCascadeFromDatasets`)
- 静态:直接扫 `schema.sql` 的 `CREATE TABLE`(`TestSchemaDeclaresNoDatasetFKForUserData`)——
只看运行时有个盲点:新建的库走的是 CREATE TABLE,开发库可能压根没跑过那次迁移。
另外**图片挂载体检**常驻后台首页(库里 N 张 / 本地盘上 M 个 / 落单 K 个):
行与对象对不上时会直接报警,不必等到照片看不见了才追。
## 地点图片
- **字节在浏览器里转成 WebP** 再上传,服务端只做形状校验、不重新编码(1 核机器扛不住解码大图)。