Skip to content

Commit

Permalink
fixed bugs
Browse files Browse the repository at this point in the history
add memory test and load test
  • Loading branch information
ben-page committed Nov 10, 2014
1 parent d1e0831 commit 2ecd058
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
63 changes: 48 additions & 15 deletions lib/connection-pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,51 @@ 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);
}

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;
Expand All @@ -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;
}
Expand All @@ -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--) {
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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;
2 changes: 1 addition & 1 deletion test/connection-pool.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('ConnectionPool', function () {
for (var i = 0; i < count; i++) {
setTimeout(function() {
pool.acquire(createRequest);
})
}, 1);
}
});

Expand Down
57 changes: 57 additions & 0 deletions test/load-test.js
Original file line number Diff line number Diff line change
@@ -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);
}
28 changes: 28 additions & 0 deletions test/memory-usage.js
Original file line number Diff line number Diff line change
@@ -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
});

0 comments on commit 2ecd058

Please sign in to comment.