AwsMySQLPoolClient runs `SELECT @@innodb_read_only` twice before every query, and `verifyOpenedConnectionType` has no effect
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- mysql, node.js, typescript
Direzione di ricerca
Start with mysql/lib/client.ts and trace AwsMySQLPoolClient.query() through connect(), then read common/lib/plugins/aurora_initial_connection_strategy_plugin.ts and common/lib/internal_pooled_connection_provider.ts. The issue’s reproduction script can confirm the extra role-check queries; common/lib/wrapper_property.ts defines the option that currently appears unused. Done means role verification follows a supported configuration or connection-lifecycle policy, without repeated checks on every pooled query.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
When using AwsMySQLPoolClient as documented (Using the Connection Pool), every pool.query() sends SELECT @@innodb_read_only as is_reader to the database before the actual query. With the initialConnection plugin (a default plugin since 2.1.1) and a reader cluster endpoint, it is sent twice per query.
The physical connection is reused from the internal mysql2 pool, but the role is verified again on every checkout. There is no way to turn this off: verifyOpenedConnectionType is defined in WrapperProperties but not read anywhere.
This became visible after adding DB spans via the mysql2 tracing channel. In a test environment:
is_readerqueries were about 63% of the total DB span time; the application's own queries were the rest.- Each application query gets 2 extra round trips (1–3 ms per request).
- Reader QPS is about 3x the actual application QPS.
- Locally,
pool.query()used about 1.5 ms more CPU per query than plain mysql2, with all plugins disabled. That is the cost of building a new client and plugin chain per query. With plugins enabled it is likely higher.
The same symptom was reported for the JDBC wrapper in aws/aws-advanced-jdbc-wrapper#1861 and reworked in JDBC 3.1.0 (aws/aws-advanced-jdbc-wrapper#1655), which made verifyOpenedConnectionType / endpointSubstitutionRole effective. The Python wrapper also has these options. The Node.js wrapper does not seem to have this change yet.
Expected Behavior
One of the following:
- The host role is verified once per physical connection (or once per instance pool, since
InternalPooledConnectionProvideralready keeps one pool per instance), not on everypool.query(). verifyOpenedConnectionType: "none"disables the verification on checkout, as in the JDBC and Python wrappers.
What plugins are used? What other connection properties were set?
plugins include initialConnection and failover2 / dialect: aurora-mysql / host: reader cluster endpoint / AwsPoolConfig with default-like settings
Current Behavior
Trace of a single request that runs one application query (reader cluster endpoint, initialConnection enabled):
GET /items/:id 4.1 ms
└─ handler
├─ SELECT @@innodb_read_only as is_reader 0.73 ms ← wrapper
├─ SELECT @@innodb_read_only as is_reader 0.41 ms ← wrapper
└─ SELECT ... FROM items WHERE ... 0.87 ms ← application
These are three separate queries on the wire (different start times and durations), not a duplicated span.
Local measurement with the script below (300 sequential select 1, same database, no plugins, host is not a cluster endpoint so initialConnection is skipped):
| Method | wall / query | CPU / query | queries sent / query | is_reader / query |
|---|---|---|---|---|
A. mysql2 createPool().query() |
27.0 ms | 0.73 ms | 1 | 0 |
B. AwsMySQLPoolClient.query() |
55.1 ms | 2.28 ms | 2 | 1 |
C. AwsMySQLPoolClient.getConnection() once, then conn.query() |
27.4 ms | 0.66 ms | 1 | 0 |
(Wall time is dominated by a ~27 ms network round trip in this local setup.)
Reproduction Steps
// repro.mjs
// DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME, PLUGINS (e.g. "initialConnection")
// Use a reader cluster endpoint + PLUGINS=initialConnection to see 2 checks per query.
import dc from "node:diagnostics_channel";
import { performance } from "node:perf_hooks";
import mysql from "mysql2/promise";
import { AwsPoolConfig, PluginManager } from "aws-advanced-nodejs-wrapper";
import { AwsMySQLPoolClient } from "aws-advanced-nodejs-wrapper/mysql";
const e = process.env;
const conn = { host: e.DB_HOST, port: Number(e.DB_PORT ?? 3306), user: e.DB_USER, password: e.DB_PASSWORD, database: e.DB_NAME };
// Count what mysql2 actually sends (mysql2 >= 3.20 tracing channels).
let isReader = 0, total = 0;
for (const name of ["mysql2:query", "mysql2:execute"]) {
dc.tracingChannel(name).subscribe({
start(m) { total++; if (String(m.query).includes("innodb_read_only")) isReader++; },
end() {}, asyncStart() {}, asyncEnd() {}, error() {},
});
}
const N = 300;
async function run(label, q) {
for (let i = 0; i < 20; i++) await q();
isReader = 0; total = 0;
const cpu0 = process.cpuUsage(); const t0 = performance.now();
for (let i = 0; i < N; i++) await q();
const ms = performance.now() - t0; const cpu = process.cpuUsage(cpu0);
console.log(`${label}: wall/q=${(ms / N).toFixed(3)}ms cpu/q=${((cpu.user + cpu.system) / 1000 / N).toFixed(3)}ms queries/q=${(total / N).toFixed(2)} is_reader/q=${(isReader / N).toFixed(2)}`);
}
const plain = mysql.createPool({ ...conn, connectionLimit: 5 });
await run("A mysql2 pool", () => plain.query("select 1"));
await plain.end();
const pool = new AwsMySQLPoolClient(
{ ...conn, plugins: e.PLUGINS ?? "", dialect: "aurora-mysql" },
new AwsPoolConfig({ maxConnections: 5 }),
);
await run("B wrapper pool.query", () => pool.query("select 1"));
const c = await pool.getConnection();
await run("C wrapper getConnection reuse", () => c.query("select 1"));
await c.release();
await pool.end();
await PluginManager.releaseResources();
Only B sends more than one query per call: is_reader/q=1, or 2 with a reader cluster endpoint and initialConnection.
Adding verifyOpenedConnectionType: "none" to the client config does not change B in the local setup (still queries/q=2.00 is_reader/q=1.00). The check from the initial connection strategy plugin cannot be disabled this way either, because the plugin does not read the property (see below).
Possible Solution
Code paths (tag 3.0.1):
mysql/lib/client.ts:642—AwsMySQLPoolClient.query()creates a newAwsMySQLPooledConnectionper query and callsconnect(), thenquery(), thenend().mysql/lib/client.ts:70-97—connect()always callspluginManager.connect(hostInfo, props, true, null)(isInitialConnection = true) and, for topology-aware dialects, callsgetHostRole()on the new client → 1stis_reader.common/lib/plugins/aurora_initial_connection_strategy_plugin.ts:205— for a reader cluster endpoint,getVerifiedReaderClient()also callsgetHostRole()→ 2ndis_reader.common/lib/internal_pooled_connection_provider.ts:118— physical connections come from a per-instance mysql2 pool, so every connection in that pool points to the same instance.common/lib/wrapper_property.ts:751—VERIFY_OPENED_CONNECTION_ROLE(verifyOpenedConnectionType) is defined, butgit grep VERIFY_OPENED_CONNECTION_ROLEfinds no other usage.
The behavior is the same in 2.0.0, 2.0.1, 2.1.0, 2.1.1, 3.0.0 and 3.0.1.
Ideas:
- Port the JDBC 3.1.0 change: make
verifyOpenedConnectionType(andendpointSubstitutionRole) effective in the initial connection strategy plugin. - Verify the role once per physical connection. mysql2's pool emits
'connection'once per new physical connection, separately from'acquire'on every checkout. Alternatively, cache the verified role per instance pool, since failover is detected separately by the failover plugins. - Avoid running the full connect pipeline for a connection that is already pooled and verified, which would also remove the per-query CPU cost.
Questions:
- Is verifying the role on every
pool.query()intended? - Is there a supported configuration today to avoid it while keeping
initialConnectionandfailover2? - If not, is the JDBC 3.1.0 behavior planned for the Node.js wrapper?
Additional Information/Context
- In the Java, Python and Go wrappers, the pool (e.g. HikariCP, SQLAlchemy
QueuePool,database/sql) keeps wrapper connection objects, so the role is verified once when a connection is created. - The pool was released with 2.0.0 as the resolution of #490 ("Please let us know if you encountered any issues when trying out the feature").
- A possible workaround is pooling long-lived
AwsMySQLClientinstances in the application, as HikariCP does with JDBC wrapper connections. Measurement C above shows this removes the extra queries. We would prefer a supported option.
The AWS Advanced NodeJs Wrapper version used
3.0.1 (same code path since 2.0.0)
Node version used
v24.22.2 (mysql2 3.24.6)
Operating System and version
Linux; also reproduced on macOS
- Lingua principale
- TypeScript
- Stelle
- 38
- Fork
- 17
- Merge medio
- 3g 11h
- PR unite (30g)
- 4
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di aws/aws-advanced-nodejs-wrapper
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
aws/aws-advanced-nodejs-wrapper#718 · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
Exporting Typescript typesApertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 65/100
aws/aws-advanced-nodejs-wrapper#527 · 7 commenti · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di aws/aws-advanced-nodejs-wrapper
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
pending close triaging
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
anomalyco/opencode#62582 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
feature request
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
callstack/react-native-paper#5189 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
live-codes/livecodes#1079 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Add: ESP 2 ROApertaapproved check:passed streams:add
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno