Skip to content

服务持久化与重载语义参考

本文档梳理 Aalis 内置 / 一方插件提供的关键服务在 App.reloadPlugin(name)recompute(reason)、整进程 restart() 三种生命周期事件下的状态保持情况,供 插件作者与运维人员判断"我能不能热重载这个插件"。

概念

  • 进程内态(in-memory):仅存于运行时内存,重启即丢。bounce 时若服务 归属的插件被重新 apply,其内存态会重建为空。
  • 磁盘 / DB 态(persistent):通过 storage 服务、SQLite / MongoDB / 文件 落盘,重建后会从磁盘读回。
  • bounce-safe:服务所属插件被 App.reloadPlugin 重新激活后,对外可见的 状态能在合理时间内恢复(要么数据从磁盘 / 上游回流,要么内存重建后下游会 自然重新填充)。

服务状态对照表

服务名提供方插件内存态内容持久化来源bounce 是否保留说明
app@aalis/coreApp 实例n/an/a由 host 注入,永不 bounce
plugins@aalis/corePluginManagern/an/a由 host 注入,永不 bounce
commands@aalis/plugin-commands命令注册表(Map)n/abounce 后由各插件 apply 时重新 register
agent@aalis/plugin-agentpreprocessor / processor 列表n/a同上,依赖下游插件 apply 时回注
tools@aalis/plugin-tool-systemtool 定义 Mapn/a同上
webui-server@aalis/plugin-webui-server已注册页面 Mapn/abounce 后下游插件通过 useWebuiService.registerPage 重新注册
doctor@aalis/plugin-doctorlastReport 单例n/a重载后报告丢失,需重新 runChecks
scheduler@aalis/plugin-scheduler任务调度状态data/scheduler-jobs.json是(持久部分)bounce 后从 JSON 读回;运行中的 timer 会重建
authority@aalis/plugin-authority角色规则配置文件规则随配置一起回填
session-manager@aalis/plugin-session-manager活跃 session Map视下游 memory 插件部分历史走 memory 服务,活跃句柄丢失需重建
memory@aalis/plugin-memory-sqlite / -mongodb / -summary / -inmemoryLRU / 缓存SQLite / MongoDB(内存版除外)是(持久驱动)/ 否(inmemory)inmemory 驱动 bounce 即清空
vectorstore@aalis/plugin-vectorstore-lancedb / -flat索引句柄data/lancedb/ / 平铺文件句柄重建后数据可用
embedding@aalis/plugin-embedding-*HTTP clientn/an/a(无状态)重建即可用
llm-*各 LLM provider 插件HTTP clientn/an/a同上
storage@aalis/plugin-storage-localfs handles磁盘文件系统抽象,bounce 安全
tools.search@aalis/plugin-tool-search配置n/an/a重建即可用
tools.code-runner@aalis/plugin-tool-code-runnerrunner 进程池n/a重建会创建新进程池,正在执行的任务会丢失
tools.browser@aalis/plugin-tool-browserPlaywright 会话n/a重建会断开当前浏览器会话
todo-list@aalis/plugin-todo-list任务列表视配置(默认内存)否(默认)默认内存态,bounce 即丢
user-profile@aalis/plugin-user-profileprofile 缓存memory 服务走 memory 服务持久化

重载注意事项

重要变更(新契约):下游依赖默认随 provider bounce 级联重启。 只有显式声明 PluginModule.requiresBounceOnDepChange: true 的下游会被级联 evict。 其他下游应使用 lazy ctx.getService() 透明拿到新实例(参见 plugin-session-manager / plugin-memory-summary / plugin-message-archive 实现)。

  1. 级联 bounce 是 opt-inApp.reloadPlugin(target) 时,target dispose 导致其 provided 服务从 ServiceContainer 中移除。

    • 下游声明 requiresBounceOnDepChange: true 的、且在 inject.required / inject.optional 中声明了该服务 → 会被 recompute(service-down) 自动 dispose+pending, 随后 target 重新 apply 注册新服务,下游被重新激活。
    • 声明该字段的下游 → 不被级联,lazy lookup 下次调用时拿新实例。
  2. 避免 bounce 持有外部连接的插件:上表中标注 "重建即可用" 的插件可以 安全 bounce;标注 "进程池 / 浏览器会话" 的服务 bounce 会断开外部资源, 建议先停止依赖工作流再重载。

  3. 配置变更走 updatePluginConfig 而非 reloadPlugin:前者会把新配置 写回 ctx.config 后 bounce;后者只重新 import 代码,不更新配置。

  4. 多实例插件 (name:suffix) 仅作用于指定 instanceId:同 module 的其 他实例不受影响,需各自调用 reloadPlugin(instanceId)

  5. integration / e2e 推荐在 bounce 后等 plugins:changed:直接调用 bounce 之后立即 assert 服务可用会读到 dispose 中间态,应等 recompute 收收尾发出 plugins:changed 事件后再断言。

增量重载的 API 速查

ts
// 重新 import 单个插件的代码并 bounce
await app.reloadPlugin('@aalis/plugin-foo');

// 不重新 import,仅 dispose+重新 apply(罕用,调试时可手动调)
await ctx.getService('plugins').bouncePlugin('@aalis/plugin-foo');

// 配置变化(含 enable/disable)后的标准入口
await ctx.getService('plugins').updatePluginConfig(name, newConfig);

// 全局收敛:所有生命周期路径的统一入口,按 reason 调度拓扑 dispose / activate
await ctx.getService('plugins').recompute({ type: 'plugin-state-changed' });
// (softReload() 是它的薄壳,仍可用)
await ctx.getService('plugins').softReload();