133 lines
8.5 KiB
Markdown
133 lines
8.5 KiB
Markdown
# 同步流程(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 直接抛错,终止当前路径。
|