623 lines
19 KiB
YAML
623 lines
19 KiB
YAML
meta:
|
||
name: node_state_machine
|
||
version: 0.1
|
||
language: zh-CN
|
||
desc: "节点状态机配置(用于驱动/校验未来的状态机组件)"
|
||
|
||
# 命名约定(配置内唯一真名;图上不再有 display/别名):
|
||
# - Sxx: State / PseudoState(节点)
|
||
# - Exx: Event(事件)
|
||
# - 事件分支由渲染器在边上追加 a/b/c...(对应 outcomes 顺序)
|
||
id_convention:
|
||
state_prefix: "S"
|
||
event_prefix: "E"
|
||
|
||
# Variables are computed by the caller and passed into the state machine.
|
||
# Rule: the YAML never directly mutates "peer" nodes. Cross-node changes must be
|
||
# represented as separate invocations with different inputs.
|
||
context:
|
||
is_create_zombie: { type: bool, desc: "初始化时判定为 CREATE 僵尸:action=CREATE 且 (status=FAILED 或 data_id 为空)" }
|
||
bootstrap_source_present: { type: bool, desc: "bootstrap 阶段 datasource 刷新后,当前节点是否在最新数据源中命中" }
|
||
|
||
has_binding_record: { type: bool, desc: "binding_manager 里存在绑定记录 (remote_id/local_id 非空)" }
|
||
core_binding_result: { type: enum, values: [NORMAL, WARNING, ABNORMAL], desc: "SyncDecisionEngine.determine_core_status() 给本节点的结果(仅在 has_binding_record=true 时有效)" }
|
||
peer_core_binding_result: { type: enum, values: [NORMAL, WARNING, ABNORMAL], desc: "SyncDecisionEngine.determine_core_status() 给对方节点的结果(用于静态约束 core matrix 对称性)" }
|
||
|
||
data_present: { type: bool, desc: "node.get_data() 非空" }
|
||
has_depend_fields: { type: bool, desc: "config.depend_fields 非空" }
|
||
dep_satisfied: { type: bool, desc: "依赖满足:空依赖值跳过;非空依赖值必须能在 collection 中按 data_id 找到且节点状态可用" }
|
||
|
||
auto_bind_enabled: { type: bool, desc: "config.auto_bind && auto_bind_fields 非空" }
|
||
auto_bind_outcome:
|
||
type: enum
|
||
values: [ONE_TO_ONE, AMBIGUOUS, ZERO, KEY_MISSING, KEY_ID_RESOLVE_FAIL, DATA_MISSING]
|
||
desc: "自动绑定结果(由业务代码完成全集搜索+去噪后 1:1 判定)"
|
||
|
||
create_enabled: { type: bool, desc: "create() 是否对当前方向启用 (local_orphan_action/remote_orphan_action)" }
|
||
create_id_resolve_ok: { type: bool, desc: "IDResolver.resolve_and_validate() 是否成功" }
|
||
spawn_success: { type: bool, desc: "创建副作用(spawn + bind)是否成功" }
|
||
|
||
update_enabled: { type: bool, desc: "update() 是否启用 (update_direction != NONE)" }
|
||
target_has_data_id: { type: bool, desc: "UPDATE 目标节点 data_id 非空" }
|
||
update_id_resolve_ok: { type: bool, desc: "UPDATE 前 IDResolver.resolve_and_validate() 是否成功" }
|
||
has_diff: { type: bool, desc: "needs_update_check 判定有差异" }
|
||
|
||
handler_result:
|
||
type: enum
|
||
values: [SUCCESS, FAILED, SKIPPED, IN_PROGRESS]
|
||
desc: "TaskResult.status"
|
||
poll_timeout: { type: bool, desc: "异步轮询超过 max_retries" }
|
||
execute_action:
|
||
type: enum
|
||
values: [CREATE, UPDATE, DELETE, NONE]
|
||
desc: "E40 执行时的动作类型(用于区分 SKIPPED 的落点)"
|
||
|
||
# State IDs are for visualization and static validation.
|
||
# NOTE: 本状态机按“加载时清空执行状态,再开始本轮”建模:
|
||
# - 加载/初始化后,稳态节点的 status 统一回到 PENDING
|
||
# - 上一轮遗留的 FAILED/SUCCESS 等不作为本轮的入口状态
|
||
states:
|
||
# --- Stable entry to bind (after init reset + zombie cleanup) ---
|
||
S00:
|
||
binding_status: UNCHECKED
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
进入 bind 前的唯一起点
|
||
binding_status: UNCHECKED
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
# --- Bind results ---
|
||
S01:
|
||
binding_status: NORMAL
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
绑定正常(待后续 update 检查)
|
||
binding_status: NORMAL
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
S02:
|
||
binding_status: MISSING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
孤儿节点(可能进入 create / auto_bind / delete)
|
||
binding_status: MISSING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
S03:
|
||
binding_status: ABNORMAL
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
数据损坏/缺失(阻断态,需人工)
|
||
binding_status: ABNORMAL
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
S04:
|
||
binding_status: WARNING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
不可自动处理(阻断态,需人工)
|
||
binding_status: WARNING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
S05:
|
||
binding_status: DEPENDENCY_ERROR
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
依赖项不满足/ID解析失败(阻断态)
|
||
binding_status: DEPENDENCY_ERROR
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
# --- Create & Update prepared actions (target-side nodes) ---
|
||
S06:
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: PENDING
|
||
data_id_exist: false
|
||
shape: pseudo
|
||
desc: |
|
||
待执行 CREATE(执行入口)
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: PENDING
|
||
data_id_exist: false
|
||
S07:
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
准备更新
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
S08:
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: PRECONDITION_FAILED
|
||
data_id_exist: [false, true]
|
||
desc: |
|
||
更新前置失败(缺 data_id / 依赖ID未解析等)
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: PRECONDITION_FAILED
|
||
data_id_exist: false | true
|
||
|
||
S09:
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
待执行 DELETE(执行入口)
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
# --- Sync results ---
|
||
S10C:
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: IN_PROGRESS
|
||
data_id_exist: false
|
||
desc: |
|
||
CREATE 异步执行中
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: IN_PROGRESS
|
||
data_id_exist: false
|
||
S10U:
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: IN_PROGRESS
|
||
data_id_exist: true
|
||
desc: |
|
||
UPDATE 异步执行中
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: IN_PROGRESS
|
||
data_id_exist: true
|
||
S10D:
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: IN_PROGRESS
|
||
data_id_exist: true
|
||
desc: |
|
||
DELETE 异步执行中
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: IN_PROGRESS
|
||
data_id_exist: true
|
||
S11C:
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
desc: |
|
||
CREATE 执行成功
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
S11U:
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
desc: |
|
||
UPDATE 执行成功
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
S11D:
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
desc: |
|
||
DELETE 执行成功(随后删除节点)
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: SUCCESS
|
||
data_id_exist: true
|
||
S12C:
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: FAILED
|
||
data_id_exist: [false, true]
|
||
desc: |
|
||
CREATE 执行失败
|
||
binding_status: NORMAL
|
||
action: CREATE
|
||
status: FAILED
|
||
data_id_exist: false | true
|
||
S12U:
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: FAILED
|
||
data_id_exist: [false, true]
|
||
desc: |
|
||
UPDATE 执行失败
|
||
binding_status: NORMAL
|
||
action: UPDATE
|
||
status: FAILED
|
||
data_id_exist: false | true
|
||
S12D:
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: FAILED
|
||
data_id_exist: [false, true]
|
||
desc: |
|
||
DELETE 执行失败
|
||
binding_status: NORMAL
|
||
action: DELETE
|
||
status: FAILED
|
||
data_id_exist: false | true
|
||
|
||
S13:
|
||
binding_status: PEER_CREATING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
desc: |
|
||
对侧创建进行中(本侧等待提交)
|
||
binding_status: PEER_CREATING
|
||
action: NONE
|
||
status: PENDING
|
||
data_id_exist: true
|
||
|
||
# Handler 在 update 路径中可能把 action 降级为 NONE 并返回 SKIPPED。
|
||
# NOTE: 这会产生 (NORMAL, NONE, SKIPPED),并且该节点会被统计到 none 列。
|
||
S14:
|
||
binding_status: NORMAL
|
||
action: NONE
|
||
status: SKIPPED
|
||
data_id_exist: true
|
||
desc: |
|
||
更新被跳过(执行时降级为 NONE)
|
||
binding_status: NORMAL
|
||
action: NONE
|
||
status: SKIPPED
|
||
data_id_exist: true
|
||
|
||
S17:
|
||
binding_status: ABNORMAL
|
||
action: NONE
|
||
status: FAILED
|
||
data_id_exist: [false, true]
|
||
desc: |
|
||
bootstrap 加载异常隔离态
|
||
持久化恢复后存在绑定,但 datasource 刷新未命中最新数据
|
||
binding_status: ABNORMAL
|
||
action: NONE
|
||
status: FAILED
|
||
data_id_exist: false | true
|
||
|
||
pseudo_states:
|
||
S15:
|
||
desc: "节点被清理删除(zombie cleanup)"
|
||
S16:
|
||
desc: "持久化加载输入态:本轮 E01 的输入节点集合(可包含上轮遗留与已稳定节点)"
|
||
|
||
stages:
|
||
bootstrap:
|
||
desc: "启动/初始化:持久化恢复后先做 datasource 刷新,再按 E01 清理僵尸或归并/隔离"
|
||
entry_states: [S16]
|
||
exit_states: [S00, S15, S17]
|
||
|
||
bind:
|
||
desc: "绑定判定:core matrix +(可选)依赖检查 +(可选)自动绑定;stage 仅约束边界,不要求内部顺序"
|
||
entry_states: [S00]
|
||
exit_states: [S01, S02, S03, S04, S05, S13]
|
||
|
||
create_prepare:
|
||
desc: "Create: 从 MISSING 源节点发起创建请求并在副作用提交后归并"
|
||
entry_states: [S02, S13]
|
||
exit_states: [S01, S02, S04, S05, S09, S13]
|
||
|
||
update_prepare:
|
||
desc: "Update: 检测差异并在目标节点上设置 UPDATE/PRECONDITION_FAILED"
|
||
entry_states: [S01]
|
||
exit_states: [S01, S07, S08]
|
||
|
||
post_create_ready:
|
||
desc: "创建成功后归并:CREATE 成功节点回到 NORMAL/NONE/PENDING,作为 update 起点"
|
||
entry_states: [S11C]
|
||
exit_states: [S01]
|
||
|
||
sync_execute:
|
||
desc: "Sync: DataSource 调用 handler,根据 TaskResult 更新 status;部分 handler 可能降级 action"
|
||
entry_states: [S06, S07, S09, S10C, S10U, S10D]
|
||
exit_states: [S10C, S10U, S10D, S11C, S11U, S11D, S12C, S12U, S12D, S14]
|
||
|
||
transitions:
|
||
E01:
|
||
stage: bootstrap
|
||
desc: "初始化:先依据 datasource 刷新结果判定 source 是否存在,再做僵尸清理/隔离/归并"
|
||
from: [S16]
|
||
outcomes:
|
||
- when: { is_create_zombie: true }
|
||
to: S15
|
||
- when: { is_create_zombie: false, bootstrap_source_present: false, has_binding_record: false }
|
||
to: S15
|
||
emit:
|
||
note: "持久化孤儿节点未命中最新数据源,按僵尸清理"
|
||
- when: { is_create_zombie: false, bootstrap_source_present: false, has_binding_record: true }
|
||
to: S17
|
||
emit:
|
||
note: "存在绑定但最新数据源未返回节点,进入加载异常隔离"
|
||
- when: { is_create_zombie: false, bootstrap_source_present: true }
|
||
to: S00
|
||
emit:
|
||
note: "bootstrap 已刷新到最新数据,归并到起点"
|
||
|
||
# --- Bind phase1 core ---
|
||
E10:
|
||
stage: bind
|
||
desc: "核心绑定判定:有绑定按core结果分流;无绑定→MISSING"
|
||
from: [S00]
|
||
outcomes:
|
||
- when: { has_binding_record: true, core_binding_result: NORMAL, peer_core_binding_result: NORMAL }
|
||
to: S01
|
||
emit:
|
||
note: "绑定关系一致,进入正常态"
|
||
- when: { has_binding_record: true, core_binding_result: WARNING, peer_core_binding_result: ABNORMAL }
|
||
to: S04
|
||
emit:
|
||
note: "对端异常,本端标记WARNING"
|
||
- when: { has_binding_record: true, core_binding_result: ABNORMAL, peer_core_binding_result: WARNING }
|
||
to: S03
|
||
emit:
|
||
note: "本端异常,进入ABNORMAL"
|
||
- when: { has_binding_record: true, core_binding_result: ABNORMAL, peer_core_binding_result: ABNORMAL }
|
||
to: S03
|
||
emit:
|
||
note: "双方异常,进入ABNORMAL"
|
||
- when: { has_binding_record: false }
|
||
to: S02
|
||
emit:
|
||
note: "无绑定记录,标记为MISSING"
|
||
|
||
# --- Bind phase2 dependency ---
|
||
E15:
|
||
stage: bind
|
||
desc: "依赖检查:失败→阻断;缺少依赖配置→WARNING;通过→不变(不在图上画自环)"
|
||
from: [S02]
|
||
outcomes:
|
||
- when: { has_depend_fields: false, data_present: true }
|
||
to: S04
|
||
emit:
|
||
note: "缺少依赖字段配置,无法自动处理"
|
||
- when: { data_present: false }
|
||
to: S03
|
||
emit:
|
||
note: "节点数据为空或不存在"
|
||
- when: { data_present: true, has_depend_fields: true, dep_satisfied: false }
|
||
to: S05
|
||
emit:
|
||
note: "依赖项不满足"
|
||
|
||
# --- Bind phase3 auto bind ---
|
||
E16:
|
||
stage: bind
|
||
desc: "自动绑定:1:1→NORMAL;歧义/缺字段/数据为空→阻断;N:0 且允许创建→S13 等待创建提交"
|
||
from: [S02]
|
||
outcomes:
|
||
- when: { auto_bind_enabled: false }
|
||
to: S04
|
||
emit:
|
||
note: "自动绑定未启用"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: DATA_MISSING }
|
||
to: S04
|
||
emit:
|
||
note: "节点数据为空,无法自动绑定"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: ONE_TO_ONE }
|
||
to: S01
|
||
emit:
|
||
note: "自动绑定成功(1:1)"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: AMBIGUOUS }
|
||
to: S04
|
||
emit:
|
||
note: "存在多个候选节点,无法自动绑定"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: KEY_MISSING }
|
||
to: S04
|
||
emit:
|
||
note: "缺少业务键字段,无法自动绑定"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: KEY_ID_RESOLVE_FAIL }
|
||
to: S05
|
||
emit:
|
||
note: "业务键中的ID字段解析失败"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: ZERO, create_enabled: true }
|
||
to: S13
|
||
emit:
|
||
spawn_request: true
|
||
spawn_target_node_state: S06
|
||
note: "自动绑定识别为需创建,进入创建提交中间态"
|
||
- when: { auto_bind_enabled: true, auto_bind_outcome: ZERO, create_enabled: false }
|
||
to: S04
|
||
emit:
|
||
note: "未匹配到候选节点且不允许创建"
|
||
|
||
# --- Create prepare ---
|
||
E20:
|
||
stage: create_prepare
|
||
desc: "创建准备阶段2:spawn 成功则提交到 NORMAL;失败保持等待态(不迁移)"
|
||
from: [S13]
|
||
outcomes:
|
||
- when: { spawn_success: true }
|
||
to: S01
|
||
emit:
|
||
note: "创建副作用提交成功"
|
||
|
||
E21:
|
||
stage: create_prepare
|
||
desc: "创建依赖解析失败→DEPENDENCY_ERROR"
|
||
from: [S02]
|
||
outcomes:
|
||
- when: { create_enabled: true, data_present: true, create_id_resolve_ok: false }
|
||
to: S05
|
||
emit:
|
||
note: "创建前依赖ID解析失败"
|
||
|
||
E22:
|
||
stage: create_prepare
|
||
desc: "孤儿自删准备:从 MISSING 进入 DELETE 执行入口"
|
||
from: [S02]
|
||
outcomes:
|
||
- when: {}
|
||
to: S09
|
||
emit:
|
||
note: "孤儿节点按策略进入自删"
|
||
|
||
# --- Update prepare (target-side) ---
|
||
E30:
|
||
stage: update_prepare
|
||
desc: "更新准备:缺data_id/依赖未解析→PRECONDITION_FAILED;有差异→UPDATE"
|
||
from: [S01]
|
||
outcomes:
|
||
- when: { update_enabled: true, target_has_data_id: false }
|
||
to: S08
|
||
emit:
|
||
note: "目标节点缺少data_id"
|
||
- when: { update_enabled: true, target_has_data_id: true, update_id_resolve_ok: false }
|
||
to: S08
|
||
emit:
|
||
note: "更新前依赖ID未解析"
|
||
- when: { update_enabled: true, target_has_data_id: true, update_id_resolve_ok: true, has_diff: true }
|
||
to: S07
|
||
emit:
|
||
note: "检测到数据差异,准备更新"
|
||
|
||
# --- Post-create normalization ---
|
||
E41:
|
||
stage: post_create_ready
|
||
desc: "创建成功后归并到 update 起点"
|
||
from: [S11C]
|
||
outcomes:
|
||
- when: { execute_action: CREATE }
|
||
to: S01
|
||
emit:
|
||
note: "创建成功后转入更新起点"
|
||
|
||
# --- Sync execute (split by action) ---
|
||
E40C_START:
|
||
stage: sync_execute
|
||
desc: "CREATE 执行入口:进入异步执行中"
|
||
from: [S06]
|
||
outcomes:
|
||
- when: { handler_result: IN_PROGRESS }
|
||
to: S10C
|
||
emit:
|
||
note: "CREATE 进入异步执行中"
|
||
|
||
E40C:
|
||
stage: sync_execute
|
||
desc: "CREATE 执行结果回写:进行中/成功/失败"
|
||
from: [S10C]
|
||
outcomes:
|
||
- when: { handler_result: SUCCESS }
|
||
to: S11C
|
||
emit:
|
||
note: "CREATE 执行成功"
|
||
- when: { handler_result: FAILED }
|
||
to: S12C
|
||
emit:
|
||
note: "CREATE 执行失败"
|
||
|
||
E40U_START:
|
||
stage: sync_execute
|
||
desc: "UPDATE 执行入口:进入异步执行中"
|
||
from: [S07]
|
||
outcomes:
|
||
- when: { handler_result: IN_PROGRESS }
|
||
to: S10U
|
||
emit:
|
||
note: "UPDATE 进入异步执行中"
|
||
|
||
E40U:
|
||
stage: sync_execute
|
||
desc: "UPDATE 执行结果回写:进行中/成功/失败/跳过"
|
||
from: [S10U]
|
||
outcomes:
|
||
- when: { handler_result: SUCCESS }
|
||
to: S11U
|
||
emit:
|
||
note: "UPDATE 执行成功"
|
||
- when: { handler_result: FAILED }
|
||
to: S12U
|
||
emit:
|
||
note: "UPDATE 执行失败"
|
||
- when: { handler_result: SKIPPED }
|
||
to: S14
|
||
emit:
|
||
note: "更新降级并跳过"
|
||
|
||
E40D_START:
|
||
stage: sync_execute
|
||
desc: "DELETE 执行入口:进入异步执行中"
|
||
from: [S09]
|
||
outcomes:
|
||
- when: { handler_result: IN_PROGRESS }
|
||
to: S10D
|
||
emit:
|
||
note: "DELETE 进入异步执行中"
|
||
|
||
E40D:
|
||
stage: sync_execute
|
||
desc: "DELETE 执行结果回写:进行中/成功/失败"
|
||
from: [S10D]
|
||
outcomes:
|
||
- when: { handler_result: SUCCESS }
|
||
to: S11D
|
||
emit:
|
||
note: "DELETE 执行成功"
|
||
delete_node: true
|
||
- when: { handler_result: FAILED }
|
||
to: S12D
|
||
emit:
|
||
note: "DELETE 执行失败"
|
||
|
||
invariants:
|
||
INV-1_UNCHECKED_ACTION_NONE:
|
||
desc: "UNCHECKED 不允许有动作"
|
||
when: { binding_status: UNCHECKED }
|
||
require: { action: NONE }
|
||
|
||
INV-2_BLOCKING_NO_ACTION:
|
||
desc: "阻断态不允许执行动作"
|
||
when_any:
|
||
- { binding_status: ABNORMAL }
|
||
- { binding_status: WARNING }
|
||
- { binding_status: DEPENDENCY_ERROR }
|
||
require: { action: NONE }
|
||
|
||
INV-3_PRECOND_IMPLIES_UPDATE:
|
||
desc: "PRECONDITION_FAILED 必须伴随 action=UPDATE"
|
||
when: { status: PRECONDITION_FAILED }
|
||
require: { action: UPDATE }
|