Files
dsh_ai1net_server/交付物/建库口接线-B档落地-20260923.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 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)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

6.7 KiB
Raw Blame History

建库口接线(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 ✅

关键实现点(三条最容易写错的)

  1. 建库的语句姿势 —— CREATE DATABASE 走 pool.query()(implicit autocommit), ⛔ 绝不 withTx。这是 D 档被证伪的同一条内核限制(cannot be executed from a function / 不能进事务块),B 档只是不再需要函数封装。
  2. 库名的两重防线 —— ① pluginDbNameOf 生成(⛔ 不收请求体里的库名); ② assertPluginDbName 在拼 SQL 之前复校,再加 quoteIdent(" 翻倍)标准引用。 单测有一条注入形态(x'; DROP DATABASE dshs; --)专门钉这条。
  3. 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 / 对账集合逻辑

六 下一棒(仍未做,⛔ 本棒不预登记队列)

  1. 三份文档回写 —— 交接单 §五 S4-2 / §十-D1、DB-03 §三/§四/§六、DEPLOY-本部署.md (追加 ALTER ROLE dshs CREATEDB;)。共享文件,须持锁成批做。
  2. S3 跨机装配 · S5-a 兼容矩阵 · S6 门户三段式 UI(执行棒①遗留)。
  3. 跨节点内容分发。
  4. 迁移前结构备份(本棒记的已知缺口,见 §二)。