AwsMySQLPoolClient runs `SELECT @@innodb_read_only` twice before every query, and `verifyOpenedConnectionType` has no effect
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- mysql, node.js, typescript
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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
- Ngôn ngữ chính
- TypeScript
- Star
- 38
- Fork
- 17
- Merge trung bình
- 3 ngày 11 giờ
- Pull request đã merge (30 ngày)
- 4
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của aws/aws-advanced-nodejs-wrapper
-
bug
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 55/100
aws/aws-advanced-nodejs-wrapper#718 · 1 reaction ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Exporting Typescript typesĐang mởbug
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 65/100
aws/aws-advanced-nodejs-wrapper#527 · 7 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của aws/aws-advanced-nodejs-wrapper
Issue tương tự
-
area:frontend kind:bug priority:p2 ready
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
handsontable/hyperformula#1879 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
RunestoneInteractive/rs#1661 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
capricorn86/happy-dom#2594 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày