From 2ecd0587370709e86bb0dd010321c665eb8f9848 Mon Sep 17 00:00:00 2001 From: Ben Page Date: Mon, 10 Nov 2014 16:47:57 -0600 Subject: [PATCH] fixed bugs add memory test and load test --- README.md | 3 +- lib/connection-pool.js | 63 +++++++++++++++++++++++++++--------- test/connection-pool.test.js | 2 +- test/load-test.js | 57 ++++++++++++++++++++++++++++++++ test/memory-usage.js | 28 ++++++++++++++++ 5 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 test/load-test.js create mode 100644 test/memory-usage.js diff --git a/README.md b/README.md index 13cd318..7e77f98 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,9 @@ When the connection is released it is returned to the pool and is available to b * `poolConfig` {Object} the pool configuration object * `min` {Number} The minimun of connections there can be in the pool. Default = `10` * `max` {Number} The maximum number of connections there can be in the pool. Default = `50` - * `idleTimeout` {Number} The number of milliseconds before closing an unused connection. Default = `30000` + * `idleTimeout` {Number} The number of milliseconds before closing an unused connection. Default = `300000` * `retryDelay` {Number} The number of milliseconds to wait after a connection fails, before trying again. Default = `5000` + * `log` {Boolean|Function} Set to true to have debug log written to the console or pass a function to receive the log messages. Default = `undefined` * `connectionConfig` {Object} The same configuration that would be used to [create a tedious Connection](http://pekim.github.com/tedious/api-connection.html#function_newConnection). diff --git a/lib/connection-pool.js b/lib/connection-pool.js index 33bb5df..45b97bf 100644 --- a/lib/connection-pool.js +++ b/lib/connection-pool.js @@ -19,9 +19,22 @@ function ConnectionPool(poolConfig, connectionConfig) { this.max = poolConfig.max || 50; this.min = poolConfig.min || 10; - this.idleTimeout = poolConfig.idleTimeout || poolConfig.idletimeoutMillis || 30000; //5 min + this.idleTimeout = poolConfig.idleTimeout || poolConfig.idletimeoutMillis || 300000; //5 min this.retryDelay = poolConfig.retryDelay || 5000; - this.log = poolConfig.log; + + if (poolConfig.log) { + if (Object.prototype.toString.call(poolConfig.log) == '[object Function]') + this.log = poolConfig.log; + else { + this.log = function(text) { + console.log('Tedious-Connection-Pool: ' + text); + }; + } + } else { + this.log = function() {}; + } + + this.drained = false; setTimeout(fill.bind(this), 4); } @@ -29,20 +42,28 @@ function ConnectionPool(poolConfig, connectionConfig) { util.inherits(ConnectionPool, EventEmitter); function createConnection(pooled) { - if (this.connections === undefined) //pool has been drained + if (this.drained) //pool has been drained return; var self = this; + + this.log('creating connection'); var connection = new Connection(this.connectionConfig); connection.pool = this; - if (!pooled) + if (pooled) { + pooled.con = connection; + pooled.status = PENDING; + } else { pooled = { con: connection, status: PENDING }; - this.connections.push(pooled); + + this.connections.push(pooled); + } var handleError = function(err) { + self.log('connection closing because of error'); self.emit('error', err); pooled.status = RETRY; @@ -54,7 +75,9 @@ function createConnection(pooled) { }; connection.on('connect', function (err) { - if (self.connections === undefined) { //pool has been drained + self.log('connection connected'); + if (self.drained) { //pool has been drained + self.log('connection closing because pool is drained'); connection.close(); return; } @@ -66,15 +89,16 @@ function createConnection(pooled) { var callback = self.waitingForConnection.shift(); if (callback !== undefined) - setUsed.call(this, pooled, callback); + setUsed.call(self, pooled, callback); else - setFree.call(this, pooled); + setFree.call(self, pooled); }); connection.on('error', handleError); connection.on('end', function () { - if (self.connections === undefined) //pool has been drained + self.log('connection ended'); + if (self.drained) //pool has been drained return; for (var i = self.connections.length - 1; i >= 0; i--) { @@ -88,7 +112,7 @@ function createConnection(pooled) { } function fill() { - if (this.connections === undefined) //pool has been drained + if (this.drained) //pool has been drained return; var available = 0; @@ -106,13 +130,16 @@ function fill() { this.min - this.connections.length, //amount to create to reach min amount); + if (amount > 0) + this.log('filling ' + amount); + for (i = 0; i < amount; i++) { createConnection.call(this); } } ConnectionPool.prototype.acquire = function (callback) { - if (this.connections === undefined) //pool has been drained + if (this.drained) //pool has been drained return; var free; @@ -144,14 +171,17 @@ function setUsed(pooled, callback) { } function setFree(pooled) { + var self = this; + pooled.status = FREE; pooled.timeout = setTimeout(function() { + self.log('closing idle connection'); pooled.con.close(); }, this.idleTimeout); } ConnectionPool.prototype.release = function(connection) { - if (this.connections === undefined) //pool has been drained + if (this.drained) //pool has been drained return; var callback = this.waitingForConnection.shift(); @@ -170,14 +200,17 @@ ConnectionPool.prototype.release = function(connection) { }; ConnectionPool.prototype.drain = function () { - if (this.connections === undefined) //pool has been drained + this.log('draining pool'); + if (this.drained) //pool has been drained return; + this.drained = true; + for (var i = this.connections.length - 1; i >= 0; i--) this.connections[i].con.close(); - this.connections = undefined; - this.waitingForConnection = undefined; + this.connections = null; + this.waitingForConnection = null; }; module.exports = ConnectionPool; diff --git a/test/connection-pool.test.js b/test/connection-pool.test.js index 442c629..7dd9538 100644 --- a/test/connection-pool.test.js +++ b/test/connection-pool.test.js @@ -78,7 +78,7 @@ describe('ConnectionPool', function () { for (var i = 0; i < count; i++) { setTimeout(function() { pool.acquire(createRequest); - }) + }, 1); } }); diff --git a/test/load-test.js b/test/load-test.js new file mode 100644 index 0000000..4bd490c --- /dev/null +++ b/test/load-test.js @@ -0,0 +1,57 @@ +var ConnectionPool = require('../lib/connection-pool'); +var Request = require('tedious').Request; + +var poolConfig = {min: 20, max: 100, log: true}; +var pool = new ConnectionPool(poolConfig, { + userName: 'test', + password: 'test', + server: 'dev1' +}); + +var clients = 1000; +var connections = 1000; +var total = clients * connections; + +var c = 0; +var p = 0; + +var createRequest = function (connection) { + if (c >= total) + return; + + var request = new Request('select 42', function () { + connection.release(); + + c++; + var m = Math.round(c / total * 100); + if (m > p) { + p = m; + console.log(p); + } + + //console.log(c); + if (c === total - 1) { + console.log('done'); + pool.drain(); + + setTimeout(function() { + process.exit(); + }, 10000); + return; + } + + setTimeout(function() { + pool.acquire(createRequest); + }, 0); + }); + + request.on('row', function (columns) { + //console.log(columns[0].value); + }); + + connection.execSql(request); +}; + +for (var i = 0; i < clients; i++) { + pool.acquire(createRequest); +} diff --git a/test/memory-usage.js b/test/memory-usage.js new file mode 100644 index 0000000..e9b4323 --- /dev/null +++ b/test/memory-usage.js @@ -0,0 +1,28 @@ +//test for memory leak on bad connections +var ConnectionPool = require('../lib/connection-pool'); +var fs = require('fs'); + +var count = 0; +var mem = []; +var groupCount = 10; +var poolSize = 1000; + +var pool = new ConnectionPool({ max: poolSize, min: poolSize, retryDelay: 1}, { + userName: 'testLogin', + password: 'wrongPassword', + server: 'localhost' +}); + +pool.on('error', function() { + var i = Math.floor(count++ / poolSize); + if (i === groupCount) { + var previous = 0; + for (var f = 0; f < groupCount; f++) { + var size = (mem[f] / poolSize); + fs.writeSync(1, ((f+1) * poolSize) + ': ' + Math.round(mem[f] / poolSize * 100) + 'KB\n'); + previous = size; + } + process.exit(0); + } + mem[i] = (mem[i] || 0) + (process.memoryUsage().heapUsed / 1000); //kilobytes +}); \ No newline at end of file