Files
ai_site/docs/数据同步-中间件.md
whm b04b180d30 feat: harden loose-offline sync for user JWT, schema, and console ops
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>
2026-08-05 09:47:35 +08:00

8.5 KiB
Raw Blame History

跨库数据同步中间件

支持方言 Postgres生产线上库 A 推荐) / MySQL / SQLite。
形态 B本机 agent:终端 agent 经平台 push 写线上 A平台不直连用户本机 SQLite

冲突策略默认 自动 LWW源端覆盖;落败写入平台超级管理员覆盖日志。公司管理员无冲突台

权限与隔离

说明
谁可配通道 公司顶级权限(管理员),「数据同步」
Agent push / 拉白名单 JWT 含「数据同步」(人类管理员或智能体凭证)
LWW 覆盖审计 仅平台超级管理员;公司 top → 403
数据隔离 通道带 tenant_id;公司 A 看不到公司 B

推荐场景(松离线 B→AA=Postgres

角色
B 本机正式库UUID 主键);开通且白名单表本地可见
A 线上 Postgres(通道 remoteagent 经平台 push 幂等写入
Agent 读本机 outbox → POST /api/v1/agent/sync/channels/:id/push

推荐配置:

  1. 通道:remote.driver=postgres + DSN方向 local_to_remote;策略 lww_source
  2. 表白名单须 UUID TEXT/UUID PK + FK 闭包(保存时校验)。
  3. 客户端显式 local_dbsync(见开通说明);未开通用户零感。
  4. 怀疑漏数时点 同步修复(对账)(有最小间隔限流)。

单服务器 / 多服务器(同一套 API

部署 配置
单服务器 一条通道指向该 Postgres一个 channel_id
多服务器 每库一条通道(不同 remote DSN客户端按库选择 channel_id / Binding

协议相同,均为「经智建 push」无需第二套接口。

如何保证不漏、不多

手段 防什么
触发器 / 本机 outbox → agent push
WithApplying / 远端应用不回写 outbox 多(回声)
目标 meta 同 version 跳过 多(重复投递)
LWW + 超管覆盖日志 并发同 PK
主键对账 reconcile 漏(存量差)

能力一览

说明
方言 生产 Apostgres;兼容 mysqlsqlite 仅联调临时 A不宜多 writer
方向 本地→线上(推荐)/ 线上→本地 / 双向
冲突 默认 lww_sourcelww_targetqueue 仅调试(租户不可见)
Agent GET .../agent/sync/.../whitelistPOST .../push(管理员/智能体「数据同步」,或登录用户 + 本人 Binding
Binding GET/POST /api/v1/admin/sync/bindings(同上鉴权;普通用户仅本人)
对账 POST .../reconcile(默认最少间隔 300s仍需「数据同步」
审计 GET /api/v1/platform/dbsync/lww-overrides(超管)

持久化:data/dbsync/channels.jsonbindings.jsonlww_overrides.json(及遗留 conflicts.json)。

用户自助同步(库级三态)

终端用户可在客户端对名下库选择仅本地 / 仅线上 / 同步;选 同步 后用登录 JWT(无需单独「数据同步」管理员权)调 agent API。

角色 范围
「数据同步」管理员 / 智能体 租户内通道push 不强制 Binding
普通登录用户 仅本人 Bindingchannel_id + online_db_idpush 须带 online_db_id

表白名单策略Z4已冻结

行为
权限源 登录 JWT + 本人 Binding 的 online_db_id(不是通道 tables
push 接受 Binding 库内任意表;表不存在时自动建表TEXT 列 + PK
通道 tables 仅历史兼容 / whitelist 接口展示;不拒收

管理员不必再为业务表维护白名单。宇恒默认 YXD_SYNC_ENFORCE_WHITELIST=0

下行 / 仅线上Z5

形态 B平台不直连本机 B,只读线上 A把行返回给本机 agent 写入 B建议 WithApplying 防回声)。

mode 用途
bootstrap 全量分页灌库(「仅线上」首启 / 本地空库)
pks 只列 A 上主键,客户端 diff 后再 rows
rows row_pks 取行补齐
POST /api/v1/agent/sync/channels/{id}/pull
POST /api/v1/agent/sync/channels/{id}/bootstrap   # 强制 mode=bootstrap
{
  "mode": "bootstrap",
  "table": "orders",
  "after_pk": "",
  "limit": 200,
  "online_db_id": "用户自助必填"
}

循环直到 result.has_more=false。单页默认 200、上限 500。

Agent push 幂等与重复投递

  • 正确性:同 (table, row_pk, version) 已落地 → result.skipped=true 且 HTTP 2xx线上 A 不双行

  • 允许客户端重复 push例如多 agent / 双 drain去重是客户端优化(进程内 drain 锁),不是平台必做。

  • 通道 statspushed_applied / pushed_skipped 可观察重推噪声。

  • remote 暂不可达HTTP 503 + retryable: trueagent 可稍后重试,勿换 UUID

  • Postgres A 适合正常并发;若联调仍用 SQLite 作 A请客户端按库串行 drain

  • 平台对 remote 连接做池化复用 + 通道启动预热,避免每次 push 冷开 SQLite 触发超时。

请求日志怎么查

来源 位置 / 接口 内容
Platform HTTP .runtime/logs/platform.err.loggo-zero 路径、耗时、状态码;超时曾见 slowcall(3000ms)
Platform dbsync 同文件,关键字 dbsync push / dbsync pull channel、table、pk、user、outcome、dur、req=(X-Request-Id)
Gateway .runtime/logs/gateway.out.log sync 路径与 ≥500gateway POST /api/... status=... req=
审计 API GET /api/v1/audit/logs(需「查看审计」) actiondbsync.push / dbsync.pull / *.errordetail JSON 带 req_id

联调时用响应头 / 请求头 X-Request-Id 可在三处日志对齐同一笔请求。

线上 Postgres DSN 示例

postgres://sync_user:pass@127.0.0.1:5432/app_online?sslmode=disable

联调临时 SQLite A不推荐生产

file:E:/path/to/remote.db?_pragma=foreign_keys(1)

勿用反斜杠;平台会自动规范化并补 busy_timeout

控制台用法

  1. 数据同步 → 新建通道remote 选 postgres)→ 测试 → 保存 → 启动
  2. 默认策略选 源端覆盖;方向首期用 本地 → 线上
  3. 同步修复 做主键对账(勿连续狂点,有限流)
  4. LWW 明细在 平台工作台(超管),不在公司同步页
  5. 多服务器:为每台库再新建一条通道,把对应 channel_id 发给客户端

开通与迁移:见 数据同步-开通说明.md数据同步-迁移手册.md

API

公司管理员(「数据同步」)

方法 路径
GET/POST /api/v1/admin/sync/channels
GET/PUT/DELETE /api/v1/admin/sync/channels/{id}
POST /api/v1/admin/sync/test
POST /api/v1/admin/sync/channels/{id}/prepare|start|stop
POST /api/v1/admin/sync/channels/{id}/reconcile
POST /api/v1/admin/sync/channels/{id}/ingest(写通道 local偏形态 A
GET/POST /api/v1/admin/sync/bindings
GET/POST /api/v1/admin/sync/conflicts*403(已迁超管审计)

本机 Agent / 用户自助

鉴权Bearer = 「数据同步」 已登录人类 JWT后者须先 POST .../bindings 且 push 带本人 online_db_id)。

方法 路径
GET /api/v1/agent/sync/channels/{id}/whitelist
POST /api/v1/agent/sync/channels/{id}/push
POST /api/v1/agent/sync/channels/{id}/push/batch
POST /api/v1/agent/sync/channels/{id}/pull
POST /api/v1/agent/sync/channels/{id}/bootstrap
GET/POST /api/v1/admin/sync/bindings(用户自助亦可)

平台超级管理员

方法 路径
GET /api/v1/platform/dbsync/lww-overrides
POST /api/v1/platform/dbsync/lww-overrides/{id}/rollback

push 示例

POST /api/v1/agent/sync/channels/{id}/push
{
  "table": "orders",
  "op": "insert",
  "row_pk": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "row": { "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "title": "x" },
  "version": 1710000000000000000,
  "client_outbox_id": "..."
}

相关