feat: ship loose-offline dbsync (validate, agent push, LWW audit)
Add UUID/FK channel checks, agent whitelist/push APIs, bindings, super-admin LWW audit with rollback, reconcile rate limits, and sync docs. Default customers stay opt-in; company conflict UI is removed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
294
松离线-dbsync方案-最终版.md
Normal file
294
松离线-dbsync方案-最终版.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# 松离线 + 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 | **不做** |
|
||||
|
||||
```text
|
||||
[开通 + 白名单表]
|
||||
业务写 ──► 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` 且表在白名单
|
||||
|
||||
1. 无 `id` → **网关自动补 UUID**(规范化小写带连字符),响应带回 `id` / `inserted_id`;并记 auto_id 日志。
|
||||
2. **始终写 B 正式表**;成功即返回成功。
|
||||
3. 写 outbox;agent 异步推 A。
|
||||
4. agent 未装 / 停运:本地成功 + UI「待同步」;**不失败保存**(定案 R1)。
|
||||
5. 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. 双方承诺(冻结)
|
||||
|
||||
### 宇恒
|
||||
|
||||
1. M0/M1:模式三分 + 白名单进网关;旧分支零行为 diff(附回归清单)。
|
||||
2. agent 非安装强依赖;同步 UI 仅开通后出现。
|
||||
3. M2 起对接 ingest,不自造第二套写语义。
|
||||
4. 合入门禁:未开通用户行为 diff ≠ 0 → 不予合入。
|
||||
|
||||
### 智建
|
||||
|
||||
1. M0:入通道 UUID + 闭包校验。
|
||||
2. M2 前:ingest 幂等认客户端 UUID。
|
||||
3. M3:超管审计与租户控制台分离。
|
||||
4. 发版说明写明:默认客户无感;`local_dbsync` 为增值开通。
|
||||
5. 书面确认: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|whitelist`(agent JWT 需「数据同步」);**宇恒本机 agent / 写网关由宇恒侧自行落地,智建不改对方仓库** |
|
||||
| 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 补全。**全员默认 / 宇恒灰度由对方与运营决策,本仓不改宇恒** |
|
||||
| 2026-07-31 | P2 智建 | 超管按 LWW 落败快照回滚单行:`POST /api/v1/platform/dbsync/lww-overrides/:id/rollback` + 平台工作台按钮 |
|
||||
| 2026-07-31 | 收尾 智建 | OpenAPI 补 sync/agent/lww;yaml TTL/限流;INTEGER PK 拒绝集成测;发版说明 |
|
||||
Reference in New Issue
Block a user