Files
ecm_sync_system/docs/archive/from_root_docs/节点生命周期状态转移.md
T
strepsiades 4a9c444b10 first commit
2026-03-09 16:31:42 +08:00

23 KiB
Raw Blame History

节点生命周期状态转移 (Node Lifecycle State Transitions)

本文档从一个单节点视角出发,完整描述一个 SyncNode 从诞生到持久化的全部状态变化。

代码映射

  • 持久化重置:sync_system_new/common/state_reset.pyNodeStateReset
  • 绑定判定:sync_system_new/sync_system/decision.pySyncDecisionEngine
  • 策略执行:sync_system_new/sync_system/strategy.pyDefaultSyncStrategy
  • ID 解析:sync_system_new/sync_system/resolve_ids.pyIDResolver
  • 物理同步:sync_system_new/datasource/datasource.pyBaseDataSource
  • Handler 降级:sync_system_new/domain/*/api_handler.py

状态字段一览

字段 类型 含义 谁负责设置
binding_status BindingStatus 绑定健康状态 Strategy.bind()
action SyncAction 待执行动作 Strategy.create() / update()
status SyncStatus 执行结果状态 DataSource.sync_all()
data_id str 业务主键ID 数据加载 / DataSource 回填
error str | None 错误信息 各阶段

完整生命周期

第一次运行(无持久化数据)

┌─────────────────────────────────────────────────────────────────────────────┐
│                          节点尚不存在                                        │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                            数据源加载 (Stage 1)
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 节点诞生                                                                     │
│                                                                             │
│ binding_status = UNCHECKED     ← Collection 创建节点时的初始值                │
│ action         = NONE                                                       │
│ status         = PENDING                                                    │
│ data_id        = "abc-123"     ← 从数据源获取的业务主键                       │
│ data           = {...}         ← 从数据源获取的业务数据                       │
│ origin_data    = {...}         ← 同上,原始快照                              │
│ depend_ids     = []            ← 空,待 bind 阶段填充                        │
│ error          = None                                                       │
└─────────────────────────────────────────────────────────────────────────────┘

第二次及后续运行(有持久化数据)

┌─────────────────────────────────────────────────────────────────────────────┐
│ 从持久化恢复(原始值)                                                        │
│                                                                             │
│ binding_status = NORMAL (上次值)                                             │
│ action         = UPDATE (上次值)                                             │
│ status         = SUCCESS (上次值)                                            │
│ data_id        = "abc-123"                                                  │
│ error          = None (上次值)                                               │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                          阶段1:加载时重置 (NodeStateReset)
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段1重置后                                                                  │
│                                                                             │
│ binding_status = UNCHECKED     ← 重置(每次重新判定)                         │
│ action         = UPDATE        ← 保留!(用于阶段2识别 CREATE 失败)          │
│ status         = SUCCESS       ← 保留!(不自动重置)                         │
│ data_id        = "abc-123"     ← 不变                                       │
│ data           = None          ← 重置(等待数据源覆盖)                       │
│ origin_data    = None          ← 重置(等待数据源覆盖)                       │
│ error          = None          ← 清空(避免上次错误干扰)                     │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                          阶段2CREATE 僵尸清理
                          (只影响 action=CREATE 且 status=FAILED 或 data_id="" 的节点)
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段2清理后(正常节点)                                                       │
│                                                                             │
│ action         = NONE          ← 全部重置为 NONE,准备进入策略阶段            │
│ 其他字段不变                                                                  │
│                                                                             │
│ (若为 CREATE 僵尸节点:直接从 Collection 删除 + 解除绑定)                   │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                           数据源加载覆盖 (Stage 1 后半)
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据源加载后                                                                  │
│                                                                             │
│ data           = {...}         ← 数据源最新数据覆盖                           │
│ origin_data    = {...}         ← 数据源最新数据覆盖                           │
│ 其他字段不变                                                                  │
└─────────────────────────────────────────────────────────────────────────────┘

Bind 阶段状态转移 (Stage 2.1)

Bind 分三个子阶段顺序执行,每个子阶段只修改 binding_status

Phase 1: 核心状态判定

代码strategy_ops/bind_ops.py::phase_core_state()SyncDecisionEngine.determine_core_status()

输入:binding_status = UNCHECKED

                    ┌─ 有绑定记录?
                    │
              ┌─────┤
              │ YES │
              │     └─── 本地数据 + 远程数据 都健康?
              │              │
              │         ┌────┴────┐
              │         │ YES     │ NO
              │         ▼         ▼
              │      NORMAL    WARNING / ABNORMAL
              │
              │ NO
              └──────────────► MISSING
输入条件 binding_status 输出
有绑定,双方数据健康 NORMAL
有绑定,自身数据损坏 ABNORMAL
有绑定,对方数据损坏 WARNING
无绑定记录 MISSING

Phase 2: 依赖检查

代码strategy_ops/bind_ops.py::phase_dependency_check()strategy_ops/bind_ops.py::check_dependencies_for_nodes()

仅处理 binding_status = MISSING 的节点,其他状态跳过。

输入:binding_status = MISSING

                    ┌─ 有配置 depend_fields
                    │
              ┌─────┤
              │ NO  └──► 保持 MISSING(无依赖,直接通过)
              │
              │ YES ──► 逐一检查依赖节点:
              │              │
              │         ┌────┴────────────────────────┐
              │         │ 全部依赖节点:               │
              │         │  - 存在? ✅                │
              │         │  - binding_status=NORMAL ✅ │
              │         │  - data_id 非空? ✅         │
              │         └────┬────────────────────────┘
              │              │
              │         ┌────┴────┐
              │         │ 全通过   │ 任一失败
              │         ▼         ▼
              │      MISSING   DEPENDENCY_ERROR
              │                + error = "依赖项不满足: ..."
              │                + depend_ids 已填充

副作用:无论检查是否通过,都会填充 node.depend_ids

Phase 3: 自动绑定

代码strategy_ops/bind_ops.py::phase_auto_binding()

仅处理 binding_status = MISSING 且依赖已通过的节点

输入:binding_status = MISSING

auto_bind = False?
    └──► MISSING → WARNING"跳过自动绑定,无法确认是否需要创建"

auto_bind = True:
    │
    ├─ 业务键字段缺失?
    │   └──► MISSING → WARNING"缺少业务键字段: ...")
    │
    ├─ 业务键中 ID 字段解析失败?
    │   └──► MISSING → DEPENDENCY_ERROR"业务键中的ID字段解析失败")
    │
    ├─ 去噪后 1:1 匹配?
    │   └──► MISSING → NORMAL(执行自动绑定,建立绑定记录)
    │
    ├─ N:0(无远程匹配)?
    │   ├──► create_enabled = True  → MISSING → NORMAL,并 spawn 目标 CREATE 节点(S06
    │   └──► create_enabled = False → MISSING → WARNING(孤儿且不允许自动创建,需人工处理)
    │
    └─ N:M / 多对多 / 多对一?
        └──► MISSING → WARNING"存在多个候选节点,无法自动绑定")

Bind 阶段结束后可能的状态

binding_status 后续可执行的操作
NORMAL UPDATE(如有差异)
MISSING CREATE
ABNORMAL 无(需人工)
WARNING 无(阻止自动操作)
DEPENDENCY_ERROR 无(等待依赖修复)

Create 阶段状态转移 (Stage 2.2)

代码create()strategy_ops/create_ops.py::add_create_action()

对齐说明(与状态机配置一致)

  • create_prepare 的入口仍是源节点 binding_status = MISSING
  • 但当前配置下,N:0 + create_enabled=true 可以在自动绑定阶段直接把源节点转为 NORMAL,并 emit spawn_target_node_state=S06
  • 因此“触发创建”的入口有两条:
    1. auto_bind 分支(E08f)直接 spawn
    2. create_prepare 分支(E09b)在 create 检查成功后 spawn。

仅处理 binding_status = MISSING 的节点

输入:源节点 binding_status = MISSING, action = NONE

        ┌─ 源节点数据为空?
        │   └──► 跳过,记录日志
        │
        ├─ IDResolver.resolve_and_validate() 失败?
        │   └──► binding_status → DEPENDENCY_ERROR
        │        error = "创建时依赖 ID 未解析: ..."
        │
        └─ 成功 ──► 在目标 Collection 创建新节点
                    │
                    ├─ 源节点:
                    │   binding_status → NORMAL"从本地创建到远程"
                    │   action 不变(仍为 NONE
                    │
                    └─ 新建目标节点:
                        binding_status = NORMAL
                        action = CREATE
                        status = PENDING
                        data_id = ""(待远程返回)
                        data = 已替换 ID 的数据副本
                        │
                        └─ 同时建立绑定记录:local_id ↔ remote_id

Commit Creates — 物理同步 (Stage 2.3)

代码DataSource.sync_all() → Handler.create()

输入:action = CREATE, status = PENDING

        ┌─ Handler 执行 API 调用
        │
        ├─ 同步 API 成功(200/201)
        │   └──► status → SUCCESS
        │        data_id = "remote-xyz"(远程分配的 ID 回填)
        │        data["id"] = "remote-xyz"(主键字段回写)
        │
        ├─ 异步 API 提交成功(202)
        │   └──► status → IN_PROGRESS
        │        context.task_id = "task-xxx"
        │        │
        │        └─ 轮询结果:
        │            ├─ 成功 → status → SUCCESS, data_id 回填
        │            ├─ 失败 → status → FAILED, error = "..."
        │            └─ 超时 → status → FAILED, error = "Timeout"
        │
        └─ API 失败(4xx/5xx
            └──► status → FAILED
                 error = "远程操作失败: ..."

Update 阶段状态转移 (Stage 2.4)

代码update()strategy_ops/update_ops.py::add_update_action()

对齐说明:图里的 E12aS01 出发并不矛盾。S01 约束的是“源节点”有 data_id E12a 检查的是“目标节点”是否有 data_idguard: target_has_data_id=false),两者不是同一个对象。

过滤条件binding_status = NORMALstatus != FAILED

输入:源节点 binding_status = NORMAL, action = NONE

        ┌─ status == FAILED
        │   └──► 跳过(保护上次失败的状态和错误信息)
        │
        ├─ 源节点数据为空?
        │   └──► 跳过
        │
        ├─ 通过绑定找不到目标节点?
        │   └──► 跳过,记录日志
        │
        ├─ 目标节点 data_id 为空?
        │   └──► 目标节点 status → PRECONDITION_FAILED
        │        "目标节点缺少 data_id"
        │
        ├─ IDResolver.resolve_and_validate() 失败?
        │   └──► 目标节点 action → UPDATE
        │        目标节点 status → PRECONDITION_FAILED
        │        目标节点 error = "依赖 ID 未解析: ..."
        │
        ├─ _needs_update() 判定无差异?
        │   └──► 跳过(不设置 action)
        │
        └─ 有差异 ──► 更新目标节点:
                      action → UPDATE
                      status → PENDING
                      data = 已替换 ID 的数据副本
                      error = None

Commit Updates — 物理同步 (Stage 2.5)

代码DataSource.sync_all() → Handler.update()

输入:action = UPDATE, status = PENDING

        ┌─ Handler 构建 API 请求
        │
        ├─ Handler 发现无实际变更字段
        │   └──► action → NONEHandler 降级,不提交 API
        │
        ├─ 同步 API 成功(200)
        │   └──► status → SUCCESS
        │
        ├─ 异步 API 提交成功(202)
        │   └──► status → IN_PROGRESS → (轮询)→ SUCCESS / FAILED
        │
        └─ API 失败
            └──► status → FAILED
                 error = "远程操作失败: ..."

持久化阶段 (Stage 5)

节点当前状态原样写入存储。

持久化字段:
  ✅ node_id, data_id, data, origin_data
  ✅ binding_status, action, status, error, context
  ❌ depend_ids(不持久化,下次 bind 时从 data 实时计算)

全景时间线表

下表展示一个节点在典型场景中的状态变化

场景 A:首次创建(本地 → 远程)

阶段 binding_status action status data_id error
数据加载 UNCHECKED NONE PENDING "abc-123" None
bind phase1 MISSING NONE PENDING "abc-123" None
bind phase2 MISSING NONE PENDING "abc-123" None
bind phase3 MISSING NONE PENDING "abc-123" None
create NORMAL NONE PENDING "abc-123" None
(新建远程节点) NORMAL CREATE PENDING "" None
commit create NORMAL CREATE SUCCESS "remote-456" None
持久化 NORMAL CREATE SUCCESS "remote-456" None

场景 B:日常更新

阶段 binding_status action status data_id error
持久化恢复 NORMAL→UNCHECKED CREATE→保留 SUCCESS→保留 "abc-123" 清空
阶段2 action重置 UNCHECKED NONE SUCCESS "abc-123" None
数据源覆盖 UNCHECKED NONE SUCCESS "abc-123" None
bind phase1 NORMAL NONE SUCCESS "abc-123" None
update 检测差异 NORMAL NONE SUCCESS "abc-123" None
(目标节点) NORMAL UPDATE PENDING "remote-456" None
commit update NORMAL UPDATE SUCCESS "remote-456" None

场景 C:依赖阻断

阶段 binding_status action status data_id error
数据加载 UNCHECKED NONE PENDING "contract-789" None
bind phase1 MISSING NONE PENDING "contract-789" None
bind phase2 DEPENDENCY_ERROR NONE PENDING "contract-789" "依赖项不满足: project_id: 状态=dep_err"
(后续阶段不处理)
持久化 DEPENDENCY_ERROR NONE PENDING "contract-789" "依赖项不满足..."

场景 D:CREATE 失败 → 下次自动恢复

阶段 binding_status action status data_id error
第 1 次运行
create NORMAL CREATE PENDING "" None
commit create 失败 NORMAL CREATE FAILED "" "远程操作失败: 500"
持久化 NORMAL CREATE FAILED "" "远程操作失败: 500"
第 2 次运行
阶段1恢复 UNCHECKED CREATE FAILED "" None
阶段2僵尸清理 (节点被删除,绑定被解除)
数据源重新加载 UNCHECKED NONE PENDING "" → 新 node_id None
bind → create 重新走首次创建流程

场景 E:UPDATE 失败 → 保留供人工处理

阶段 binding_status action status data_id error
第 1 次运行
update NORMAL UPDATE PENDING "remote-456" None
commit update 失败 NORMAL UPDATE FAILED "remote-456" "403 Forbidden"
持久化 NORMAL UPDATE FAILED "remote-456" "403 Forbidden"
第 2 次运行
阶段1恢复 UNCHECKED UPDATE FAILED "remote-456" None
阶段2 action重置 UNCHECKED NONE FAILED "remote-456" None
bind NORMAL NONE FAILED "remote-456" None
update 跳过 (status=FAILED,自动跳过)
持久化 NORMAL NONE FAILED "remote-456" None
(需人工重置 status 后才能重新更新)

状态转换合法性约束

binding_status 允许的转换

UNCHECKED → NORMAL          (核心判定: 有绑定且健康)
UNCHECKED → MISSING         (核心判定: 无绑定记录)
UNCHECKED → ABNORMAL        (核心判定: 自身数据损坏)
UNCHECKED → WARNING         (核心判定: 对方数据损坏)

MISSING → DEPENDENCY_ERROR  (依赖检查: 父节点不满足)
MISSING → WARNING           (自动绑定: 业务键缺失 / 多候选 / auto_bind=False)
MISSING → DEPENDENCY_ERROR  (自动绑定: ID 字段解析失败)
MISSING → NORMAL            (自动绑定: 1:1 成功)
MISSING → NORMAL            (create: 源节点创建成功后)
MISSING → DEPENDENCY_ERROR  (create: ID 解析失败)

注意:当前 SyncNode.set_binding_status() 不做转换合法性校验【未实现】。

action 允许的转换

NONE → CREATE               (create: 新建目标节点)
NONE → UPDATE               (update: 检测到差异)
UPDATE → NONE               (Handler 降级: 实际无变更字段)

status 允许的转换

PENDING → IN_PROGRESS       (DataSource: 异步提交成功)
PENDING → SUCCESS           (DataSource: 同步执行成功)
PENDING → FAILED            (DataSource: 执行失败)
PENDING → SKIPPED           (DataSource: 被跳过)
                            (常见于 CREATE/UPDATEHandler 返回 SKIPPED(无可执行操作/业务条件不满足),对应状态机里的 E19→S13 或 E20→S14
PENDING → PRECONDITION_FAILED (Strategy: 前置条件不满足)
IN_PROGRESS → SUCCESS       (DataSource: 异步轮询成功)
IN_PROGRESS → FAILED        (DataSource: 异步轮询失败/超时)

注意:当前 SyncNode.set_status() 不做转换合法性校验【未实现】。


参考文档