18 KiB
同步系统状态定义 (Final Spec)
本文档定义了同步系统的状态机逻辑。逻辑流转遵循以下顺序:
- 核心状态判定 (Core State)
- 依赖检查与ID解析 (Dependency)
- 自动绑定/发现 (Discovery)
- 策略执行 (Action)
0. 绑定状态枚举 (BindingStatus)
| 状态值 | 含义 | 来源 | 允许创建 |
|---|---|---|---|
| UNCHECKED | 未执行绑定检查 | 加载后的初始状态 | ❌ |
| NORMAL | 绑定正常 | 核心状态判定 | ❌ |
| MISSING | 无绑定记录(孤儿) | 核心状态判定 | ✅ |
| WARNING | 业务键不完整 | 自动绑定阶段 | ❌ |
| ABNORMAL | 数据损坏 | 核心状态判定 | ❌ |
| DEPENDENCY_ERROR | 依赖未就绪 | 依赖检查或自动绑定 | ❌ |
关键原则:
- UNCHECKED 阻止创建:加载后节点状态为 UNCHECKED,只有执行了
bind()后才会转为其他状态 - 只有 MISSING 状态才能触发 CREATE:
create()方法只查找binding_status == MISSING的节点 - 如果 bind() 被跳过(如 skip_sync=True),节点保持 UNCHECKED,不会被创建
1. 核心状态判定表 (Core Binding Matrix)
说明:
- Binding Record Pair: 数据库中的绑定记录 (必须成对出现,已排除单边绑定)。
- Data Valid: 指数据是否可读、Schema校验是否通过。
- Warning vs Abnormal:
ABNORMAL: 自身数据损坏。WARNING: 自身健康,但因为对方损坏导致连接不可用。
| Bind Pair (Record) | Local Data (Valid?) | Remote Data (Valid?) | -> | Local Status | Remote Status | 说明 |
|---|---|---|---|---|---|---|
| YES | YES | YES | -> | NORMAL | NORMAL | 稳态。链路完整,数据健康,可进行 Diff/Update。 |
| YES | YES | NO | -> | WARNING | ABNORMAL | 远程损坏。本地虽好,但链路已断 (Link Degraded)。禁止同步。 |
| YES | NO | YES | -> | ABNORMAL | WARNING | 本地损坏。远程虽好,但链路已断。禁止同步。 |
| YES | NO | NO | -> | ABNORMAL | ABNORMAL | 双向损坏。 |
| NO | YES | N/A | -> | MISSING | N/A | 本地孤儿。进入“依赖与发现”流程。 |
| NO | N/A | YES | -> | N/A | MISSING | 远程孤儿。进入“依赖与发现”流程。 |
2. 依赖与业务键解析 (Dependency & ID Resolution)
适用范围: 所有涉及外键依赖 (Foreign Key) 的节点。
前置条件: 核心状态 = MISSING (准备进行自动绑定或新建)。
在进行 Auto-Bind 或 Create 之前,必须先尝试构建远程业务键。
例如:本地 Contract(proj_id="P1", code="C01") 想找远程对象,必须先查询 P1 对应的远程 ID RP_99,转换成 (RP_99, "C01") 才能去远程搜索。
| 依赖项 (Dependency) | 依赖项绑定状态 | -> | 能否构建业务键 | 节点状态 | 说明 |
|---|---|---|---|---|---|
| 无依赖 | N/A | -> | ✅ Yes | MISSING | 根节点 (如 Project),直接进入下一阶段。 |
| 有依赖 | NORMAL 且 data_id 非空(status 任意) |
-> | ✅ Yes | MISSING | 依赖项已获得远程ID,可进入下一阶段。 |
| 有依赖 | NORMAL 且 data_id 为空("",含 status = FAILED) |
-> | ❌ No | DEPENDENCY_ERROR | 依赖项未回填ID,阻断绑定与同步。 |
| 有依赖 | MISSING / ABNORMAL | -> | ❌ No | DEPENDENCY_ERROR | 依赖阻断。父节点未同步或损坏,无法计算远程键,无法绑定,无法创建。 |
| 有依赖 | WARNING | -> | ❌ No | DEPENDENCY_ERROR | 父节点状态不确定(如连接降级),禁止基于此建立新关系。 |
处理策略: 处于
DEPENDENCY_ERROR状态的节点,其动作强制为 SKIP (或 PENDING)。必须先修复父节点。错误信息格式:
DEPENDENCY_ERROR节点的error字段会包含详细的依赖失败信息:
dep_err: not_found: field_name(node_type)=value- 依赖节点未找到dep_err: field_name: status=FAILED- 依赖节点状态为 FAILED- 多个依赖失败时用分号分隔:
dep_err: not_found: project_id(project)=P1; contract_id: status=FAILED
3. 自动绑定状态处理 (Auto-Binding State Handling)
此逻辑遵循“去噪后 1:1”原则:只有在排除掉所有健康同步对后,若剩余节点形成绝对的 1:1 关系,才允许自动绑定。
配置说明与触发机制:
- 初始化场景: 通常在系统第一次初始化或首次接入新实体时,配置
auto_bind = True来对齐历史数据。 - 后续同步: 系统在日常同步中不建议开启全局自动绑定。自动绑定仅作为“发现”手段,一旦建立过关系的节点,其后续绑定操作通常仅在“自动创建”逻辑中伴随产生(详见第 4 节孤儿处理)。
- 执行前提: 只有当节点状态为
MISSING且依赖解析成功(DEPENDENCY_ERROR = False)时,才会尝试以下探测逻辑。 - 业务键完整性: 若业务键字段缺失(如
serial_number = null),直接标记binding_status = WARNING,并在node.error记录原因(Auto-bind skipped: missing fields ...),不参与自动绑定与孤儿创建判定。- ID字段解析失败: 若业务键包含ID字段(如supplier_id)且依赖节点未绑定,ID解析失败,标记binding_status = DEPENDENCY_ERROR,阻止孤儿创建。 核心算法:
- 全集搜索: 寻找本地/远端所有共享同一个业务键(Business Key)的记录。
- 排除稳态对: 识别并移除成对且互绑的
NORMAL记录。- 只有当本地 A 与远端 B 互为绑定对象,且状态皆为
NORMAL时,才将这一对记录从待判定集合中剔除。 - 若出现单边
NORMAL记录(即其配对项不在本次搜索范围内或状态不属于另一端的NORMAL),则不予剔除,视为环境污染项。
- 只有当本地 A 与远端 B 互为绑定对象,且状态皆为
- 判定剩余: 检查排除上述稳态对后的剩余集合(包含
MISSING,ABNORMAL,WARNING及单边已占用的NORMAL记录)。
术语:
L_remain= 本地记录中,排除“成对稳态”后剩余的数量。R_remain= 远端记录中,排除“成对稳态”后剩余的数量。
L_remain |
R_remain |
-> | 动作 | 说明 |
|---|---|---|---|---|
| 1 | 1 | -> | BIND (Link) | 1对1 自动对齐:仅当两端剩余记录皆为 MISSING 且绝对 1:1 时,执行自动绑定。 |
| 1 | 1 | -> | SKIP | 带障不处理:若剩余记录中包含 ABNORMAL 等异常状态,说明存在历史冲突,不执行自动动作。 |
| N | 0 | -> | CREATE | 可判定缺失:业务键完整且唯一,本地存在而远端不存在(N:0)时,才允许进入孤儿创建策略。 |
| 其他 | 其他 | -> | SKIP | 非 1:1 不处理:存在多对多、多对一或无候选情况,系统保持原状。 |
设计优势:
- 对故障极端敏感:环境中存在任何
ABNORMAL记录都会阻断自动绑定,强制人工介入处理异常。 - 状态纯净:失败时不产生任何中间态(如已废弃的
AMBIGUOUS),保持语义清晰。 - 静默自愈: 当外界清理了环境且满足 1:1 条件后,系统下次同步会自动完成绑定。
3.1 空数据处理策略
原则:空数据不推送。
空数据定义:
- 初始化时创建但未编辑的业务记录(如空合同、空施工任务)
- 业务键字段为空或关键字段缺失的记录
处理方式:
- 在自动绑定阶段,若业务键字段缺失,标记
binding_status = WARNING,记录原因 - 不参与自动绑定与孤儿创建判定
- 不推送到远程系统
设计理由:
- 远程系统简化:远程只需处理完整数据,无需兼容空数据校验逻辑
- 避免垃圾数据:空数据通常会被本地删除,若已推送则清理复杂
- 数据质量保障:远程系统只能看到本地已正确编辑保存的业务
配置方式(Strategy 层):
# 在 Strategy 配置中指定必填字段
default_config = StrategyConfig(
auto_bind_fields=["contract_id", "task_type"], # 业务键字段
# 如果这些字段为空,节点将被标记为 WARNING 而非 MISSING
)
4. 动作策略表 (Action Policy Matrix)
此表定义了在特定状态下,execute 阶段允许产生的 意图行动 (SyncAction)。
A. 稳态更新 (NORMAL)
适用于已有绑定关系且链路健康的节点。
| 状态 | 动作条件 | 执行动作 (SyncAction) |
|---|---|---|
| NORMAL | compute_diff 判定存在差异 |
UPDATE |
| NORMAL | compute_diff 判定无差异 |
NONE |
B. 异常处理 (阻断态)
处于这些状态的节点,引擎会自动中止后续的读写操作。
| 状态 | 说明 | 动作 (SyncAction) |
|---|---|---|
| DEPENDENCY_ERROR | 父节点缺失/异常同步导致阻断 | NONE |
| ABNORMAL / WARNING | 数据损坏或链路降级,需人工修复 | NONE |
C. 孤儿处理 (Create Only)
适用于最终探测状态为 MISSING 且无法/不满足自动绑定的节点。
本地孤儿 (Local Orphan): 本地有,远程无。
策略配置 (local_orphan_action) |
对应的同步模式 | 动作 (SyncAction) |
|---|---|---|
| "create_remote" | Push (本地推远端) | CREATE |
| "delete_local" | Pull (远端拉本地) | 报错 (未实现) |
| "none" | Any | NONE |
远程孤儿 (Remote Orphan): 远程有,本地无。
策略配置 (remote_orphan_action) |
对应的同步模式 | 动作 (SyncAction) |
|---|---|---|
| "create_local" | Pull (远端拉本地) | CREATE |
| "delete_remote" | Push (本地推远端) | 报错 (未实现) |
| "none" | Any | NONE |
5. 执行同步状态转换 (Physical Sync State Transitions)
在策略执行完成、设置好 action 后,DataSource 层负责执行实际的同步操作并更新 status 字段。
A. 执行状态枚举 (SyncStatus)
| 状态值 | 含义 | 适用场景 |
|---|---|---|
| PENDING | 待执行 | 初始状态、无动作的节点 |
| IN_PROGRESS | 执行中 | 异步接口已提交,等待结果回调 |
| SUCCESS | 执行成功 | 同步操作完成且成功 |
| FAILED | 执行失败 | 同步操作失败,error 字段记录原因 |
| SKIPPED | 已跳过 | 节点被主动跳过(如依赖未满足) |
| PRECONDITION_FAILED | 前置条件失败 | create/update 内部验证发现问题 |
B. 执行转换矩阵
同步接口(一步式)
适用于同步 API(调用后立即返回成功/失败)。
| 输入: action | API 调用结果 | → | 输出: status | 副作用 |
|---|---|---|---|---|
| CREATE | 成功返回 (200/201) | → | SUCCESS | data_id 设置为远程返回的ID |
| CREATE | 失败返回 (4xx/5xx) | → | FAILED | error 记录错误信息 |
| UPDATE | 成功返回 (200) | → | SUCCESS | - |
| UPDATE | 失败返回 (4xx/5xx) | → | FAILED | error 记录错误信息 |
| DELETE | 成功返回 (200/204) | → | SUCCESS | 节点可能被移除 |
| DELETE | 失败返回 (4xx/5xx) | → | FAILED | error 记录错误信息 |
| NONE | - | → | PENDING | 不执行,保持原状 |
异步接口(两步式)
适用于异步 API(提交后需要轮询或回调获取结果)。
| 输入: action | API 提交结果 | → | 输出: status | 后续操作 |
|---|---|---|---|---|
| CREATE | 提交成功 (202 Accepted) | → | IN_PROGRESS | 记录 task_id,等待轮询 |
| CREATE | 提交失败 (4xx/5xx) | → | FAILED | error 记录错误 |
| IN_PROGRESS | 轮询返回成功 | → | SUCCESS | data_id 设置为远程ID |
| IN_PROGRESS | 轮询返回失败 | → | FAILED | error 记录错误 |
| IN_PROGRESS | 超时未完成 | → | FAILED | error 记录 "Timeout" |
B+. Handler 降级(action 取消)
代码实现:各
domain/*/api_handler.py中的update()方法。
当 Handler 在构建 API 请求数据时,发现实际无需更新的字段(如 schema 校验后所有字段与远程一致),
Handler 会主动将节点的 action 从 UPDATE 降级为 NONE:
action=UPDATE → Handler 校验 → 无实际变更字段 → action=NONE(不提交 API)
此机制确保 DataSource 不会向远程发送无意义的空更新请求。
C. 关键副作用
| 动作 | 成功时的副作用 | 说明 |
|---|---|---|
| CREATE | 临时绑定已建立 | 绑定关系可在提交前创建,用于推送与追踪;是否成功不影响该绑定存在 |
| CREATE | 设置 data_id |
成功后从API响应中提取远程分配的ID,更新到节点 |
| CREATE | 回写 data 主键 |
DataSource 将远程返回的ID写回 SyncNode.data(如 id/*_id 字段) |
| CREATE | 更新绑定记录 | 若使用临时ID,成功后替换为真实的远程ID |
| UPDATE | 更新 origin_data |
将当前 data 保存为新的 origin_data |
| DELETE | 移除绑定记录 | 删除成功后,清除对应的绑定关系 |
D. 错误处理
| 错误类型 | 记录方式 | 重试策略 |
|---|---|---|
| 网络错误 | error = "Network timeout" |
可重试 |
| 权限错误 | error = "403 Forbidden" |
不可重试,需人工介入 |
| 数据校验错误 | error = "Validation failed: ..." |
不可重试,需修复数据 |
| 依赖缺失 | status = PRECONDITION_FAILED |
不执行,等待依赖修复 |
6. 同步检查规范 (Sync Check Specification)
在执行同步前后,需要进行状态检查以确保操作的合法性和完整性。
A. 内联前置检查(create/update 阶段)
实现说明:原
_pre_commit_check方法已移除(保留空壳兼容接口)。 所有有效检查已内联到strategy_ops/create_ops.py::add_create_action()和strategy_ops/update_ops.py::add_update_action()中。
create() 阶段内联检查:
| 检查类别 | 检查条件 | 不通过时 | 说明 |
|---|---|---|---|
| 数据完整性 | src_node.get_data() 不为空 |
跳过该节点,记录日志 | MISSING 但数据为空 |
| 依赖 ID 解析 | id_resolver.resolve_and_validate() 成功 |
binding_status → DEPENDENCY_ERROR |
依赖字段无法解析到目标系统 ID |
update() 阶段内联检查:
| 检查类别 | 检查条件 | 不通过时 | 说明 |
|---|---|---|---|
| FAILED 保护 | src_node.status != FAILED |
跳过该节点 | 不覆盖上次失败的状态和错误 |
| 数据完整性 | src_node.get_data() 不为空 |
跳过该节点 | NORMAL 但数据为空 |
| 目标节点存在 | 通过绑定找到目标节点 | 跳过该节点 | 绑定记录指向的节点不在集合中 |
| data_id 完整性 | target_node.data_id 非空 |
status → PRECONDITION_FAILED |
UPDATE 必须有业务主键 |
| 依赖 ID 解析 | id_resolver.resolve_and_validate() 成功 |
status → PRECONDITION_FAILED |
依赖字段无法解析到目标系统 ID |
| 差异检测 | _needs_update() 返回 True |
跳过该节点 | 无差异则不更新 |
注意:以下检查项在当前实现中 未覆盖【未实现】:
- 状态合法性:
binding_status必须是已知状态- 动作合法性:
CREATE/UPDATE只能用于NORMAL节点- 绑定一致性:
NORMAL节点必须有绑定记录
B. 同步后检查 (Post-Sync Check)
目标: 验证同步结果的完整性,发现残留问题。
实现说明:当前
_stage4_post_sync_check只打印摘要表格,不做异常检测。以下检查项均为【未实现】。
检查项清单【未实现】
| 检查类别 | 检查条件 | 异常情况 | 严重程度 |
|---|---|---|---|
| CREATE 完成性 | CREATE 节点应该有 data_id |
action = CREATE, status = SUCCESS 但 data_id = "" |
🔴 ERROR |
| CREATE 绑定 | CREATE 节点应该有绑定记录 | CREATE 成功但无绑定 | 🔴 ERROR |
| UPDATE 完成性 | UPDATE 节点应该成功 | action = UPDATE 但 status = FAILED |
⚠️ WARNING |
| 绑定保持 | UPDATE 节点的绑定应该保持 | UPDATE 后绑定记录丢失 | 🔴 ERROR |
| 残留异常 | 不应有 ABNORMAL 节点 | 同步后仍有 binding_status = ABNORMAL |
⚠️ WARNING |
| 残留依赖错误 | 不应有 DEPENDENCY_ERROR | 同步后仍有依赖阻断 | ⚠️ WARNING |
统计指标
| 指标 | 计算方式 | 用途 |
|---|---|---|
| 成功率 | SUCCESS / (CREATE + UPDATE) |
评估同步质量 |
| 异常率 | (ABNORMAL + WARNING) / total |
发现数据质量问题 |
| 阻断率 | DEPENDENCY_ERROR / total |
发现依赖关系问题 |
决策规则
| 检查结果 | ERROR 数量 | 决策 |
|---|---|---|
| ✅ 通过 | 0 | 继续持久化,正常结束 |
| ⚠️ 有警告 | 0, WARNING ≥1 | 继续持久化,打印警告报告 |
| ❌ 有错误 | ≥1 | 继续持久化(已同步无法回滚),打印错误报告 |
注意: Post-Sync Check 即使有 ERROR 也不会终止流程,因为同步已经执行,无法回滚。检查目的是发现问题并记录,供后续修复。
附录: 状态字段说明
SyncNode 核心字段
| 字段 | 类型 | 设置阶段 | 含义 |
|---|---|---|---|
binding_status |
BindingStatus | Bind 阶段 | 绑定健康状态 |
action |
SyncAction | Create/Update 阶段 | 待执行动作 |
status |
SyncStatus | Physical Sync 阶段 | 执行结果状态 |
data_id |
str | 数据加载 / CREATE 后 | 业务主键ID(CREATE 前为 "") |
depend_ids |
List[str] | 数据加载 | 依赖节点的 node_id 列表 |
error |
str | None | Physical Sync 阶段 | 错误信息(失败时) |
状态流转时序
[数据加载] [Bind] [Create/Update] [Physical Sync]
binding_status: UNCHECKED → NORMAL/MISSING/... → (保持) → (保持)
action: NONE → NONE → CREATE/UPDATE → (保持)
status: PENDING → PENDING → PENDING → SUCCESS/FAILED
data_id: 从源加载 → (保持) → 可能为 "" → CREATE 后设置