Skip to content

[refactor-design] cluster-061-workflow-binding-query-runtime-side-read #925

@loning

Description

@loning

human_brief

问题标题:Workflow 绑定查询把运行时对象形态当成业务事实

问题说明:当前查询 reader 表面上是在读取 workflow binding readmodel,但读取后又继续询问 actor runtime:这个 actor 当前是否存在、是否符合某个具体 GAgent 类型。这会让查询结果依赖当前节点的 runtime 可见性,而不是已经提交并物化的 workflow 事实。

单机环境下它可能表现正常;但在分布式、重启、投影延迟或 runtime 实现替换时,同一个 readmodel 可能因为本地 runtime 形态不同而返回不同结果。开发者需要关注它,因为 workflow run / resume / query 的稳定性会被隐藏地绑定到运行时实现细节上。

示例位置src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:17-30

public ProjectionWorkflowActorBindingReader(
    IProjectionDocumentReader<WorkflowActorBindingDocument, string> documentStore,
    IActorRuntime runtime, // problem: 查询 reader 直接依赖 runtime 形态
    IAgentTypeVerifier agentTypeVerifier) // problem: 用本地类型探测决定业务绑定类型
{
    ArgumentNullException.ThrowIfNull(documentStore);
    ArgumentNullException.ThrowIfNull(runtime);
    ArgumentNullException.ThrowIfNull(agentTypeVerifier);

    _getDocumentAsync = (actorId, ct) => documentStore.GetAsync(actorId, ct);
    _queryDocumentsAsync = documentStore.QueryAsync;
    _existsAsync = runtime.ExistsAsync; // problem: 查询结果依赖 actor 当前是否被 runtime 看见
    _isExpectedAsync = agentTypeVerifier.IsExpectedAsync; // problem: 用实现类型形态判断业务 kind
}

为什么需要设计:修复不是简单删几行,因为当前 reader 同时承担“读已物化 binding”和“兜底判断未绑定 actor 类型”的职责。需要 maintainer 决定:未物化 binding 时是返回 unknown / not found,还是新增 actor-owned binding/status committed fact 来覆盖这个语义。这会影响 workflow run 创建、resume/signal target resolve,以及已有 actor 的兼容查询行为。

Evidence

  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:19 注入 IActorRuntime
  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:28 保存 runtime.ExistsAsync
  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:29 保存 type verifier runtime-shape check。
  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:49 在 runtime 表示 actor 不存在时拒绝 query result。
  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:102-103 按 runtime existence/type 过滤已物化 run bindings。
  • src/workflow/Aevatar.Workflow.Projection/Orchestration/ProjectionWorkflowActorBindingReader.cs:204-207 从 runtime type shape 派生 binding kind。

相关架构约束:

  • 运行时形态不是业务事实:不得把本地实例类型、代理类型、对象可见结构当成业务绑定依据。
  • 查询始终走 readmodel:对外查询只读 readmodel,不暴露 actor 内部状态、state mirror payload 或 event replay 为查询主路径。
  • 禁止侧读冒充 query:跨 actor 读取必须走 actor-owned contract、projection 或 readmodel。

Fix Boundary

  • 不新增第二条 query path。
  • 不在 ProjectionWorkflowActorBindingReader 中查询 actor runtime。
  • 从 committed workflow binding facts 物化 workflow actor kind / existence / binding。
  • 如果产品仍需要 unbound actor lookup,定义显式 actor-owned binding/status contract,而不是读取 runtime type。
  • 已有 command provisioning 仍可通过 infrastructure port 使用 runtime lifecycle;本 cluster 只约束 query/read path shape。

design_question

当 workflow actor 还没有 binding document 时,查询层应该返回 not found / unsupported,还是要求 actor 在创建或绑定时提交一个明确的 binding/status fact?

⟦AI:AUTO-LOOP⟧

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-loopCreated by codex-refactor-loop skillauto-loop-resumeSet when design decision is ready; codex-refactor-loop will resume implementphase9-auto-solveOperator opted this design issue into Phase 9 auto-solverefactor-design-neededCluster flagged requires_design by codex-refactor-loop auto audit🎉 phase:merged

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions