Skip to content

switch user back to pool default on connection release #1088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ This file is a manually maintained list of changes for each release. Feel free
to add your changes here when sending pull requests. Also send corrections if
you spot any mistakes.

## HEAD
## NEXT_MINOR

* Pool resets credentials to pool configuration on returned connection
instead of destroying after changeUser is called #837

## v2.7.0 (2015-05-05)

* Provide static require analysis

Expand Down
12 changes: 9 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ addition to those options pools accept a few extras:

### connection

The pool will emit a `connection` event when a new connection is made within the pool.
The pool will emit a `connection` event when a new connection is made within the pool.
If you need to set session variables on the connection before it gets used, you can
listen to the `connection` event.

Expand Down Expand Up @@ -459,7 +459,7 @@ poolCluster.getConnection('MASTER', function (err, connection) {});
// Target Group : SLAVE1-2, Selector : order
// If can't connect to SLAVE1, return SLAVE2. (remove SLAVE1 in the cluster)
poolCluster.on('remove', function (nodeId) {
console.log('REMOVED NODE : ' + nodeId); // nodeId = SLAVE1
console.log('REMOVED NODE : ' + nodeId); // nodeId = SLAVE1
});

poolCluster.getConnection('SLAVE*', 'ORDER', function (err, connection) {});
Expand All @@ -479,7 +479,7 @@ poolCluster.end(function (err) {

## PoolCluster Option
* `canRetry`: If `true`, `PoolCluster` will attempt to reconnect when connection fails. (Default: `true`)
* `removeNodeErrorCount`: If connection fails, node's `errorCount` increases.
* `removeNodeErrorCount`: If connection fails, node's `errorCount` increases.
When `errorCount` is greater than `removeNodeErrorCount`, remove a node in the `PoolCluster`. (Default: `5`)
* `restoreNodeTimeout`: If connection fails, specifies the number of milliseconds
before another connection attempt will be made. If set to `0`, then node will be
Expand Down Expand Up @@ -519,6 +519,12 @@ The available options for this feature are:
A sometimes useful side effect of this functionality is that this function also
resets any connection state (variables, transactions, etc.).

In the context of connection being returned to a Pool the connection will be
reset back to the defaults for the pool before the connection is released to
another `getConnection()` call. When this happens the pool will emit
`connection` to give the listener a chance to setup state on the connection
again since it will have been reset.

Errors encountered during this operation are treated as fatal connection errors
by this module.

Expand Down
27 changes: 25 additions & 2 deletions lib/Pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ Pool.prototype.releaseConnection = function releaseConnection(connection) {
}

if (connection._purge) {
// purge connection from pool
this._purgeConnection(connection);
// restore the switched user
this.restoreUser(connection, function (err) {
if (err) return this._purgeConnection(connection);
connection._pool.releaseConnection(connection);
});
return;
} else if (this._freeConnections.indexOf(connection) !== -1) {
// connection already in free connection pool
Expand Down Expand Up @@ -247,6 +250,26 @@ Pool.prototype._removeConnection = function(connection) {
this.releaseConnection(connection);
};

Pool.prototype.restoreUser = function restoreUser(connection, callback) {
var pool = this;

var poolConfig = pool.config.connectionConfig;
var config = connection.config;

// only when credentials have been changed from the pool defaults
if (config.user !== poolConfig.user || config.database !== poolConfig.database || config.password !== poolConfig.password || config.charset !== poolConfig.charset) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no such thing as config.charset, only config.charsetNumber.

connection.changeUser(poolConfig, function (err) {
if (err) return callback(err);
// emit as if it were a new connection so consumers can setup session state again
pool.emit('connection', connection);
callback(null);
});
connection._purge = false;
} else {
process.nextTick(callback);
}
}

Pool.prototype.escape = function(value) {
return mysql.escape(value, this.config.connectionConfig.stringifyObjects, this.config.connectionConfig.timezone);
};
Expand Down
3 changes: 2 additions & 1 deletion lib/PoolConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ inherits(PoolConnection, Connection);
function PoolConnection(pool, options) {
Connection.call(this, options);
this._pool = pool;
this._purge = false
this._purge = false;

// When a fatal error occurs the connection's protocol ends, which will cause
// the connection to end as well, thus we only need to watch for the end event
Expand All @@ -29,6 +29,7 @@ PoolConnection.prototype.changeUser = function changeUser(options, callback) {

PoolConnection.prototype.release = function release() {
var pool = this._pool;
var connection = this;

if (!pool || pool._closed) {
return;
Expand Down
35 changes: 30 additions & 5 deletions test/unit/pool/test-change-user-eject.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,29 @@ var pool = common.createPool({
var closed = 0;
var server = common.createFakeServer();
var thread = 0;
var connections = 0;

server.listen(common.fakeServerPort, function(err) {
assert.ifError(err);

pool.on('connection', function(conn) {
connections++;
});

var conn0;
var threadId;
pool.getConnection(function(err, conn) {
assert.ifError(err);
assert.ok(conn.threadId === 1 || conn.threadId === 2);
conn.query('SELECT CURRENT_USER()', function (err, rows) {
assert.ifError(err);
assert.strictEqual(rows[0]['CURRENT_USER()'].indexOf('user_1'), 0);
});
conn0 = conn;
threadId = conn.threadId;
});


pool.getConnection(function(err, conn) {
assert.ifError(err);
assert.ok(conn.threadId === 1 || conn.threadId === 2);
Expand All @@ -31,24 +41,39 @@ server.listen(common.fakeServerPort, function(err) {
conn.changeUser({user: 'user_2'}, function(err) {
assert.ifError(err);
assert.strictEqual(conn.threadId, threadId);
conn.query('SELECT CURRENT_USER()', function (err, rows) {
assert.ifError(err);
assert.strictEqual(rows[0]['CURRENT_USER()'].indexOf('user_2'), 0);
});
conn.release();
conn0.release();
});
});

pool.getConnection(function(err, conn1) {
assert.ifError(err);
assert.strictEqual(conn1.threadId, 3);
assert.ok(conn1.threadId === 1 || conn1.threadId === 2);
assert.strictEqual(conn1.config.user, 'user_1');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test needs to run the query SELECT CURRENT_USER() to be sure the server-side actually got the change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8989512

conn1.query('SELECT CURRENT_USER()', function (err, rows) {
assert.ifError(err);
assert.strictEqual(rows[0]['CURRENT_USER()'].indexOf('user_1'), 0);
});

pool.getConnection(function(err, conn2) {
assert.ifError(err);
assert.strictEqual(conn2.threadId, threadId);
conn1.release();
conn2.release();
assert.ok(conn2.threadId === 1 || conn2.threadId === 2);
assert.strictEqual(conn1.config.user, 'user_1');
conn2.query('SELECT CURRENT_USER()', function (err, rows) {
assert.ifError(err);
assert.strictEqual(rows[0]['CURRENT_USER()'].indexOf('user_1'), 0);
conn1.release();
conn2.release();
});

pool.end(function(err) {
assert.ifError(err);
assert.strictEqual(closed, 3);
assert.strictEqual(connections, 3);
assert.strictEqual(closed, 2);
server.destroy();
});
});
Expand Down