背景/目标:tier2 富游戏 BackendStore 真实现已写好却未通电,且 save 走 MinIO-first、半落库会留无 manifest 指向的隐形对象,manifest 表游离在治理外(in-code DDL + 留档 .sql 双源无机器门)。本切片按已拍板的 manifest-first 方案把 SRC 一面推到「接生产、schema 受治理、取回可重建、半落库孤儿有修法」。只碰富游戏这 一面,不动便宜档 Java 面、不做素材、不接 fetch 生产消费方。 改动(六件核心 + T1/T3/T4): - T2① status 列(pending/committed):新建走 CREATE DDL、存量走受治理 ALTER;BackendStore._ensure_schema 按 information_schema 探测缺列才补(每实例一次,跨 MySQL/MariaDB 稳)。 - T2② save 改写入序:MySQL 落 pending 行 → MinIO put → 翻 committed;命中 committed 幂等返回、命中 pending 复用 versionId 续跑自愈(同前缀真覆盖)。半落库崩溃只留可见 pending 行、无隐形对象。 - T2③ fetch 两条分支(取最新 / 取指定版本)都补 AND status='committed',pending 半成品对取回不可见。 - T2④ 治理归属:.sql 留 tier2/config/schema/(owner=tier2、不走 huijing Flyway、无执行副本), contracts/README.md 加治理指针认领、明说不参与 Flyway 对账;不塞 db-schemas/ 根。 - T2⑤ 可跑对账机器门 test_store_ddl_reconcile.py:规范化比对 in-code(_DDL+_ALTER)与 .sql,改一处 漏改另一处即红。 - T2⑥ 修 store.py 模块头 docstring 整块(占位/seam/绝不实连/forbidden-import 守着 的陈旧误述改为与真 实现一致,并纠正守卫范围:check-forbidden-import.sh 只禁 wg1.*/SAA/L2,不禁 pymysql/minio)。 - T1 接线单点:TIER2_STORE=backend 激活配方 + 前置 checklist(CREATE DATABASE / status 列状态确认)落 staging-ops §7(tier2 gen-worker 部署 env 单点);不改 default_store 工厂形态。 - T3 取回重建 harness store_roundtrip_harness.py:五条(确定性重建优先 bundle sha256 字节相等、非字节稳 才退九门 verdict 逐项一致+无 missingContent、不拿近乎恒真 buildInputHash 承重;版本寻址;幂等;缺源 显式失败;半落库两失败点),本地 LocalFsStore 自检覆盖 1-4、真库+bundle 字节相等标 follow-up 窗口。 - T4 孤儿清扫 store_cleanup_job.py + BackendStore.cleanup_orphans:乐观删(status=pending CAS 先删行 再删对象)防与续 put 的 TOCTOU 竞态,幂等,owner=tier2,可追溯日志。 契约(contract-first):.sql DDL additive 加 status 列 + 首份受治理 ALTER;contracts/README.md 治理指针; 落库寻址 doc↔code 断言点(表名/双唯一键/「pending 对 fetch 不可见」)。不改 tier2-source-project.schema.json addressing、不改 buildInputHash 字段、不改 GamePackage / 便宜档 source-project.schema。 验证:本地 14 个新单测真跑全绿(DDL 对账 3 + manifest-first 行为 8 + harness 本地 3);harness 脚本 LocalFsStore 自检 5/5;docs-gate 七检全绿;forbidden-import 守卫通过。真 MySQL+MinIO 往返 + bundle 字节 相等 = mini-infra 窗口 follow-up(worktree 无真库/无 esbuild,诚实未跑)。缺重依赖的既有测试(agentscope/ nacos)本极简 venv 未跑,均不 import store、与本改动无关。 计划锚点:docs/plans/2026-07-05-001-feat-asset-src-源工程存储-plan.md §4 T1-T4 / §5。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
4.9 KiB
SQL
51 lines
4.9 KiB
SQL
-- tier2/config/schema/tier2_source_project_version.sql
|
|
-- ════════════════════════════════════════════════════════════════════════════
|
|
-- tier2 富游戏自治生成线 · 第二装载落库(U2)源工程版本表。
|
|
--
|
|
-- 【这张表解决什么】
|
|
-- F 族要素⑦ addressing:把 finish 交付的源工程 manifest(七要素去掉 fileTree 各文件 content)
|
|
-- 落 MySQL 一行,按 (game_id, source_hash) 幂等、按 (game_id, version_id) 取回寻址。源文件全文
|
|
-- (fileTree 各文件 content)不进本表,落 MinIO(S3 兼容 OSS),manifest.addressing.sourceUrl 指向
|
|
-- MinIO 版本前缀。改源重建 → 新 version_id,旧版本保留(F1 版本寻址 / 游戏=长生命周期项目)。
|
|
--
|
|
-- 【与代码的关系 · 同源对账门】
|
|
-- worker/store.py 的 BackendStore._ensure_schema 首次 save 时会执行等价的 CREATE TABLE IF NOT EXISTS
|
|
-- (代码内自建,见 _DDL_SOURCE_PROJECT_VERSION),并按 information_schema 探测缺 status 列则补跑
|
|
-- _ALTER_ADD_STATUS_COLUMN(= 本文件末尾那条 ALTER),故部署不强制先手动跑本文件。本文件是同源的
|
|
-- 【可审阅 DDL 留档】:DBA / 后端接线 / 评审按它对账表结构;若手工预建,在 tier2 库执行本文件即可
|
|
-- (代码再跑 IF NOT EXISTS / 探测不会重建重加)。in-code DDL 与本文件双源,由
|
|
-- tests/test_store_ddl_reconcile.py 规范化对账钉成机器门:改一处漏改另一处即红,不靠人肉纪律。
|
|
-- 表所在库 = infra.yaml mysql.database(默认 tier2;与 game-cloud 业务库隔离)。
|
|
--
|
|
-- 【治理归属】本表 owner = tier2 生成线,不走 game-cloud 的 huijing Flyway、无执行副本,故 .sql 留在
|
|
-- 本目录(tier2/config/schema/)、不塞 contracts/db-schemas/ 根(那是 huijing Flyway 授权源、须与
|
|
-- game-cloud 执行副本 diff 一致)。治理指针登记在 contracts/README.md 指针登记集群「tier2 自有受治理 schema」。
|
|
--
|
|
-- 【MinIO key 约定(与本表配套,不在本 DDL 内)】
|
|
-- object key = <game_id>/<version_id>/<工程内相对路径>,bucket = infra.yaml minio.bucket(默认 tier2-src)。
|
|
-- ════════════════════════════════════════════════════════════════════════════
|
|
|
|
CREATE TABLE IF NOT EXISTS tier2_source_project_version (
|
|
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
|
game_id VARCHAR(128) NOT NULL COMMENT '游戏稳定标识(同款多版本共享;落库 id)',
|
|
version_id VARCHAR(96) NOT NULL COMMENT '版本号 vXXXX-hash12(改源重建生成新版本)',
|
|
source_hash CHAR(64) NOT NULL COMMENT '源工程内容指纹 sha256(= source_project.contentHash;幂等键)',
|
|
manifest_json LONGTEXT NOT NULL COMMENT '源工程 manifest 七要素形状(去 fileTree content;含 addressing)',
|
|
addressing VARCHAR(512) NULL COMMENT '落库寻址摘要(store/sourceUrl;冗余出 manifest 便于检索)',
|
|
created_at DATETIME NOT NULL COMMENT '落库时刻(now_ts 转 UTC datetime)',
|
|
status VARCHAR(16) NOT NULL DEFAULT 'committed' COMMENT 'manifest-first 落库状态:pending=已占位未提交(半落库可见残行)/committed=源文件已全落可取回;fetch 只返 committed',
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY uk_game_source (game_id, source_hash) COMMENT '幂等键:同款同源不重复落',
|
|
UNIQUE KEY uk_game_version (game_id, version_id) COMMENT '版本寻址键:同款版本号唯一',
|
|
KEY idx_game_created (game_id, created_at) COMMENT 'fetch 取最新版本走它'
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='tier2 富游戏源工程版本表(U2 第二装载落库)';
|
|
|
|
-- ── status 列受治理迁移(这张表第一份 ALTER)──────────────────────────────────────────
|
|
-- 为什么单列这条:上面的 CREATE TABLE IF NOT EXISTS 对【已存在】的旧表加不了列,历史窗口里已建的无
|
|
-- status 列旧表靠 CREATE 永远补不上。故对存量表跑这条 ALTER 补列(新装已由上面 CREATE 直接带 status,
|
|
-- 不需要这条)。DEFAULT 'committed' 兜历史行(历史落进来的都是完整版本,视作已提交);新写入路径按
|
|
-- manifest-first 时序显式置 pending/committed,不吃 DEFAULT。
|
|
-- in-code 对应 worker/store.py 的 _ALTER_ADD_STATUS_COLUMN,由 BackendStore._ensure_schema 按
|
|
-- information_schema 探测缺列才跑(幂等)。本条与该常量由 tests/test_store_ddl_reconcile.py 规范化对账。
|
|
ALTER TABLE tier2_source_project_version ADD COLUMN status VARCHAR(16) NOT NULL DEFAULT 'committed';
|