Multi-Tenant 上 Prod 铺开 Checklist
v1-gaps.md 缺口 1 指出"上 prod 的步骤没有成文"——这份就是那个成文。当前(2026-07-01)所有业务都在 test 跑,callytics-prod 库无 live 负载;正式上 prod 那天按下面顺序逐步执行,每步验证通过才进下一步。上线不是一天内的原子动作,fail-open 会把漏掉的步骤静默吞成 NULL,所以顺序和验证都不能省。
前置条件(不满足先补,都在 v1-gaps 里追踪)
Step 1 — Apply 业务表 tenant_id migration 到 callytics-prod
走 CI migration 流程(callytics-common 的 migration workflow),不手动跑 SQL。
验证(在 callytics-prod 上跑,期望 = 10,与 test 一致):
Step 2 — 为真实客户建 tenant + tenant_members
- member 的
user_id必须是真实 Cognito user_id——test/test b两个脏 tenant 的教训就是用了字面量字符串,永远解析不出任何东西 - 每个 prod 店主的 user_id 都要进
tenant_members(2026-07-01 实测:prod 3 个店主 user_id 都不在,mapping 和 derived fallback 双双落空)
验证(control plane retaintive-identity):
Step 3 — Seed prod 店的 mapping
每家 active 店恰好一条 active mapping。注意:studio-api 的 ensureTenantMappingsForStores(#516)只在建新店时写 mapping,存量店必须显式 seed(source='backfill')。
验证(双向 diff,跨两个 Neon project 手动两步,或直接用缺口 4 的对账 job):
Step 4 — 确认写入方带 tenant 标注
- 每个写入 lambda 的 env 有
CONTROL_PLANE_DATABASE_URL_PARAM,且部署 region 的 SSM 里真有那个参数——SSM parameter 是 region-scoped 的,2026-07-01 实测 us-east-1 只有/studio/prod/control-plane-database-url,没有 test 版;us-west-2 只有 test 版 - lead-tracking 部署版本必须含 #197/#200(检查 Lambda
LastModified晚于修复部署日,deploy.yml 是workflow_dispatch手动触发,merge 不等于 deploy)
验证(上线后按表查新写入 NULL 率):
Step 5 — 跑一次完整对账
缺口 4 的对账 job(或 Step 3 的手动 SQL)再跑一遍:unmapped = 0 且 orphan = 0。这一步是为了抓 Step 1–4 之间新 sync 出来的店。
Step 6 — tenant status 翻 active
把真实 tenant 从 provisioning 翻成 active。当前没有任何代码读 tenants.status(v1-gaps 缺口 5),翻它不改变行为——但上线前必须把 status 生命周期的 owner 和语义定下来(谁翻、suspended 挡不挡访问),不然"suspend 一个客户就能挡住访问"的误解会变成真事故。
退出条件(= v1-gaps 缺口 1 的验收)
- prod 全部 active 店 100% 有 active mapping
- 店主 user_id 全部在
tenant_members - 业务表新写入行带
tenant_id(NULL 率告警安静)