sidorares / sidorares/node-mysql2
Feature: pool `connectionLifeTime` (max connection age) option for connection recycling
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4.4k
- Forks
- 680
- Avg merge
- 9h 7m
- Merged PRs (30d)
- 59
Description
mysql2's pool can only recycle connections when they're idle (idleTimeout + maxIdle).
There's no way to cap a connection's maximum age. Under sustained load a connection never
goes idle, so it lives indefinitely and stays pinned to whichever backend it first connected to.
This is a problem when the pool sits in front of a load-balanced / elastic MySQL topology:
- Aurora / RDS reader endpoints: each pooled connection is routed to one reader for its
lifetime. When a reader is added (auto-scaling) or after a failover, existing
long-lived connections don't move — the new capacity stays underused until connections churn.
(RDS Proxy's reader endpoint doesn't fix this; it also pins a connection to a single reader.) - Any HAProxy / NLB / proxy setup where you want connections to periodically re-resolve and
rebalance.
A max-lifetime (with jitter) is the standard remedy — e.g. HikariCP maxLifetime, and the
legacy mysqljs/mysql had this via a community fork - https://github.com/vita-mojo/mysql. mysql2 has no equivalent as far as i know.
Describe the solution you'd like
A pool option that discards a connection once it exceeds a maximum age, replacing it on next
borrow, with small random jitter to avoid a synchronized die-off:
mysql.createPool({
// ...
maxLifetime: 600000, // ms; 0 = unlimited (default). Recycle connections older than this.
});
- Name
maxLifetime(ms) to match HikariCP and mysql2's existing ms conventions
(idleTimeout). (Themysqljs/mysqlfork called itconnectionLifeTimein seconds — either
is fine, but ms +maxLifetimeis more consistent here.) - Apply built-in jitter (e.g. up to 5–10%) so pooled connections don't all expire at once.
Proposed implementation
We've run this behaviour in production for a long time via a fork of the legacy mysqljs/mysql
driver that adds a connectionLifeTime option (in seconds)
(https://github.com/mysqljs/mysql/compare/master...vita-mojo:mysql:master), and we're now
carrying the same logic on mysql2 as a patch-package patch. We'd like to see it upstream, as
mysql2 has no equivalent.
The sketch below is the same ~30-line change, but renamed to the proposed upstream API
— maxLifetime in milliseconds (to match idleTimeout and HikariCP) instead of the fork's
connectionLifeTime in seconds. Naming/units are of course open to whatever maintainers prefer.
Mapped onto mysql2's current internals:
lib/pool_config.js — parse the option:
this.maxLifetime = isNaN(options.maxLifetime) ? 0 : Number(options.maxLifetime);
lib/pool_connection.js — stamp an expiry on creation (with jitter):
if (pool.config.maxLifetime > 0) {
const jitter = Math.floor(Math.random() * (pool.config.maxLifetime / 20 + 1)); // up to 5%
this._expiresAt = Date.now() + pool.config.maxLifetime - jitter;
}
lib/base/pool.js — in getConnection, skip/close expired free connections instead of
handing them out:
while (this._freeConnections.length > 0) {
connection = this._freeConnections.pop();
if (connection._expiresAt && Date.now() > connection._expiresAt) {
connection._pool = null;
spliceConnection(this._allConnections, connection);
connection.destroy(); // frees the slot; a fresh connection is created below
continue;
}
this.emit('acquire', connection);
return process.nextTick(() => { connection._released = false; cb(null, connection); });
}
Plus: register maxLifetime so it isn't flagged by the "invalid configuration option"
validation in lib/connection_config.js. (Optional enhancement: also proactively close expired
connections in the existing _removeIdleTimeoutConnections sweep so they don't linger until the
next borrow.)
Alternatives considered
idleTimeout/maxIdle— idle-based only; busy connections never recycle.- App-level recycling / scheduled process restarts — works but coarse and app-specific.
- RDS Proxy — helps failover / writer pooling, but its reader endpoint still pins a
connection to a single reader, so it doesn't rebalance reads after scaling.
Prior art
- HikariCP
maxLifetime(de-facto standard for JDBC pools). mysqljs/mysqlcommunity fork addingconnectionLifeTime.
I'm happy to open a PR with the implementation above + tests if maintainers are open to it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lib/pool_config.js, lib/pool_connection.js, lib/base/pool.js, and lib/connection_config.js to trace pool options, connection creation, borrowing, and validation. Add the agreed maxLifetime behavior with jitter and verify that expired free connections are recycled without invalid-option errors; the payload does not name a test file, so inspect existing pool tests first.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- mysql, node.js
- Domain
- database
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100