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

210 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 跨库数据同步中间件
支持方言 **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**(通道 `remote`agent 经平台 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 | 漏(存量差) |
## 能力一览
| 项 | 说明 |
|----|------|
| 方言 | 生产 A`postgres`;兼容 `mysql``sqlite` 仅联调临时 A不宜多 writer |
| 方向 | 本地→线上(推荐)/ 线上→本地 / 双向 |
| 冲突 | 默认 `lww_source``lww_target``queue` 仅调试(租户不可见) |
| Agent | `GET .../agent/sync/.../whitelist``POST .../push`(管理员/智能体「数据同步」,或登录用户 + 本人 Binding |
| Binding | `GET/POST /api/v1/admin/sync/bindings`(同上鉴权;普通用户仅本人) |
| 对账 | `POST .../reconcile`(默认最少间隔 300s仍需「数据同步」 |
| 审计 | `GET /api/v1/platform/dbsync/lww-overrides`(超管) |
持久化:`data/dbsync/channels.json``bindings.json``lww_overrides.json`(及遗留 `conflicts.json`)。
## 用户自助同步(库级三态)
终端用户可在客户端对名下库选择仅本地 / 仅线上 / 同步;选 **同步** 后用**登录 JWT**(无需单独「数据同步」管理员权)调 agent API。
| 角色 | 范围 |
|------|------|
| 「数据同步」管理员 / 智能体 | 租户内通道push 不强制 Binding |
| 普通登录用户 | 仅本人 Binding`channel_id` + `online_db_id`push **须带** `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` 取行补齐 |
```http
POST /api/v1/agent/sync/channels/{id}/pull
POST /api/v1/agent/sync/channels/{id}/bootstrap # mode=bootstrap
```
```json
{
"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 锁),不是平台必做。
- 通道 stats`pushed_applied` / `pushed_skipped` 可观察重推噪声。
- remote 暂不可达HTTP **503** + `retryable: true`agent 可稍后重试,勿换 UUID
- **Postgres A** 适合正常并发;若联调仍用 SQLite 作 A请客户端**按库串行 drain**。
- 平台对 remote 连接做**池化复用 + 通道启动预热**,避免每次 push 冷开 SQLite 触发超时。
## 请求日志怎么查
| 来源 | 位置 / 接口 | 内容 |
|------|-------------|------|
| Platform HTTP | `.runtime/logs/platform.err.log`go-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 路径与 ≥500`gateway POST /api/... status=... req=` |
| 审计 API | `GET /api/v1/audit/logs`(需「查看审计」) | `action``dbsync.push` / `dbsync.pull` / `*.error`detail JSON 带 `req_id` |
联调时用响应头 / 请求头 `X-Request-Id` 可在三处日志对齐同一笔请求。
## 线上 Postgres DSN 示例
```text
postgres://sync_user:pass@127.0.0.1:5432/app_online?sslmode=disable
```
联调临时 SQLite A不推荐生产
```text
file:E:/path/to/remote.db?_pragma=foreign_keys(1)
```
勿用反斜杠;平台会自动规范化并补 `busy_timeout`
## 控制台用法
1. **数据同步** → 新建通道remote 选 **postgres**)→ 测试 → 保存 → 启动
2. 默认策略选 **源端覆盖**;方向首期用 **本地 → 线上**
3.**同步修复** 做主键对账(勿连续狂点,有限流)
4. LWW 明细在 **平台工作台**(超管),不在公司同步页
5. 多服务器:为每台库再新建一条通道,把对应 `channel_id` 发给客户端
开通与迁移:见 [数据同步-开通说明.md](./数据同步-开通说明.md)、[数据同步-迁移手册.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 示例
```json
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": "..."
}
```
## 相关
- [同步表约定.md](./同步表约定.md)
- [数据同步-开通说明.md](./数据同步-开通说明.md)
- [数据同步-迁移手册.md](./数据同步-迁移手册.md)