AwsMySQLPoolClient runs `SELECT @@innodb_read_only` twice before every query, and `verifyOpenedConnectionType` has no effect
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- mysql, node.js, typescript
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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
- Lenguaje dominante
- TypeScript
- Estrellas
- 38
- Forks
- 17
- Merge medio
- 3 d 11 h
- PR fusionados (30 d)
- 4
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de aws/aws-advanced-nodejs-wrapper
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
aws/aws-advanced-nodejs-wrapper#718 · 1 reacción ·
Los mantenedores suelen responder en 2 días
-
Exporting Typescript typesAbiertobug
Dificultad 3/5 1-2 días Aptitud para principiantes 65/100
aws/aws-advanced-nodejs-wrapper#527 · 7 comentarios · 1 reacción ·
Los mantenedores suelen responder en 2 días
Todos los issues de aws/aws-advanced-nodejs-wrapper
Issues similares
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
-
pending close triaging
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
anomalyco/opencode#62582 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
feature request
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
callstack/react-native-paper#5189 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
live-codes/livecodes#1079 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Add: ESP 2 ROAbiertoapproved check:passed streams:add
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 1 día