You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[probe] 8 ref'ed timers still alive
[probe] setTimeout: at ObjectKernel.initPluginWithTimeout (packages/core/dist/index.js:1855)
[probe] setTimeout: at ObjectKernel.startPluginWithTimeout (packages/core/dist/index.js:1891)
同一个文件里 shutdown() 自己的超时守卫已经做对了,连注释都写好了:
constt=setTimeout(()=>{reject(newError('Shutdown timeout exceeded'));},this.config.shutdownTimeout);// Don't let this timer keep the event loop aliveif(t.unref)t.unref();
发现自 #4747 的实现过程。未认领。 与 #4747 是不同的 bug,但它正是 #4747 得以发生的前提条件。
现象
一条
os migrate子命令的实际工作 3 秒就做完了(JSON 已输出、✅ Graceful shutdown complete已打印),然后进程再挂 120 秒才被外部杀掉:原因
packages/core/src/kernel.ts的两处超时守卫:startPluginWithTimeout()里是同一段。插件赢了这场 race 之后,那个setTimeout既没被clearTimeout,也没被unref()—— 它带着 ref 一直挂到startupTimeout走完。ObjectQLPlugin.startupTimeout = 120_000,于是最长的那根定时器把进程钉住整整 120 秒。实测正是 8 根还带 ref 的 Timeout(4 个 init + 4 个 start):同一个文件里
shutdown()自己的超时守卫已经做对了,连注释都写好了:修法就在隔壁 30 行。清除(
finally { clearTimeout(t) })比unref()更彻底,两者都可以。为什么值得修
os migrate/os meta resync全家都是干完活挂 30–120 秒。脚本里串起来的 CI 步骤会白等,退出码还不稳定。os migrate子命令关停时,悬空引用巡检都会把sys_metadata/sys_view_definition报成unreadableObjects(连接已关闭) #4747 的使能条件。isSystem写入仍可产生悬空 lookup 引用——需要一条只报告不拦截的巡检(#4441 残留) #4551 巡检的首次 sweep 排在启动后 60 秒;一个干完活就退出的进程永远碰不到它。正是这 120 秒的空转把进程留到了 60 秒定时器触发,才让巡检在连接池已关之后跑起来。每个os migrate子命令关停时,悬空引用巡检都会把sys_metadata/sys_view_definition报成unreadableObjects(连接已关闭) #4747 已从生命周期契约那一侧修好(关停真正到达 LifecycleService),不依赖本条;但只要这些定时器还在漏,任何「启动后 N 秒才做的事」都会在一个自以为已经结束的进程里醒来。os serve里同样漏,只是那里进程本来就长命,看不出来。复现
挂 handle 的证据:
边界
packages/core/src/kernel.ts的两个守卫。别顺手改startupTimeout的取值 —— 慢启动的插件需要那个上限,问题不在时长而在没人回收。process.getActiveResourcesInfo()里不应留下这些守卫定时器(或直接断言守卫定时器被 clear)。Refs #4747。