Skip to content

Commit e72247f

Browse files
feat: gracefully end pool connections #3148 (#3776)
* feat: add gracefulEnd connection config in order to allow pool connections to be gracefully closed (#3148) * fix: website lint error * test: split graceful end tests into separate files and use process.exit in assertions --------- Co-authored-by: Weslley Araújo <46850407+wellwelwel@users.noreply.github.com>
1 parent 349f870 commit e72247f

File tree

10 files changed

+172
-1
lines changed

10 files changed

+172
-1
lines changed

lib/base/pool.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,11 @@ class BasePool extends EventEmitter {
200200
Date.now() - this._freeConnections.get(0).lastActiveTime >
201201
this.config.idleTimeout)
202202
) {
203-
this._freeConnections.get(0).destroy();
203+
if (this.config.connectionConfig.gracefulEnd) {
204+
this._freeConnections.get(0).end();
205+
} else {
206+
this._freeConnections.get(0).destroy();
207+
}
204208
}
205209
} finally {
206210
this._removeIdleTimeoutConnections();

lib/base/pool_connection.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class BasePoolConnection extends BaseConnection {
3030
}
3131

3232
end() {
33+
if (this.config.gracefulEnd) {
34+
this._removeFromPool();
35+
super.end();
36+
37+
return;
38+
}
39+
3340
const err = new Error(
3441
'Calling conn.end() to release a pooled connection is ' +
3542
'deprecated. In next version calling conn.end() will be ' +

lib/connection_config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const validOptions = {
6868
queueLimit: 1,
6969
waitForConnections: 1,
7070
jsonStrings: 1,
71+
gracefulEnd: 1,
7172
};
7273

7374
class ConnectionConfig {
@@ -190,6 +191,7 @@ class ConnectionConfig {
190191
};
191192
this.maxPreparedStatements = options.maxPreparedStatements || 16000;
192193
this.jsonStrings = options.jsonStrings || false;
194+
this.gracefulEnd = options.gracefulEnd || false;
193195
}
194196

195197
static mergeFlags(default_flags, user_flags) {

test/common.test.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ exports.getConfig = function (input) {
145145
idleTimeout: args && args.idleTimeout,
146146
jsonStrings: args && args.jsonStrings,
147147
disableEval,
148+
gracefulEnd: args && args.gracefulEnd,
148149
};
149150
return params;
150151
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
const createPool = require('../../common.test.cjs').createPool;
3+
const { assert } = require('poku');
4+
5+
/**
6+
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
7+
*
8+
* @see https://github.com/sidorares/node-mysql2/issues/3148
9+
*/
10+
11+
/**
12+
* By default, the end method of a pooled connection will just release it back to the pool.
13+
* This is compatibility behavior with mysqljs/mysql.
14+
*/
15+
const pool = new createPool();
16+
let warningEmitted = false;
17+
18+
pool.getConnection((_err1, connection) => {
19+
connection.on('warn', (warning) => {
20+
warningEmitted = true;
21+
assert(
22+
warning.message.startsWith(
23+
'Calling conn.end() to release a pooled connection is deprecated'
24+
)
25+
);
26+
});
27+
28+
connection.end();
29+
pool.end();
30+
});
31+
32+
process.on('exit', () => {
33+
assert(warningEmitted, 'Warning should be emitted');
34+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
const createPool = require('../../common.test.cjs').createPool;
3+
const { assert } = require('poku');
4+
5+
/**
6+
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
7+
*
8+
* @see https://github.com/sidorares/node-mysql2/issues/3148
9+
*/
10+
11+
/**
12+
* By providing gracefulEnd when creating the pool, the end method of a pooled connection
13+
* will actually close the connection instead of releasing it back to the pool.
14+
*/
15+
const pool = new createPool({ gracefulEnd: true });
16+
let warningEmitted = false;
17+
18+
pool.getConnection((_err1, connection) => {
19+
connection.on('warn', () => {
20+
warningEmitted = true;
21+
});
22+
23+
connection.end();
24+
pool.end();
25+
});
26+
27+
process.on('exit', () => {
28+
assert(!warningEmitted, 'Warning should not be emitted');
29+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
const createPool = require('../../common.test.cjs').createPool;
3+
const { assert } = require('poku');
4+
5+
/**
6+
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
7+
*
8+
* @see https://github.com/sidorares/node-mysql2/issues/3148
9+
*/
10+
11+
const pool = new createPool({
12+
connectionLimit: 2,
13+
maxIdle: 1,
14+
idleTimeout: 500,
15+
debug: true,
16+
});
17+
18+
let quitCommandReceived = false;
19+
const originalLog = console.log;
20+
console.log = (message) => {
21+
if (message === 'Add command: Quit') {
22+
quitCommandReceived = true;
23+
}
24+
};
25+
26+
pool.getConnection((_err1, connection1) => {
27+
pool.getConnection((_err2, connection2) => {
28+
connection1.release();
29+
connection2.release();
30+
31+
setTimeout(() => {
32+
pool.end();
33+
}, 2000);
34+
});
35+
});
36+
37+
process.on('exit', () => {
38+
assert(!quitCommandReceived, 'quit command should not have been received');
39+
console.log = originalLog;
40+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
const createPool = require('../../common.test.cjs').createPool;
3+
const { assert } = require('poku');
4+
5+
/**
6+
* This test case tests that the pool releases connections gracefully after the idle timeout has passed.
7+
*
8+
* @see https://github.com/sidorares/node-mysql2/issues/3148
9+
*/
10+
11+
const pool = new createPool({
12+
connectionLimit: 2,
13+
maxIdle: 1,
14+
idleTimeout: 500,
15+
debug: true,
16+
gracefulEnd: true,
17+
});
18+
19+
let quitCommandReceived = false;
20+
const originalLog = console.log;
21+
console.log = (message) => {
22+
if (message === 'Add command: Quit') {
23+
quitCommandReceived = true;
24+
}
25+
};
26+
27+
pool.getConnection((_err1, connection1) => {
28+
pool.getConnection((_err2, connection2) => {
29+
connection1.release();
30+
connection2.release();
31+
32+
setTimeout(() => {
33+
pool.end();
34+
}, 2000);
35+
});
36+
});
37+
38+
process.on('exit', () => {
39+
assert(quitCommandReceived, 'quit command should have been received');
40+
console.log = originalLog;
41+
});

typings/mysql/lib/Connection.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ export interface ConnectionOptions {
335335
* (Default: false)
336336
*/
337337
jsonStrings?: boolean;
338+
339+
gracefulEnd?: boolean;
338340
}
339341

340342
declare class Connection extends QueryableBase(ExecutableBase(EventEmitter)) {

website/docs/documentation/extras.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,14 @@ const pool = mysql.createPool({
130130
```
131131

132132
In addition to password `createConnection()`, `createPool()` and `changeUser()` accept `passwordSha1` option. This is useful when implementing proxies as plaintext password might be not available.
133+
134+
## Graceful pool connection end
135+
136+
```js
137+
const pool = mysql.createPool({
138+
gracefulEnd: true,
139+
});
140+
```
141+
142+
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.
143+
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.

0 commit comments

Comments
 (0)