Files
ecm_sync_system/docs/sync_flow.md
T

133 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.
# 同步流程(pipeline -> strategy -> datasource
## 1. 全流程主线
1. pipeline 装配组件并加载持久化。
2. pipeline 为 datasource 绑定 collection,但不在 bootstrap 阶段全量拉取业务数据。
3. pipeline 在每个 `node_type` 开始执行前,单独加载该 `node_type` 的最新数据。
4. pipeline 调用 `strategy.bind()`
5. pipeline 调用 `strategy.create()` / `strategy.update()` 生成动作节点。
6. pipeline 调用 `datasource.sync_all()` 执行动作并回写执行状态。
7. pipeline 输出统计并持久化。
## 2. reset 时序
### 2.1 加载时恢复
-`sync_state_machine/common/collection.py::load_from_persistence()` 执行。
- 语义:仅恢复持久化状态,并记录本轮 `sync_log``persistence` 恢复来源;不在此阶段做状态归并。
- 恢复后保留 `data/origin_data`,等待 bootstrap 阶段的 datasource 刷新覆盖最新数据。
### 2.2 bootstrap datasource refresh
- pipeline 在持久化恢复后、`E01` 之前,按已恢复到内存的 `node_type` 做一次 datasource 刷新。
- 实际实现:`sync_state_machine/pipeline/full_sync_pipeline.py::_bootstrap_refresh_latest_data()`
- 语义:
- 如果 datasource 命中节点,则用最新数据覆盖持久化快照,并清除 `_loaded_from_persistence` 标记。
- 如果 datasource 未命中节点,则节点保留持久化快照,供 `E01` 判断它是 stale zombie 还是 bootstrap 异常隔离对象。
### 2.3 加载后 cleanup
- pipeline 在 datasource refresh 完成后调用 `run_phase2_cleanup()`
- 实际实现:`sync_state_machine/sync_system/strategy_ops/reset_ops.py::run_phase2_cleanup()`
- 核心事件:`E01``S16 -> S00/S15`)。
- `S16` 表示“bootstrap 输入态”:持久化恢复出的节点,叠加 datasource refresh 后仍待 `E01` 判定的节点集合。
- 判定:
- `is_create_zombie=true` -> `S15`,节点删除(并尝试解绑);
- `bootstrap_source_present=false && has_binding_record=false` -> `S15`,说明该节点仅残留在 persistence 中且未绑定,按 stale zombie 清理;
- `bootstrap_source_present=false && has_binding_record=true` -> `S17`,说明该节点存在绑定,但 datasource refresh 未返回,进入 bootstrap 加载异常隔离;
- 其余情况 -> `S00`,节点归并为 `UNCHECKED/NONE/PENDING` 并清空 `error`
### 2.2.1 `enable_persistence` 与 bootstrap 关系
- `enable_persistence=true`:先 `load_from_persistence()`,再做 bootstrap datasource refresh,最后对输入节点执行 `E01``S16` 域内处理)。
- `enable_persistence=false`:本轮没有持久化输入,`S16/E01` 不参与;随后 Stage1 新加载节点直接进入本轮 bind/create/update/sync 主流程。
### 2.4 加载异常隔离(S17
- 持久化中若出现非法枚举值(`action/status/binding_status`),节点标记为 `bootstrap_blocked``S17` 语义)。
- 持久化恢复后若节点存在绑定,但 bootstrap datasource refresh 未返回该节点,也会进入 `S17` 语义并隔离。
- 这类节点保留 `error`,用于人工排查。
- 这类节点不会进入 `E01`,也不会参与 bind/create/update/sync 自动流程。
## 3. bind 流程(strategy_ops
- 入口:`sync_state_machine/sync_system/strategy.py::DefaultSyncStrategy.bind()`
- 入口状态:`S00`
### bind 编排步骤
- strategy 先通过 `bind_ops.collect_bind_entry_nodes()` 收集两侧 `S00` 节点,作为 bind 入口工作集。
- `phase_core_state()` 对入口工作集触发 `E10`,把“已有绑定记录”和“无绑定记录”的节点分流到 `S01/S03/S04/S02`
- `phase_dependency_check()` 对仍需继续处理的缺绑节点触发 `E15`,把缺数据、依赖不满足、语义错误的节点拦到 `S03/S05/S04`
- strategy 再通过 `bind_ops.collect_auto_bind_ready_nodes()` 收窄工作集,只保留当前 `S02 + binding_status=MISSING` 的节点进入自动绑定判定。
- strategy 在顶层显式构造 orphan create 控制参数,并传给 `bind_ops.plan_auto_bind_updates()` 生成自动绑定/自动创建决策。
- `apply_auto_bind_updates()` 对这些决策逐个触发 `E16`,把节点迁移到 `S01/S04/S05/S13`
- `refresh_bind_data_id()` 最后按绑定关系回填两侧节点的 `context.bind_data_id`
### 核心绑定判定
- `phase_core_state()` 内触发 `E10`
- 有绑定记录:根据本端/对端有效性进入 `S01/S03/S04`
- 无绑定记录:进入 `S02`
注意:如果节点在 bootstrap 阶段已被隔离为 `S17` 语义(`bootstrap_blocked=true`),默认不会进入 bind 工作集。
### 依赖检查
- `phase_dependency_check()` -> `check_dependencies_for_nodes()` 内触发 `E15`
- `data_present=true``dep_satisfied=true` -> 不迁移,节点保持在 `S02`,继续进入自动绑定判定。
- `data_present=false` -> `S03`
- `dep_satisfied=false` -> `S05`
- `depend_fields` 为空本身不会触发阻断;若节点数据存在,则等价于“当前依赖满足”,继续留在 `S02`
### 自动绑定
- `phase_auto_binding()` 内触发 `E16`
- `ONE_TO_ONE` -> `S01``AMBIGUOUS/KEY_MISSING/DATA_MISSING` -> `S04``KEY_ID_RESOLVE_FAIL` -> `S05`
- `ZERO + create_enabled=true` -> `S13``PEER_CREATING/NONE/PENDING`,待对侧创建中间态);`ZERO + create_enabled=false` -> `S04`
- `auto_bind=false` 时,`E16` 直接落 `S04`(自动绑定未启用),不会进入自动创建路径。
## 4. create/update 准备
### create
- 入口:`sync_state_machine/sync_system/strategy.py::DefaultSyncStrategy.create()`
- 运行时事件:`E20``E21/E22` 为配置保留分支,当前 strategy 未接入自动执行)
- 运行时入口状态:`S13`
- 说明:`E16` 负责识别“需自动创建”并将源节点置为 `S13`;业务代码执行对侧 spawn+bind 后,再通过 `E20``S13` 提交到 `S01`(成功);若 spawn 失败,`E20` 不迁移并保留在 `S13` 等待人工/重试。
- `data_id` 约束:
- `S13/S01` 要求源节点 `data_id` 存在(`data_id_exist=true`)。
- 新创建目标节点在 `S06/S10C` 阶段允许 `data_id` 为空;`CREATE SUCCESS``S11C`)必须携带非空 `result_data_id`,否则事件直接报错。
### update
- 入口:`sync_state_machine/sync_system/strategy.py::DefaultSyncStrategy.update()`
- 事件:`E30`
- 入口状态(source 节点):`S01`
- `target_has_data_id=false``update_id_resolve_ok=false` -> `S08`
- `has_diff=true` -> `S07`
### post-create 归并
- 入口:`sync_state_machine/pipeline/full_sync_pipeline.py::normalize_create_success_to_update_entry()`
- 事件:`E41`
- 语义:`CREATE` 执行成功后,节点先落 `S11C`SUCCESS),再归并 `S11C -> S01`,作为同轮 update 起点。
## 5. 执行回写(datasource
- 入口:`sync_state_machine/datasource/datasource.py`
- 事件:`E40C_START/E40C``E40U_START/E40U``E40D_START/E40D`
- 当前约束:delete stage 尚未接入 strategy 主流程;若运行时检测到 `S09/DELETE` 节点,DataSource 会显式抛错阻断(避免散落删除执行)。
- 执行准备入口状态:
- `CREATE`: `S06`
- `UPDATE`: `S07`
- `DELETE`: `S09`
- `S10` 为执行中状态(由本轮执行产生),执行流转固定为“入口 -> S10 -> 终态”,不再出现入口直接到成功/失败。
- 结果:
- `SUCCESS -> S11C | S11U | S11D`(按动作)
- `FAILED -> S12C | S12U | S12D`(按动作)
- `IN_PROGRESS -> S10C | S10U | S10D`(按动作,支持异步 polling 闭环)
- `SKIPPED + action=UPDATE -> S14`
## 6. 各阶段入口状态总表
| 阶段 | 入口状态 | 说明 |
|---|---|---|
| Reset(E01) | `S16` | create_zombie -> `S15`;其余 -> `S00` |
| Bind(E10/E15/E16) | `S00` | bind 三段统一从 `S00` 进入 |
| Create Prepare(E20/E21/E22) | `S02` / `S13` | 配置入口含 `S02/S13`;当前 strategy 运行时只处理 `S13``E20` 提交,`S02` 分支保留未接入 |
| Post-create Ready(E41) | `S11C` | `CREATE` 成功节点归并到 `S01`,进入同轮 update |
| Update Prepare(E30) | `S01` | 仅从 NORMAL 继续判定(create 成功先经 E41 归并) |
| Sync Execute(E40C/U/D) | `S06` / `S07` / `S09` / `S10C` / `S10U` / `S10D` | 执行与回写:按动作分事件;入口先进入 `S10*`,再由 `S10*` 进入 `S11*`/`S12*`;轮询返回 `IN_PROGRESS` 时只记录任务进度,不新增状态迁移;仅 UPDATE 可走 `S14` |
| 隔离(人工) | `S17` | 不参与自动阶段,仅人工处理 |
## 7. 不变量保障
- 每次事件落地后,`sync_state_machine/engine/events.py::_apply_decision()` 会执行 `runtime.check_invariants()`
- 违反 invariant 直接抛错,终止当前路径。