From c35b66c6272eccbace7bcd2340393e158d36a921 Mon Sep 17 00:00:00 2001 From: Donal Linehan Date: Thu, 16 Aug 2018 16:30:40 +0100 Subject: [PATCH 1/3] Use LIFO for connection retrieval from pool --- Readme.md | 6 ++- lib/Pool.js | 4 +- test/unit/pool/test-connection-cycling.js | 45 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 test/unit/pool/test-connection-cycling.js diff --git a/Readme.md b/Readme.md index 5af3ed261..adc736342 100644 --- a/Readme.md +++ b/Readme.md @@ -367,8 +367,10 @@ time one is needed. Connections are lazily created by the pool. If you configure the pool to allow up to 100 connections, but only ever use 5 simultaneously, only 5 connections -will be made. Connections are also cycled round-robin style, with connections -being taken from the top of the pool and returning to the bottom. +will be made. Connections are cycled on a last in first out basis, with connections +being taken from the top and returning to the top of the pool. This allows for +connections to be killed off by the server due to inactivity after `wait_timeout` +for the connection has been reached. When a previous connection is retrieved from the pool, a ping packet is sent to the server to check if the connection is still good. diff --git a/lib/Pool.js b/lib/Pool.js index 87a40114a..f30c34592 100644 --- a/lib/Pool.js +++ b/lib/Pool.js @@ -137,8 +137,8 @@ Pool.prototype.releaseConnection = function releaseConnection(connection) { // this won't catch all double-release cases throw new Error('Connection already released'); } else { - // add connection to end of free queue - this._freeConnections.push(connection); + // add connection to front of free queue + this._freeConnections.unshift(connection); this.emit('release', connection); } } diff --git a/test/unit/pool/test-connection-cycling.js b/test/unit/pool/test-connection-cycling.js new file mode 100644 index 000000000..a639af304 --- /dev/null +++ b/test/unit/pool/test-connection-cycling.js @@ -0,0 +1,45 @@ +var after = require('after'); +var assert = require('assert'); +var common = require('../../common'); +var pool = common.createPool({port: common.fakeServerPort}); + +var server = common.createFakeServer(); +var lastConnectionId = null; +var numberOfConnections = 4; + +server.listen(common.fakeServerPort, function (err) { + assert.ifError(err); + + var getConnectionAndEnsureItsTheLastConnection = function(cb) { + pool.getConnection(function (err, connection) { + assert.ifError(err); + assert.ok(connection.id === lastConnectionId); + connection.release(); + if (cb) cb(); + }); + }; + + var done = after(numberOfConnections, function () { + getConnectionAndEnsureItsTheLastConnection(function() { + getConnectionAndEnsureItsTheLastConnection(function() { + pool.end(function (err) { + assert.ifError(err); + server.destroy(); + }); + }); + }); + }); + + var counter = 1; + + var createIndexedConnection = function () { + pool.getConnection(function (err, connection) { + connection.id = counter++; + lastConnectionId = connection.id; + assert.ifError(err); + connection.release(); + done(); + }); + }; + Array.apply(null, {length: numberOfConnections}).map(createIndexedConnection); +}); From 3ae2c416b542e32d1ab05e52cc2d0fcf2360f1d8 Mon Sep 17 00:00:00 2001 From: Donal Linehan Date: Thu, 16 Aug 2018 17:13:35 +0100 Subject: [PATCH 2/3] Add roundRobinConnectionCycling option --- Readme.md | 8 ++-- lib/Pool.js | 6 ++- lib/PoolConfig.js | 4 ++ ...ing.js => test-connection-cycling-lifo.js} | 7 ++- .../test-connection-cycling-round-robin.js | 47 +++++++++++++++++++ 5 files changed, 65 insertions(+), 7 deletions(-) rename test/unit/pool/{test-connection-cycling.js => test-connection-cycling-lifo.js} (90%) create mode 100644 test/unit/pool/test-connection-cycling-round-robin.js diff --git a/Readme.md b/Readme.md index adc736342..a5cc74950 100644 --- a/Readme.md +++ b/Readme.md @@ -367,10 +367,7 @@ time one is needed. Connections are lazily created by the pool. If you configure the pool to allow up to 100 connections, but only ever use 5 simultaneously, only 5 connections -will be made. Connections are cycled on a last in first out basis, with connections -being taken from the top and returning to the top of the pool. This allows for -connections to be killed off by the server due to inactivity after `wait_timeout` -for the connection has been reached. +will be made. When a previous connection is retrieved from the pool, a ping packet is sent to the server to check if the connection is still good. @@ -393,6 +390,9 @@ constructor. In addition to those options pools accept a few extras: * `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`) +* `roundRobinConnectionCycling`: Whether the pool should cycle through connections + or not. If set to false, connections will be retrieved and returned to the pool + on a last in first out basis. (Default: `true`) ## Pool events diff --git a/lib/Pool.js b/lib/Pool.js index f30c34592..2750f5a71 100644 --- a/lib/Pool.js +++ b/lib/Pool.js @@ -136,8 +136,12 @@ Pool.prototype.releaseConnection = function releaseConnection(connection) { // connection already in free connection pool // this won't catch all double-release cases throw new Error('Connection already released'); + } else if (this.config.roundRobinConnectionCycling) { + // add connection to the bottom of the free queue + this._freeConnections.push(connection); + this.emit('release', connection); } else { - // add connection to front of free queue + // add connection to the top of the free queue this._freeConnections.unshift(connection); this.emit('release', connection); } diff --git a/lib/PoolConfig.js b/lib/PoolConfig.js index 8c5017a27..970fabdc1 100644 --- a/lib/PoolConfig.js +++ b/lib/PoolConfig.js @@ -20,6 +20,10 @@ function PoolConfig(options) { this.queueLimit = (options.queueLimit === undefined) ? 0 : Number(options.queueLimit); + this.roundRobinConnectionCycling = (options.roundRobinConnectionCycling === undefined) + ? true + : Boolean(options.roundRobinConnectionCycling); + } PoolConfig.prototype.newConnectionConfig = function newConnectionConfig() { diff --git a/test/unit/pool/test-connection-cycling.js b/test/unit/pool/test-connection-cycling-lifo.js similarity index 90% rename from test/unit/pool/test-connection-cycling.js rename to test/unit/pool/test-connection-cycling-lifo.js index a639af304..d3cdd706e 100644 --- a/test/unit/pool/test-connection-cycling.js +++ b/test/unit/pool/test-connection-cycling-lifo.js @@ -1,7 +1,10 @@ var after = require('after'); var assert = require('assert'); var common = require('../../common'); -var pool = common.createPool({port: common.fakeServerPort}); +var pool = common.createPool({ + port : common.fakeServerPort, + roundRobinConnectionCycling : false +}); var server = common.createFakeServer(); var lastConnectionId = null; @@ -34,9 +37,9 @@ server.listen(common.fakeServerPort, function (err) { var createIndexedConnection = function () { pool.getConnection(function (err, connection) { + assert.ifError(err); connection.id = counter++; lastConnectionId = connection.id; - assert.ifError(err); connection.release(); done(); }); diff --git a/test/unit/pool/test-connection-cycling-round-robin.js b/test/unit/pool/test-connection-cycling-round-robin.js new file mode 100644 index 000000000..36c53edc7 --- /dev/null +++ b/test/unit/pool/test-connection-cycling-round-robin.js @@ -0,0 +1,47 @@ +var after = require('after'); +var assert = require('assert'); +var common = require('../../common'); +var pool = common.createPool({port: common.fakeServerPort}); + +var server = common.createFakeServer(); +var numberOfConnections = 4; +var connectionIds = []; +var connectionNumber = 0; + +server.listen(common.fakeServerPort, function (err) { + assert.ifError(err); + + var getConnectionAndEnsureItsTheCorrectOne = function(cb) { + pool.getConnection(function (err, connection) { + assert.ifError(err); + assert.ok(connection.id === connectionIds[connectionNumber]); + connectionNumber++; + connection.release(); + if (cb) cb(); + }); + }; + + var done = after(numberOfConnections, function () { + getConnectionAndEnsureItsTheCorrectOne(function() { + getConnectionAndEnsureItsTheCorrectOne(function() { + pool.end(function (err) { + assert.ifError(err); + server.destroy(); + }); + }); + }); + }); + + var counter = 1; + + var createIndexedConnection = function () { + pool.getConnection(function (err, connection) { + assert.ifError(err); + connection.id = counter++; + connectionIds.push(connection.id); + connection.release(); + done(); + }); + }; + Array.apply(null, {length: numberOfConnections}).map(createIndexedConnection); +}); From 6172ea68c73f54d498d6b7dab890b12f98bf5e6b Mon Sep 17 00:00:00 2001 From: Donal Linehan Date: Thu, 16 Aug 2018 17:27:18 +0100 Subject: [PATCH 3/3] Fix for Node 0.6 --- test/unit/pool/test-connection-cycling-lifo.js | 2 +- test/unit/pool/test-connection-cycling-round-robin.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/pool/test-connection-cycling-lifo.js b/test/unit/pool/test-connection-cycling-lifo.js index d3cdd706e..a910a99d0 100644 --- a/test/unit/pool/test-connection-cycling-lifo.js +++ b/test/unit/pool/test-connection-cycling-lifo.js @@ -44,5 +44,5 @@ server.listen(common.fakeServerPort, function (err) { done(); }); }; - Array.apply(null, {length: numberOfConnections}).map(createIndexedConnection); + Array.apply(null, Array(numberOfConnections)).map(createIndexedConnection); }); diff --git a/test/unit/pool/test-connection-cycling-round-robin.js b/test/unit/pool/test-connection-cycling-round-robin.js index 36c53edc7..b4d37e45c 100644 --- a/test/unit/pool/test-connection-cycling-round-robin.js +++ b/test/unit/pool/test-connection-cycling-round-robin.js @@ -43,5 +43,5 @@ server.listen(common.fakeServerPort, function (err) { done(); }); }; - Array.apply(null, {length: numberOfConnections}).map(createIndexedConnection); + Array.apply(null, Array(numberOfConnections)).map(createIndexedConnection); });