Skip to content
Merged
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
6 changes: 5 additions & 1 deletion lib/base/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,11 @@ class BasePool extends EventEmitter {
Date.now() - this._freeConnections.get(0).lastActiveTime >
this.config.idleTimeout)
) {
this._freeConnections.get(0).destroy();
if (this.config.connectionConfig.gracefulEnd) {
this._freeConnections.get(0).end();
} else {
this._freeConnections.get(0).destroy();
}
}
} finally {
this._removeIdleTimeoutConnections();
Expand Down
7 changes: 7 additions & 0 deletions lib/base/pool_connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ class BasePoolConnection extends BaseConnection {
}

end() {
if (this.config.gracefulEnd) {
this._removeFromPool();
super.end();

return;
}

const err = new Error(
'Calling conn.end() to release a pooled connection is ' +
'deprecated. In next version calling conn.end() will be ' +
Expand Down
2 changes: 2 additions & 0 deletions lib/connection_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const validOptions = {
queueLimit: 1,
waitForConnections: 1,
jsonStrings: 1,
gracefulEnd: 1,
};

class ConnectionConfig {
Expand Down Expand Up @@ -190,6 +191,7 @@ class ConnectionConfig {
};
this.maxPreparedStatements = options.maxPreparedStatements || 16000;
this.jsonStrings = options.jsonStrings || false;
this.gracefulEnd = options.gracefulEnd || false;
}

static mergeFlags(default_flags, user_flags) {
Expand Down
1 change: 1 addition & 0 deletions test/common.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ exports.getConfig = function (input) {
idleTimeout: args && args.idleTimeout,
jsonStrings: args && args.jsonStrings,
disableEval,
gracefulEnd: args && args.gracefulEnd,
};
return params;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';
const createPool = require('../../common.test.cjs').createPool;
const { assert } = require('poku');

/**
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
*
* @see https://github.com/sidorares/node-mysql2/issues/3148
*/

/**
* By default, the end method of a pooled connection will just release it back to the pool.
* This is compatibility behavior with mysqljs/mysql.
*/
const pool = new createPool();
let warningEmitted = false;

pool.getConnection((_err1, connection) => {
connection.on('warn', (warning) => {
warningEmitted = true;
assert(
warning.message.startsWith(
'Calling conn.end() to release a pooled connection is deprecated'
)
);
});

connection.end();
pool.end();
});

process.on('exit', () => {
assert(warningEmitted, 'Warning should be emitted');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';
const createPool = require('../../common.test.cjs').createPool;
const { assert } = require('poku');

/**
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
*
* @see https://github.com/sidorares/node-mysql2/issues/3148
*/

/**
* By providing gracefulEnd when creating the pool, the end method of a pooled connection
* will actually close the connection instead of releasing it back to the pool.
*/
const pool = new createPool({ gracefulEnd: true });
let warningEmitted = false;

pool.getConnection((_err1, connection) => {
connection.on('warn', () => {
warningEmitted = true;
});

connection.end();
pool.end();
});

process.on('exit', () => {
assert(!warningEmitted, 'Warning should not be emitted');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';
const createPool = require('../../common.test.cjs').createPool;
const { assert } = require('poku');

/**
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
*
* @see https://github.com/sidorares/node-mysql2/issues/3148
*/

const pool = new createPool({
connectionLimit: 2,
maxIdle: 1,
idleTimeout: 500,
debug: true,
});

let quitCommandReceived = false;
const originalLog = console.log;
console.log = (message) => {
if (message === 'Add command: Quit') {
quitCommandReceived = true;
}
};

pool.getConnection((_err1, connection1) => {
pool.getConnection((_err2, connection2) => {
connection1.release();
connection2.release();

setTimeout(() => {
pool.end();
}, 2000);
});
});

process.on('exit', () => {
assert(!quitCommandReceived, 'quit command should not have been received');
console.log = originalLog;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';
const createPool = require('../../common.test.cjs').createPool;
const { assert } = require('poku');

/**
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
*
* @see https://github.com/sidorares/node-mysql2/issues/3148
*/

const pool = new createPool({
connectionLimit: 2,
maxIdle: 1,
idleTimeout: 500,
debug: true,
gracefulEnd: true,
});

let quitCommandReceived = false;
const originalLog = console.log;
console.log = (message) => {
if (message === 'Add command: Quit') {
quitCommandReceived = true;
}
};

pool.getConnection((_err1, connection1) => {
pool.getConnection((_err2, connection2) => {
connection1.release();
connection2.release();

setTimeout(() => {
pool.end();
}, 2000);
});
});

process.on('exit', () => {
assert(quitCommandReceived, 'quit command should have been received');
console.log = originalLog;
});
2 changes: 2 additions & 0 deletions typings/mysql/lib/Connection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ export interface ConnectionOptions {
* (Default: false)
*/
jsonStrings?: boolean;

gracefulEnd?: boolean;
}

declare class Connection extends QueryableBase(ExecutableBase(EventEmitter)) {
Expand Down
11 changes: 11 additions & 0 deletions website/docs/documentation/extras.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,14 @@ const pool = mysql.createPool({
```

In addition to password `createConnection()`, `createPool()` and `changeUser()` accept `passwordSha1` option. This is useful when implementing proxies as plaintext password might be not available.

## Graceful pool connection end

```js
const pool = mysql.createPool({
gracefulEnd: true,
});
```

By default, calling end on a pool connection will result in a warning being emitted and the connection being released back to the pool. This is for compatibility with mysqljs/mysql.
By providing `gracefulEnd: true` when creating the pool, calling end on a pooled connection will actually close the connection instead of releasing it back to the pool.
Loading