回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复: - 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录) - tmp/(32.4 M,按接续棒命名的过程临时区) - .workbuddy/tmp/(39.5 M) - 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物) - tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留 入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与 接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、 .workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。 排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、 打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
6.7 KiB
建库口接线(B 档)· 落地件 — 2026-09-23
归口:插件投放与分库线 · 第 4 棒 · 执行棒(automation
6e8cedcd)。 前置件:交付物/数据面管理面-建库与更新流程-20260922.md §八(设计件)+交付物/插件建库权限-B档落地-20260923.md(B 档权限与契约)。 状态:✅ 代码面已落地 + 47 真机取证通过 + 服务器零残留。
一 一句话结论
设计件 §八 里最后一个没做的口(建库执行口 datastore.ts)已接线:CREATE DATABASE 真调用、
currentSchemaOf 去占位真读 information_schema、台账+审计落点、migrate 去 503、
台账 × pg_database 对账告警。B 档"三点兜底"全部由代码兑现。
二 落地清单(逐条对 §5 本轮动作)
| # | 要求 | 落点 | 状态 |
|---|---|---|---|
| 1 | CREATE DATABASE 真调用(单语句、⛔ 不走事务) |
datastore.ts:createPluginDatabase(pool.query 隐式自动提交,⛔ 不包 BEGIN) |
✅ |
| 2 | currentSchemaOf 去占位 |
datastore.ts:readCurrentSchema(真读 information_schema.columns + pg_indexes + p_meta_schema);路由层 currentSchemaOf 改调它 |
✅ |
| 3 | 台账 + 审计落点 | 建库成功 state='created';失败 state='error' + last_error;两步都 recordPluginDataAudit |
✅ |
| 4 | migrate 去 503 |
datastore.ts:executePlan(建库项非事务/表级 DDL 同一事务)+ 路由真执行 |
✅ |
| 5 | 台账 × pg_database 对账 |
datastore.ts:reconcilePluginDatabases + GET /api/plugins/business/datastores/reconcile |
✅ |
关键实现点(三条最容易写错的)
- 建库的语句姿势 ——
CREATE DATABASE走pool.query()(implicit autocommit), ⛔ 绝不withTx。这是 D 档被证伪的同一条内核限制(cannot be executed from a function/ 不能进事务块),B 档只是不再需要函数封装。 - 库名的两重防线 —— ①
pluginDbNameOf生成(⛔ 不收请求体里的库名); ②assertPluginDbName在拼 SQL 之前复校,再加quoteIdent("翻倍)标准引用。 单测有一条注入形态(x'; DROP DATABASE dshs; --)专门钉这条。 plan_hash的COALESCE语义 —— 建库 / 迁移路径一律不传planHash(omit ⇒ 保留旧值), ⛔ 传null会把预演写下的指纹清掉 ⇒ 等于拆掉 TOCTOU 防护。真机取证已断言保留成立。
已知缺口(⛔ 不假装做了)
| 缺口 | 原因 | 何时补 |
|---|---|---|
迁移前结构备份(设计件 §七-3 pg_dump --schema-only) |
属运维面(pg_dump + /opt/dsh/backups/plugin-db/),与 S5-a 同批 |
归入「S5-a 兼容矩阵 / 运维件」棒 |
| 47 控制面停在迁移 v13 | v14/v15(含两张台账表)要平台启动时由 runPgMigrations 落;本棒未部署 47 |
部署本棒代码时随 restart dshs 自动补 |
风险等价性说明:缺口下的执行失败靠事务回滚兜(表级 DDL 全在同一事务,失败即整体回退),
且「只增不减」已由 forbidden 把死 ⇒ 破坏面 = 只增结构(可人工 DROP)。
三 47 真机取证(用真实编译产物,非手写 SQL)
机位:47(/opt/dshs · PG 13.23 127.0.0.1:15432)。探针 tmp/evid47/probe2.mjs。
created : "exists" ← 第二次跑(首次已建有库);首跑读数 = "created",71 ms
idempotent : "exists" ← 重复建库幂等,⛔ 不报错
badNameRejected : "invalid_plugin_db_name" ← evil_probe4 被平台侧拦下,⛔ 未下发 PG
currentRead : { exists: true, schemaVersion: 7,
tables: ["p_bselftest4_probe:1", "p_meta_schema:2"] } ← 去占位:真表真列真版本
ledgerRow : { db_name: "dshs_pl_bselftest4", state: "ready", schema_version: 7 }
planHashPreserved : true ← COALESCE 语义成立(第二次 upsert 未传 planHash 仍保留)
auditRows : 1 ← 审计流水落了
reconcile.orphans : ["dshs_pl_borphan4"] ← 🔴 绕过平台建的库**被抓出**
reconcile.missing : []
cleaned : { pgPluginDbsLeft: 0, controlTablesLeft: 13, expectedControlTables: 13 }
残留核查(独立于探针,清理后另跑一次):
pluginDbs: [] | 控制面表 = 原始 13 张(plugin_datastores / plugin_data_audit 均已 DROP)
| systemctl is-active dshs = active | /tmp/evid4 已删。
⚠️ 取证姿势的一条实测:探针第一次跑失败了(在台账步骤,因 47 无 v15 表), 而那次失败跳过了清理 ⇒ 测试库
dshs_pl_bselftest4留在了 PG 上。 第二次跑把清理放进catch兜底后重跑,才回到零残留。 教训:真机取证的清理必须写在catch里(失败路径也会留东西),⛔ 只在成功路径清。
四 验证(本机三闸)
| 闸 | 判据 | 实测 |
|---|---|---|
| 构建 | npm run build rc=0 |
rc=0 |
| 回归 | npm test(Node 22)0 败 |
426 tests / 424 过 / 0 败 / 2 跳过,rc=0(基线 391 过 ⇒ 本棒 +35) |
| 分层 | node scripts/check-layering.mjs 无新增违规 |
✅ 无新增违规(added: 0) |
npm run verify 未跑(按本线惯例它必红且与本线无关)。
五 改了哪些文件(本棒)
| 文件 | 改动 |
|---|---|
src/db/plugin-data/datastore.ts(新 · 479 行) |
建库口 / 现状真读 / 计划执行 / 对账 / 错误码映射 / 连接管理 |
src/web/routes/business-plugins.ts |
currentSchemaOf 去占位;新增 POST /datastore(建库)+ GET /datastores/reconcile;/datastore/plan 的 executable 改为真判据;/datastore/migrate 去 503 真执行;控制面连接池(懒创建 + onClose) |
web/portal.html |
「建库」按钮改调新路由(原为"尚未接线"禁态);createDatastore();「确认执行」禁用文案改由服务端原因驱动 |
src/db/plugin-data/diff.ts |
仅注释:去掉"建库口尚未接线"的过时说明 |
test/plugin-data.test.mjs |
+35 条:引用转义 / pluginDbUrlOf / 错误码分类 / 台账表 DDL / 对账集合逻辑 |
六 下一棒(仍未做,⛔ 本棒不预登记队列)
- 三份文档回写 —— 交接单 §五 S4-2 / §十-D1、
DB-03 §三/§四/§六、DEPLOY-本部署.md(追加ALTER ROLE dshs CREATEDB;)。共享文件,须持锁成批做。 - S3 跨机装配 · S5-a 兼容矩阵 · S6 门户三段式 UI(执行棒①遗留)。
- 跨节点内容分发。
- 迁移前结构备份(本棒记的已知缺口,见 §二)。