Repository navigation
Description
Summary
trace: false is only honoured by four of the promise-API entry points. Every other promise method still calls captureStackHolder(...) unconditionally, so the per-call Error.captureStackTrace cannot be opted out of. Three of the ungated call sites are per-query hot paths, not just lifecycle methods.
Current state on master
Gated (respect config.trace):
| site | method |
|---|---|
lib/promise/connection.js:33 |
PromiseConnection#query |
lib/promise/connection.js:53 |
PromiseConnection#execute |
lib/promise/pool.js:40 |
PromisePool#query |
lib/promise/pool.js:60 |
PromisePool#execute |
Not gated β per-query paths:
| site | method |
|---|---|
lib/promise/prepared_statement_info.js:14 |
PromisePreparedStatementInfo#execute |
lib/promise/pool_cluster.js:28 |
PromisePoolNamespace#query |
lib/promise/pool_cluster.js:44 |
PromisePoolNamespace#execute |
Not gated β lifecycle/per-connection:
| site | method |
|---|---|
lib/promise/connection.js:85 |
beginTransaction |
lib/promise/connection.js:96 |
commit |
lib/promise/connection.js:105 |
rollback |
lib/promise/connection.js:116 |
ping |
lib/promise/connection.js:131 |
reset |
lib/promise/connection.js:146 |
connect |
lib/promise/connection.js:162 |
prepare |
lib/promise/connection.js:181 |
changeUser |
lib/promise/pool.js:80 |
PromisePool#end |
The most user-visible consequence is PromisePreparedStatementInfo#execute: the documented way to reuse a prepared statement is const stmt = await conn.prepare(sql) followed by repeated stmt.execute(params), and that loop pays a stack capture per query with no way to turn it off. PoolCluster users are in the same position for every query they run.
Why it matters
Measured on the gated path, so it is a direct indication of what the ungated paths still pay. Workload: 8 concurrent pool.execute() of a single-row prepared query against MySQL 9.7.2 (local Docker), metric = client CPU microseconds per query, median of interleaved runs, fresh process each:
| runtime | trace default |
trace: false |
delta |
|---|---|---|---|
| node v26.0.0 | 38.8 us/query | 32.9 us/query | -15% |
| bun 1.4.0 | 37.1 us/query | 35.3 us/query | -5% |
(Repeat runs put the node figure in the 11-15% band and bun in the 5-9% band.)
Suggested fix
Apply the same ternary already used at connection.js:33 to the remaining sites, reading config.trace (or config.connectionConfig.trace for pool/cluster, this.statement.connection.config.trace or similar for the prepared-statement wrapper). makeDoneCb / applyCapturedStack already handle stackHolder === undefined, so no other change is needed.
This stays backward compatible: ConnectionConfig sets this.trace = options.trace !== false, so only an explicit false opts out β unset, true, 0, null and undefined all keep today's behaviour.
Note on affected versions
Worth flagging for anyone reading this later: trace was parsed but never read anywhere in the library before 3.24.0 (in 3.16.3-3.23.0 the only references are the default and this.trace = options.trace !== false in connection_config.js). The promise wrapper captured unconditionally regardless of the setting. Gating was introduced in 3.24.0 for query/execute; this issue is about finishing that work.
Happy to send a PR if the approach looks right.
Activity
fix(promise): honour `trace: false` on every promise-API method (sidoβ¦