Skip to content

bug: 每次启动都报 Driver already registered, skipping {"driverName":"com.objectstack.driver.sql"} —— SQL driver 被注册了两次 #4773

Closed

Description

@os-zhuang

现象

pnpm dev(showcase)每次启动,boot diagnostics 里固定有一条:

WARN Driver already registered, skipping {"driverName":"com.objectstack.driver.sql"}

冷启、热启都有,稳定复现。

说明

这条是 showcase 冷启 24 条启动告警之一,在排查 pnpm dev 控制台刷屏(#4765)时顺带发现的。我没有深挖根因 —— 记录在这里,免得它继续以"已知噪音"的身份留在启动日志里。

需要查清的是:

  1. 谁注册了第一次、谁注册了第二次?看起来至少有两条路径都在注册 com.objectstack.driver.sql(候选:SqlDriver 插件自身 / 运行时按 OS_DATABASE_URL 推断的驱动装配 / defineStack 里的 datasource 声明)。
  2. 第二次注册被跳过,赢的是第一次 —— 这两次注册的配置是否一致?如果不一致(比如一次带连接串、一次带不同的能力集),那"skipping"就不只是噪音,而是静默采用了其中一份配置。
  3. 如果两次注册本来就是设计内的幂等重入,那这条日志就不该是 warn —— 一个每次启动都出现、且不代表任何异常的 warn,只会训练人忽略 warn 这个级别。

换句话说:要么它是真重复注册(修调用方),要么它是正常幂等(降级日志)。当前状态是两者都没确定。

复现

rm -rf examples/app-showcase/.objectstack && pnpm dev

启动摘要的 ⚠ Boot diagnostics 段落里能看到。

Activity

  1. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    on Aug 3, 2026
  2. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    分诊:入队 pm:queue,本轮未派发(第 3 轮 4 个位置已满,且与在飞的 #4769 有 objectql 侧撞车风险,下一轮派)。

    交叉链接 #4776:同一份启动日志里拆出的 #4769 / #4771 / #4772 是同一个形状(启动期把「提供方尚未注册」当成「没有提供方」并记账),已立决策单。本 issue 可能是同一族的第四例,取决于你列的第 3 点:如果两次注册本就是设计内的幂等重入,那它属于「启动期重入」而非「过早判断」,是相邻但不同的问题;如果是真重复注册,那要看第二次注册的调用方是不是也在做「我没看到 X,所以我来注册」这类过早判断。

    派发时的验收要求已经写在你的正文里,照做即可 —— 三个问题(谁注册了两次 / 两次配置是否一致 / 若是幂等则日志该降级)必须逐条给出结论,不能只修掉日志。特别是第 2 点:如果两次注册配置不一致,「skipping」就不是噪音,而是静默采用了其中一份配置 —— 那是 #4632 规则下的第二类降级,要按那条规则处理。


    Generated by Claude Code

  3. claude commented on Aug 3, 2026

    @claude
    Contributor

    🔒 认领:PM 循环第 4 轮(engine.ts 家族串行第 2 单,#4993 落地后解锁)
    会话:session_01NrmBxj8rK2uGCnh9aipjwX(账号 xuyushun441-sys)
    分支:claude/issue-4773-driver-double-registration
    Worktree:objectstack-issue-4773
    域:domain:engine
    文件面:packages/objectql/src/engine.ts(registerDriver 及调用方诊断所及)+ 相关启动装配路径(以诊断结论为准,越界即停);showcase 只读复现

    ⚠️ 同日 churn:engine.ts 今日已被 #4972、#4993 两单改过,基于 origin/main 工作。
    ⛔ 硬约束:spec/** 及生成物零改动;protocol.ts 零改动;releases/ 零触碰。
    家族剩余(#4862/#4839/#4903/#4619 + rule-validator 的 #4762)继续压队,本单落地后放行下一单。


    Generated by Claude Code

  4. claude commented on Aug 3, 2026

    @claude
    Contributor

    ✅ 验收通过 —— PR #5007(draft,CI 跑完入队)

    对照实际 diff 复核(5 文件,+324/-4;⛔ spec / protocol.ts / releases 零触碰,逐一核实):

    1. 诊断按 issue 的判定树走到了底,且有插桩证据:两条注册腿是同一次往返——腿 1(赢,isDefault=true)DatasourceConnectionService.attemptConnect(经 DefaultDatasourcePlugin.init 驱动),该插件把从引擎读回的实例再发布成 driver.{name} 服务,腿 2(被跳过)ObjectQLPlugin.start 的发现循环。file:line 俱在 PR 正文。
    2. 承重问题的答案是干净的:call1.driver === call2.driver —— 同一对象引用,从未有配置被静默取舍。
    3. 修法比「一刀切降级」更对:按对象同一性分流 —— 幂等重入 → debug(往返机制注释在日志点);真碰撞(不同实例同名)仍 warn 且点名 KEPT vs DISCARDED 双方配置;isDefault 请求被丢 → warn 而非消失。注册行为零变化(先到先赢)。这保住了 warn 级别对真异常的信号价值。
    4. 负控制抓住了自己的空转测试:第一版 pin 拦 console.warn 对 origin/main 恒绿(ObjectLogger 直写 stdout),负控制识破后改拦 process.stdout.write —— 与 serve 的 boot-quiet 窗口同一条流;回退 engine.ts 后该 pin 确实红。
    5. 实测收口:showcase 冷启告警 2 → 1,剩余那条即已立案的 OS_STORAGE_ROOT 对任何非默认值不生效:CLI 与设置服务的 env 通道错位,schema 默认值覆盖宿主构造配置(原报「swap 假警告」归因已被 #4096 时间线推翻,警告本身是准确的) #4968(同形:LocalStorageAdapter → LocalStorageAdapter 的假交换 warn),不重复立案。objectql 1793 / runtime 1095 全过,门禁链全绿。

    CI 绿后转 ready 入合并队列。engine.ts 家族下一单(#4862)在本 PR 落地后放行。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions