DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →

Linux 上 FileSystemWatcher 停收事件,却没有触发 Error

Linux .NET 10 的目录改名交错可使 FileSystemWatcher 静默退出。armed 不代表存活,需探针检测、重建与全量状态核对。

FileSystemWatcher.EnableRaisingEvents 仍是 true,应用也没报错,工作区后续修改却再没有通知。若此前发生了连续目录改名,尤其旧名称被另一目录立刻复用,需要检查 Linux watcher 的事件处理线程是否已经退出。

已知触发组合包括 Linux x64、.NET 10.0.10 与 10.0.11,递归监听开启。典型操作是先把 lib 移成 src,再把 code 移成 lib,周围同时存在其他文件写入。这里不是建议在真实工作区运行破坏性改名测试;用隔离目录保留相同结构即可。

armed 标志不能证明后台还活着

Linux inotify 用 cookie 配对移动前后的通知。目录改名交错到达时,某个 IN_MOVED_FROM 可能没有紧跟相配的 IN_MOVED_TO。受影响实现清理子目录监听后返回 false,外层把这个返回值当成停止整个处理循环,最终关闭 inotify 句柄。

这个退出分支没有抛异常,所以不会自然进入 Error 回调。增加 Error 订阅仍有必要,却无法单独检测本题。正式 10.0.12 源码仍保留相同的 return false;不能因为修复已合入维护分支就认为部署版本已经包含它。

还有一个容易误判的地方:.NET 11 的 Linux 实现经过重写,与 .NET 10 的线程和句柄设计不同。直接阅读 main,再用那套共享实例结构解释 10.x,会把诊断对象弄错。本题只按正式 10.x 的处理路径判断。

增加独立存活检查,并补回当前状态

对依赖 watcher 维持索引的应用,可以在受控、已获准写入的监听目录中安排专用探针文件,由独立定时器写入带唯一标记的新状态,要求在合理超时内收到对应通知。探针自身写入失败要单独报告,不能当成 watcher 已死;事件过滤器也必须允许它通过。

一次超时仍可能来自线程拥塞、磁盘延迟或应用回调阻塞,应结合连续探针、队列状态和实际文件状态判断。不要在 watcher 回调里等待自己的探针,以免制造死锁或假超时。

确认失效后,由应用的单一控制流程停止并释放旧 watcher,建立新实例,恢复原 Path、过滤器、递归设置和事件订阅。重建期间要协调一次目录全量扫描,并与通知队列串行合并,恢复当前文件状态。仅重新订阅未来事件,无法补回失效期间已经漏掉的变化。

这不是事件回放方案。已经出现后又删除的中间文件,未必能从当前目录重建;需要逐项审计的业务应依赖持久记录,而不是把文件通知当成可靠事务日志。重复通知也要按原有幂等规则处理。

部署前区分运行时、缓存与内核配额

记录实际运行时版本及部署方式。自包含发布带着自己的运行时,升级宿主机 SDK 或系统运行时不会自动替换旧产物。采用后续维护版本时,要确认该正式版本的 Linux 源码已将清理分支改为继续事件循环,再重新发布并复核原工作负载。

若收到 ENOSPC、EMFILE 或明确的溢出错误,则还应检查配额和消费速度;本题的无 Error 静默停止,不能靠一律调高 inotify 上限处理。也不应回退到已结束支持的 .NET 7,只因为一个对照程序在那里没有复现。

验收时保留连续目录改名后的探针结果,检查监听持续存活、原业务索引与全量扫描一致,并验证重建不会留下重复 watcher。任何恢复策略都应保留原配置和状态快照;撤回新逻辑时,还要明确由谁继续承担失效检测与全量核对,不能只删除探针让告警安静下来。