Skip to content

Commit eb58eef

Browse files
committed
Fix pool.end race conditions
fixes #915
1 parent dc0d600 commit eb58eef

File tree

5 files changed

+125
-10
lines changed

5 files changed

+125
-10
lines changed

Changes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This file is a manually maintained list of changes for each release. Feel free
44
to add your changes here when sending pull requests. Also send corrections if
55
you spot any mistakes.
66

7+
## HEAD
8+
9+
* Fix `pool.end` race conditions #915
10+
711
## v2.5.0 (2014-09-07)
812

913
* Add code `POOL_ENQUEUELIMIT` to error reaching `queueLimit`

lib/Pool.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Pool.prototype.getConnection = function (cb) {
2727
}
2828

2929
var connection;
30+
var pool = this;
3031

3132
if (this._freeConnections.length > 0) {
3233
connection = this._freeConnections.shift();
@@ -39,17 +40,25 @@ Pool.prototype.getConnection = function (cb) {
3940

4041
this._allConnections.push(connection);
4142

43+
connection._pool = null;
4244
return connection.connect({timeout: this.config.acquireTimeout}, function (err) {
43-
if (this._closed) {
44-
return cb(new Error('Pool is closed.'));
45+
if (pool._closed) {
46+
connection.destroy();
47+
pool._removeConnection(connection);
48+
cb(new Error('Pool is closed.'));
49+
return;
4550
}
51+
4652
if (err) {
47-
return cb(err);
53+
pool._removeConnection(connection);
54+
cb(err);
55+
return;
4856
}
4957

50-
this.emit('connection', connection);
51-
return cb(null, connection);
52-
}.bind(this));
58+
connection._pool = pool;
59+
pool.emit('connection', connection);
60+
cb(null, connection);
61+
});
5362
}
5463

5564
if (!this.config.waitForConnections) {
@@ -70,13 +79,20 @@ Pool.prototype.acquireConnection = function acquireConnection(connection, cb) {
7079

7180
connection._pool = null;
7281
connection.ping({timeout: this.config.acquireTimeout}, function(err) {
73-
if (!err) {
82+
if (!err && !pool._closed) {
7483
connection._pool = pool;
7584
cb(null, connection);
7685
return;
7786
}
7887

7988
connection.destroy();
89+
90+
if (pool._closed) {
91+
pool._removeConnection(connection);
92+
cb(new Error('Pool is closed.'));
93+
return;
94+
}
95+
8096
pool._connectionQueue.unshift(cb);
8197
pool._removeConnection(connection);
8298
});
@@ -136,7 +152,7 @@ Pool.prototype.end = function (cb) {
136152
return;
137153
}
138154

139-
if (err || ++closedConnections >= this._allConnections.length) {
155+
if (err || this._allConnections.length === 0) {
140156
calledBack = true;
141157
return cb(err);
142158
}
@@ -148,10 +164,19 @@ Pool.prototype.end = function (cb) {
148164

149165
while (this._allConnections.length) {
150166
connection = this._allConnections[0];
151-
connection._pool = null;
152-
connection._realEnd(endCB);
167+
168+
if (connection._pool === this) {
169+
closedConnections++;
170+
connection._pool = null;
171+
connection._realEnd(endCB);
172+
}
173+
153174
this._removeConnection(connection);
154175
}
176+
177+
if (closedConnections === 0) {
178+
return process.nextTick(endCB);
179+
}
155180
};
156181

157182
Pool.prototype.query = function (sql, values, cb) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
var assert = require('assert');
2+
var common = require('../../common');
3+
var cluster = common.createPoolCluster();
4+
var server = common.createFakeServer();
5+
6+
var poolConfig1 = common.getTestConfig({port: common.bogusPort});
7+
var poolConfig2 = common.getTestConfig({port: common.fakeServerPort});
8+
cluster.add('SLAVE1', poolConfig1);
9+
cluster.add('SLAVE2', poolConfig2);
10+
11+
server.listen(common.fakeServerPort, function(err) {
12+
assert.ifError(err);
13+
14+
var pool = cluster.of('SLAVE*', 'ORDER');
15+
var wait = 2;
16+
17+
pool.getConnection(function (err, connection) {
18+
assert.ifError(err);
19+
assert.strictEqual(connection._clusterId, 'SLAVE2');
20+
if (!--wait) server.destroy();
21+
});
22+
23+
pool.getConnection(function (err, connection) {
24+
assert.ifError(err);
25+
assert.strictEqual(connection._clusterId, 'SLAVE2');
26+
if (!--wait) server.destroy();
27+
});
28+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
var assert = require('assert');
2+
var common = require('../../common');
3+
var pool = common.createPool({
4+
connectionLimit : 1,
5+
port : common.bogusPort
6+
});
7+
8+
pool.getConnection(function (err, conn) {
9+
assert.ok(err);
10+
assert.ok(err.fatal);
11+
assert.equal(err.code, 'ECONNREFUSED');
12+
13+
pool.end(function (err) {
14+
assert.ifError(err);
15+
});
16+
});

test/unit/pool/test-end-ping.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
var common = require('../../common');
2+
var assert = require('assert');
3+
var pool = common.createPool({
4+
connectionLimit : 1,
5+
port : common.fakeServerPort,
6+
queueLimit : 5,
7+
waitForConnections : true
8+
});
9+
10+
var conn1Err = null;
11+
var conn2Err = null;
12+
var poolEnded = false;
13+
var server = common.createFakeServer();
14+
15+
server.listen(common.fakeServerPort, function (err) {
16+
assert.ifError(err);
17+
18+
pool.getConnection(function (err, conn) {
19+
assert.ifError(err);
20+
conn.release();
21+
22+
pool.getConnection(function (err, conn) {
23+
assert.ok(err);
24+
assert.equal(err.message, 'Pool is closed.');
25+
});
26+
27+
pool.end(function (err) {
28+
assert.ifError(err);
29+
server.destroy();
30+
});
31+
});
32+
});
33+
34+
server.on('connection', function (conn) {
35+
conn.handshake();
36+
conn.on('ping', function () {
37+
setTimeout(function () {
38+
conn._sendPacket(new common.Packets.OkPacket());
39+
conn._parser.resetPacketNumber();
40+
}, 100);
41+
});
42+
});

0 commit comments

Comments
 (0)