TGViewer
开发者工具箱|编程·开发工具·资源 开发者工具箱|编程·开发工具·资源 @devtoolboxhub · 780 subscribers
Post #1584 5
N+1 检测器如何找回被 ORM 藏起的代码行

作者在给 Node 写运行时 N+1 查询检测器时发现,检测本身很快完成,但定位到具体触发代码行却花了更久。检测器通过插桩数据库驱动工作,当同一查询形状在单次请求内多次执行时,会报告触发位置的文件和行号。最初在普通驱动上运行良好,但指向使用 Drizzle 的应用时,报告变成了「unknown call site」——查询被检测到、计数了,却无法归因到任何代码。

作者第一反应是帧过滤器过于激进,跳过了 node_modules 和 node:internal 等框架帧。打印完整调用栈后发现,一次简单的 db.select() 查询产生了 12 帧,没有一帧属于应用代码。过滤器没问题,是信息本身不存在了。

原因在于 Drizzle 的查询是惰性 thenable。db.select().from(items).where(...) 并不执行任何操作,只是构建对象。真正执行发生在有人调用 .then() 时,而 await 时调用 .then() 的是 JavaScript 运行时,不是你的代码。此时你的函数早已返回,帧已从栈中消失。TypeORM 没有这个问题,因为 repo.find() 是你直接调用的 async 函数,Node 会在 await 边界保留异步栈轨迹。

解决方案是:在查询构建期间捕获调用位置,用 AsyncLocalStorage 携带到执行时刻,让驱动级插桩优先使用这个环境值而非栈回溯。调用位置在 db.select() 时捕获一次,随构建器链式传递,直到执行方法触发。最终 SQL 来自驱动,行号来自 ORM。


修复后同一脚本在 PostgreSQL 16、Drizzle 0.45.2、postgres.js 3.4.9 上从「unknown call site」变为准确的 at drizzle-test.mjs:35。作者还提醒一个陷阱:postgres.js 中 .values() 会执行查询,但 Drizzle 中 db.insert(t).values({...}) 只是构建 insert,误判会静默丢失 insert 的归因。工具是 nplusone,MIT 协议,零运行时依赖,NODE_ENV=production 时自动关闭。

#开发者 #工具 #Nodejs #Nplusone #Drizzle #TypeORM #PostgreSQL #性能分析
@DevToolboxHub
More from @devtoolboxhub
  1. Sep 26, 2026PostgreSQL 复制槽上限参数 max_replication_slots 解析 max_replication_slots 决定共享内存中复制槽数组的长度,仅此而已。它不决…
  2. Sep 26, 2026五仓库架构踩坑:Gitlink 与双 CI Flude 团队复盘了多仓库架构的实践代价。项目从第一分钟起就选择拆分成独立仓库,Pipeline、engine、design-docs…
  3. Sep 26, 2026Xeno Core:TypeScript 后端架构框架 Node.js 复杂系统的架构选型往往决定代码的长期命运。开发者常在两条路之间纠结:要么依赖重度使用实验性装饰器和反射(如…
  4. Sep 25, 2026用 SLO 给 AI Agent 的行为定个预算 Grafana Labs 提出把可靠性工程里的错误预算(error budget)思路用到 AI Agent 上。延迟、token…
  5. Sep 25, 2026Xcode 27.2 改用 JSON 项目格式 Xcode 27.2 用基于 JSON 的 project.xcproj 取代了沿用多年的 project.pbxproj。新建项目…
  6. Sep 25, 2026PostgreSQL 的 max_prepared_transactions 该不该开 prepared transaction 是脱离会话独立存在的两阶段提交事务。执行 PREP…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →