From ad2eaac10d737d7d9d7f27af78a96bbc829b45d5 Mon Sep 17 00:00:00 2001 From: yq <201688516@qq.com> Date: Fri, 16 Jul 2021 20:54:41 +0800 Subject: [PATCH] release idle connections after timeout --- .gitignore | 2 + Readme.md | 4 ++ lib/Pool.js | 23 ++++++++- lib/PoolConfig.js | 6 +++ lib/PoolConnection.js | 6 +++ .../unit/pool/test-idle-connection-timeout.js | 47 +++++++++++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/unit/pool/test-idle-connection-timeout.js diff --git a/.gitignore b/.gitignore index cfdb519e7..1e9ed59ed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ coverage/ node_modules/ npm-debug.log +.vscode +.idea diff --git a/Readme.md b/Readme.md index 0b765a60c..91de672bf 100644 --- a/Readme.md +++ b/Readme.md @@ -399,6 +399,10 @@ constructor. In addition to those options pools accept a few extras: pool will immediately call back with an error. (Default: `true`) * `connectionLimit`: The maximum number of connections to create at once. (Default: `10`) +* `maxIdle`: The maximum number of idle connections. + (Default: `connectionLimit`) +* `idleTimeout`: The maximum idle time of a connection in the pool, in milliseconds. + (Default: `60000`) * `queueLimit`: The maximum number of connection requests the pool will queue before returning an error from `getConnection`. If set to `0`, there is no limit to the number of queued connection requests. (Default: `0`) diff --git a/lib/Pool.js b/lib/Pool.js index 87a40114a..733324375 100644 --- a/lib/Pool.js +++ b/lib/Pool.js @@ -17,6 +17,10 @@ function Pool(options) { this._freeConnections = []; this._connectionQueue = []; this._closed = false; + if (this.config.maxIdle < this.config.connectionLimit) { + // create idle connection timeout automatically release job + this._removeIdleTimeoutConnections(); + } } Pool.prototype.getConnection = function (cb) { @@ -34,7 +38,7 @@ Pool.prototype.getConnection = function (cb) { var pool = this; if (this._freeConnections.length > 0) { - connection = this._freeConnections.shift(); + connection = this._freeConnections.pop(); this.acquireConnection(connection, cb); return; } @@ -277,6 +281,23 @@ Pool.prototype._removeConnection = function(connection) { this.releaseConnection(connection); }; +Pool.prototype._removeIdleTimeoutConnections = function() { + if (this._removeIdleTimeoutConnectionsTimer) { + clearTimeout(this._removeIdleTimeoutConnectionsTimer); + } + + var _this = this; + this._removeIdleTimeoutConnectionsTimer = setTimeout(function () { + try { + while (_this._freeConnections.length > _this.config.maxIdle && Date.now() - _this._freeConnections[0].lastActiveTime > _this.config.idleTimeout) { + _this._purgeConnection(_this._freeConnections[0]); + } + } finally { + _this._removeIdleTimeoutConnections(); + } + }, 1000); +}; + Pool.prototype.escape = function(value) { return mysql.escape(value, this.config.connectionConfig.stringifyObjects, this.config.connectionConfig.timezone); }; diff --git a/lib/PoolConfig.js b/lib/PoolConfig.js index 8c5017a27..6defca3ef 100644 --- a/lib/PoolConfig.js +++ b/lib/PoolConfig.js @@ -17,6 +17,12 @@ function PoolConfig(options) { this.connectionLimit = (options.connectionLimit === undefined) ? 10 : Number(options.connectionLimit); + this.maxIdle = (options.maxIdle === undefined) + ? this.connectionLimit + : Number(options.maxIdle); + this.idleTimeout = (options.idleTimeout === undefined) + ? 60000 + : Number(options.idleTimeout); this.queueLimit = (options.queueLimit === undefined) ? 0 : Number(options.queueLimit); diff --git a/lib/PoolConnection.js b/lib/PoolConnection.js index 064c99d32..620f1d21e 100644 --- a/lib/PoolConnection.js +++ b/lib/PoolConnection.js @@ -9,6 +9,9 @@ function PoolConnection(pool, options) { Connection.call(this, options); this._pool = pool; + // The last active time of this connection + this.lastActiveTime = Date.now(); + // Bind connection to pool domain if (Events.usingDomains) { this.domain = pool.domain; @@ -32,6 +35,9 @@ PoolConnection.prototype.release = function release() { return undefined; } + // update last active time + this.lastActiveTime = Date.now(); + return pool.releaseConnection(this); }; diff --git a/test/unit/pool/test-idle-connection-timeout.js b/test/unit/pool/test-idle-connection-timeout.js new file mode 100644 index 000000000..06807b2bc --- /dev/null +++ b/test/unit/pool/test-idle-connection-timeout.js @@ -0,0 +1,47 @@ +var assert = require('assert'); +var common = require('../../common'); +var pool = common.createPool({ + port : common.fakeServerPort, + connectionLimit : 10, + maxIdle : 1, + idleTimeout : 5000 +}); + +var server = common.createFakeServer(); + +server.listen(common.fakeServerPort, function(err){ + assert.ifError(err); + + pool.once('release', function(connection) { + assert.ok(connection); + }); + + pool.getConnection(function (err, connection1) { + assert.ifError(err); + assert.ok(connection1); + pool.getConnection(function (err, connection2) { + assert.ifError(err); + assert.ok(connection2); + assert.notEqual(connection1, connection2); + connection1.release(); + connection2.release(); + assert.equal(pool._allConnections.length, 2); + assert.equal(pool._freeConnections.length, 2); + setTimeout(function() { + assert.equal(pool._allConnections.length, 1); + assert.equal(pool._freeConnections.length, 1); + pool.getConnection(function (err, connection3) { + assert.ifError(err); + assert.ok(connection3); + assert.equal(connection3, connection2); + assert.equal(pool._allConnections.length, 1); + assert.equal(pool._freeConnections.length, 0); + connection3.release(); + connection3.destroy(); + server.destroy(); + setTimeout(function () { process.exit(0); }, 1000); + }); + }, 7000); + }); + }); +});