Skip to content

[core/ADR-0116] init 阶段取服务的顺序契约是自愿声明的——不声明就没人拦,#4085 与 #4420 都是这么发生的 #4471

Description

@os-zhuang

#4420 的后续 —— 关于根因模式,而不是那一个 bug。

问题

ADR-0116 / packages/core/src/plugin-order.ts 已经把插件顺序讲清楚了,三种边各有语义(dependencies 硬、optionalDependencies 有则排前、requiresServices 声明"我在 init() 里同步取这个服务",由 assertInitServiceRequirements 具名报错)。机制是完备的。

缺的是:这些声明全部是自愿的。 一个插件在 init()getService('manifest') 却什么都不声明,没有任何一层会指出来——它只会在某个组合顺序下静默拿不到服务。而这类失败通常又被写成 best-effort 的 try/catch + warn(因为插件确实想在没有该服务时也能跑),于是问题被吞两次:一次在顺序上,一次在日志级别上。

已经发生过两次:

第二次的代价是数据一致性级别的,而且症状与原因隔了一次发版,现场极难自查。

现状调查(#4460 时顺手扫的)

大部分 manifest 消费方其实是声明了的:

插件 声明
plugin-approvals dependencies = ['com.objectstack.engine.objectql']
service-messaging 同上
service-realtime 同上
platform-objects optionalDependencies = [...objectql]
service-automation (#4460 已补 optionalDependencies)

apps/setupapps/studioapps/account 走的是另一条路——它们故意放在 start() 注册(源码里有注释说明),而且注册的是 app/nav 而非带表的对象,漏了会表现为界面缺失,不会静默丢数据。风险等级不同,不必按同一把尺子量。

所以这不是一个"到处都是"的问题——恰恰相反,它是少数漏网的那个最贵。靠人工 review 维持的一致性,漏掉一个就够了。

期望

把这个约定变成可强制的。方向(择一即可,倾向前者):

  1. 加一个 check:* 门禁:静态扫描插件源码,凡在 init() 里同步 getService('X'),而 X 由某个已知插件 providesServices 提供,却没在自己的 dependencies / optionalDependencies / requiresServices 里提到那个插件的,报错。providesServices 的清单已经存在,这一步是可做的。
  2. 或者运行期兜底:kernel 在 init() 期间记录每个插件实际解析过哪些服务,与其声明比对,不一致就在 bootstrap 结束时警告——比静态扫描弱,但不受代码形态影响。

另外值得单独定一条的是日志级别的约定:当一个 best-effort 的降级会导致"看起来正常、实则不持久"时(#4420 正是如此),它就不该是 warn。#4460 在自动化这边把它提到了 error,但这只是一个点上的修复,没有形成规则。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions