Enable Binding-scoped agent push/pull, empty-table schema ensure, SyncPage inspect/drop-table, default module import, and agent-bound publish docs from the 宇恒联调意见. Co-authored-by: Cursor <cursoragent@cursor.com>
13 KiB
13 KiB
松离线 + dbsync 方案 · 最终版(冻结)
效力:本文为双方协商后的唯一开工依据。
原方案稿 / 审评追加 / 决议草稿见同目录松离线-dbsync方案.md(过程稿,不再改口径)。
冻结日期:2026-07-31
0. 一句话
技术:松离线写本地 B(最终 UUID)+ 本机 agent 经 dbsync 幂等对齐线上 A;冲突自动 LWW,落败入平台超级管理员覆盖日志。
产品:宇恒默认零影响;仅显式 local_dbsync + 表白名单 opt-in;M4 前不对全员切默认。
1. 硬约束(不可回退)
| # | 约束 |
|---|---|
| H1 | 未开通同步的用户:保存 / 自增表 / 插件行为与现网一致(行为 diff = 0) |
| H2 | 默认不得自动升为 local_dbsync |
| H3 | agent 停运不得导致 apply_write / 保存失败;心跳只驱动 UI |
| H4 | 默认建表模板保持自增;仅「同步表模板」为 UUID |
| H5 | 旧 online_primary 客户禁止静默关双写;仅新客或显式迁移客评估切换 |
| H6 | 未开通用户零同步文案(无强制状态条、无「已自动合并」) |
2. 架构定案
| 项 | 定案 |
|---|---|
| 离线 | 松离线:开通且白名单表 → 本地正式库落最终 UUID,立刻可见 |
| 同步 | 接法① dbsync(非 HTTP 双写主路径) |
| 通道形态 | B 本机 sync agent(平台不直连用户 SQLite) |
| 首期方向 | B→A;双向另开,默认关 |
| 冲突 | 自动 LWW;首期投递以 outbox 单调 version 为主,偏源(本机推上)时覆盖目标并记审计 |
| 落败追溯 | LWW 自动覆盖日志;仅平台超级管理员;公司 top 403;建议 TTL 90 天 |
| 终端冲突 UI | 不做 |
| 形态 A / CRDT / 对账替代 UUID | 不做 |
[开通 + 白名单表]
业务写 ──► B(UUID)──► outbox ──► 本机 agent ──► A(ingest/upsert)
UI:仅本机 / 待同步 / 已同步(不影响保存成败)
[未开通]
现网路径不变(local_only 或 online_primary)
3. 模式三分(宇恒)
| 模式 | 含义 | 默认 |
|---|---|---|
local_only |
只写本地 | 可作默认之一 |
online_primary |
现网 HTTP 双写 / 离线 pending | 已上线客户保留 |
local_dbsync |
松离线 + agent(新) | 仅显式配置 |
- 未配置 / 未识别 → 不得当成
local_dbsync。 - 互斥粒度(定案 O6):同一张表不可同时走
online_primary双写与local_dbsync;同一库允许部分表白名单走local_dbsync,其余表保持该库原模式写本地(不强制整库切模式)。
4. 白名单(定案 O1 / O2)
| 项 | 定案 |
|---|---|
| 源 of truth | 智建通道配置 |
| 宇恒 | 本地缓存副本;拉取前 / 拉失败 → 视为未开通同步表(走原写路径) |
| 谁可改 | 公司 top(现「数据同步」权限);终端只读 |
| 闭包 | 父子外键须同进白名单,否则保存通道失败 |
| 入通道 | 非 id TEXT UUID → 拒绝 |
5. 写路径规则
5.1 未进白名单或未开 local_dbsync
与现网完全一致:可不传 id、允许自增;online_primary 语义不变。
5.2 已开 local_dbsync 且表在白名单
- 无
id→ 网关自动补 UUID(规范化小写带连字符),响应带回id/inserted_id;并记 auto_id 日志。 - 始终写 B 正式表;成功即返回成功。
- 写 outbox;agent 异步推 A。
- agent 未装 / 停运:本地成功 + UI「待同步」;不失败保存(定案 R1)。
- delete:outbox
delete或墓碑;agent 按 outbox id 保序;失败禁止换新 UUID。
开通判定(写路径):mode=local_dbsync 且 白名单缓存命中该表;否则走 §5.1。
5.3 M1 无 agent 时「开通」含义(定案 O3)
允许只落 B + 积压待同步;文案须写明 「需 agent 才上云」,不得暗示已上云。
6. 建表(定案 R2)
| 模板 | 主键 |
|---|---|
| 默认 create(双端) | 仍自增(不动) |
| 同步表模板(双端新增) | id TEXT UUID + 建议 updated_at / 配合 outbox version |
禁止「全局默认改成 UUID」。
7. version / LWW(定案 O5)
| 项 | 定案 |
|---|---|
| 主序 | outbox 单调 id / version |
| 辅 | 客户端 updated_at(不对时不挡保存) |
| 落败 | 写超管覆盖日志(两边 payload、胜出策略、tenant、表、pk、时间) |
| 弱提示「已自动合并」 | 默认关;仅开通用户可配置打开(定案 R4) |
8. 绑定与迁移
| 项 | 定案 |
|---|---|
| Binding API | P1,不挡 M2(定案 O4);M2 可用现网派生 online_db_id |
存量 online_primary → local_dbsync |
另附迁移手册(定案 O7):加白名单 → 装 agent → 观察 → 关 HTTP;禁止静默迁移(定案 R5/H5) |
9. 本机 agent 最小集
- 可选装;未开通可不装。
- 读
_ajz_sync_outbox(或等价),保序推 A(复用/扩展智建 ingest)。 - 凭证:最小权限、按
online_db_id隔离、可吊销;outbox 敏感列脱敏或加密。 - 心跳 → UI only。
10. 智建平台交付
| 优先级 | 项 |
|---|---|
| P0 | 入通道 UUID + 闭包校验;ingest 认客户端 UUID 幂等;模式/开通说明(默认客户无感) |
| P0 | (M3)超管 LWW 审计 API/存储,与租户「数据同步」菜单分离;TTL |
| P1 | Binding 登记/查询;限流 reconcile;设置页「同步修复」(可选、不弹窗强打断) |
| P1 | 表白名单 B→A / 双向开关(默认 B→A) |
| P2 | 超管按快照回滚单行 |
公司 top:配通道、白名单、对账(现权)。
平台超管:覆盖日志。
终端:无冲突台。
11. 分期与退出标准
| 阶段 | 目标 | 退出标准 |
|---|---|---|
| M0 | 同步表模板 + 入通道校验 + 模式三分文档 | 自增表无法误入通道 |
| M1 | 宇恒旁路:local_dbsync + 白名单松离线写 B(可无 agent) |
未开通用户自动化回归 = 现网;开通用户离线可见;文案不承诺上云 |
| M2 | agent 最小 B→A + ingest 幂等 | 同 UUID 上云、不双行;agent 停仍可本地保存 |
| M3 | 超管 LWW 日志 + 限流 reconcile | top 403;双入口覆盖可查 |
| M4 | 扩白名单;仅评估新客或显式迁移客是否默认 local_dbsync |
旧客默认不变 |
M4 前禁止全员切默认、禁止一刀切关 HTTP 双写。
12. 验收清单(冻结)
不影响原有
- 未开
local_dbsync:现网回归通过(自增不传 id、原online_primary)。 - 非白名单:不强制 UUID;入通道拒绝。
- 关
local_dbsync/ 清白名单缓存:行为回原模式。 - 未开通:无强制 agent、无同步文案、保存不因同步失败。
- 同表未同时走双路径;关同步后无双写放大。
开通后(白名单)
- 离线写 B 可见;联网同 UUID 上 A;幂等不双行。
- agent 停:可保存 + 待同步;恢复后追上。
- delete/重放保序;不换新 UUID。
- LWW 落败在超管日志;公司 top 不可见。
- 闭包不完整无法保存通道。
- M1 文案不暗示已上云。
13. 双方承诺(冻结)
宇恒
- M0/M1:模式三分 + 白名单进网关;旧分支零行为 diff(附回归清单)。
- agent 非安装强依赖;同步 UI 仅开通后出现。
- M2 起对接 ingest,不自造第二套写语义。
- 合入门禁:未开通用户行为 diff ≠ 0 → 不予合入。
智建
- M0:入通道 UUID + 闭包校验。
- M2 前:ingest 幂等认客户端 UUID。
- M3:超管审计与租户控制台分离。
- 发版说明写明:默认客户无感;
local_dbsync为增值开通。 - 书面确认:R1–R4 全部按上文硬约束执行(已并入本文 §1/§5/§6/§7/§8)。
14. 对第二轮「待拍板」的最终答复
| 编号 | 决议 |
|---|---|
| R1 | 确认:心跳只驱动 UI,永不因 agent 拒绝保存 |
| R2 | 确认:只加同步模板,默认建表仍自增 |
| R3 | 确认:不一刀切关旧客 HTTP 双写 |
| R4 | 确认:弱提示默认关,仅开通用户可配 |
| O1 | 通道为源;宇恒缓存;无名单=未开通 |
| O2 | 公司 top 改白名单;终端只读 |
| O3 | M1 可无 agent;文案「需 agent 才上云」 |
| O4 | Binding 不挡 M2 |
| O5 | outbox 单调 version 为主;不对时不挡保存 |
| O6 | 同表互斥(非同库整库互斥) |
| O7 | 迁移手册 + 禁止静默迁移 |
15. 最终口径
旁路 opt-in 的松离线 + 本机 agent + dbsync;LWW 自动、超管可追溯;默认不影响宇恒现网。按 M0→M4 开工,本文冻结。
宇恒追加-实施钉死项(摘录已并入 §16)
见过程稿全文;下列 §16 为智建复核后的采纳与补钉(不回退 H1–H6)。
16. 实施钉死项 · 智建复核结论(2026-07-31)
宇恒 N1–N6 总体可采纳,与最终版无冲突。下列为「写死值 + 防坑补钉」。
16.1 对 N1–N6 的定案
| # | 宇恒建议 | 智建结论 | 写死值 |
|---|---|---|---|
| N1 | 无 id → 网关自动补 UUID | 采纳 | 自动生成并在响应带回 id(可用 inserted_id 作别名兼容);同时打日志「auto_id」 便于排障 |
| N2 | 开通判定 | 采纳并收紧 | 硬条件:mode=local_dbsync 且 白名单缓存命中该表。租户「同步开通」标志若存在,作为下发白名单的前提(无标志则不下发名单),不再在写路径上增加第三道易漂移条件 |
| N3 | 按表分支 + 单测 | 采纳 | M1 必测:同库表白名单 / 非白名单分叉 |
| N4 | outbox;未开通不建表 | 采纳并优选 | 优先与 permanent 同库 _ajz_sync_outbox(触发器简单);未开通库不创建该表;首次开通时迁移创建 |
| N5 | UUID 小写带连字符 | 采纳 | 双端入库前 normalize(小写 + 标准 8-4-4-4-12) |
| N6 | 旧字段保留、新字段仅开通附加 | 采纳 | 未开通响应形状与现网一致(可多不可少成功语义) |
16.2 宇恒反馈有没有问题?
没有方向性问题。 N1–N6 都是在落实「不影响原有」,应进 M0/M1。
需注意的不是反对项,而是执行时的灰区(见下补钉)。
16.3 仍不完善 · 建议补钉(P0/P1)
| # | 缺口 | 建议 | 优先级 |
|---|---|---|---|
| P0-a | 白名单缓存过期 | 定 TTL(如 5–15 min)+ 开通/改名单后主动推送或下次启动强制拉;过期失败 → 未开通(已定),避免长期用过期「仍在名单」误走同步 | P0 |
| P0-b | 同表从 HTTP 双写迁入白名单 | 迁移手册写死:先停该表 online_primary 双写 → 再进白名单 / local_dbsync;禁止「边双写边进名单」 |
P0(并入 O7 手册) |
| P0-c | 未开通回归套件归属 | 宇恒维护基线用例;智建提供「通道拒绝非 UUID」用例;M1 退出双方签字 | P0 |
| P1-a | M1 无 agent 积压 | outbox/待同步条数告警阈值(如 >N 提示装 agent);仍不挡保存 | P1 |
| P1-b | agent 凭证续期 | ingest Token 刷新与吊销流程写入 agent 最小集;过期只影响上云,不影响本地写 | P1 |
| P1-c | 多 local_database_id |
回归:同一 user 多库绑定不串 online_db_id;名单按库缓存 |
P1 |
| P2-a | 自动补 UUID 掩盖调用方漏传 | 开通路径 metrics:auto_id_count;超管/日志可查,不挡业务 |
P2 |
16.4 不采纳 / 不回潮
- 不因「自动补 UUID」改为默认
local_dbsync。 - 不恢复「写前 agent 心跳失败则拒绝保存」。
- 不把 LWW 审计开放给公司 top。
16.5 复核一句话
宇恒钉死项应采纳;再补缓存 TTL、迁表白名单与双写互斥顺序、回归归属,M0/M1 即可干净开工。
17. 变更记录(落地)
| 日期 | 范围 | 说明 |
|---|---|---|
| 2026-07-31 | M0 智建 | 通道保存 / PrepareChannel:UUID TEXT PK + FK 闭包校验(platform/internal/dbsync/validate.go);文档 docs/同步表约定.md;SyncPage 表白名单提示 |
| 2026-07-31 | M2 智建 | PushToRemote + `/api/v1/agent/sync/.../push |
| 2026-07-31 | 约定 | M1(宇恒 local_dbsync 旁路)与 agent 消费 outbox:以本文 + docs/同步表约定.md 为接口约定,不在智建仓改宇恒代码 |
| 2026-07-31 | M3 智建 | LWW 覆盖审计 lww_overrides.json;GET /api/v1/platform/dbsync/lww-overrides(仅超管);公司 conflicts API 403;对账限流;默认 lww_source;SyncPage 去冲突台 |
| 2026-07-31 | M4 智建 | Binding API;开通说明/O7 迁移手册;中间件文档 LWW 对齐;SyncPage「同步修复」+ opt-in 文案;apidef 补全。全员默认 / 宇恒灰度由对方与运营决策,本仓不改宇恒 |